先说一下问题
做 AI 应用的时候,Function Calling 有个很头疼的问题——模型会乱调工具。
举个例子,你定义了一个queryOrder用来查订单。用户说"我要查订单",它调了,正常。但用户说"我想退单",它可能也调了queryOrder而不是cancelOrder。或者传参传了一个不存在的订单号,然后告诉你查不到。
这个问题在圈子里叫Function Calling 幻觉。不是模型坏了,是它在"猜"应该用哪个工具,而不是真的理解业务场景。
我这段时间在折腾这个,从各种资料和实验里整理了一套方案,分三个层面。
问题出在哪
模型的 Function Calling 逻辑大致是这样的:看了你写的 function 描述,比对当前用户输入,找语义上最匹配的那个工具调。
但有两个问题:
第一,描述是自然语言,模型的理解有边界。你写"查询用户信息",它不知道查询和修改的区别。你写"当用户想查看个人信息时调用",它可能把"查一下我的等级"也匹配进来。
第二,工具越多,准确率越低。我的测试结果,10 个以内的 function,准确率还行。超过 20 个,模型开始频繁选错。跟人一样,选项太多就容易纠结。
防御层:让模型少犯错
防御层是在调用发生之前做的优化,成本最低,效果最明显。
1. 描述里加"负面约束"
最常见的错误写法:
查询用户的订单信息模型拿到的信息太少,全靠猜。改一下:
当用户明确提供了用户ID或订单号,并且意图是查询订单状态或详情时调用。 如果用户没有提供具体的查询条件,不要调用,先反问用户要查什么。 不要用于修改订单、取消订单、查询其他用户的信息。核心改动是加了“什么时候不要调”的说明。实测下来,负面约束比正面描述更管用。
2. Few-shot 示例
在 System Prompt 里放几个调用示例,效果比优化描述都好。
正确的调用方式: 用户:"我最近的订单到了吗" → queryOrder(status='RECENT') 用户:"你好" → 不调用任何工具,直接回复 用户:"我的收货地址是什么" → queryUserAddress() 错误的调用方式: 用户:"你们客服电话多少" → ❌ 不应该调任何工具3 到 5 个就够,多了占上下文,少了覆盖不全。
3. 动态控制工具数量
不要让模型一次面对几十个工具。先做意图分类,判断用户大概想干什么,只暴露相关的工具给模型。
比如用户说"帮我查一下快递",系统判断属于物流类,只给模型暴露物流相关的 3 个 function,其他 20 个工具不进入这次上下文。
工具数量从 20 降到 3,模型选错的概率低很多。
纠正层:让模型错了能改
防御层再怎么做,模型还是会犯错。所以要在调用过程中加纠正机制。
1. 参数校验 + 反馈闭环
不直接信任模型生成的参数。在执行工具之前,先校验参数格式和范围。
比如orderId规定是ORD-开头,模型传了个12345。这时候不要直接报错,而是把校验失败的信息包装成一条系统反馈,再发给模型:
系统提示:你传入的 orderId 格式不对,必须是 ORD- 开头,请修正后重试。模型看到反馈后,通常会自己修正参数重新调用。这个过程完全自动,你只需要写校验规则和反馈模板。
但要注意一点:模型可能在同一个错误上反复循环,所以要限制重试次数,2 次差不多,超过就走降级。
2. 调用后的自反思
工具执行完后,让模型自己确认结果是不是正常。如果返回空数据或者报错,模型可以决定怎么处理。
比如模型调了天气查询接口,传了城市中文名但接口只支持拼音,返回为空。系统反馈异常信息,模型意识到参数错了,换成拼音重试。
在 Prompt 里加一句"调用工具后检查返回结果,异常时可以修正参数重试"就能启用这个能力。
兜底层:系统扛得住
防御和纠正都失效的时候,兜底保证业务不崩。
1. 重试上限 + 降级
同一个工具调用最多重试 2 次。超过了就走降级:
- 返回用户提示:“暂时查不到,请稍后再试”
- 记录异常日志
- 切到备选方案(不用模型,用关键词匹配)
2. 业务补偿
如果模型调错了工具产生了副作用,要有撤销能力。比如错误地发了一条通知、改了某个状态,得有一个对应的"撤销"操作。
这个比较复杂,适合核心业务链路。非核心场景走降级就行。
3. 人工确认
涉及写操作、权限变更、支付类的场景,可以做成半自动:模型生成调用请求后,先让用户确认再执行。形式上就是个确认弹窗,但能挡住大部分误操作。
实际项目怎么选
不是所有场景都需要三层全上:
| 场景 | 建议 |
|---|---|
| 纯问答、信息查询 | 防御 + 纠正 |
| 数据查询和报表 | 防御 + 纠正 + 重试降级 |
| 涉及写操作 | 三层全上 + 人工确认 |
防御层的投入产出比最高。改改描述、加几个示例,不需要改代码结构就能提升准确率。
最后
Function Calling 幻觉不会彻底消失,模型迭代会改善但不会根除。把"模型会犯错"当作默认前提来设计,比期待下一个模型版本能解决所有问题更实际。
防御层让它少犯错,纠正层让它错了能改,兜底层让系统不崩——三层加起来,多数场景就够用了。
—— 焦虑的饼干