Python 复刻:agents/s09_agent_teams.py
Claude Code 可以同时跑几个独立的 agent,每个有自己的人设、自己的对话历史。它们之间通过一个共享邮箱互相发消息,能互相看见、互相回应。模拟评审小组、跨学科咨询这类需要多视角讨论的场景就靠这套机制。
9.1 队友与子会话的区别
子会话和队友都是"不止一个 agent 在工作"的实现方式,但生命周期完全不同。子会话详见第 10 节。
子会话是一次性的。主会话派出去给它一个任务,子会话跑完返回一段摘要,随即销毁。下次再派一个子会话是全新的,跟之前那个没关系。
队友是常驻的。一旦启动就一直存在。你可以发消息给它、它处理后回复你、过两天再发消息它还记得之前的讨论。队友的"记忆"——也就是它的对话历史——持久保留。
简单的派单用子会话,复杂的多轮讨论用队友。模拟评审小组属于后者:方法学审稿人提"OLS 与 IV 系数差 10 倍的合理解释是 OLS 同时被反向因果向上偏与遗漏变量向下偏抵消,最终系数趋近 0",计量审稿人看到后会反驳"也可能是 IV 弱工具变量放大偏误,需要查 K-P Wald F 值是否真的大于 10"。这种交叉影响子会话做不到。
9.2 队友怎么组织
每个队友在工作目录的 .team/<name>/ 下有自己的资料夹。资料夹里有几样东西:identity.md 描述这个队友的人设,自己的对话历史文件,自己的待处理消息列表。
这个项目里 4 个队友的 identity.md 各自摆明视角:
.team/method_reviewer/identity.md:"方法学审稿人,熟读 Imbens & Rubin 2015 与 Angrist-Pischke 2009。关注 IV 工具变量合理性、识别策略前后一致、PSM 协变量平衡。常问:'反向因果的方向是?工具变量的排他性约束你怎么写?'".team/stat_reviewer/identity.md:"计量审稿人,熟读 Cameron-Miller 2015 聚类标准误综述。关注 reghdfe firm+year 双固定效应、企业层 cluster 聚类、是否需要双重聚类、K-P Wald F 与 Hansen J 的报告完整度。常问:'cluster 单位选 firmid 是否足够?年份聚类是否要加?'".team/writing_reviewer/identity.md:"写作审稿人,关注叙事一致性。常问:'主回归 β=0.0004 不显著与稳健性 A1/B2/Bank/Rdebt 全部显著并存,文字怎么叙述才不前后打架?'".team/contribution_reviewer/identity.md:"新意审稿人,熟读唐亮 2025、李思飞 2025、代飞 2025、强国令 2025 四篇前置。关注本项目相对前置的边际贡献是否被讲清楚——A2 跨框架稳健性矩阵、债权侧效应大于股权侧、绿色金融试验区与数字化转型异质性新增维度。"
队友状态完全独立。主会话退出不影响队友。某个队友进入等待状态不影响其他队友继续工作。
队友之间通过 .team/inbox/ 这个共享邮箱通信。这个邮箱是一个目录,里面每条消息是一份独立的文本文件。所有队友都能读这个邮箱,按消息上的"收件人"字段判断哪些与自己相关。
9.3 一次真实运行里队友的互动
下面是 4 人审稿队那次评审会的关键消息线,按时间戳排开。
msg-001 (broadcast, from main):"请按 identity.md 视角各自起一份对 PC_ESG_主表汇总.docx 的初评。表 6 OLS 0.0004 / IV1 0.0052** / IV3 0.0021** / IV1+IV2 0.0042* Hansen J p=0.057;表 7 PSM ATE 0.0032***(t=3.976) / Placebo p_perm=0.000 / Heckman lambda=0.018* / Change β=-0.0001 不显著;表 8 KZ 通道不显著 / Myopia 通道 a 段 β=0.0008* 与假设方向相反;表 9 仅非国有 β=0.0009** 显著;表 10A A1/B2/Bank/Rdebt 全部显著但 A2 主测度不显著。"
msg-002 (from method_reviewer):"IV1+IV2 Hansen J p=0.057 临界。文献里这个 p 值是被审稿人最常 challenge 的点。建议单独跑 IV1 与 IV2 各自的 Hansen 不可识别检验。另外 PSM ATE 0.0032*** vs PSM 后再 reghdfe β=0.0004 不显著——这两个看似矛盾的结果一定要在文中讲清楚 PSM 是横截面 ATE 而 reghdfe 是 within-firm 效应,否则审稿人会以为你在挑结果。"
msg-003 (from stat_reviewer, replying to msg-002):"同意 method 那条。补充:表 6 OLS β=0.0004 SE=0.0003,t≈1.3;IV1 β=0.0052 SE=0.0025,t≈2.1。t 值差异主要来自 β 抬升而不是 SE 缩小,说明 IV 矫正后系数确实变大、不是弱工具放大。K-P Wald F = 210.259 远大于 10 排除弱 IV 担忧。把这个 F 值写进表 6 注脚很重要。"
msg-004 (from writing_reviewer, replying to msg-001):"叙事的最大风险点:'A2 主测度 OLS 不显著 → IV 显著 + PSM 显著' 这条故事线读者第一反应是怀疑 OLS 是真的 β。建议在引言里就把它定位为'OLS 偏误约 10 倍(0.0004 vs 0.0052)'。同时表 10A 里 PC 仅 Rdebt β=0.0159*** 远高于 PC 仅 Bank 0.0036**——这个差异本身值得在 PC 内部分解那一节专门讨论,是 PC 内部结构差异的强证据。"
msg-005 (from contribution_reviewer):"本项目相对前置文献的边际贡献我读到的是:(1) A2 跨框架稳健性矩阵填补唐亮、李思飞均仅用 A1 的空白;(2) 债权侧效应明显大于股权侧(Bank 0.0036** + Rdebt 0.0159*** vs 仅 Stequity 0.0020 不显著)与唐亮 2025 的结论方向相反,必须正面讨论;(3) Myopia 机制与假设方向相反(a 段 β=0.0008* 是 PC 提高反增短视)——这条要么作为新发现讨论、要么按代飞 2025 重做 myopia 测度后报告。建议在引论第 4 段专门讲这三条边际贡献。"
msg-006 (from method_reviewer, replying to msg-005):"contribution 提到 Myopia 方向相反这点很关键。建议在机制章节加一段 'a 段系数为正但绝对值很小(0.0008)、b 段不显著(-0.0084),结合 Sobel z=-3.089 但间接效应 a*b ≈ -0.00001(接近 0),可以判定 Myopia 通道实际上未识别出有效中介,而不是相反方向的中介。'"
msg-007 (from writing_reviewer, replying to msg-006):"采纳 method 那条。我把这段写进机制小节草稿,今晚发给 main 审一遍。"
整场讨论 7 条消息覆盖 4 个视角,其中 method ↔ stat ↔ writing 三方在 K-P Wald F、PSM 与 reghdfe 的差异上有交叉互证,contribution 单独提了边际贡献结构。这种"一人提观点、两人反驳补强、第四人收稿改文"的协作子会话做不到。
9.4 消息怎么收发
队友发消息时调用 send_message 工具,传入三件信息:收件人列表、消息类型、消息内容。代码侧的执行函数把这三件信息写成一份新文件放进邮箱目录,文件名带时间戳和发件人名字,确保按时序排列。
收件人填一个具体名字就是定向消息——比如 stat_reviewer 给 method_reviewer 单独发"PSM 后再 reghdfe 系数掉到 0.0004,怀疑效应主要在 firm 横截面,你怎么看"。收件人填 inbox 就是广播,所有队友都能看到这条消息。
队友读邮箱时调用 read_inbox 工具。它扫一遍邮箱目录,找出所有"收件人含自己名字"且"上次没读过"的消息,返回给模型。模型把这些消息加进自己的对话历史,作为下一轮的输入。
每个队友在自己的循环里周期性调用 read_inbox。这样它能定期被其他队友的消息唤醒,做出回应。如果没人发消息它就保持等待状态。
9.5 用邮箱通信的设计
让队友之间通过文件通信,不走直接互相调函数的路径,背后有几方面好处。
每条消息是一份独立文件,写入是天然不会被打断的——两个队友同时给某第三个队友发消息,两份文件各自落盘,谁也不会冲掉谁。不需要复杂的并发控制机制。
文件还提供了一份完整的讨论历史。所有消息按时间戳排列在 .team/inbox/ 里,看一眼这个目录就能复盘整个评审讨论。后续 v2 准备好再来时,同一组队友读自己的对话历史和邮箱里 v1 的讨论,自然在 v1 的基础上继续推进——例如审稿意见回来后让 method_reviewer 重新评估"反向因果方向的论证",它会先翻 inbox 里 msg-002 的历史观点再补新评。
队友之间还可能进入"等待回应"状态,停下不持续轮询,节省资源。某个队友把意见发出去后没事可做,就停下来等其他队友的回应到达。这种"被消息唤醒"的模式比一直空跑省资源。
9.6 容易踩的坑
队友数量增长后邮箱里的消息量增长更快。10 个队友的全互动每轮可能产生几十条消息,10 轮就是几百条。邮箱目录定期归档可以缓解,但邮箱本身没有自动清理。
队友的人设由 identity.md 单向定义。这份文件一旦写好,队友的所有判断都受这份人设影响。让 method_reviewer "换个新意视角看"需要显式发消息要求,identity.md 本身不会自动调整。
队友之间的协调依赖消息被及时读取。如果某队友长时间没调用 read_inbox,发给它的消息会堆积。Claude Code 通常在每轮循环开始时插一次 read_inbox,但具体频率取决于实现。
9.7 知识地图
| 关键词 | 含义 | 容易误会的点 |
|---|---|---|
.team/<name>/ | 每个队友的独立资料夹 | 每个队友是独立 agent,不是同一个 Claude 角色扮演 |
.team/inbox/ | 共享邮箱目录,所有队友可读 | 每条消息一份文件,按时间戳排列;天然支持并发写入 |
| 广播 vs 定向 | 收件人填 inbox 或具体名字 | 同一份文件,所有人读同一份;不会被复制多份 |
| 与子会话区别 | 队友常驻、互相可见、自主驱动 | 子会话跑完即销毁;队友可以多轮交互、持续讨论 |