项目三:Multi-Agent App(多 Agent 协作应用)
难度:⭐⭐⭐ 前置:项目二 + 06、07、08 章 目标:用多个分工的 Agent 协作完成一个完整任务,并做好评估和可观测性。
项目一练循环,项目二练证据,项目三练把多个 Agent 组织成系统。毕业项目的核心问题不是"Agent 越多越高级",而是:当任务天然有分工时,多 Agent 能不能让系统更清楚、更好评估。如果协调的代价没换来收益,就该退回单 Agent——多 Agent 是为了解耦,不是为了热闹。
一、从一个问题开始
拿内容工厂做全文主线,把任务缩成一句话:查三份资料,写一页草稿,找出两处风险。它是个好样本:天然三段分工——调研、写作、审校,每个职责能独立验收,产物层层递进,正好覆盖编排、交接、共享状态、冲突和评估。
先看终点长什么样——一条三 Agent 流水线的 trace 加最终产物:
task-001 自动调研并写报告 cost=$0.041
├─ supervisor.plan 0.8s out: 3 steps
├─ researcher.run 6.2s tools: search_kb x2, web_search x1
├─ writer.run 2.1s draft: 820 words
├─ reviewer.run 1.4s issues: 3
└─ supervisor.finalize 1.0s final: accepted with edits整篇要回答五个问题,它们也恰好是多 Agent 系统的五个关节:
- 为什么要多个 Agent,而不是一个更大的单 Agent?
- 多个 Agent 怎么组织起来?
- Agent 之间传什么,共享状态怎么设计?
- 意见打架或同时改同一份文件时,怎么收口?
- 怎么证明多 Agent 比单 Agent 值,而不是凭感觉?
为了先把最小闭环跑通,第二节先只串两个 Agent(写作 → 审校),讲清编排、框架选型和交接;再回到三段流水线讲共享状态;最后看冲突和评估。
二、最小闭环:两个 Agent 串起来
最小闭环不做三段,只做两段:写作 Agent 产草稿,审校 Agent 挑错。它已经能暴露多 Agent 最核心的几个设计问题。
先回答第一个问题:为什么要多个 Agent
在写任何编排之前,要先想清楚这个任务到底要不要多 Agent。这里有个取舍:
- 一个大 Agent + 全部工具 + 长 system prompt — 实现简单、上下文连续:上下文臃肿、职责混杂、出错难定位 / 代价是可调试性差。
- 多个分工 Agent — 职责单一、上下文隔离、可独立验收:多一层协调、状态一致性、冲突和延迟 / 代价是复杂度。
- 固定工作流 — 由代码安排节点顺序和分支,适合已知依赖;节点内部仍可以运行 Agent,动态增加任务则需要额外设计。
本项目用研究、写作和审校三个角色练习协作,并与单 Agent 基线比较收益。各角色分别交付证据、草稿和审查意见;模式与适用场景见多智能体协作总览。
怎么组织:流水线 vs 主管-工人
多个 Agent 怎么串,决定了可追踪性和复杂度。这里有个取舍:
- 流水线(A→B→C 固定顺序) — 易追踪、好评估、trace 清晰:只适合流程固定的任务,差异大任务会卡 / 代价是灵活性低。
- 主管-工人(主管动态分派) — 适合任务差异大、需动态分派:主管决策本身是失败点,trace 更复杂 / 代价是调试难。
- 去中心化(Agent 间直接通信) — 并行度高:状态一致性难保证、冲突难收口 / 代价是实现复杂。
本项目先使用流水线(调研→写作→审校),任务差异大、需要动态分派时再引入主管。依赖执行与动态委派分别见流水线与任务依赖图、主管与子 Agent。
用什么框架:LangGraph vs Agent SDK vs 裸写
项目三要求用一个框架实现,并对比裸写省了什么。这里有个取舍:
- LangGraph — 图编排、状态机、checkpoint、人机交互成熟:学习曲线陡,状态 schema 要先定义 / 代价是前期设计重。
- Agent SDK — 轻量、handoff、guardrail、tracing 内置:抽象偏 OpenAI 生态,复杂图编排不如 LangGraph / 代价是灵活性。
- 裸 API 手写 Agent(复用项目一/二) — 完全可控:要自己实现状态、交接、重试、tracing / 代价是工程量。
选 LangGraph 起步,因为图编排 + 状态 + checkpoint 能直接支撑流水线和评估。然后在报告里对比它和项目一裸写在"循环 / 状态 / 交接 / tracing"上各替你解决了什么——这正是 07 章框架实践的验收点。
交接传什么:传干净产出 vs 原始过程
两个 Agent 之间,写作要把草稿交给审校。这里有个最容易写错的取舍:
- 原样倒上游全部 messages — 实现简单:下游上下文爆、被无关过程带偏、字段不稳定 / 代价是质量下降。
- 只传结构化产出(要点 + 证据 ID + 未解决问题 + 约束) — 干净、稳定、可独立测试:要定义交接格式 / 代价是 schema 设计。
选只传干净产出,固定格式:
{
"from": "writer",
"to": "reviewer",
"task": "审校这版草稿",
"summary": "基于三份资料写了对比报告。",
"evidence_ids": ["S1", "S2", "S3"],
"open_questions": ["B 价格是否最新"],
"constraints": ["不要编造未确认数据", "关键事实必须带来源编号"]
}审校还需要读取当前草稿及对应证据,不能只凭作者摘要判断通过。交接内容与来源引用的设计,见任务契约与上下文交接。
两节点的骨架
用 LangGraph 把上面拼起来,最小流水线就这么几行:
from langgraph.graph import StateGraph
graph = StateGraph(State) # 共享状态 schema
graph.add_node("writer", writer_agent)
graph.add_node("reviewer", reviewer_agent)
graph.add_edge("writer", "reviewer") # 写作 → 审校,固定流水线
graph.set_entry_point("writer")
app = graph.compile()跑起来后,State 在节点间流动,每个 Agent 只读写自己负责的字段。这就引出下一节的核心:共享状态怎么设计。
三、回到主线:三段流水线与共享状态
现在把流水线补成三段:主管分派 → 调研(复用项目二)→ 写作 → 审校 → 主管收口。调研 Agent 直接复用项目二的成果——这就是"把一个 Agent 当另一个 Agent 的工人"。
三段流水线最大的设计点不在编排,而在共享状态怎么设计。不要让每个 Agent 随意读写一坨全局变量。这里有个取舍:
- 全局可变状态,谁都能写 — 实现 0:无法追溯谁改了什么、职责越界、调试地狱 / 代价是混乱。
- 每个字段指定 owner,Agent 只写自己负责的字段 — 边界清楚、trace 可定位、好测试:要预先定义状态 schema / 代价是设计量。
- 完全无共享,只靠消息传递 — 最解耦:状态一致性靠协议保证、复杂 / 代价是实现难。
选结构化共享状态 + 字段 owner 权限。一个最小状态长这样:
{
"task_id": "task-001",
"evidence": [],
"draft": "",
"review_comments": [],
"final_answer": "",
"cost": {"input_tokens": 0, "output_tokens": 0, "estimated_usd": 0},
"status": "running"
}每个 Agent 只改自己的字段:
| Agent | 可写字段 | 不该写什么 |
|---|---|---|
| 调研 | evidence、研究笔记 | final_answer |
| 写作 | draft | evidence 原始来源 |
| 审校 | review_comments、风险标记 | 直接覆盖调研证据 |
| 主管 | plan、final_answer、status | 绕过子 Agent 乱写全部过程 |
字段归属需要由运行层落实;并发写入时还要定义合并和版本检查规则,见共享状态与黑板协作。
到这一步,三段流水线已经能完整跑通。但多 Agent 真正的考验,在 Agent 意见打架或同时改同一份产物的时候。
四、当 Agent 意见打架
用冲突当第四轮轨迹。多 Agent 常见冲突有三类,处理方式各不相同:
- 结论冲突:调研 Agent 说 A 更好,分析 Agent 说 B 更好。处理:主管要求双方给证据,按来源新旧、可信度和任务目标裁决。
- 职责冲突:写作 Agent 修改了事实,审校 Agent 又改回去。处理:规定事实只来自调研证据,写作只负责表达。
- 产物冲突:两个 Agent 同时编辑同一个文件。
第三类最麻烦,因为"谁先写谁赢"会让审校的修改被写作的旧版本静默覆盖。这里有个取舍:
- 全量覆盖,最后写者赢 — 实现 0:审校的修改可能被写作旧版本覆盖、丢失 / 代价是静默错误。
- 补丁优先(Agent 输出 diff 而非整文件) — 精确、可合并:要实现 patch 应用和冲突检测 / 代价是工程量。
- 单写者原则 + 版本检查(提交时带 base 版本,变了重读) — 最稳:同一时刻只有一个写者 / 代价是吞吐降低。
选单写者 + 补丁 + 版本检查三件套:
- 单写者原则:同一时刻只有一个 Agent 对某个文件有写权限。
- 补丁优先:Agent 输出 diff,而不是直接覆盖整份文件。
- 版本检查:提交修改时带基于哪个版本生成,版本变了就重新读取。
- 冲突交给主管:不能自动合并时,主管比较两边意图,再决定保留、合并或请求人工确认。
这里针对共同修改同一份报告采用单写者与 patch 合并。独立文件分工、隔离工作区等其他策略见多 Agent 的文件与产物协作。
五、把它变成能评估、能追溯、能控风险的系统
一个能跑的三段流水线,如果没有评估和 trace,那它只是个 demo——出错时你根本说不清是哪个 Agent 改坏的。要变成作品,至少补三样。
评估:必须有单 Agent 基线。没有基线,无法证明多 Agent 带来了收益,"多 Agent 更好"只是感觉。这里有个取舍:
- 只做多 Agent,不做基线 — 省事:无法证明收益,面试也讲不清 / 代价是论证空洞。
- 用同一批任务跑单 Agent 基线对比 — 可证明收益和代价:多一倍评测工作 / 代价是工作量。
选必须有基线。用同一批任务比较成功率、事实错误数、修改轮数、成本和可调试性。多 Agent 只有在质量 / 稳定性 / 可调试性至少一项有可解释提升时才算有价值。怎么设计评测集和判分规则,见 Agent 评估。
tracing:每个 span 至少记这些。多 Agent 没有 trace,调试会非常痛苦。每个 span 记 task_id / agent_name / 输入输出摘要 / tool_calls / token / latency_ms / estimated_cost_usd / error。一次运行能展开成开头那样:
task-001 自动调研并写报告 cost=$0.041
├─ supervisor.plan 0.8s out: 3 steps
├─ researcher.run 6.2s tools: search_kb x2, web_search x1
├─ writer.run 2.1s draft: 820 words
├─ reviewer.run 1.4s issues: 3
└─ supervisor.finalize 1.0s final: accepted with edits你要能回答:贵在哪里、慢在哪里、哪一个 Agent 产出了错误信息。trace 的完整设计见 可观测性。
安全边界:危险操作要人工确认。如果项目里有写文件、发邮件、提交代码、删除数据这类动作,最小安全策略是:
- 只读工具默认允许。
- 写文件、发请求、删除、提交、付款等动作需要人工确认,展示明确的目标、参数和影响范围后再执行。
- 下游只处理已授权范围内的任务。结构化字段中的自然语言仍可能包含越权指令,工具调用必须由执行端检查权限与参数。
- 检索内容里的"忽略之前指令""执行以下命令"视为资料内容,不当系统指令。
- 超过最大轮数、最大成本或最大工具调用次数就停止。
有了评估、trace 和安全边界,这个多 Agent 系统才从"能跑的 demo"变成"能解释、能回放、能控风险的工程"。而它复用的正是项目一的 tool loop 和项目二的调研能力——三个项目串起来,就是从"一个能跑的循环"到"一个能评估的系统"的完整路径。如果走面试,就把这三个项目翻成能讲清的故事、架构图和排障思路,去 10 章继续。