第 16 章:Handoff 机制:让 Agent 之间丝般顺滑地“交接棒”
如果说编排器是指挥,Agent 是乐手,那么 Handoff(交接)就是空气中传播的声波。没有它,乐手们就是在演默剧,谁也听不见谁。
想象这样一个场景: 你安排 Agent A 去搜集特斯拉的财报,安排 Agent B 去计算利润率。 Agent A 辛辛苦苦搜到了 100 页 PDF,任务完成,退场了。 Agent B 上场了,一脸茫然:“数据呢?A 也没给我啊?”
这就是 Handoff(交接)缺失的后果。
在简单的单体程序里,变量是共享的,A 算出的 x,B 直接读就行。 但在分布式 Agent 系统里,A 和 B 可能跑在不同的服务器上,甚至相隔几天执行。如何保证 A 的产出能 精确、完整、可理解 地传递给 B?
本章我们来拆解 Agent 协作的三种通信模式。
01. 通信的三个层级:从简单到复杂
并不是所有场景都需要复杂的通信协议。架构设计的原则永远是 KISS (Keep It Simple, Stupid) 。
层级一:接力棒模式 (Dependency Injection)
场景:线性的 DAG 任务(A -> B -> C)。 比喻:4x100 米接力赛。 逻辑:
- Agent A 跑完,把手里的棒子(结果数据)直接塞给 Agent B。
- Agent B 拿着棒子继续跑。
- 优点:极其简单,零延迟。
- 缺点:耦合度高,A 必须明确知道 B 存在。
层级二:黑板模式 (Workspace)
场景:多个 Agent 并行工作,或者需要数据共享。 比喻:教室里的黑板。 逻辑:
- Agent A 查到了数据,写在黑板的“财经区”(Topic)。
- Agent B 需要计算,抬头看一眼黑板,读走数据。
- Agent C 需要写报告,也抬头看一眼黑板,读走数据。
- 优点:解耦。A 根本不需要知道 B 和 C 的存在,只管写黑板就行。
- 缺点:需要引入中间件(如 Redis),架构稍微复杂一点。
层级三:谈判桌模式 (P2P Messaging)
场景:复杂的动态协作。 比喻:自由市场。 逻辑:
- Agent A:“谁能处理这个 Python 报错?”(广播需求)
- Agent B:“我能,但我现在忙,预计 5 分钟后。”(协商)
- Agent A:“行,等你。”(确认)
- 优点:极度灵活,支持自组织。
- 缺点:最复杂,容易死锁或超时,除非必要,尽量别用。
架构师建议: 90% 的场景,用 前两层 就够了。不要为了炫技去搞 P2P。
02. Plan IO:先签合同,再干活
在代码层面,Agent 之间怎么知道传什么数据?靠 Plan IO 协议。 这就好比两个微服务之间的 API 文档(Swagger/OpenAPI)。
在任务分解阶段,每个 Agent 必须声明两件事:
- Produces(我生产什么) :承诺输出的数据主题。
- Consumes(我消费什么) :执行任务必须依赖的数据主题。
# 伪代码:任务定义
Task_Fetch_Data:
produces: ["financial_report"] # 我承诺产出财报
Task_Analyze:
consumes: ["financial_report"] # 我需要财报才能开工
dependencies: ["Task_Fetch_Data"]
编排器(Orchestrator)看到这个协议,就会明白: “哦,Analyze 必须等 Fetch_Data 完工,并且要把 Fetch_Data 的产出注入给 Analyze。”
03. 工程实战:前序结果注入(Context Injection)
对于最常用的 接力棒模式,实现非常简单粗暴。
当 Agent B 启动时,系统会自动把 Agent A 的运行结果(Result)塞进 B 的 Prompt 上下文 里。
Agent B 收到的 Prompt 可能是这样的:
你是一个财务分析师。
你的任务:计算利润增长率。
--- 上下文数据 (来自 Agent A) ---
【特斯拉2024财报摘要】
营收:960亿美元
净利润:150亿美元
同比增速:19%
-------------------------------
请基于以上数据进行计算...
小技巧:数值提取 如果 A 输出了一大段废话,B 很难解析。 我们可以在 A 结束时加一个 后处理(Post-process) 步骤,用正则提取出关键数值(如 numeric_value: 15000000000),直接传给 B 的代码解释器,这样比传文本更精准。
04. 进阶实战:工作空间(Workspace)
对于 黑板模式,我们通常使用 Redis 作为底层的“黑板”。
核心设计:Topic 与 Seq
- Topic(主题) :数据的分类,比如
financial_data。 - Seq(序列号) :数据的版本号,全局递增。
**为什么要 Seq?**为了支持 增量读取。 Agent B 可能是一个长期运行的监控任务。它第一次读取了 Seq 1-10 的新闻。过了一小时,它不需要把旧新闻再读一遍,只需要告诉黑板:“给我 Seq > 10 的数据。”
# 伪代码:Agent 写数据
def workspace_append(topic, data):
seq = redis.incr("global_seq")
entry = { "seq": seq, "data": data, "timestamp": now() }
redis.rpush(f"topic:{topic}", entry)
# 伪代码:Agent 读数据
def workspace_list(topic, since_seq):
# 只返回序列号比 since_seq 大的新数据
return filter(lambda x: x.seq > since_seq, redis.lrange(topic))
05. 优雅的等待:指数退避(Exponential Backoff)
当 Agent B 依赖 Agent A 的数据,但 A 还没跑完时,B 怎么办?
- 方案 A:每隔 1 秒问一次:“好了没?”(高频轮询,浪费资源)
- 方案 B:死等。(不知道等到什么时候)
最佳实践:指数退避。 B 会这样问:
- 第 1 次:等 1 秒,问一下。
- 第 2 次:还没好?等 2 秒,问一下。
- 第 3 次:等 4 秒...
- 第 4 次:等 8 秒...
这种策略既保证了数据刚产出时能被尽快发现,又在长时间等待时极大地节省了 CPU 和网络资源。这就是系统设计的 “礼貌” 。
06. 常见的大坑
坑 1:数据太大了
现象:Agent A 查了一篇 5万字的论文,直接塞进 Prompt 传给 B。 后果:B 的 Context Window 爆了,任务崩溃。 解法:传引用,不传值。 A 把论文存进 S3(对象存储),只把 s3_link 传给 B。B 需要读哪一段,再去 S3 读。
坑 2:生产者缺位
现象:Plan 里说 B 需要消费 stock_price,但没有任何 Agent 承诺生产 stock_price。 后果:B 会无限等待,直到超时饿死。 解法:在任务启动前进行 静态检查(Static Analysis) 。检查所有 consumes 的主题,是否都在 produces 列表中有对应。如果有缺口,直接报错,不启动任务。
总结
Handoff 机制是 Agent 系统的 血管。
- 接力棒模式:解决了最基础的串行传参。
- 黑板模式:解决了复杂的并发数据共享。
- Plan IO:定义了数据流转的契约。
- 指数退避:实现了优雅的依赖等待。
搞定了编排(大脑)和交接(血管),你的多 Agent 系统已经是一个健康的有机体了。
Part 6:高级推理模式 预告
到目前为止,我们的 Agent 主要是“做事”——查资料、写代码、发邮件。 但是,面对真正烧脑的难题(比如“推导一个数学定理”或“制定一个商业战略”),普通的思考方式可能不够用了。
下一章,我们将进入 高级推理 领域,探讨 Tree-of-Thoughts(思维树) :当线性思考走到死胡同时,如何让 Agent 学会像人类专家一样,进行多路径探索和回溯。