从0搭AI Agent(六):什么时候该把任务拆给多个 Agent

0 阅读5分钟

第 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 把几个工具挂上去:

# 早期示意:实际工具数后来涨到十几个
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:八字、紫微等命理排盘与解读专用 Agent
  • report_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 和状态字段:

# 结构示意: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 开销。如果只是「工具多但同领域」,先试这三招,往往就够了:

  1. 工具分组:把同类工具收敛,提示词里明确「什么场景用哪组」;
  2. 压缩工具返回:排盘 JSON 上万字符,先压成千字符级的摘要再注入(玄镜的 _compact_paipan_summary 就是干这个的);
  3. 提示词分层:通用规则放系统提示,领域规则放工具描述。

只有当出现术语污染 / 约束冲突 / 上下文挤占 / 失败模式不同,且上述手段压不住时,再拆专用 Agent。一个很实用的铁律:子 Agent 不要把原始排盘 JSON 全量回灌主 Agent,只回传结构化摘要和已校验字段——这正好呼应前面「大段 JSON 挤占上下文」的痛点。

写在最后

这是「从0搭 AI Agent」系列的收官篇。玄镜是我独立从 0 到 1 交付的生产级 AI 产品(LangGraph 多智能体 + RAG + SSE 流式,FastAPI + Next.js)。我目前在看前端 + AI 应用 / 全栈 AI 方向的机会,欢迎交流。

接下来开一个新系列聊 RAG——玄镜没用裸向量检索,而是加了一层「特征加权」,原因和做法下篇展开。

下一篇(新系列「RAG 实战」第 1 篇):RAG 别只会向量检索:玄镜为什么加了「特征加权」