研究提案

[WIP] [v2] 工具选择决策数据:训练 Agent 的判断力而非记忆力

现有工具调用训练数据只记录「用了什么工具」,缺少「为什么选这个而不选那个」。本文提出一种基于三元组对比数据的构建方案,旨在让 Agent 学会工具边界的判断力,而非靠记忆背答案。

Agent 工具调用 训练数据 LLM 工具选择

1. 动机:工具选择错误是被低估的失败来源

1.1 失败有多普遍?

Toolbench 的基准测试显示,即使是 GPT-4 在多工具场景下的解决率也只有约 49%(Qin et al., 2023)。失败原因中,除了规划错误和参数填错之外,工具选择错误是一个常被归入”其他”类别而实际上相当普遍的问题——在工具数量超过 10 个的场景中,约 30% 的失败可归因于此。

典型失败模式有三类:

  1. 近义工具混淆:任务是「查询用户最近的操作日志」,Agent 调用了 get_user_profile 而不是 get_audit_log,因为两者描述里都有”用户信息”。
  2. 功能覆盖错误:任务需要执行一段有副作用的 shell 脚本,Agent 却调用了沙箱隔离的 execute_python,导致文件系统写入被拦截,任务静默失败。
  3. 语义漂移:工具名叫 search_document,但该版本实际上只搜索标题不搜正文,Agent 按字面意思理解后用错了工具。

这三类失败有一个共同特征:模型知道有这些工具,却不知道它们之间的边界在哪。

1.2 现有数据为什么不够?

主流工具调用数据集——ToolBench、APIBench(ToolEval)、ToolAlpaca——都有一个共同的结构性缺陷:只记录成功调用轨迹。

数据格式大致如下:

任务: "获取今天北京的天气"
调用: get_weather(city="北京", date="today")
结果: 成功

这条数据告诉模型”遇到天气任务就用 get_weather”,但没有告诉它:

  • 为什么不用 search_web(query="北京今日天气")?
  • 什么情况下 search_web 才是更好的选择?
  • get_weather 什么场景下会失效(比如查询历史数据或超范围城市)?

这就像只给学生看标准答案,不讲解题过程,也不告诉他们哪些看似合理的思路是错的。结果是:模型学到了模式匹配,而不是判断力。

1.3 核心论点

工具选择能力的本质是对工具适用边界的判断,而非对「任务→工具」映射的记忆。

一个真正具备工具选择能力的 Agent 需要能回答:「这个任务理论上有三个工具都能处理,为什么我选这个,而不选另外两个?」这是推理,不是查表。要训练推理,就需要包含对比决策过程的数据。


2. 与现有工作的对比

工作核心贡献数据层面的局限
ToolBench (Qin et al., 2023)大规模真实 API 调用数据集,16000+ 工具只有成功轨迹,无负样本,无拒绝理由
APIBench (Patil et al., 2023)细粒度 API 调用评测同上,缺乏跨工具对比场景
ToolAlpaca (Tang et al., 2023)多工具 Agent 指令微调合成数据为主,工具间无对比决策
Toolformer (Schick et al., 2023)自监督学习工具使用时机框架层面创新,数据中无”为什么不用别的工具”
ReAct (Yao et al., 2022)推理+行动交织的 Agent 框架思维链不涉及工具间的对比选择

本方案的差异化在于数据结构本身:引入决策三元组,把”为什么拒绝其他选项”作为一等公民写进训练数据,而不是附注或隐含信息。


3. 具体方案

3.1 什么是「工具选择困难场景」

并非所有工具选择都有训练价值——如果任务是「发送邮件」而工具池里只有 send_email,这条数据什么都教不了。

高价值场景的四类特征:

① 功能重叠型 多个工具都能”处理”同一类任务,但适用场景不同。典型例子:

  • search_web:搜索公开互联网内容,实时性强,无结构
  • search_knowledge_base:搜索内部文档库,结构化,覆盖有限
  • search_code:搜索代码仓库,支持语法感知查询

三个工具都能「搜索」,但选错了轻则结果不准确,重则完全找不到答案。

② 能力相近但约束不同型 功能几乎一样,但有某个关键约束使它们在特定场景下不可互换:

  • execute_python:沙箱隔离,无文件系统写入权限
  • run_bash:直接执行,有副作用,需谨慎

错误选择不会报错,只会静默失败或产生意外副作用——这是最难调试的一类错误。

③ 名称误导型 工具名称在语义上接近任务描述,但实际功能有偏差。比如:

  • get_file_content:获取文件内容(只读)
  • read_file:读取并解析文件(含格式转换)

这类场景专门测试模型是否真正理解了工具文档,而不是靠名称猜测。

④ 版本差异型 同名工具在不同版本有不同行为。这类场景主要用于测试模型能否利用工具描述中的版本信息做判断,而不是依赖参数名称的习惯性匹配。


3.2 数据构建流程

ToolBench/APIBench 现有数据
         │
         ▼
Step 1: 提取「任务 + 正确工具」对
         │
         ▼
Step 2: 自动生成 2-3 个语义相近的干扰工具
    (LLM 生成,约束:描述相似、参数相近、但功能边界不同)
         │
         ▼
Step 3: LLM 生成每个干扰工具被拒绝的具体理由
    (GPT-4o / Claude,prompt 要求给出结构化的拒绝依据)
         │
         ▼
Step 4: 质量验证
    ├─ 自动验证:语义相似度 > 阈值,理由长度 > 30 字
    └─ 人工审核:抽样 10%,验证拒绝理由是否准确
         │
         ▼
Step 5: 构造三元组并写入训练集
    {task, correct_tool, [{wrong_tool, rejection_reason}, ...]}

3.3 数据格式:以数据处理 Agent 为例

以下是一个完整的三元组示例,场景是数据工程师使用 Agent 处理 CSV 数据:

{
  "id": "dp-0042",
  "task": {
    "description": "用户上传了一个 500MB 的 CSV 文件,需要过滤出 age > 30 且 country == 'CN' 的行,并将结果写入新文件 output.csv",
    "context": {
      "file_path": "/workspace/users_data.csv",
      "row_count": 8200000,
      "available_memory": "2GB"
    }
  },
  "correct_tool": {
    "name": "run_pandas_chunked",
    "description": "按块(chunk)读取大型 CSV 文件,逐块过滤并追加写入目标文件,内存峰值可控。",
    "parameters": {
      "input_path": "/workspace/users_data.csv",
      "filter_expr": "age > 30 and country == 'CN'",
      "output_path": "/workspace/output.csv",
      "chunk_size": 100000
    }
  },
  "rejected_tools": [
    {
      "name": "run_pandas_full",
      "description": "将整个 CSV 文件读入 pandas DataFrame,执行过滤操作后写出结果。",
      "rejection_reason": "该工具会将完整数据集加载进内存。500MB 的 CSV 在 pandas 中展开后实际内存占用约 1.5-2GB,加上 filter 操作的临时副本,峰值极可能超过可用内存(2GB),导致 OOM 崩溃。应改用 chunked 读取方案。",
      "rejection_category": "资源约束"
    },
    {
      "name": "execute_python",
      "description": "在沙箱环境中执行任意 Python 代码片段,支持标准库和常见数据处理包。",
      "rejection_reason": "该工具运行在沙箱隔离环境中,对宿主机文件系统的写入操作会被拦截,无法将 output.csv 写入 /workspace 目录。任务要求写出结果文件,沙箱限制使该工具不可用于此场景。",
      "rejection_category": "权限约束"
    },
    {
      "name": "query_duckdb",
      "description": "使用 DuckDB SQL 引擎对本地文件执行 SQL 查询,支持直接查询 CSV/Parquet 文件。",
      "rejection_reason": "DuckDB 在技术上完全可以完成此过滤任务,且性能更好。但当前工具版本(v0.8.x)在写出超过 100MB 的 CSV 文件时存在已知的编码问题(issue #6821),输出文件中文字段可能出现乱码。在确认版本升级前,此场景应避免使用。",
      "rejection_category": "版本缺陷"
    }
  ],
  "metadata": {
    "difficulty": "hard",
    "rejection_categories": ["资源约束", "权限约束", "版本缺陷"],
    "source": "synthetic_from_toolbench",
    "human_verified": true,
    "annotator": "A03"
  }
}

这个示例体现了三种不同类型的拒绝理由——资源约束、权限约束、版本缺陷——避免了所有负样本都是同一类型的单调问题。


3.4 规模估算与质量控制

规模目标:初期 5000 条三元组,覆盖 20 个工具类别,每类别至少 200 条。

成本估算:

  • Step 2(干扰工具生成):约 1000 tokens/条,GPT-4o 价格约 $0.03/条
  • Step 3(拒绝理由生成):约 500 tokens/条/干扰工具,3 个干扰工具约 $0.045/条
  • 5000 条总生成成本:约 $375
  • 人工审核(10% 抽样):500 条,每条约 3 分钟,约 25 人时

质量控制机制:

阶段检查项工具/方法
干扰工具生成语义相似度 ≥ 0.75(与正确工具)sentence-transformers
拒绝理由长度 ≥ 30 字,包含具体约束词正则 + 关键词检测
逻辑一致性拒绝理由与工具描述不矛盾LLM 二次验证
人工抽样10% 样本人工确认标注平台 + 双人交叉验证

4. 关键技术难点

4.1 干扰工具的「甜蜜点」问题

干扰工具有一个难以把握的甜蜜点:必须足够接近正确工具,让模型觉得”这个可能对”;但又必须确实不正确,让人类也能说清楚为什么错。

太容易辨别(功能完全不同)→ 数据没有训练价值,模型看都不用看就能判断。 太难辨别(几乎完全一样)→ 人类标注者也会出错,拒绝理由质量无法保证。

目前最可行的做法是参数化控制相似度:在生成干扰工具时,约束语义相似度在 0.75-0.90 区间。低于 0.75 的扔掉,高于 0.90 的交人工处理。这个区间的确定本身需要通过小规模实验标定。

🔄 [v2 改进]

干扰工具甜蜜点的具体构造方法(四步流程)

上述「约束语义相似度在 0.75–0.90 区间」只是必要条件,不是充分条件。以下提供一套可执行的干扰工具构造流程:

Step 1:用工具文档语义相似度筛选候选干扰工具

对工具库中所有工具两两计算文档 embedding 的余弦相似度(建议使用 text-embedding-3-large 或同级模型)。选取 cos_sim > 0.6 且 < 0.9 的工具对作为候选干扰工具池。低于 0.6 的工具对语义差异过大,作为干扰工具太容易被识别;高于 0.9 的工具对过于相似,边界难以清晰表述。

Step 2:用任务执行结果验证干扰难度(实证筛选)

对候选干扰工具,用当前最强的模型(如 GPT-4o)尝试用该干扰工具完成目标任务:

  • 执行失败(任务无法完成、结果错误、出现预期外副作用)→ 确认为有效干扰工具
  • 执行成功 → 说明该工具实际上也能完成任务,不能作为干扰工具(需要重新检查任务条件或换工具)

这一步的目的是确保「干扰工具确实是错的」,而不只是语义接近但实际也可用。

Step 3:按错误类型分类干扰工具

将通过 Step 2 验证的干扰工具按失败原因分类,确保覆盖多种错误模式:

错误类型典型例子拒绝理由特征
功能相似但参数语义不同search_by_title vs search_fulltext功能看似一致,但搜索范围不同
范围重叠但有约束差异execute_python(沙箱)vs run_bash(全权限)核心功能相同,但权限/环境约束不同
上下文依赖不满足需要已建立连接才能调用的工具先决条件未满足,调用会静默失败

每条训练数据中建议覆盖至少 2 种不同错误类型的干扰工具,避免模型只学会识别单一类型的错误。

Step 4:构造有效的难度梯度负样本集

每个任务构造 2–4 个干扰工具,并按难度排序(easy → hard):

  • easy 干扰工具(cos_sim ≈ 0.65):功能差异较明显,拒绝理由一目了然
  • medium 干扰工具(cos_sim ≈ 0.75):需要理解工具文档才能辨别
  • hard 干扰工具(cos_sim ≈ 0.85):仅在特定约束条件下不可用,需要结合任务上下文判断

这种梯度设计支持课程学习(Curriculum Learning):训练初期多用 easy/medium 负样本,后期逐步增大 hard 负样本比例。

4.2 LLM 生成的拒绝理由可能本身是错的

让 GPT-4o 或 Claude 生成拒绝理由,存在一个递归问题:如果模型本身不清楚工具的边界,它生成的拒绝理由可能听起来合理但实际上是错的——而这恰好是我们想解决的问题。

缓解策略:

  1. 双模型交叉验证:用 GPT-4o 生成,用 Claude 审核(反之亦然),不一致的条目进入人工队列
  2. 工具文档锚定:生成时强制 LLM 引用工具文档中的具体字段(如 constraints.memory_limit),减少幻觉
  3. 反驳测试:生成拒绝理由后,再用同一个 LLM 尝试「反驳」该理由,若反驳成功则标记为低质量

4.3 跨工具集的迁移能力

在工具集 A(比如 ToolBench 的 API 工具)上训练的选择能力,能迁移到工具集 B(比如内部系统工具)吗?

这个问题目前没有确定答案,但有初步证据表明:如果拒绝理由是基于通用约束类别(资源、权限、版本、副作用),而不是基于工具的具体名称,迁移效果会更好。这也是为什么数据格式中专门设计了 rejection_category 字段——它强迫模型学习的是约束类别的推理模式,而不是特定工具的记忆。

初步实验方向:在 ToolBench 子集上训练后,测试模型在内部系统工具集(无任何重叠工具名)上的零样本选择准确率,与只在成功轨迹上训练的基线对比。


5. 数据来源

现有方案:ToolBench/APIBench 扩展

数据构建流程(见 3.2 节)以 ToolBench/APIBench 现有数据为起点,提取「任务+正确工具」对后扩展负样本和拒绝理由。这条路线的优势是起步规模大(ToolBench 有 16000+ 工具、49 个类别),但质量参差不齐,合成成分多。

🔄 [v2 改进]

双轨数据来源策略

单纯依赖 ToolBench/APIBench 合成扩展存在质量天花板:数据来源单一,工具类别偏向公开 API,覆盖不了真实 Agent 生产环境中的内部工具选择失败场景。建议采用双轨并行策略:

轨道 A:ToolBench/APIBench 合成扩展(规模优先)

  • 流程:按 3.2 节流程,从现有数据中提取并扩展负样本
  • 规模目标:3000–4000 条(占总数据 ~75%)
  • 优势:快速获得大规模初始数据集
  • 劣势:工具描述为文档级别、缺少真实执行失败记录,质量有限

轨道 B:真实 Agent 生产日志构造(质量优先)

  • 来源:从真实 Agent 系统的生产日志中提取工具选择失败案例(工具选了但任务失败、用户反馈选错了工具等)
  • 流程:日志过滤 → 人工标注正确工具和拒绝理由 → 结构化为三元组格式
  • 规模目标:1000–1500 条(占总数据 ~25%)
  • 优势:真实场景、真实失败模式,质量高
  • 劣势:依赖生产系统访问权限,收集周期长

轨道 B 作为轨道 A 的质量校准基准

两条轨道不应割裂,建议用以下方式校准:

  1. 从轨道 B 中随机抽取 100 条数据,找到与其任务类型最接近的 100 条轨道 A 数据
  2. 对比两组数据中拒绝理由的以下指标:
    • 拒绝理由平均长度(字符数)
    • 拒绝理由引用工具文档约束的比例(通过关键词匹配估算)
    • 人工评分:随机抽 20 条,每条由两名评审独立打分(1–5分,信息量+准确性)
  3. 量化两轨道的质量差距,作为轨道 A 数据的质量折扣系数(若差距过大,轨道 A 数据需额外过滤或降低训练权重)

6. 客观评价

真正有价值的地方

这个方案的本质价值在于数据结构的范式转变:从「任务→工具」的单向映射,变成「任务 + 工具对比 + 推理」的三元关系。这不是小改进,而是对「工具使用能力」这个问题本身的重新定义。

模型不应该知道「任务 X 用工具 Y」,而应该知道「在有工具 Y、Z、W 可选时,任务 X 选 Y 是因为…」

这种训练数据更接近人类专家在做工具选择时的思维过程——专家不查表,他们推理。

主要局限

构建成本偏高:相比单纯整理成功轨迹,每条数据需要额外生成 2-3 个干扰工具 + 拒绝理由 + 质量验证,成本约为传统数据的 3-4 倍。对于资源有限的团队,这是真实门槛。

干扰工具质量高度依赖生成模型:当前方案以 GPT-4o/Claude 为生成核心,如果这些模型本身对某类工具理解不足(比如高度专业化的领域工具),生成的干扰工具和拒绝理由质量会大幅下降。

领域依赖性强:在数据处理场景上训练出的工具边界判断,不一定能直接迁移到安全扫描或数据库运维场景。如果目标工具集变化较大,可能需要针对新领域重新构建数据,而不是复用现有集合。

适合什么团队

这个方案最适合以下情形:

  • 工具集相对固定(工具数量 20-200,不是每周大幅变动)
  • 已经有一批现成的成功轨迹数据(可以从 ToolBench 或自有日志提取)
  • 团队中有能力做小规模人工标注验证(不需要大型标注团队,10% 抽样即可)
  • 目标场景中工具选择错误是可观测到的痛点(比如 Agent 频繁选错工具导致任务失败)

不适合的情况:工具集极度动态(每天新增大量工具);工具描述本身质量很差(垃圾进垃圾出);团队没有任何人工验证资源。


7. 评估方案

🔄 [v2 改进]

严格的评估设计:验证「决策依据」的真实价值

本方案的核心主张是:包含拒绝理由(决策依据)的三元组数据,能让模型学会真正的工具边界判断力,而不只是受益于更大的训练规模。这个主张需要被严格验证,而不能只做整体性能比较。

第一层:ToolBench 标准评估集(基线比较)

在 ToolBench 官方评测集上测试训练前后的工具选择准确率,与以下基线对比:

  • 基线:只在 ToolBench 成功轨迹上 fine-tune 的模型(无负样本、无拒绝理由)
  • 本方案:在三元组数据(正样本 + 负样本 + 决策依据)上 fine-tune 的模型

预期结论:本方案在工具数量 > 10 的复杂场景下优势更明显;简单场景(工具数量 ≤ 3)可能无显著差异。

第二层:对抗性工具选择测试集(核心评测)

构造一个专门针对「工具边界判断」的测试集(与训练集工具不重叠):

  • 每道题提供 4 个功能相似的候选工具(1 个正确 + 3 个不同难度的干扰工具)
  • 测量模型选对正确工具的比例(@1 准确率)
  • 额外测量:模型给出的拒绝理由与标准答案的一致性(用 BERTScore 或人工评分)

测试集构造原则:干扰工具均通过 4.1 节 Step 2 的执行验证,确保「确实是错的」;覆盖资源约束、权限约束、版本缺陷、语义漂移四类干扰类型各 25%。

第三层:消融实验(验证「决策依据」的贡献)

这是最关键的实验,用于排除「性能提升只是数据量增加」的替代解释:

实验组训练数据预期表现
组 1:纯正样本只有正确工具调用轨迹基线,学会模式匹配
组 2:正样本 + 负样本正确轨迹 + 错误工具(无拒绝理由)若明显优于组 1 → 负样本有价值;若无差异 → 负样本质量不够
组 3:完整三元组正确轨迹 + 错误工具 + 拒绝理由应显著优于组 2 → 验证「决策依据」的核心价值

若实验结果显示「组 3 vs 组 2」差异不显著,则说明当前拒绝理由的质量不足以提供有效训练信号,需要回头改进拒绝理由的生成质量(而非增加数据量)。

评估结论的解读框架:

  • 组 3 >> 组 2 >> 组 1 → 方案验证成功,「判断力 > 记忆力」的假设成立
  • 组 3 ≈ 组 2 >> 组 1 → 负样本有价值,但拒绝理由质量需提升(优先改进 Step 3 的生成 prompt)
  • 组 3 ≈ 组 2 ≈ 组 1 → 三元组数据结构可能不是关键瓶颈,需重新审视任务设置或评测方式

8. 后续方向

  1. 动态难度调节:根据模型当前能力自动调整干扰工具的相似度阈值,形成课程学习(Curriculum Learning)
  2. 负反馈闭环:将 Agent 在生产环境中选错工具的真实案例直接转化为训练数据,比合成数据更有针对性
  3. 评测基准:构建配套评测集(与训练集工具集不重叠),专门测量「工具边界判断力」而不是工具调用成功率

本文为研究提案草稿,部分数字为估算值,待实验验证后更新。v2 基于批判性评审意见补充了干扰工具构造四步流程、双轨数据来源策略及严格消融实验设计。

原始版本:tool-selection-decision-training-data