程云来 / 杭州
返回文章·Agent 与 Harness
·约 4 分钟

思想|Agent Teams,另一位队友有了新线索怎么办?

从源码与预期的并行调查出发,区分独立上下文、邮箱存信和下一轮接入,让队友沿用自己的调查继续。


两个人查完了,为什么还得从头再来?

用户想知道:main.py 写了什么,和 expected.txt 里的预期是否一致?先别运行程序,我们只做一次只读调查。

让 reader 查源码,让 reviewer 查预期。reader 很快读到 print(6 * 7),先向 Lead 报告;reviewer 随后找到预期 42,想请 reader 再对照一下。如果此时重新创建一个调查者,它还得重新读取源码。更自然的安排是:给刚才的 reader 留一条消息,让它带着原来的调查记录继续。

第六课的子任务把一段调查隔离出去,再带回短结论。本课进一步给调查者保留名字和自己的上下文,使它可以接到后续消息。Lead 负责分工和汇总,reader 与 reviewer 各自调用模型、使用各自的消息历史。

LEADOWN CONTEXTREADERMAIN.PYREVIEWEREXPECTED.TXTINBOX.JSONLINBOX.JSONLLead协调readerreviewerreader收件箱Lead收件箱
Lead把调查拆给两个成员

reader查main.py,reviewer查expected.txt。每个成员有自己的角色和历史;第一轮可以并行,不把两个调查过程塞进Lead同一段对话。

  1. Lead把调查拆给两个成员:reader查main.py,reviewer查expected.txt。每个成员有自己的角色和历史;第一轮可以并行,不把两个调查过程塞进Lead同一段对话。
  2. reader先保存源码,报告自己知道的事:reader真实读取main.py,把源码记录送到Lead收件箱。它没有运行程序,也不会因为一轮回答结束就丢掉自己的调查历史。
  3. reviewer把预期放进reader的收件箱:reviewer读到expected.txt里的42,再发给reader。queued只表示消息已存,对方当前发出的模型请求不可能因此自动改变。
  4. reader下一次准备时接入补充信息:宿主把收件箱消息加入reader的历史候选,原循环提交后再请求模型。只交接这条信息,没有复制reviewer完整对话。
  5. 同一个reader沿用上次源码继续回答:第二轮保留第一次的read_file结果,再结合收到的预期发补充报告。它不必重新读取main.py,身份与上下文也没有重建。
  6. Lead在自己的下一轮看见两份报告:Lead接入初次源码报告和补充报告,再给用户汇总。两个报告仍是只读信息;程序没有执行,团队消息也不会代替执行许可。

同一位队友,究竟保留了什么?

宿主登记三个成员:lead、reader、reviewer。每个成员保存角色、历史、当前是否占用、状态与已交付次数。reader 第二次工作时,拿到的仍是自己的旧历史,其中已经有第一次 read_file 的工具结果。

这不要求某条操作系统线程一直等在那里。本例第一阶段用两个真实线程并行调查,结束后线程退出;第二阶段再向同一个 reader 交付追问。持续存在的是宿主里的成员记录与上下文,线程只是执行某一轮工作的载体。

共享目录也不会自动共享理解。两人都能读到同一份文件,不代表 reviewer 的模型已经看见 reader 的工具结果。需要沟通的内容仍然要通过明确交接进入对方的上下文。

“发过去了”为什么还不等于“看见了”?

reviewer 调用 send_message,收件人是 reader,正文是查到的预期。宿主把消息写入 reader 的 JSONL 收件箱,返回 queued 收据。到这里,发生的是一次保存。

如果 reader 的模型请求已经发出,它使用的输入不会因为文件里多一行而改变。等 reader 下一次准备请求,宿主才读取邮箱,将尚未出现在自己历史里的消息组成普通文本,交回原循环。原循环提交这份历史后,下一次模型调用才包含它。

因此有三个可以分别观察的位置:收件箱里有记录、历史候选中有通知、实际模型请求中有通知。 queued 只能证明第一个。本例不显示“已读”回执,也不会把收件箱数量当成模型已知信息的数量。

读邮箱时,为什么不顺手把信删了?

想象宿主读完消息,先清空邮箱,随后准备模型输入时出错。消息既没进入成功提交的历史,也不在邮箱里了,这次交接就丢失了。

本例保留追加记录。prepare_inbox 只复制历史,再追加缺少的通知;它不删信、不提前确认。候选被丢弃时,下一次仍能读到;候选被原循环提交后,再准备时用历史中的完整通知文本去重。

这是为小型教学会话选择的做法:每个收件箱最多八条,正文最多 600 字符,历史最多 80 条。长期团队需要设计归档、确认和恢复,本课先让“保存”和“接入”各自可验证。

两人能同时工作,也得知道什么时候停

不同成员可以同时占用,同一个成员不能同时开始两轮。宿主用同一把锁检查并设置占用,随后释放锁,让各自的 Agent 循环工作。把模型调用也包在锁里,就会让所有成员排成一队。

正常回答后,成员回到 idle,可以再交付;本例最多两次。调用额度用尽则 stopped,异常则 failed,不会自动不断重启。每次交付沿用原循环的四次逻辑请求上限;请求恢复还有独立的尝试预算。

这些停止条件分别限制“这一轮还能问几次”和“这一位还能接几轮”。协作没有消除循环边界,只是多了一层成员生命周期。

报告会合以后,仍要看证据是什么

reader 发给 Lead 两条信息:初次源码报告,以及收到 reviewer 预期后的补充。Lead 在自己的下一轮接入它们,再汇总给用户。示例中程序没有执行,团队也没有 run_main 工具,所以结论只能说明读到的源码与预期如何对应,不能写成“已运行,输出 42”。

队友消息是另一个角色提供的信息,不会自动扩大权限。sender 由宿主绑定,也只是本进程里的角色约定,并不是跨机器的身份认证。

实践篇展开存信、接信、交付成员三个函数,并用断点观察锁、候选与旧历史。参考课程 Agent Teams提供了独立成员与邮箱的学习方向;Claude Code 的官方团队文档也描述独立上下文和成员通信。本文的有限交付、追加日志与停止策略是自己的 Python 设计,不能据此推断产品内部源码。