首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >12 个工具塞进一轮:模型只调对 4 个,还编了 2 个参数|PRIVO AI智能操作助手 三合一·网页+桌面+IDE

12 个工具塞进一轮:模型只调对 4 个,还编了 2 个参数|PRIVO AI智能操作助手 三合一·网页+桌面+IDE

原创
作者头像
用户12722577
发布于 2026-11-06 17:42:13
发布于 2026-11-06 17:42:13
1250
举报

工具描述不是写给人看的文档,是写给模型看的约束条件。这两件事的区别,我是在同一批 12 个工具上改到第三版才分清的。

一开始我以为 description 越详细越好,把每个参数的业务含义、返回值示例、异常码全写了进去。结果模型挑工具时错得更多——它把大段描述当成了"背景说明",直接跳过,只看函数名。

第一版:把文档搬进 description

12 个工具对应一套订单后台:查订单、改地址、退款、发券、查库存、批量导出、改价、加备注、拆单、合单、查物流、催发货。

第一版 description 平均 180 字,写了业务背景、权限说明、调用前置条件。60 次单轮任务跑下来:

  • 工具选错 26 次(43%)
  • 参数编造 19 次(32%)
  • 一次调对的 21 次(35%)

最典型的一次:任务写"把这个订单的收货地址改到杭州仓",模型调了 export_orders,参数里填了 region: "杭州"——它把"改地址"和"按区域导出"混成了一个动作。

第二版:加"必须""严禁",反而更糟

我的第二反应是加强语气。每条 description 前面加"你必须先……",参数说明里加"严禁传入……"。

选错率从 43% 降到 39%,几乎没动。参数编造率还升到 34%。

原因后来想明白了:否定式约束在工具列表里是噪声。模型一次看到 12 个工具、每个工具带 3 条"不要做 X",它记住的是 X 本身,不是那个"不要"。

第三版:把判断改成可照抄的条件

真正有效的改动是三件具体的事,跟措辞强弱无关。

一是工具名自带动词和对象。export_orders 改成 export_orders_to_csv,update_order 拆成 update_order_shipping_address 和 update_order_price。模型不再需要在两个语义重叠的工具之间做取舍。

二是description 第一句写"什么时候用",第二句写"什么时候不用",各一句,不超过 40 字。

三是枚举值直接列在参数里,不让模型自己映射。region 从前是自由字符串,改成 4 个固定取值,中文对照写进 description 末尾。

改完后的结构大致是这样:

代码语言:json
复制
{
  "name": "update_order_shipping_address",
  "description": "修改订单收货地址。仅当用户明确给出新地址时使用;只改联系方式请用 update_order_contact。region 取值:east=华东, south=华南, north=华北, west=西部分拣。",
  "parameters": {
    "type": "object",
    "properties": {
      "order_id": {"type": "string"},
      "region": {"type": "string", "enum": ["east", "south", "north", "west"]},
      "detail": {"type": "string", "maxLength": 120}
    },
    "required": ["order_id", "region", "detail"]
  }
}

跑测试的方式很土,但够用:同一批 60 条任务指令,每条跑一遍,记录三件事——选中了哪个工具、传了什么参数、是否命中预期。

代码语言:python
复制
import json, subprocess

CASES = [c.strip() for c in open("cases.txt", encoding="utf-8") if c.strip()]

stat = {"tool_ok": 0, "arg_ok": 0, "fabricated": 0}
for i, prompt in enumerate(CASES, 1):
    out = subprocess.run(
        ["python", "run_agent.py", "--prompt", prompt, "--tools", "tools_v3.json"],
        capture_output=True, text=True, timeout=120,
    )
    call = json.loads(out.stdout)["tool_calls"][0]
    ok_tool = call["name"] == EXPECTED[i]
    ok_arg = set(call["arguments"]) <= ALLOWED[call["name"]]
    stat["tool_ok"] += ok_tool
    stat["arg_ok"] += ok_arg
    stat["fabricated"] += not ok_arg
    print(f"{i:02d} {ok_tool!s:5} {ok_arg!s:5} {call['name']}")
print(stat)

三版对比:

版本

description 平均字数

工具选对率

参数合法率

编造参数

v1 文档式

180

35%

68%

19/60

v2 加语气词

210

39%

66%

20/60

v3 条件式 + 拆工具

62

87%

93%

2/60

选对率从 35% 到 87%,靠的不是模型变强,是把"需要模型推理的判断"变成了"可以照抄的枚举"。12 个工具、60 条用例、三版 schema,一共跑了 180 次,前后花了两天。

还有 13% 没解决

剩下的失败集中在两类。

一是跨工具任务。指令是"先退款再补一张等额券",模型能选对第一个工具,但不会主动串第二个。单轮多工具调用在 12 个工具的规模下,有串行依赖的任务成功率只有一半左右。

二是参数值的来源。模型知道要填 order_id,但没拿到订单号时会编一个格式完全正确的假号。这里我踩了个坑:最初给 order_id 加了 ^SO\d{10}$ 的正则校验,看起来是防守,实际帮了它——格式对了就过,业务层反而查不出来。后来把 pattern 去掉,让无效值在业务层报错,再由 Agent 把错误原文回灌给模型重试。宁可多一轮,也不要让它"格式正确地错"。

这套东西现在跑在 PRIVO 的桌面端里,同一份工具定义可以换模型复用,换模型时失败率会变化,工具选对率基本不动——说明约束写在 schema 里比写在 prompt 里更耐迁移。

局限

60 条用例是我按业务自己写的,不是真实用户分布,长尾指令覆盖不到。另外 12 个工具的结论不能外推到 40 个工具的场景:我试过 30 个工具,v3 的选对率掉到 61%,工具一多,名字再清楚也会互相干扰。工具规模到 20 个以上时,分组和分层调用比继续优化 description 更划算。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

目录
  • 第一版:把文档搬进 description
  • 第二版:加"必须""严禁",反而更糟
  • 第三版:把判断改成可照抄的条件
  • 还有 13% 没解决
  • 局限
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档