Merge pull request #4951 from deepseek-harness/fix/teammate-lead-guidance
fix(agent-team): explain teammate discovery and messaging
This commit is contained in:
commit
7c296b3f08
7 files changed
+29
-13
No files matched your search
@@ -189,9 +189,9 @@ class TeamFixtureAdapter extends LlmAdapter {
|
||||
}
|
||||
const chunks = identity === 'TEAM_WORKFLOW_CHILD'
|
||||
? textChunks('Fresh workflow child complete.')
|
||||
: identity === '<system-reminder>\nYou are teammate "researcher".\n</system-reminder>'
|
||||
: identity === '<system-reminder>\nYou are teammate "researcher".\nYour Team Lead is named "lead".\nUse list_agents({}) to find your teammates and their names.\nTo message your Team Lead, use send_message({ target: "lead", message: "..." }).\nTo message another teammate, use send_message({ target: "<teammate name>", message: "..." }).\n</system-reminder>'
|
||||
? researcher(options.messages)
|
||||
: identity === '<system-reminder>\nYou are teammate "implementer".\n</system-reminder>'
|
||||
: identity === '<system-reminder>\nYou are teammate "implementer".\nYour Team Lead is named "lead".\nUse list_agents({}) to find your teammates and their names.\nTo message your Team Lead, use send_message({ target: "lead", message: "..." }).\nTo message another teammate, use send_message({ target: "<teammate name>", message: "..." }).\n</system-reminder>'
|
||||
? implementer(options.messages)
|
||||
: lead(options.messages)
|
||||
for (const chunk of chunks) {
|
||||
|
||||
@@ -862,9 +862,17 @@ describe('headless stream-json snapshots', () => {
|
||||
"identityReminders": [
|
||||
"<system-reminder>
|
||||
You are teammate "implementer".
|
||||
Your Team Lead is named "lead".
|
||||
Use list_agents({}) to find your teammates and their names.
|
||||
To message your Team Lead, use send_message({ target: "lead", message: "..." }).
|
||||
To message another teammate, use send_message({ target: "<teammate name>", message: "..." }).
|
||||
</system-reminder>",
|
||||
"<system-reminder>
|
||||
You are teammate "researcher".
|
||||
Your Team Lead is named "lead".
|
||||
Use list_agents({}) to find your teammates and their names.
|
||||
To message your Team Lead, use send_message({ target: "lead", message: "..." }).
|
||||
To message another teammate, use send_message({ target: "<teammate name>", message: "..." }).
|
||||
</system-reminder>",
|
||||
],
|
||||
"memberEdges": 4,
|
||||
|
||||
@@ -2,5 +2,5 @@
|
||||
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
||||
# after editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write packages/experimental/tool-agent-team/README.md
|
||||
README.md: bfa3fc7f466d4b7f405231de3de4cf648fc690de
|
||||
README.zh.md: 990e786fd69877af17cef339a696bb5e93d5925c
|
||||
README.md: 0918686d28f7212558aafa988a4b968c18aa8664
|
||||
README.zh.md: 8243f8471a67fe55d5a5d368d98e20c5519f42f3
|
||||
@@ -125,7 +125,7 @@ Read these pages when the package-level contract is not enough. They move from t
|
||||
|
||||
#### What the model sees
|
||||
|
||||
One shared system policy states the explicit-delegation requirement, shared-cwd behavior, filesystem stale-version recovery, Bash/formatter/codegen risk, task and write-scope coordination, Steer delivery, the no-retry mailbox rule, and the Lead's duty to wait before answering. All nine Team schemas are identical for Leads and teammates; execution enforces Lead-only operations. `spawn_teammate` prefixes its initial user message with `<system-reminder>\nYou are teammate "<name>".\n</system-reminder>`, followed by a blank line and the task. The prefix contains no Team id and works when runtime context is disabled. Forks inherit history without an additional Lead identity message.
|
||||
One shared system policy states the explicit-delegation requirement, shared-cwd behavior, filesystem stale-version recovery, Bash/formatter/codegen risk, task and write-scope coordination, Steer delivery, the no-retry mailbox rule, and the Lead's duty to wait before answering. All nine Team schemas are identical for Leads and teammates; execution enforces Lead-only operations. `spawn_teammate` prefixes its initial user message with `<system-reminder>\nYou are teammate "<name>".\nYour Team Lead is named "lead".\nUse list_agents({}) to find your teammates and their names.\nTo message your Team Lead, use send_message({ target: "lead", message: "..." }).\nTo message another teammate, use send_message({ target: "<teammate name>", message: "..." }).\n</system-reminder>`, followed by a blank line and the task. The prefix contains no Team id and works when runtime context is disabled. Forks inherit history without an additional Lead identity message.
|
||||
|
||||
#### Token effect
|
||||
|
||||
|
||||
@@ -125,7 +125,7 @@ member scope 上的一个 `team:policy` 段落说明共享的协作规则;固
|
||||
|
||||
#### 模型看到什么
|
||||
|
||||
一段共享 system 策略会说明显式 delegation 要求、共享 cwd 行为、文件陈旧版本恢复、Bash/formatter/codegen 风险、task/write-scope 协调、Steer 投递、mailbox 不重试规则,以及 Lead 必须在回答前等待。Lead 与 teammate 的全部九个 Team schema 相同;执行时检查仅限 Lead 的操作权限。`spawn_teammate` 在初始 user 消息前加上 `<system-reminder>\nYou are teammate "<name>".\n</system-reminder>`,接着是一个空行和任务。该前缀不含 Team id,禁用运行时上下文时也能生效。fork 继承历史,不额外添加 Lead 身份消息。
|
||||
一段共享 system 策略会说明显式 delegation 要求、共享 cwd 行为、文件陈旧版本恢复、Bash/formatter/codegen 风险、task/write-scope 协调、Steer 投递、mailbox 不重试规则,以及 Lead 必须在回答前等待。Lead 与 teammate 的全部九个 Team schema 相同;执行时检查仅限 Lead 的操作权限。`spawn_teammate` 在初始 user 消息前加上 `<system-reminder>\nYou are teammate "<name>".\nYour Team Lead is named "lead".\nUse list_agents({}) to find your teammates and their names.\nTo message your Team Lead, use send_message({ target: "lead", message: "..." }).\nTo message another teammate, use send_message({ target: "<teammate name>", message: "..." }).\n</system-reminder>`,接着是一个空行和任务。该前缀不含 Team id,禁用运行时上下文时也能生效。fork 继承历史,不额外添加 Lead 身份消息。
|
||||
|
||||
#### Token 影响
|
||||
|
||||
|
||||
@@ -193,7 +193,15 @@ function install(agent: Agent, ctx: Context, config: Required<Config>): () => vo
|
||||
name: args.name,
|
||||
description: args.description,
|
||||
prompt: [
|
||||
{ type: 'text', text: `<system-reminder>\nYou are teammate "${args.name.trim()}".\n</system-reminder>\n\n` },
|
||||
{ type: 'text', text: `<system-reminder>
|
||||
You are teammate "${args.name.trim()}".
|
||||
Your Team Lead is named "lead".
|
||||
Use list_agents({}) to find your teammates and their names.
|
||||
To message your Team Lead, use send_message({ target: "lead", message: "..." }).
|
||||
To message another teammate, use send_message({ target: "<teammate name>", message: "..." }).
|
||||
</system-reminder>
|
||||
|
||||
` },
|
||||
{ type: 'text', text: args.prompt },
|
||||
],
|
||||
context,
|
||||
|
||||
@@ -238,12 +238,12 @@ describe('dsh-tool-team', () => {
|
||||
expect(renderPrompt(childAssembly)).toBe(leadPrompt)
|
||||
expect(renderContextSnapshot(childAssembly)).not.toContain('team:identity')
|
||||
expect(child.session.deriveMessages().some(message => message.content.some(block =>
|
||||
block.type === 'text' && block.text === '<system-reminder>\nYou are teammate "tool-worker".\n</system-reminder>\n\n'))).toBe(true)
|
||||
block.type === 'text' && block.text === '<system-reminder>\nYou are teammate "tool-worker".\nYour Team Lead is named "lead".\nUse list_agents({}) to find your teammates and their names.\nTo message your Team Lead, use send_message({ target: "lead", message: "..." }).\nTo message another teammate, use send_message({ target: "<teammate name>", message: "..." }).\n</system-reminder>\n\n'))).toBe(true)
|
||||
const initialPrompt = child.session.snapshotEvents().find(event => event.type === 'user/message'
|
||||
&& event.data.source.kind === 'user')
|
||||
expect(initialPrompt?.type === 'user/message'
|
||||
? initialPrompt.data.content.flatMap(block => block.type === 'text' ? [block.text] : [])
|
||||
: []).toEqual(['<system-reminder>\nYou are teammate "tool-worker".\n</system-reminder>\n\n', 'stay available'])
|
||||
: []).toEqual(['<system-reminder>\nYou are teammate "tool-worker".\nYour Team Lead is named "lead".\nUse list_agents({}) to find your teammates and their names.\nTo message your Team Lead, use send_message({ target: "lead", message: "..." }).\nTo message another teammate, use send_message({ target: "<teammate name>", message: "..." }).\n</system-reminder>\n\n', 'stay available'])
|
||||
|
||||
const denied = await execute(ctx, child, 'spawn_teammate', {
|
||||
name: 'nested', description: 'not allowed', prompt: 'no',
|
||||
@@ -279,14 +279,14 @@ describe('dsh-tool-team', () => {
|
||||
expect(JSON.stringify(childRequest.messages)).not.toContain('Parent task')
|
||||
}
|
||||
expect(childRequest.messages.at(-1)?.content.slice(0, 2)).toEqual([
|
||||
{ type: 'text', text: '<system-reminder>\nYou are teammate "reviewer".\n</system-reminder>\n\n' },
|
||||
{ type: 'text', text: '<system-reminder>\nYou are teammate "reviewer".\nYour Team Lead is named "lead".\nUse list_agents({}) to find your teammates and their names.\nTo message your Team Lead, use send_message({ target: "lead", message: "..." }).\nTo message another teammate, use send_message({ target: "<teammate name>", message: "..." }).\n</system-reminder>\n\n' },
|
||||
{ type: 'text', text: 'Review the work' },
|
||||
])
|
||||
await using persisted = await ctx.sessionPersistence.open(childId, 'read')
|
||||
const { events } = await persisted.read()
|
||||
const initial = events.findLast(event => event.type === 'user/message' && event.data.source.kind === 'user')
|
||||
expect(initial?.type === 'user/message' ? initial.data.content : []).toEqual([
|
||||
{ type: 'text', text: '<system-reminder>\nYou are teammate "reviewer".\n</system-reminder>\n\n' },
|
||||
{ type: 'text', text: '<system-reminder>\nYou are teammate "reviewer".\nYour Team Lead is named "lead".\nUse list_agents({}) to find your teammates and their names.\nTo message your Team Lead, use send_message({ target: "lead", message: "..." }).\nTo message another teammate, use send_message({ target: "<teammate name>", message: "..." }).\n</system-reminder>\n\n' },
|
||||
{ type: 'text', text: 'Review the work' },
|
||||
])
|
||||
})
|
||||
@@ -377,7 +377,7 @@ describe('dsh-tool-team', () => {
|
||||
})
|
||||
const childId = spawnedChildId(ctx, lead, spawned)
|
||||
await waitNoAgent(ctx, childId)
|
||||
const reminder = '<system-reminder>\nYou are teammate "reviewer".\n</system-reminder>\n\n'
|
||||
const reminder = '<system-reminder>\nYou are teammate "reviewer".\nYour Team Lead is named "lead".\nUse list_agents({}) to find your teammates and their names.\nTo message your Team Lead, use send_message({ target: "lead", message: "..." }).\nTo message another teammate, use send_message({ target: "<teammate name>", message: "..." }).\n</system-reminder>\n\n'
|
||||
const first = serializeRequest(adapter.requests[0]!).messages
|
||||
expect(first.at(-1)?.content.slice(0, 2)).toEqual([{ type: 'text', text: reminder }, { type: 'text', text: 'Review the work' }])
|
||||
expect(adapter.requests[1]!.messages.filter(message => message.content.some(block =>
|
||||
@@ -419,7 +419,7 @@ describe('dsh-tool-team', () => {
|
||||
expect(spawned.isError, text(spawned)).toBe(false)
|
||||
await waitNoAgent(ctx, spawnedChildId(ctx, lead, spawned))
|
||||
expect(serializeRequest(adapter.requests[0]!).messages.at(-1)?.content).toEqual([
|
||||
{ type: 'text', text: '<system-reminder>\nYou are teammate "reviewer".\n</system-reminder>\n\n' },
|
||||
{ type: 'text', text: '<system-reminder>\nYou are teammate "reviewer".\nYour Team Lead is named "lead".\nUse list_agents({}) to find your teammates and their names.\nTo message your Team Lead, use send_message({ target: "lead", message: "..." }).\nTo message another teammate, use send_message({ target: "<teammate name>", message: "..." }).\n</system-reminder>\n\n' },
|
||||
{ type: 'text', text: 'Review the work' },
|
||||
])
|
||||
})
|
||||
|
||||
Reference in new issue
Block a user