第 1 篇(为什么不是调一次 API)里我列过本系列大纲,说好要讲「多智能体编排」。前面 5 篇把单 Agent 的循环、LangGraph 编排、SSE 流式、生产级运行时讲完了,这一篇补上最后一块:什么时候不该用「一个大 Agent 包打天下」。
以下代码均来自玄镜(OracleMind)真实源码:
oraclemind-ai/src/agent/home_agent.py、harness/boundary/langgraph_nodes.py。示例省略导入与模型初始化;create_react_agent的参数名(system_prompt/prompt/state_modifier)以你当前 LangChain / LangGraph 依赖版本为准。
一、单 Agent 的「撑死」临界点
玄镜的 home_agent 是首页通用入口:负责闲聊 / 通用问答,必要时调排盘工具。最初它就是用 LangGraph 的 ReAct 把几个工具挂上去:
ini
# 早期示意:实际工具数后来涨到十几个
tools = [bazi_paipan, ziwei_paipan, name_generate, name_detail]
agent = create_react_agent(
model=model,
tools=tools,
system_prompt=HOME_AGENT_SYSTEM_PROMPT,
)
看起来很优雅------一个 Agent 通过 ReAct 自己决定调哪个工具。但当工具数涨到十几个、每个工具还带着「无生辰禁止排盘」之类的强约束时,问题来了:
- 系统提示越写越长,模型漏掉硬约束的概率上升;
- 工具返回的大段排盘 JSON 挤占上下文,后面的解读质量肉眼可见地下降;
- 跨领域术语互相污染 prompt:塔罗的「逆位」和八字的「逆克」根本不是一回事,塞同一个提示词里模型会串味。
于是玄镜拆成了一个入口 Agent + 多个专用 Agent:
home_agent:首页通用入口,负责闲聊 / 通用问答;命理排盘调用工具或转交ming_agent:八字、紫微等命理排盘与解读专用 Agentreport_agent/scale_report_agent:报告、量表生成assessment_agent:评估类
每个专用 Agent 的工具集和提示词都收窄到自己的领域,互不干扰;入口 Agent 只做路由和通用问答。
二、拆完怎么保证「不可靠的模型」不把流程搞崩
多 Agent 最怕某一个环节抽风,导致整条链路在用户面前崩掉 。玄镜把「生成 → 校验 → 重试/降级」做成了可复用的 harness(langgraph_nodes.py)。
拆成多个专用 Agent 后,每个子 Agent(如 ming_agent)的生成节点后都接同一套 boundary harness;入口 Agent(home_agent)只接收已校验的结构化结果,不直接透传子 Agent 的原始输出。
真实源码里这套节点由 make_boundary_nodes(validator, max_retries=2) 工厂生成;下面为了看清路由逻辑,直接展示等价的 route_boundary 和状态字段:
csharp
# 结构示意:state 至少包含
# validation: {ok: bool, reason: str} # 校验器 validator 的硬校验结果
# feedback: str # 回灌给 generate 的错误信息
# retry_count: int # 已重试次数
# degraded: bool # 是否已进入降级(由 degraded_output 节点写入)
MAX_RETRIES = 2
def route_boundary(state):
v = state.get("validation", {})
if v.get("ok"):
return "valid"
if state.get("retry_count", 0) >= MAX_RETRIES: # 重试耗尽
return "degraded"
return "retry"
graph.add_conditional_edges("boundary_check", route_boundary, {
"valid": END,
"retry": "generate", # 带 feedback 重新生成
"degraded": "degraded_output", # 模板兜底 / 只返回已校验字段
})
闭环职责:
boundary_check节点:调用校验器 validator 做格式 / 越界硬校验 ,写validation;校验失败时 把 validator 的reason写入feedback,并递增retry_count;- 路由:校验通过 →
valid结束;未过但还能重试 →retry(把feedback塞回 prompt 重来);重试耗尽 →degraded; degraded_output节点:模板兜底 / 只返回已校验字段 / 转人工,尽量避免把未校验内容直接透出。
注意这套回路叫「生成 → 校验 → 重试/降级」,降级是其中的一环,不是额外步骤。
三、判断清单:该拆还是该合
| 判断信号 | 建议 |
|---|---|
| 工具 ≤5、同领域、提示词稳定(经验阈值,非硬规则) | 单 ReAct Agent 够用(如 home_agent) |
| 术语 / 约束互相污染、工具返回挤爆上下文、失败模式不同 | 按领域拆专用 Agent |
| 关键输出需格式 / 越界硬校验 | 套 boundary harness |
| 多 Agent 要传结构化上下文 | 用 StateGraph 的 state 显式传,别靠 prompt 隐式猜 |
四、拆也有代价,别无脑拆
拆 Agent 不是免费的:会多出路由、状态传递、延迟、调试成本,以及跨 Agent 的 token 开销。如果只是「工具多但同领域」,先试这三招,往往就够了:
- 工具分组:把同类工具收敛,提示词里明确「什么场景用哪组」;
- 压缩工具返回 :排盘 JSON 上万字符,先压成千字符级的摘要再注入(玄镜的
_compact_paipan_summary就是干这个的); - 提示词分层:通用规则放系统提示,领域规则放工具描述。
只有当出现术语污染 / 约束冲突 / 上下文挤占 / 失败模式不同 ,且上述手段压不住时,再拆专用 Agent。一个很实用的铁律:子 Agent 不要把原始排盘 JSON 全量回灌主 Agent,只回传结构化摘要和已校验字段------这正好呼应前面「大段 JSON 挤占上下文」的痛点。
写在最后
这是「从0搭 AI Agent」系列的收官篇。玄镜是我独立从 0 到 1 交付的生产级 AI 产品(LangGraph 多智能体 + RAG + SSE 流式,FastAPI + Next.js)。我目前在看前端 + AI 应用 / 全栈 AI 方向的机会,欢迎交流。
接下来开一个新系列聊 RAG------玄镜没用裸向量检索,而是加了一层「特征加权」,原因和做法下篇展开。
下一篇(新系列「RAG 实战」第 1 篇):RAG 别只会向量检索:玄镜为什么加了「特征加权」