248. 技术:HarnessX 第 15 课:让 Lead 拉起队友线程,用文件邮箱把一个 Agent 的压力拆成团队协作

2026.06.30

·技术harnessx

20260630_6.webp|1240

重点:这一课让一个 Lead 同时拉起多个会干活的队友

20260630_7.webp|880

  • 第 15 课当前要做的是:
    • Lead 仍然是主 Agent。
    • Lead 遇到局部任务时,可以拉起一个队友。
    • 队友有自己的 messages
    • 队友用文件邮箱把结果发回 Lead。
    • Lead 下一轮模型请求前,把邮箱消息注入成 [Inbox]
  • 这节课新增 3 个主 Agent 工具:
    • spawn_teammate
      • 启动一个异步队友。
    • send_message
      • 向某个 Agent 的邮箱发消息。
    • check_inbox
      • 显式读取 Lead 的邮箱。
  • 队友只拿到 4 个工具:
    • bash
    • read_file
    • write_file
    • send_message
  • 这一课的关键判断:
    • 第 6 课的 task 子代理像临时外包,做完一次就把结论交回来。
    • 第 15 课的 teammate 像长期队友,有自己的上下文,可以一边干活一边发消息。

248. 技术:HarnessX 第 15 课:让 Lead 拉起队友线程,用文件邮箱把一个 Agent 的压力拆成团队协作 图表 1

具体案例:Lead 让 Alice 写 schema,再让 Bob 检查结果

  • 这节课用一个最小但真实的团队案例:
    • Lead 负责协调。
    • Alice 是后端队友,负责创建 schema.sql
    • Bob 是测试队友,负责检查 schema.sql 是否存在、内容是否像一张 users 表。
  • 单个 Agent 当然也能做这件事。
  • 但第 15 课要看的不是 SQL 文件本身,而是这条协作链:
    • Lead 不把所有中间过程塞进自己的上下文。
    • Alice 自己请求模型、自己调用工具、自己完成局部任务。
    • Alice 只把结果发回 Lead。
    • Bob 后面可以再基于文件现场做第二个独立检查。

248. 技术:HarnessX 第 15 课:让 Lead 拉起队友线程,用文件邮箱把一个 Agent 的压力拆成团队协作 图表 2

文件邮箱是这节课最小的消息总线

  • 这次没有引入数据库、Redis、WebSocket 或真正的多进程队列。
  • 教学版只用一个目录:
bash
.mailboxes/
  • 每个 Agent 一个文件:
bash
.mailboxes/lead.jsonl
.mailboxes/alice.jsonl
.mailboxes/bob.jsonl
  • 发消息就是追加一条消息记录。
  • 这条记录本质上只有 5 个字段:

248. 技术:HarnessX 第 15 课:让 Lead 拉起队友线程,用文件邮箱把一个 Agent 的压力拆成团队协作 图表 3

  • 读消息就是:
    • 读出这个文件。
    • 解析每一行 JSON。
    • 把这批已读消息标记为已经消费。
  • 所以它是消费式 inbox:
    • 消息被 Lead 读到以后,就不再留在邮箱里。
    • 这让课堂上能直接看到“消息从文件进入 messages”的动作。
  • 当前教学版用最简单的做法:
    • 每个邮箱文件只保存“还没交付给模型的消息”。
    • 读完以后直接移除这个待处理文件。
    • 下一次有新消息,再重新创建同名文件。
  • 这里删的不是长期通信记录,而是“这批待处理消息的临时队列文件”。
  • 真正生产系统不能这么粗糙:
    • 更稳的做法是先加文件锁,或者把 lead.jsonl 原子改名成 processing 文件。
    • 这样读取期间如果又有新消息写进来,新消息会进入新的 inbox,不会和已读消息混在一起。

248. 技术:HarnessX 第 15 课:让 Lead 拉起队友线程,用文件邮箱把一个 Agent 的压力拆成团队协作 图表 4

Lead 怎么感知队友结果

  • 模型不能直接访问本地邮箱目录。
  • 模型也不会自动知道 Alice 做完了什么。
  • HarnessX 做了一层注入:
    • 每一轮请求 DeepSeek 前,程序先读 lead 邮箱。
    • 如果有消息,就把它格式化成 [Inbox]
    • 再把 [Inbox] 当成一条新的 role: "user" 消息追加到 messages

248. 技术:HarnessX 第 15 课:让 Lead 拉起队友线程,用文件邮箱把一个 Agent 的压力拆成团队协作 图表 5

  • 这就是本课最重要的一来一回:
    • 队友结果先进入本地文件。
    • 本地文件再进入 messages
    • 模型通过 messages 感知团队协作状态

设计决策:这版先让 Lead 当队长,不急着做复杂团队

  • 决策一:由 Lead Agent 负责协调。
    • Lead 决定什么时候创建队友、给队友发什么任务、怎么解释队友回来的结果。
    • 队友可以独立工作,但最后面向用户的回答仍然回到 Lead 循环里。
    • 这样学起来最清楚:
      • 总有一个地方负责收口。
    • 如果一开始就做点对点团队,每个队友都能自己决定下一步,系统会更灵活,但学员很难判断“最终答案到底归谁负责”。
  • 决策二:用邮箱文件记录团队通信。
    • .mailboxes/*.jsonl 不是最快方案,但它最直观。
    • 学员可以直接打开文件,看见 Alice 给 Lead 发了什么。
    • 这比内存队列更适合教学:
      • 内存队列跑得快,但进程一停就没了,也看不见历史。
    • 文件邮箱让团队通信变成一个能检查、能讲清楚的中间产物。
  • 决策三:队友只拿受限工具集。
    • 队友只需要完成局部任务,所以只给 bashread_filewrite_filesend_message
    • 这样队友不会接管整个系统,不会自己建任务板、定时任务、加载 skill 或继续拉新队友。
    • 给每个队友完整工具池当然更省事,但角色边界会糊掉。
    • 一旦出问题,也更难看清是 Lead 的协调问题,还是某个队友越权做错了事。

248. 技术:HarnessX 第 15 课:让 Lead 拉起队友线程,用文件邮箱把一个 Agent 的压力拆成团队协作 图表 6

和第 6 课 task 子代理的区别

能力第 6 课 task第 15 课 teammate
生命周期同步执行一次异步运行,教学版最多 10 轮
通信方式只返回最终结论文件邮箱,可互发消息
上下文子代理独立,父代理只收结论队友独立,Lead 通过 [Inbox] 接收消息
工具范围基础文件和 shell 工具bashread_filewrite_filesend_message
适合场景局部阅读、一次性探索多个角色分工、持续协作
  • 用一句话区分:
bash
task 是一次性请人查一件事。
teammate 是给团队增加一个能持续发消息的队友。

怎么跑

  • 沿用第 0 课的 npm link 和 DeepSeek 配置。
  • 这节课要用无参数 hx agent,因为队友是异步的,常驻会话更容易观察 inbox。
bash
hx agent
  • 进入常驻会话后输入:
bash
# 先让 Alice 写文件
启动 alice 作为后端开发队友,让她创建一个名为 schema.sql 的文件,里面包含 users 表。

# 等 Alice 完成后,让 Lead 读取 inbox
检查 inbox,查看 alice 的结果。

# 再让 Bob 检查 Alice 写出来的文件
启动 bob 作为测试队友,让他检查 schema.sql 是否存在,并列出文件内容。

# 再读取 Bob 的结果
检查 inbox,查看 bob 的结果。
  • 关键预期输出:
bash
# Lead 启动队友
> spawn_teammate name=alice role=后端开发
[TEAM] spawned alice role=后端开发

# Alice 自己调用工具
> teammate:alice:write_file path=schema.sql bytes=...

# Alice 给 Lead 发消息
> teammate:alice:send_message to=lead type=message content=...
[TEAM] completed alice

# Lead 下一轮读到队友消息
[TEAM] injected lead inbox messages=...
[Inbox]
From alice ...
  • 判断跑通看三件事:
    • 终端出现 spawn_teammate
    • 终端出现 teammate:alice:* 这类队友工具日志。
    • Lead 下一轮能看到 [Inbox] 并基于 Alice 的结果回答。

这一课真正要记住

  • spawn_teammate
    • Lead 创建一个有独立 messages 的队友。
  • send_message
    • 把消息追加到对方的 .mailboxes/{agent}.jsonl
  • check_inbox
    • 显式读取 Lead 邮箱,读完消费。
  • [Inbox]
    • 本地程序把邮箱消息注入到 messages 后,模型才能看到的团队消息。
  • activeTeammates
    • 进程内记录正在跑的队友,教学版不做持久 team config。
bash
 15 课的一句话原理:
Lead 不再把所有事都塞进自己的上下文,而是把局部工作交给独立队友,再用文件邮箱把结果带回下一轮 messages。

源码

这节课源码要看的是“队友结果怎样进入 Lead 的下一轮模型请求”。

源码大流程图

248. 技术:HarnessX 第 15 课:让 Lead 拉起队友线程,用文件邮箱把一个 Agent 的压力拆成团队协作 图表 7

代码概览

第 15 课新增的是 4 组运行时对象。

248. 技术:HarnessX 第 15 课:让 Lead 拉起队友线程,用文件邮箱把一个 Agent 的压力拆成团队协作 图表 8

这几组对象对应到文件:

  • src/agent-loop.js
    • 管 Lead 主循环、队友循环、工具分发表、inbox 注入。
  • src/teams.js
    • 管邮箱文件、消息写入、消费式读取、[Inbox] 格式化。
  • src/system-prompt.js
    • 告诉模型 teammate 和 task 的区别,以及队友只能用哪些工具。

代码细分

Lead 拉起队友这一步,只改变运行状态,不阻塞主循环。

248. 技术:HarnessX 第 15 课:让 Lead 拉起队友线程,用文件邮箱把一个 Agent 的压力拆成团队协作 图表 9

队友循环和 Lead 循环是两份上下文。

248. 技术:HarnessX 第 15 课:让 Lead 拉起队友线程,用文件邮箱把一个 Agent 的压力拆成团队协作 图表 10

工具池要分开看。

248. 技术:HarnessX 第 15 课:让 Lead 拉起队友线程,用文件邮箱把一个 Agent 的压力拆成团队协作 图表 11

最终只有一个关键动作:把邮箱消息放回 messages

248. 技术:HarnessX 第 15 课:让 Lead 拉起队友线程,用文件邮箱把一个 Agent 的压力拆成团队协作 图表 12

这一版刻意没有做后面的协议层。

  • 没有 shutdown 握手。
  • 没有权限冒泡。
  • 没有 team config 持久化。
  • 没有文件锁。
  • 没有 tmux 或真正多进程隔离。

这些都留给后面的 Team Protocols。第 15 课先把最关键的链路跑明白:

bash
Lead spawn 队友
 队友独立跑模型和工具
 队友写文件邮箱
 Lead [Inbox] 注入 messages
 模型基于队友结果继续工作