一个 Agent 能把任务做完,和它能在生产环境里活下来,中间隔着一整套工程。

Google 最近复盘 AI Agents Challenge 的优秀提交,归纳出双向 MCP、事件驱动并发、同标准回退、分层路由四个模式。名单看起来不算新鲜,任何做过一点 Agent 的人都能认出里面的词。但我反而觉得,这次复盘最有价值的地方不在于又多了四个架构名词,而在于它戳破了一个常见误会。

很多团队把 Agent 当成更会调用工具的 Chatbot。给模型塞进搜索、数据库、代码执行和几个 API,然后看着它在 Demo 里完成一次漂亮的任务,就觉得离上线不远了。

离得其实很远。

真正难的部分不是让模型调用一次工具,而是让它在工具慢、消息重复、外部系统失败、用户中途改口、模型判断失误时,仍然不把事情弄乱。Java 和 Spring 开发者对这件事应该很熟。一个 Controller 能返回 200,不代表整个订单链路就可靠。Agent 也是同一回事,只是那个最不稳定的组件变成了语言模型。

这篇文章不打算把四个模式包装成万能配方。我更想拆开看看,它们各自在对付什么问题,哪些地方会变得别扭,以及如果你在 LangGraph 或 Spring 服务里做 Agent,第一版到底该从哪里下手。

先把一个错觉放下

不少 Agent 原型的路径很像这样。

用户提问,模型决定要不要调工具,工具返回结果,模型组织一段回答。整个过程没有明显的问题,日志也很干净。可一旦请求不是十秒内结束的单轮对话,原型的边界就开始漏水。

比如用户让 Agent 查一批订单异常,顺手生成工单,再通知值班同学。查库可能要几十秒,工单系统可能限流,通知服务可能重试,用户还可能在中途补一句「把华东区域排除掉」。如果你的状态只放在模型上下文里,补充条件该落到哪里。如果工具请求超时后重发,工单会不会重复建。如果模型已经开始分析旧数据,谁来阻止它把过期结论发出去。

这些不是模型能力问题。

它们是分布式系统问题,只是以前我们把状态机、幂等键和补偿事务写在服务代码里。现在,模型的计划和工具调用把状态转移变得不完全可预测,于是原来藏在框架里的麻烦,全都重新露出来了。

我一直觉得,Agent 工程最该警惕的一句话是「让模型自己判断」。模型当然应该判断语义和策略,但不能顺手接管权限、重试、数据一致性和审计。把这些都交给模型,短期看代码少了,长期看只是把确定性的约束换成了一次次祈祷。

双向 MCP 解决的不是工具数量

MCP 很容易被理解成模型调用外部工具的一套统一协议。这个理解没错,但只说了一半。

传统的工具调用是单向的。Agent 发起请求,工具返回结果,控制权回到模型。它很适合查天气、读文档、执行一次 SQL 这类短操作。可生产任务里,真正重要的事件经常不是由模型发起的。

支付状态变化、CI/CD 跑完、人工审核通过、库存告急、客服工单被补充,这些信号从系统外面主动涌入。Agent 如果只能轮询,体验和成本都会很差。更糟的是,轮询期间它不知道一条新事件是不是已经让原计划失效。

Google 提到的双向 MCP,值得借鉴的地方是把连接从「工具箱」变成一条持续的协作通道。Agent 可以发起动作,外部系统也可以把状态变化推回来。你可以把它理解成 Webhook 加上受控的上下文更新,而不是给工具定义更多 JSON Schema。

拿一个代码审查 Agent 举例。它提交检查任务后,不需要每隔五秒问 CI 有没有结束。CI 完成时,通过事件把构建编号、结论和日志摘要推回来。Agent 收到事件后再决定下一步,是分析失败原因、要求修复,还是等人工确认。

这里的关键不在「实时」这两个字,而在事件必须能被可靠地归属。每一个外部事件至少要带上 run_id、任务版本、事件序号和来源签名。没有 run_id,系统不知道它属于哪次执行。没有版本,旧任务的回调可能覆盖新任务的状态。没有序号或去重键,消息重复投递就会让 Agent 做两次同一件事。

这块很像 Spring 里消费 Kafka 事件。你不会因为消费者代码里只有一行业务逻辑,就不做幂等处理。Agent 也不该例外。

一个更稳妥的状态记录,可以长得很朴素。

1
2
3
4
5
6
7
run_id
objective_version
state
pending_actions
completed_action_keys
last_event_sequence
human_approval_required

模型负责解释事件的业务含义,状态存储负责裁决事件能不能推进流程。这个分工有点不浪漫,却很必要。

双向连接也有代价。它扩大了攻击面。外部系统能推送消息,就必须确认谁有资格推送、消息是否被重放、附件能否污染模型上下文。特别是把邮件、工单评论、网页内容直接转交给 Agent 时,提示词注入不是一个遥远的安全议题,它就是输入校验问题。

所以我不会建议一上来就做全双工 Agent。任务如果本来就是同步的查询或一次性生成,普通 MCP 调用足够。只有任务跨越多个系统、等待时间长、且外部事件真的会改变决策时,才值得把复杂度接进来。

并发不是多开几个子 Agent

第二个模式是事件驱动并发。它也很容易被误读成「把任务拆给更多 Agent」。这往往是事故的开端。

并发的价值,不是让一群模型同时思考,而是缩短那些彼此独立的等待时间。搜索资料、读取用户权限、拉取订单、检查库存,这些 I/O 操作如果没有依赖关系,就不该排成长队。模型推理通常已经够慢了,再把网络等待串行化,用户只会觉得 Agent 很笨。

但凡事都有边界。一个任务能并发,得先回答两个问题。

第一,它们是否读取同一份会变化的状态。第二,它们的副作用能否同时发生。

查询三个独立数据源,适合并发。三个 Agent 同时给同一张工单改优先级,就很荒唐。前者需要聚合结果,后者需要一个明确的写入仲裁者。

我更倾向于把 Agent 流程切成两种节点。读节点可以扇出,写节点必须收口。读节点拿到的是证据,写节点执行的是承诺。模型可以参与两边的判断,但不应该绕过那个收口点。

在 LangGraph 里,这个思路可以落为 fan-out 和 reducer。多个检索节点各自产出结构化结果,再由一个聚合节点把证据、来源和置信度合并。之后的执行节点只读取已经归一化的计划,不直接从各路原始输出里挑一句话就写库。

1
2
3
4
5
6
7
8
9
用户目标
└─ 计划节点
├─ 并发读取 A
├─ 并发读取 B
└─ 并发读取 C
└─ 证据聚合
└─ 策略判断
└─ 单一执行器
└─ 事件回写

这比「主 Agent 加三个子 Agent」少一点想象力,多一点可维护性。你能知道哪一步慢,哪一份证据缺失,哪个动作真的产生了副作用。排障时尤其重要。

还有一个经常被忽略的问题,取消。用户取消任务,不等于进程已经停下。已经发出的 HTTP 请求、队列里的工作和正在执行的浏览器操作都可能继续跑。一个合格的 Agent 需要 cancellation token,并且让每个工具适配它。取消后的回调也不能再推动状态机。

很多 Demo 从不展示这件事,因为它不好看。但用户在真实环境里一定会取消,尤其当 Agent 看起来卡住的时候。

回退要对齐结果,不是只换一个模型

Google 给出的第三个模式是同标准回退。这个说法很准确。

常见做法是模型 A 失败就换模型 B,工具 A 超时就换工具 B。问题在于,所谓成功的标准经常被悄悄改掉了。主路径能返回带来源的精确结果,回退路径只给一段猜测性的总结。主路径需要权限校验,回退路径为了「可用」跳过了校验。主路径写入前要求人工批准,回退路径直接执行。

系统表面上可用了,实际是把失败转成了更难发现的质量降级。

同标准回退要求你先写清楚输出契约。不是「尽量回答用户」,而是哪些字段必须存在,数据的新鲜度上限是多少,能否执行副作用操作,失败时要给出什么证据。任何回退路径都要满足同一份契约,做不到就应当明确失败,而不是假装完成。

比如一个企业知识库 Agent 的主路径是向量检索加权限过滤,回退路径可以改用关键词检索,但仍必须带文档链接、权限判断和引用片段。它可以召回少一点,不能把无权限内容混进去。再比如创建工单的主路径调用工单 API,回退路径可以生成待确认草稿,却不能改成发一封没有追踪号的邮件就算完成。

这话听着有点刺耳,但「诚实地失败」常常比「看似成功」便宜得多。特别在财务、客服、运维这些会留下后果的场景里。

从实现上看,回退策略最好不是写在 prompt 的一段自然语言里,而是写成可测试的策略表。

1
2
3
4
5
能力             主路径                 回退路径                 不可降级约束
知识检索 向量检索加重排 关键词检索 权限与引用必保留
代码修改 沙箱执行加测试 仅生成补丁 不直接合并
工单创建 API 创建 创建待审核草稿 不跳过审批
数据查询 实时服务 已标注时间的缓存 不伪装为实时

这里还有一个很现实的成本问题。回退不该只在错误后才出现。高成本模型处理所有请求,等它失败再切换,往往最浪费。更好的做法是根据任务风险、上下文质量和剩余预算提前选择路径。低风险的摘要可以走便宜模型,高影响写操作即使慢一点也要走更严格的验证。

这不是抠 token,而是把钱花在真正需要确定性的地方。

分层路由的重点是承认模型会看错

最后是分层路由。很多人一听路由,就想训练一个很聪明的分类器,自动决定每个请求该交给哪个模型、哪个 Agent、哪个工具。

理想很美,系统上线后常常发现,路由器本身成了最难解释的黑盒。它一旦错分,后面的链路再强也没用。

我觉得更实用的设计,是把路由分成三个层次。

第一层是确定性规则。权限、租户、数据地域、是否涉及写入、是否包含敏感操作,这些不该让 LLM 猜。用代码直接拦住。一个用户没有生产环境权限,模型写得再自信也不能拿到部署工具。

第二层是轻量语义判断。这里才适合小模型或分类器,用来判断用户是在问答、检索、分析,还是准备执行动作。它应该输出明确类别和置信度,而不是直接决定所有后续步骤。

第三层是昂贵的计划和推理。只有前两层把边界划好,复杂模型才进入。它拿到的不是无限工具列表,而是被当前身份、任务类型和预算筛过的一小组能力。

这个顺序很重要。先约束,再理解,再规划。

反过来做,先让大模型规划,再补权限和风控,团队很容易陷入在每个工具描述里堆规则的局面。Prompt 会越来越长,例外越来越多,最终谁也说不清一条动作为什么被允许。

对 Spring 项目而言,这个架构并不陌生。第一层可以是 Spring Security 和业务策略,第二层是意图分类服务,第三层才是 LangGraph 或 Agent Runtime。工具调用则通过一个明确的 execution gateway 发出去,在 gateway 上记录审计日志、限流、幂等键和审批状态。

别把这些基础设施看成 Agent 的外围配件。它们才是让 Agent 变得可控的骨架。

一条能落地的最小路径

如果你正在把一个内部 Agent 从 Demo 推向真实用户,我不建议一口气实现四种模式。那样大概率会做出一套很复杂、却没有真实任务验证的框架。

从一个有明确边界的长任务开始。比如「分析昨日失败的 CI,并生成修复建议」,不要一开始就做「帮我处理所有研发事务」。前者可以定义输入、权限、成功条件和人工出口,后者只会吸走所有需求。

接着补四件小但硬的东西。

  • 每次运行都有 run_id,所有工具调用和回调都能串回这次运行
  • 所有写操作都有 idempotency key,并且经过单一执行器
  • 每个结果都有来源、时间和状态,不拿缓存冒充实时结果
  • 每个高风险动作都能暂停,等待人确认

做到这里,你甚至还没有一个花哨的 multi-agent 架构。但你的系统已经比大量演示项目更接近可用。

然后再看真实的瓶颈。如果用户总在等外部系统,就引入事件回调和并发读取。如果模型成本压不住,就给不同任务加路由和预算。如果服务经常失败,就把输出契约和回退路径补齐。架构应该跟着故障和需求长出来,而不是跟着一张看起来很酷的图长出来。

这也是我从 Google 那四个模式里读到的共同点。它们没有把 Agent 当成一个无所不能的主体,而是把它嵌回了成熟的软件工程里。事件、状态、权限、回退、路由,这些老话题换了一个模型接口,依然是决定系统成败的东西。

Agent 的能力会继续变强,工具协议也会继续演进。但上线前那句最朴素的问题不会变。

当模型下一次判断错了,系统会发生什么。

如果这个问题你能从日志、状态机和权限策略里找到答案,才轮得到讨论它有多聪明。

参考资料

  • Google Developers Blog,4 engineering patterns behind the strongest AI Agents Challenge submissions,2026 年 9 月
  • Google AI Agents Challenge 公开复盘内容,双向 MCP、事件驱动并发、同标准回退、分层路由