上篇你给 Agent 装上了 Plan-and-Execute——它能先规划再执行,复杂任务有了章法。但当任务本身需要「研究 + 写作 + 审稿」多种能力时,让同一个 Agent 一人包办,质量往往上不去。这篇讲多智能体——把工作拆给专职角色,用流水线协作出稿。
小明想写一篇科普文
小明用上篇的 /plan 模式,给 Agent 下了个任务:
你:/plan 帮我写一篇关于人工智能的科普文章
计划出来了,看起来很靠谱:
[计划]
1. 搜集 AI 核心概念与常见误区
2. 按大纲写出完整初稿
3. 检查逻辑与可读性,输出终稿
执行完,文章有了——但小明越看越别扭:开头像研究报告,中间像教科书,结尾又突然变得很「鸡汤」。同一段里既想讲清原理,又想煽情,语气来回跳。
他拿着初稿去找老张:”明明计划分了三步,为什么写出来还是像一个人硬扛所有角色?”
老张笑了:”因为你只有一个 Agent。Planner 把步骤拆开了,但执行时还是同一个脑子在干三件事——研究、写作、编辑。计划拆的是步骤,不是角色。”
“那怎么办?”
“需要多智能体:让不同 Agent 各干一件事,中间用协议传结果。”
什么是多智能体
老张在白板上画了一条流水线:
用户输入
↓
[Orchestrator] 调度员:只拆解、传递、汇总,不写正文
↓
[Researcher] → research_output (研究)
↓
[Writer] → draft (写作)
↓
[Editor] → 终稿 (审稿)
“你可以把多智能体想成一家小报社:
-
调度员(Orchestrator)——接稿、派活、盯进度,自己不动笔
-
研究员(Researcher)——只负责把主题拆成要点和写作建议
-
-
编辑(Editor)——只盯住最关键的一条问题,改出终稿
Plan-and-Execute 是一个人对自己列清单再干活;多智能体是几个人按工种协作。前者解决’步骤乱’,后者解决’角色糊’。”
| 维度 |
单 Agent(含 Plan) |
多智能体流水线 |
| 谁做决策 |
同一个模型角色 |
Orchestrator 调度,Worker 各司其职 |
| 提示词 |
一个万能 System Prompt |
每个 Worker 有专属人设与温度 |
| 上下文 |
消息链或 prior_results |
Task.context 显式传递上游产物 |
| 失败策略 |
单点失败常整单挂掉 |
可重试;编辑失败可回退初稿 |
| 适合场景 |
工具调用、调研问答 |
内容生产、多角色协作流程 |
小明点点头:”所以多智能体的关键不是’多几个模型’,而是职责边界 + 交接协议。”
“对。代码里这两件事分别落在 agents/ 和 protocol.py。”
代码实现:先定协议,再组流水线
老张打开写作助手writing_assistant项目目录:
writing_assistant/
├── main.py
├── protocol.py ← Agent 间通信协议
├── llm.py
└── agents/
├── orchestrator.py ← 调度员
├── researcher.py
├── writer.py
└── editor.py
1. Task / TaskResult:Agent 之间只认这两张单
@dataclass
class Task:
"""Orchestrator 下发给 Worker Agent 的任务单。"""
task_id: str
agent_name: str
instruction: str
context: dict[str, Any] = field(default_factory=dict)
created_at: float = field(default_factory=time.time)
@classmethod
def new(cls, agent_name: str, instruction: str, **context: Any) -> "Task":
return cls(
task_id=str(uuid.uuid4())[:8],
agent_name=agent_name,
instruction=instruction,
context=dict(context),
)
@dataclass
class TaskResult:
"""Worker 完成后回传的结果单。"""
task_id: str
agent_name: str
status: TaskStatus
output: str
duration_ms: int = 0
metadata: dict[str, Any] = field(default_factory=dict)
“看清楚:Worker 不互相调用。研究员不认识写手,写手也不认识编辑。大家只认 Orchestrator 发来的 Task,办完交回 TaskResult。
context 才是上下文传递口——研究报告塞进 research_output,初稿塞进 draft。增删一个 Worker,只要 Orchestrator 改派活顺序,其他模块不用改。这叫松耦合。”
小明:”那 Task.new(..., research_output=...) 里的关键字参数呢?”
“工厂方法把 **context 打进 context 字典。调度时写得短,Worker 取时用 task.context.get("research_output")。”
2. 三个 Worker:同一套接口,三套人设
研究员温度偏低,偏稳:
class ResearcherAgent:
def run(self, task: Task) -> TaskResult:
topic = task.instruction
prompt = f"写作主题:{topic}\n\n请帮我分析这个主题,整理关键要点和写作建议。"
messages = [system(_SYSTEM_PROMPT), user(prompt)]
try:
output = chat(messages, temperature=0.3)
return TaskResult.success(task, output=output)
except Exception as e:
return TaskResult.failure(task, reason=str(e))
写手温度偏高,敢发挥;并且强制吃研究报告:
class WriterAgent:
def run(self, task: Task) -> TaskResult:
topic = task.instruction
research = task.context.get("research_output", "(无研究资料)")
prompt = f"""\
写作主题:{topic}
以下是研究员整理的参考资料,请基于这些内容写出完整文章:
{research}
直接输出文章正文。
"""
messages = [system(_SYSTEM_PROMPT), user(prompt)]
try:
draft = chat(messages, temperature=0.8)
return TaskResult.success(task, output=draft)
except Exception as e:
return TaskResult.failure(task, reason=str(e))
编辑只改最关键的一条,再吐终稿——避免改成「十条意见、一篇没改」:
_SYSTEM_PROMPT = """\
你是一位严格但高效的文章编辑。
你的工作分两步:
1. 给出【一条】最重要的改进建议(一句话,直接说问题在哪、怎么改)
2. 紧接着输出改进后的完整文章全文
...
"""
def _extract_final(editor_response: str) -> str:
marker = "【终稿】"
if marker in editor_response:
return editor_response[editor_response.index(marker) + len(marker):].strip()
return editor_response.strip()
“三个 Worker 的 run(task) -> TaskResult 签名完全一样。差别只在 System Prompt、温度,以及从 context 里取什么。接口统一,才能被 Orchestrator 当成可替换零件。”
3. Orchestrator:不写字,只调度 + 重试
class OrchestratorAgent:
MAX_RETRIES = 2
def run(self, user_request: str) -> str:
print(f"\n[Orchestrator] 收到任务:{user_request}")
research_result = self._run_with_retry(
agent=self._researcher,
agent_name="researcher",
instruction=user_request,
)
if research_result.status == TaskStatus.FAILED:
return f"研究阶段失败:{research_result.output}"
write_result = self._run_with_retry(
agent=self._writer,
agent_name="writer",
instruction=user_request,
research_output=research_result.output,
)
if write_result.status == TaskStatus.FAILED:
return f"写作阶段失败:{write_result.output}"
edit_result = self._run_with_retry(
agent=self._editor,
agent_name="editor",
instruction=user_request,
draft=write_result.output,
)
if edit_result.status == TaskStatus.FAILED:
print(f"[Orchestrator] Editor 失败,保留初稿。")
return write_result.output
return edit_result.output
“三处设计值得记:
-
上游输出变下游输入——
research_output、draft 显式传递,不靠’大家共享一份巨大 messages’
-
编辑可降级——审稿挂了,至少交初稿,不把整单打成零
-
每步可重试——
_run_with_retry 失败睡 1 秒再试,最多额外 2 次”
def _run_with_retry(self, agent, agent_name, instruction, **context_kwargs) -> TaskResult:
last_result: TaskResult | None = None
for attempt in range(1, self.MAX_RETRIES + 2):
task = Task.new(agent_name=agent_name, instruction=instruction, **context_kwargs)
result = agent.run(task)
if result.status == TaskStatus.DONE:
return result
last_result = result
if attempt <= self.MAX_RETRIES:
print(f" [{agent_name}] 第{attempt}次失败:{result.output},1s 后重试...")
time.sleep(1)
else:
print(f" [{agent_name}] 已达最大重试次数,放弃。")
return last_result
入口很薄——用户一句话,全交给调度员:
orchestrator = OrchestratorAgent()
article = orchestrator.run(request)
print(article)
完整运行示例
“看一次真实流水线:”
python main.py "帮我写一篇关于人工智能的科普文章"
[Orchestrator] 收到任务:帮我写一篇关于人工智能的科普文章
[Orchestrator] → 交给 Researcher...
[Orchestrator] Researcher 完成(18240ms)
[Orchestrator] → 交给 Writer...
[Orchestrator] Writer 完成(25610ms)
[Orchestrator] → 交给 Editor...
[Orchestrator] Editor 完成(19880ms)
[Orchestrator] 全部流程完成。
============================================================
**剥开AI的魔法外衣:普通人如何驾驭这场技术狂飙?**
想象这样一个场景:你坐在电脑前,面对一份毫无头绪的季度汇报...
(终稿:开头抓场景 → 讲清概率本质 → 划清能力边界 → 给普通人行动建议)
============================================================
老张说:”注意日志里的箭头——你看到的是调度轨迹,不是某个万能 Agent 的内心独白。研究员先把角度和要点钉死,写手再发挥,编辑最后只拧最关键的那一颗螺丝。所以终稿不会一会儿论文腔、一会儿口号腔。”
小明:”如果 Editor 挂了呢?”
“代码会打印 Editor 失败,保留初稿,然后把 Writer 的稿子直接返回。生产里这叫有损降级:宁可少一道工序,也不整单空白。”
单 Agent vs 多智能体:别混成一件事
| 问题 |
单 Agent + Plan |
多智能体写作流水线 |
| 解决什么 |
步骤顺序与用户确认 |
角色专精与产物交接 |
| 谁持有「人设」 |
一个 System Prompt |
三个专职 Prompt |
| 中间产物形态 |
计划步骤 / 工具结果 |
研究报告 / 初稿 / 终稿 |
| 扩展方式 |
加计划步骤或工具 |
加 Worker + 改 Orchestrator 派活 |
| 典型失败 |
计划对了,文风仍混杂 |
某环失败(可用重试/降级兜住) |
常见坑
坑 1:Worker 互相直接调用
症状:Writer 里 import ResearcherAgent,自己去跑研究。
原因:耦合回单 Agent,协议形同虚设,重试和降级都没法统一做。
修法:Worker 只实现 run(task);谁先谁后只写在 Orchestrator。
坑 2:上下文靠「口头提醒」而不是 context
症状:只把主题传给 Writer,研究报告丢在日志里。
原因:模型看不到上游产物,等于重新开写。
修法:像课件一样显式传 research_output=research_result.output。
坑 3:编辑提十条建议却不改稿
症状:输出全是意见列表,没有可用正文。
原因:编辑 Prompt 太「顾问」、太发散。
修法:强制「一条建议 +【终稿】全文」,再用 _extract_final 抠正文。
总结
老张说:”多智能体不是把模型多开几个窗口,而是把组织方式写进代码。”
“三个核心理解:
-
Orchestrator + Worker —— 调度员不写内容,Worker 不互相认识;边界清楚,才好扩展
-
Task / TaskResult 协议 —— 任务单带
instruction + context,结果单带 status + output;流水线靠协议传话,不靠共享大脑
-
重试与降级 —— 研究/写作失败可重试;编辑失败可回退初稿,保证链路韧性”
系列走到这里:
Function Calling → 会用工具
↓
工具注册表 → 方便用很多工具
↓
MCP → 用全世界的工具
↓
记忆 → 记住对话历史
↓
ReAct 循环 → 自己决定用几次、用什么
↓
Plan-and-Execute → 先规划后执行
↓
多智能体 → 多角色流水线协作
↓
完整 Agent → 能力合一 + 可观测上线
小明说:”我明白了。Plan 解决’先想清楚再做’;多智能体解决’别让一个人同时当记者、作家和主编’。”
“对。下一篇可以把记忆、ReAct、Plan、多 Agent 收进一个完整项目——再补上评估与日志,才算往生产靠近。”
Agent 的进化路线:会调用工具 → 会自主决策 → 会规划执行 → 会分工协作。从「能干的个体」走到「可编排的团队」。