瞬维AI把Agent工具调用准确率从60%提到95%的四个土办法
生产环境的真实困境
做AI Agent的人,演示时都很光鲜:问天气能查天气,问订单能查订单,问啥答啥。但真部署到生产环境,问题就来了——Agent经常乱调工具。
用户想查订单,它调了发邮件的工具;用户想订会议室,它给你查天气。我们刚开始落地的时候,工具调用准确率只有60%,意味着每10次调用就有4次出错,根本没法给用户用。
后来花了三个月,把准确率提到了95%。不是什么高深算法,就是四个工程上的土办法,一个一个往上叠。
第一招:工具描述要写"什么时候别用"
大部分人写工具描述,只写这工具是干嘛的:
"这是一个查询用户订单的工具,输入用户ID和订单号就能返回订单信息。"
这样写完全不够。大模型看到"订单"两个字,所有跟订单沾边的问题都想往这工具上套。用户问"这个商品多少钱",它也去调订单查询;用户问"物流政策是什么",它还是去调订单查询。
关键改动:在描述里加负向约束,写清楚什么时候不能用。
优化后的描述变成这样:
"查询用户订单信息,输入用户ID和订单号返回订单状态。注意:不要用本工具查询产品价格、物流政策,这些问题属于产品知识库范围,请调用知识库查询工具;如果用户没有提供订单号,不要调用本工具,先向用户询问订单号。"
就加了这几句话,准确率直接涨了25%。大模型知道了边界在哪,不会什么题都往一个工具上硬套。
代码层面的对比很明显。优化前的JSON Schema只有功能描述:
{
"name": "query_order",
"description": "这是一个查询用户订单的工具,输入用户 ID 和订单号就能返回订单信息",
"parameters": {
"type": "object",
"properties": {
"user_id": { "type": "string", "description": "用户 ID" },
"order_id": { "type": "string", "description": "订单号" }
},
"required": ["user_id", "order_id"]
}
}优化后的版本多了明确的排除场景:
{
"name": "query_order",
"description": "查询用户订单信息,输入用户 ID 和订单号返回订单状态。注意:不要用本工具查询产品价格、物流政策,这些问题属于产品知识库范围,请调用知识库查询工具;如果用户没有提供订单号,不要调用本工具,先向用户询问订单号。",
"parameters": {
"type": "object",
"properties": {
"user_id": { "type": "string", "description": "用户 ID" },
"order_id": { "type": "string", "description": "订单号" }
},
"required": ["user_id", "order_id"]
}
}背后的道理很简单:大模型做工具选择是个分类问题,只给正例它容易过拟合,给了负例才知道哪些样本要排除。这跟训练分类器是一个逻辑,只是换了个形式写在提示词里。
第二招:工具别拆太细,能合并就合并
很多人设计工具有个误区,觉得颗粒度越细越好。查订单是一个工具、改订单是一个工具、取消订单是一个工具,恨不得每个操作都独立出来。
我们最开始也是这么干的,订单相关就拆了3个工具,整个系统12个工具。结果准确率很差,大模型经常在"查/改/取消"之间选错。
问题根源:工具越多,候选空间越大,选错的概率越高。
后来把订单相关的三个操作合并成一个order_operation工具,通过action参数区分具体操作。工具总数从12个砍到6个,准确率涨了18%。
合并前的Schema是三个独立工具:
[
{
"name": "query_order",
"description": "查询订单信息,输入用户 ID 和订单号返回订单状态",
"parameters": {
"type": "object",
"properties": {
"user_id": { "type": "string", "description": "用户 ID" },
"order_id": { "type": "string", "description": "订单号" }
},
"required": ["user_id", "order_id"]
}
},
{
"name": "update_order",
"description": "修改订单信息,如改收货地址、改数量",
"parameters": {
"type": "object",
"properties": {
"order_id": { "type": "string", "description": "订单号" },
"address": { "type": "string", "description": "新的收货地址" },
"quantity": { "type": "integer", "description": "新的商品数量" }
},
"required": ["order_id"]
}
},
{
"name": "cancel_order",
"description": "取消订单",
"parameters": {
"type": "object",
"properties": {
"order_id": { "type": "string", "description": "订单号" },
"reason": { "type": "string", "description": "取消原因" }
},
"required": ["order_id"]
}
}
]合并后变成一个统一入口:
[
{
"name": "order_operation",
"description": "对订单执行统一操作,通过 action 参数区分是查询、修改还是取消",
"parameters": {
"type": "object",
"properties": {
"action": {
"type": "string",
"enum": ["query", "update", "cancel"],
"description": "要执行的操作:query 查询、update 修改、cancel 取消"
},
"order_id": { "type": "string", "description": "订单号" },
"user_id": { "type": "string", "description": "用户 ID(query 时必填)" },
"address": { "type": "string", "description": "新的收货地址(update 时使用)" },
"quantity": { "type": "integer", "description": "新的商品数量(update 时使用)" },
"reason": { "type": "string", "description": "取消原因(cancel 时使用)" }
},
"required": ["action", "order_id"]
}
}
]维护成本也降了。原来三个工具要分别写描述、分别调接口,现在一个入口按参数分发,代码量少了一半。
当然,合并也有边界。完全不相关的操作不能硬凑,比如"查天气"和"查订单"肯定不能合并。我们的经验是:同一业务域、操作互斥的可以考虑合并,跨域的保持独立。
第三招:调用前加一层校验兜底
大模型选好工具、填好参数之后,不要直接执行。加一层校验逻辑,问两个问题:
- 这个工具真的是用户当前问题需要的吗?
- 参数填全了吗?格式对吗?
举个例子。用户问"我订单现在到哪了",大模型选了订单查询工具,但没填订单号。这时候如果直接执行,接口肯定报错。
我们的做法是:校验层发现order_id缺失,就不执行调用,反过来问用户:"麻烦提供一下你的订单号,我帮你查一下物流进度。"
这层校验能挡住20%的错误调用。很多时候大模型选的工具是对的,但参数填错了或者漏了,直接执行就崩。加个兜底,准确率立马上去。
校验逻辑可以做得轻也可以做得重。轻的就是检查必填参数是否存在;重的可以调意图识别模型二次确认。我们目前用的是中等力度:参数完整性检查 + 简单规则过滤(比如订单号格式校验)。
第四招:错例收集,持续迭代
工具调用准确率不是写完提示词就完事的。我们把所有调错工具的case都收集起来,定期分析:
- 是工具描述写得不清楚?
- 还是工具拆得太细导致选错?
- 还是大模型理解错了用户意图?
分析完把典型错例补到提示词里,每个月迭代一次。有时候是加一条负向约束,有时候是合并两个工具,有时候是调整参数描述。
三个月下来,提示词版本从v1迭代到v7,工具描述平均每版改十几处。准确率从60%慢慢爬到95%,没有哪一次改动是质的飞跃,就是持续堆细节。
错例收集的具体做法:线上日志打标,把"大模型选的工具"和"实际应该调的工具"做对比,不一致的进错例库。每周review一次,按错误类型分类统计,优先修高频错误。
几个踩过的坑
别迷信官方示例。 很多大模型厂商给的工具调用示例都很简单,真到复杂业务场景完全不够用。得自己摸索边界。
提示词长度有上限。 工具描述写太长,容易挤占上下文窗口。我们的做法是:核心约束保留,详细说明放文档链接,大模型需要时再检索。
多轮对话更复杂。 单轮问答的工具选择相对简单,多轮对话要考虑上下文。用户上一轮问了订单,这一轮说"帮我取消",大模型得知道指的是刚才那个订单。这种指代消解我们单独做了处理,没放在工具选择层。
不同模型差异大。 同样的工具描述,GPT-4和国产模型的理解能力不一样。我们针对主力模型做了优化,其他模型做降级处理。
最后说两句
Agent的工具调用看起来是大模型的"原生能力",但真到生产环境,准确率绝不是开箱即用的。从工具设计、描述写作到校验兜底,每一步都得做工程优化。
我们的四个方法没什么技术含量,就是笨功夫:写清楚边界、减少候选数量、加兜底校验、持续收集反馈。四个叠加,三个月从60%提到95%。
如果你的Agent还在乱调工具,不妨从第一招试试——在工具描述里加上"什么时候不要用",大概率能看到立竿见影的效果。