不要让模型控制一切:生产级 Agent 的确定性外壳与判断岛
Mason12 MIN
很多团队第一次构建 AI Agent 时,都会把完整任务写进提示词,再让模型自行决定下一步。这种方式在演示中往往很顺畅;到了生产环境,模型却常常同时承担业务规则、流程调度、异常处理和权限判断。本文想讨论一种更克制的分工:让确定性软件掌握流程,只在确有语义歧义的地方调用模型——我把它称为“确定性外壳与判断岛”。
先说结论
我越来越倾向于把生产级 Agent 看成一套普通软件系统:其中嵌入少量擅长处理歧义的智能节点,而不是由一个模型包办整条链路。
代码负责确定性,模型负责判断。程序持有控制流,模型只提出候选结论;执行端掌握权限,模型不能仅凭“认为应该做”就改变外部世界;证据不足时,系统应允许回答“不知道”,而不是用流畅的文字掩盖不确定性。
如果要把这一点压缩成一条原则,那就是:不要让模型重新推断程序已经知道的事情。把 token、延迟和不确定性,留给真正需要判断的部分。
模型为什么不适合充当流程控制器
语言模型擅长处理开放、含糊和非结构化的问题。它可以理解措辞各异的客户邮件,从冗长对话中提取诉求,也可以比较政策文本与具体个案之间的语义关系。
但路由、调度、状态变更和错误处理通常不是开放问题。如果业务规定“完成身份验证之后才能退款”,下一步无需模型推理;订单金额超过 5,000 元必须人工审批时,也不必让模型根据上下文猜测是否该升级。支付接口返回 500 后,重试、暂停还是转人工,同样应由明确的程序逻辑决定。
把这些工作交给模型,会同时引入三个问题。
首先是可靠性。假设一个包含 20 个步骤的 Agent,每一步独立正确的概率都是 95%。那么整条链一次完全正确的概率只有:
0.95²⁰ ≈ 35.8%真实系统中的错误未必相互独立,所以这不是精确的生产预测公式。它仍说明了一件值得警惕的事:局部看来很高的正确率,被串联到长任务中后,整体可靠性会很快下降。ByteByteGo 在近期的生产 Agent 实践总结中,也使用这个例子解释为什么模型不应该拥有无限制的循环和控制流。
其次是成本与延迟。自由运行的 Agent 每前进一步,通常都要再次调用模型。模型需要重新读取提示词、工具定义、历史消息和此前的工具输出,再判断下一步。即使只是“订单存在,因此进入资格判断”,系统依然为一次本可由程序完成的跳转付出了模型调用。
最后是可调试性。如果某次退款遗漏了身份验证,仅看最终结果很难追溯原因:提示词不够明确、上下文过长、模型误解工具返回,还是某个错误响应被当成了成功?当流程路径本身也由模型生成时,问题就更难稳定复现。
什么是“确定性外壳”
确定性外壳的核心,是把 Agent 的运行过程重新交还给普通软件。代码或工作流引擎负责保存状态、决定下一跳、限制循环次数、处理超时、执行审批、检查权限,并约束哪些外部动作可以发生。模型不再管理整条流程,而是作为一种特殊的计算节点存在。
一条退款流程可以变成这样:
接收请求
↓
查询订单
↓
检查固定条件
↓
[模型判断是否属于政策例外]
↓
程序校验判断结果
↓
金额与权限检查
↓
必要时人工审批
↓
执行退款
↓
回读支付状态
↓
发送通知并关闭工单在这条链里,查询订单、计算购买天数、检查金额阈值、申请审批、调用支付接口和确认最终状态,都不需要语言模型。真正值得交给模型的,可能只有一个问题:客户描述的损坏情况是否属于退款政策中含义较模糊的“制造缺陷”。
这个模型节点像一座嵌在确定性流程中的“判断岛”:它可以发挥语言理解能力,却不能自行改变整条流程的航向。
Google 在 2026 年 7 月发布的ADK 2.0 工程说明中,明确提出了类似思路:将执行路由与语言处理分开,让普通工具、人工审批和模型 Agent 共同组成工作流。
Google 展示的退款示例中,订单查询、退款和工单更新由确定性节点完成,只有政策分析和邮件撰写调用模型。在其使用模拟 API 的示意性测试中,单次运行的 token 从 5,152 降至 2,265,延迟从 7.2 秒降至 5.7 秒。需要强调的是,Google 将这些数据标注为说明性 benchmark,而不是对生产收益的普遍承诺。这里更有参考价值的,不是具体数字,而是收益的来源:模型不再花费 token 推断程序原本已经知道的事情。
如何判断一个步骤该放在哪里
面对一条 Agent 工作流,我会先按下面五类步骤拆开看。
- 01 / 数据查询
读取订单、账户状态、配置和政策版本。默认由代码完成。
- 02 / 确定性规则
日期计算、字段校验、权限检查和金额阈值。应写成明确程序逻辑。
- 03 / 语义判断
理解投诉意图、识别例外情况、总结非结构化材料。通常才适合交给模型。
- 04 / 审批
由用户、审核员或其他责任主体确认高风险动作。
- 05 / 状态变更
退款、发送邮件、修改记录或执行命令。应由受控的执行节点完成。
如果相同输入应当稳定地产生相同结果,而且规则能够被明确写出,就优先使用代码;如果输入含糊、非结构化,答案依赖语义理解,或现实情境无法穷举,再考虑模型。
例如,“订单是否在 30 天内”应该使用日期计算,而不是让模型阅读日期后作答。“客户描述的故障是否符合政策中的制造缺陷”则可能适合模型判断。
SincLLM 作者 Mario Alexandre 在编排模式实践文章中,将这个问题概括为“谁必须保留控制权”。已知分支适合由代码路由;只有当某个专家确实需要接管后续交互时,才应该 handoff;只有当一个中心 Agent 必须比较多个专家结果并负责最终答案时,才需要 manager 模式。
换句话说,设计不应从“我能放多少个 Agent”开始,而应先问:这里是否真的存在无法用普通程序表达的判断?
一个实用技巧:Judgment Island + Unknown Lane
仅把模型限制在判断岛中还不够。一个容易被忽略的设计是:给它留出一条“我不知道”的路径。
假设模型负责判断退款资格。如果它只能返回“符合”或“不符合”,证据不足时也会被迫二选一。模型的输出看起来确定,并不代表现实中的信息已经足够。更稳妥的输出结构可以是:
{
"decision": "eligible | ineligible | unknown",
"reason_code": "damaged_in_transit | outside_window | insufficient_evidence",
"evidence_refs": [
"message:18",
"policy:refund-v7#4.2"
],
"missing_fields": [],
"confidence_band": "high | medium | low"
}模型只产生候选判断。其余工作交还给应用程序:检查输出是否符合 JSON Schema,确认枚举值是否合法,验证引用的证据是否存在,再依照业务规则决定下一跳。
result = classify_refund(minimal_evidence)
decision = RefundDecision.model_validate(result)
if not evidence_exists(decision.evidence_refs):
route("manual_review")
elif decision.decision == "unknown":
route("request_more_information")
elif decision.decision == "eligible":
if refund_amount > AUTO_REFUND_LIMIT:
route("human_approval")
else:
route("issue_refund")
else:
route("explain_denial")这里的 unknown 就是 Unknown Lane。它可以通向补充信息、重新查询、人工复核或安全终止,而不是要求模型在证据不足时猜测。
模型不应返回任意工具名或下一节点名称;它只返回受限的业务结论,真正的路由仍由程序完成。否则,即使使用了结构化输出,也只是把自由文本形式的控制流换成了 JSON 形式。
几个容易踩中的坑
确定性外壳的概念并不复杂,实现时却很容易出现“表面确定、实际仍由模型控制”的情况。
宽松解析
最常见的问题是用字符串包含关系读取模型判断,例如:
is_eligible = "true" in model_output.lower()但 "not true"、"true only if approved" 同样包含 true。生产实现应使用严格的结构化输出、枚举或布尔字段,并拒绝未声明字段,而不是猜测一段自然语言的含义。
过度相信模型置信度
语言模型的 confidence: high 并不天然经过统计校准。置信度可以参与路由,却不应单独决定是否执行高风险动作。更可靠的依据包括证据是否完整、判断是否命中已评测场景、动作是否可逆,以及是否需要人工审批。
判断与执行混在同一个节点
如果负责“判断退款资格”的 Agent 同时持有退款工具,它仍可能跳过外部流程直接执行动作。有向图本身不会自动形成安全边界。危险工具应只暴露给专门的执行节点;凭证在执行端注入,权限也必须在副作用真正发生前重新校验。
外部世界并不确定
确定性工作流不意味着外部世界也确定。支付接口可能已成功扣款或退款,却在返回响应前发生网络中断;数据库状态也可能在模型判断后、动作执行前被其他系统改变。因此,确定性外壳仍需处理超时、重试、幂等键、持久化状态和最终结果回读。
它解决的是“谁决定下一步”,并不会自动解决所有分布式系统问题。
这是否意味着我们不再需要自主 Agent
并非如此。
开放式研究、复杂代码修改、故障调查和跨系统探索,通常很难预先画出完整路径。开发者不知道要查看多少文件,研究者也无法在搜索开始前预测所有子问题。在这类任务中,让模型根据环境反馈动态规划,仍然有明显价值。
Anthropic 在经典文章Building Effective Agents中,较清楚地区分了 workflow 与 agent:workflow 通过预定义的代码路径编排模型和工具;agent 则由模型动态决定过程和工具使用。前者适合路径明确、强调一致性的任务,后者适合步骤难以预先确定的开放问题。
即使采用自主 Agent,确定性外壳仍有意义。模型可以决定探索哪些文件、执行哪些只读查询,或如何修正方案;系统仍可掌握最大轮数、成本预算、工具权限、审批要求和停止条件。
因此,问题并不是在“固定脚本”和“完全自主”之间二选一。更合适的理解是一条控制权谱系:确定性代码覆盖已知部分,模型自主性只延伸到真正未知的区域。
衡量 Agent 的新指标:模型控制面
传统 Agent 演示常强调“它自动完成了多少步骤”。但在生产系统里,我认为更值得关注的是模型控制面的大小:
模型能够决定多少个下一跳?能够接触多少危险工具?一次错误判断能够改变多少外部状态?它是否可以绕过审批?低置信时是否存在明确退出路径?
模型控制面越小,系统通常越容易测试、审计和回滚。更换模型或修改提示词时,行为变化也更容易被限制在局部,不至于扩散到整条业务链。
这并不意味着应当一味压缩模型能力。模型自主性更像其他系统权限,应根据实际价值逐步授予:先从单个判断节点开始,通过评测确认它确实优于规则或简单分类器,再扩大它可处理的情况。只有当集中规划在真实任务中带来可测量的提升时,才值得引入 manager、多 Agent 或开放式循环。
事实:Google ADK 2.0 将执行路由与语言处理分开;其示意性退款 benchmark 中,token 与延迟均有下降,但作者明确标注为 illustrative,并非生产收益承诺。
事实:Anthropic 将 workflow 定义为预定义代码路径上的编排,将 agent 定义为由模型动态决定过程与工具使用。
作者观点:“谁必须保留控制权”比“我能放多少个 Agent”更值得先回答;已知分支应留在代码路由中。
我的推断:生产 Agent 的演进,可能会从“扩大模型自主性”转向“先缩小模型控制面,再在评测证明价值后局部扩大”。
延伸阅读
- Best Practices for Building AI AgentsByteByteGo
用长链条正确率衰减解释为什么模型不应拥有无限制的循环和控制流。
- Why we built ADK 2.0Google · 2026.07
将执行路由与语言处理分开,让普通工具、人工审批和模型 Agent 共同组成工作流。
- AI Agent Orchestration PatternsSincLLM · Mario Alexandre
从“谁必须保留控制权”出发,区分代码路由、handoff 与 manager 模式。
- Building Effective AgentsAnthropic
清晰区分 workflow 与 agent:前者走预定义路径,后者由模型动态决定过程。
这篇文章补上了什么
持久化执行回答“中断后如何从正确位置继续”,权限与审批规定“危险动作能否发生”。这篇文章想补上的,是另一个常被忽略的问题:当流程本身也可能由模型临时生成时,系统如何避免让下一步变成一次难以复现的猜测。
如果只保留一条原则,我会写成:不要让模型重新推断程序已经知道的事情。把 token、延迟和不确定性,留给真正需要判断的地方;让程序拥有控制流,让模型只在判断岛上提出候选结论,并为证据不足保留一条诚实的 Unknown Lane。