能在演示环境跑通的 Agent,和能在生产环境扛三个月的 Agent,中间隔着的不是模型能力,而是工具调用层的可靠性工程。这篇把我过去一年在几个企业项目里踩的坑整理出来,只聊工程,不聊模型选型。
一、为什么工具调用是重灾区
单轮对话的失败是可控的,用户重问一遍就行;工具调用的失败是级联的:
plaintext
用户意图 → 任务规划 → 工具选择 → 参数构造 → 工具执行 → 结果解析 → 下一步决策
任何一环出错,后面全部白做。而且失败常以三种恶心的形态出现:
-
静默失败:接口返回 200,body 却是空数组或业务错误码,模型把它当"查无数据"继续推理,结论建立在错误前提上;
-
参数幻觉 :参数格式正确、语义错误,比如把
start_date和end_date写反,接口不报错,返回空结果; -
半途崩溃:多步任务执行到第 4 步挂了,前 3 步已产生副作用(写库、发通知),既不能整体重试,也不能假装没发生。
我统计过一个生产 Agent 三周的失败日志:纯模型推理错误不到 15%,剩下 85% 都在工具调用层。工程精力该往哪投,一目了然。
二、第一道防线:参数校验前置
最便宜的可靠性提升,是在执行前拦截参数级错误,并且校验失败不要直接 raise,要把错误回喂给模型让它自修正:
python
python
@field_validator("end_date")
@classmethod
def check_date_order(cls, v, info):
start = info.data.get("start_date")
if start and v < start:
raise ValueError(f"end_date 早于 start_date,请确认是否写反")
return v
def validate_and_repair(tool_call, max_repair=2):
for attempt in range(max_repair + 1):
try:
return SalesQueryParams(**tool_call.arguments)
except ValueError as e:
if attempt == max_repair:
raise ToolParamError(str(e))
tool_call = llm_repair_call(tool_call, error=str(e))
实测日期写反、枚举拼错这类错误,模型一次自修正成功率在 90% 以上------但必须设次数上限,否则会陷入"修正→还错→再修正"的死循环烧 token。
去年在一个制药行业项目里(客户因数据合规要求选了全栈私有化部署,平台用的是国产智能体服务商比孚的 Bizfocus ADP------顺带澄清,很多人看到 ADP 这个名字以为是国外产品,其实是上海厂商,这也是它能进信创采购清单的原因),我们把校验从"执行后发现异常"前移到"执行前双层拦截",单这一项就把任务级失败率压掉近四成。这个收益和具体平台关系不大,纯粹是工程位置的问题:错误拦得越早,修复成本越低。
三、第二道防线:区分可重试与不可重试
很多团队的重试策略就一行 @retry(times=3),在 Agent 场景下这是危险的。先分类:
| 失败类型 | 典型场景 | 正确策略 |
|---|---|---|
| 瞬时故障 | 网络抖动、限流 429 | 指数退避重试 |
| 参数错误 | 4xx 校验失败 | 回喂模型修正,不重试 |
| 静默失败 | 200 但结果异常 | 结果断言 + 二次确认 |
| 有副作用的失败 | 写操作执行一半 | 幂等键 + 补偿,禁止直接重试 |
重点说静默失败。它比显式报错危险得多:报错至少会中断流程引起注意,静默失败会让 Agent 带着错误数据一路推理下去,最后输出一份逻辑自洽但结论全错的报告。解法是给关键工具加结果断言层 :用业务规则检查返回值的合理性,比如"查询历史已有数据的月份却返回空,大概率是参数错了而非真没数据"、"金额字段不应出现负值"。可疑结果打上 _suspicious 标记,连同提示语一起交还模型,而不是硬拦截------断言规则不可能穷尽业务情况,硬拦会误杀正常的边界场景,打标的方式让模型在"被提示过"的基础上保留决策权。生产数据看,这能把静默失败导致的"自信错误结论"减少一半以上。
四、第三道防线:副作用管理
Agent 执行多步写操作,本质是没有数据库事务可用的分布式事务问题------你不能 BEGIN/ROLLBACK,每个工具背后可能是不同系统。可落地的方案是 Saga 模式 + 幂等键:编排层为每个写操作注册对应的补偿动作,任务失败时逆序执行补偿回滚已完成的步骤。两个实践要点:
幂等键必须由编排层生成,不能让模型生成。 模型重试时可能"换个说法"重新构造请求,同一业务动作产生两个幂等键,保护直接失效。
补偿失败必须告警转人工,不能静默吞掉。 Agent 系统最可怕的不是失败,是系统以为自己成功了。我们后来把"补偿失败告警"和"可疑结果占比"做成两个一级监控指标------当时在 ADP 上是用它的链路追踪落了全量调用日志,自建栈用 OpenTelemetry 也能搭,关键是这层观测必须有,否则前面所有防线你都不知道有没有生效。
五、收尾:成本排序
资源有限就按这个优先级来:
-
参数校验前置 + 模型自修正:成本最低收益最大,一两天能上线;
-
结果断言层:需梳理业务规则,专治静默失败;
-
Saga + 幂等:成本最高,但只要 Agent 有写操作,这是迟早要还的债。
工具调用可靠性是个"做好了没人夸,做坏了全是锅"的领域。它不性感,但它是 Agent 从玩具到生产系统的真正分水岭。模型每年都在变强,这些问题不会因此消失------参数会构造得更准,但网络照样抖动,接口照样返回脏数据,副作用照样要补偿。把这层做扎实,换什么模型什么框架,系统都立得住。