ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

分解生成+动态自反馈:低成本构建高质量工具调用合成数据

分解生成+动态自反馈:低成本构建高质量工具调用合成数据 “工具调用”已经不是什么新鲜词了。做 Agent 的人都知道模型再聪明不会按规范调用 API就永远是光说不练的纸面功夫。可真正要训练这种会调用工具的能力最卡脖子的不是模型结构而是训练数据——尤其是那种“用户问一句模型能正确选工具、填参数、处理多轮返回”的高质量配对数据。人工标注太贵真实日志又有隐私限制大家不约而同地转向合成数据。EMNLP 2026 方向上一个很有代表性的思路就是标题里“分解生成”和“动态自反馈”这两个词前者解决合成数据的组合空间怎么可控地撑开后者解决质量下限怎么不用人工也能守得住。这篇文章我会围绕这套方法论讲清楚设计逻辑、复现过程以及实操里踩过的坑。无论你是要投论文还是想把 Agent 的数据飞轮转起来这篇都能当参考。1. 先搞清楚工具调用数据为什么难搞1.1 工具调用能力在 LLM 生态里的位置大模型本身擅长的是“答”但真正的智能体需要“做”。所谓“做”就是用户提出需求之后模型能生成一段结构化的工具调用指令比如search_web(query2026年EMNLP截稿时间)、get_flight_info(departure北京, arrival上海, date2026-03-20)等到工具返回结果后模型再基于这些结果继续推理甚至引发第二次、第三次调用。这一来一回的“用户指令-工具调用序列-工具返回结果-最终回答”就是工具调用的完整闭环也是 Agent 类产品落地的核心技术路径。如果只是让模型背答案那它永远无法处理实时信息。可一旦引入工具调用就牵扯出训练数据问题模型必须学会三件事。第一什么情况下该调用工具什么时候不该调用比如闲聊就不要调用第二从多个可用工具里选出正确的那一个第三把用户话里隐含的信息准确抽取成工具参数哪怕是用户没有显式给出的默认值。这三件事每一件都需要大量的正反样本去训练。缺了这些数据模型就算在通用对话上表现再好落到真实业务场景里也像个只会背书、不会动手的书呆子。1.2 真实数据与人工标注的瓶颈我刚接手工具调用数据项目的时候第一反应也是“先去扒真实用户日志”。但实际一跑就发现这条路的成本远超预期。用户日志里虽然能挖出很多真实表达但绝大多数是闲聊、反问、信息不全的碎片能构成完整工具调用闭环的样本比例很低。更麻烦的是隐私问题涉及个人信息的对话日志根本没法直接用脱敏要花掉的工程时间比标注还多。人工标注这条路也走不顺。工具调用数据不是普通的文本问答标注员需要理解 API schema、参数约束、多轮依赖关系还要判断“某一步是否应该调用工具”。这就意味着标注员必须有很强的技术背景培训成本高而且不同标注员之间的一致性很难保证。我见过一个项目让懂技术的标注员去标“用户意图-工具序列”配对一天一个人最多也就产出几百条。对一个 SFT 数据集来说动辄需要数万条高质量样本算一笔账就知道纯人工几乎不可接受。1.3 合成数据的机遇与质量难题合成数据的思路很直接既然模型本身已经具备调用工具和生成结构化内容的能力那为什么不先定义一批工具、再让模型自己“出题”——也就是替我们编出用户指令和对应的工具调用序列这就是合成数据的核心逻辑。你可以把它理解成模拟训练真实战斗记录太少就先用演习案例把新兵的基本动作练扎实再上真实战场。但合成数据有一个挥之不去的痛点——质量不稳定。模型自己生成的数据经常会出现“看上去合理实际上经不起推敲”的样本。比如用户明明问的是“明天北京会不会下雨”模型给出的工具调用却是get_weather(北京, 2026-03-19)日期差了整整一天再比如工具明明接受city参数模型却填了一个country的值。更隐蔽的是语义漂移指令说的是“查一下张三的订单”工具却调成了“查张四的订单”。这类错误靠随机抽检很难发现因为整体分布太均匀了十条里面一条出错肉眼很难看出来但模型训练时却是实打实地学歪了。所以合成数据不是“生成一次就完事”而是一个需要精密设计和持续质检的生产线。标题里那两个关键词本质上就是在回答两个问题第一怎么让生成过程可控从而产生覆盖更广、结构更丰富的多样化数据第二怎么让质量校验从“抽样人工看”变成“全量自动化闭环”下面一个个拆开讲。2. 分解生成把一个大任务拆成可控的生成单元2.1 核心思路从“整体生成”到“分解生成”最早做合成数据大家都采用整体生成给模型一段系统提示让它“生成一条包含用户指令、工具调用、参数、回复的完整样本”。这种做法的优点是省事缺点也很快暴露出来——自由度太高模型不知道你真正想要什么样的组合。你想让数据覆盖“多工具顺序调用”的场景但整体生成时模型可能 80% 都在生成单工具场景你想让参数边界覆盖缺失值、非法值模型默认只会填合理值更麻烦的是一旦某条样本中用户指令和工具调用不匹配你还很难定位是哪个环节出的问题。分解生成的做法是把“生成一条完整样本”拆成若干个可控的小生成单元每个单元只负责一个明确的问题。还是那句话分而治之看着多几步实际上每一步都能校验、都能控分布整体兜得住。具体的分解方式我建议按下面这个逻辑来先定义工具集。工具集是整个生成空间的天花板工具都没有定义好后面一切都白搭。再定义任务分类体系。把工具可以支撑的用法分门别类地列出来。然后根据分类体系逐个单元地生成“用户指令片段”“推理过程片段”“工具调用序列片段”“参数片段”。最后把片段组合成完整样本。这个流程的本质是把一个高方差、低可控性的“大乐透生成”变成一系列低方差、高可控性的“定向下注”。2.2 “工具×场景×参数”三维解耦分解生成的第一步是给生成空间建立一个坐标系。我的经验是把空间拆成三个维度工具维度、场景维度、参数维度三个维度各自动态组合。工具维度解决的是“调用结构”问题。按工具的复杂程度可以分为单一工具调用最简单的一个指令只调一个工具。顺序多工具调用前一个工具的结果会影响后一个工具的参数比如先查天气再根据天气推荐穿搭。并行多工具调用同一时刻发起多个相互独立的工具请求比如同时查机票和酒店。条件分支调用根据用户信息或前一个工具结果决定走哪条调用分支比如“如果不忙就查最新论文否则提醒我明天要开会”。场景维度解决的是“语义环境”问题。不同的场景会带来完全不同的指令表达方式。同样是查天气命令式是“告诉我北京天气”委婉式是“我明天要去北京出差不知道穿什么合适”带约束式是“帮我看看这周末北京适不适合户外跑”。场景库的设计决定了生成数据的语义宽度这块偷懒后面的多样性一定崩。参数维度解决的则是“边界覆盖”问题。模型要能在各种输入条件下正确调用工具所以参数不能总是“合理且完整”。至少要覆盖边界值比如分页参数 page1、空列表、日期恰好是今天缺失值用户没有提供模型需要根据常识或对话上下文推断默认值类型混淆用户说“大概下周吧”模型需要把“下周”解析成具体日期异常值用户给出了明显不合理的输入模型需要识别并追问或抛出错误。把这三个维度组合起来生成空间就变成了一个可以通过“工具结构类型×场景类型×参数边界类型”来精确寻址的矩阵。想要什么数据就设定对应的坐标去生成。2.3 用户意图的多样化生成策略分解生成里用户意图的生成也有讲究。我见过不少人会在这儿偷懒直接让模型“生成 20 种不同的用户问题”结果出来的全是一个模子。真正的问题是目标不是“问题文字不同”而是“意图结构不同”。同样一句“帮我订一间上海的酒店”背后的意图结构可能有很多变体有没有明确说“多少钱”有没有说“靠近外滩”是“越快越好”还是“性价比优先”这些隐含约束的差异会让工具调用参数发生实质性变化。我的做法是给“意图生成器”喂一组“意图要素卡片”每个卡片定义一个必须包含或禁止包含的要素比如“包含时间约束”“包含预算约束”“不包含工具名关键词用户不会直接说我要调用 get_hotel”“隐含历史依赖用户上一轮已经说过日期”等等。生成器每次抽若干张卡片组合出一条用户指令。这样既能保证多样又能精确控制难度。卡片池越厚生成数据的长尾覆盖就越好这个投入非常值得。2.4 分解生成相比整体生成的优势把生成过程拆开除了可控还有一个隐藏红利每个单元的错误可以被独立识别和修正。整体生成时如果指令和工具序列不匹配你想改却不知道是改指令还是改序列分解生成时命令从“基于工具 schema 生成参数”这个单元就能单独校验“参数是否合法”从“基于用户指令生成工具序列”这个单元就能单独校验“工具是否选对”。这为后面第 3 部分的动态自反馈机制铺平了道路——没有分解就没有精细反馈。另外分解生成对 GPU 的利用率也更友好。整体生成一条长样本动辄几百甚至上千 token中间稍有偏差就整个作废分解后的小单元生成每个单元 token 数少失败成本低还可以多线程并发整体吞吐量反而更高。我在实测里分解生成能把一次数据生产的失败重生成率从 40% 压到 15% 左右还是很可观的。3. 动态自反馈让模型自己当质检员3.1 为什么单次生成必然有缺陷即使采用了分解生成模型依然会出错。原因很简单大模型没有“通义”般的指针表它生成city西安时只是按照概率在选词并不知道自己其实应该选北京。所以合成数据流程里必须有一道质检关。早期的做法是抽检加人工复核但合成数据的产出动辄几万条人工根本看不过来。于是就有了“自反馈”的思路让模型自己检查自己的输出把错误类型标注出来再带着错误信息重新生成一次。这不是简单地把同一条提示词跑两遍而是把“生成器”和“评审器”当成两个独立环节跑像写文章先写初稿再自己审稿一样只是把“自己”变成了一个有明确评审标准的检查器。3.2 “动态”到底动在哪很多人把自反馈理解成“生成一条校验一次不对就重新生成”但这只是静态重试。“动态”的意义在于反馈不是一次性给出的它会伴随整个生成迭代过程持续更新。具体来说有三个动态层面第一反馈信号是动态累积的。第一轮校验发现“参数缺失”重生成之后可能又出现“工具选择错误”第二次反馈要把第一次的错误记录保留下来作为历史上下文一起交给生成器避免模型“改了 A 却忘了 B”。第二评审标准是动态调整的。刚开始可以只用规则校验器管格式等规则跑顺了再加执行器验证语义最后再加入 LLM 评审处理开放性问题。先把地基打好再上精细校验这个顺序在工程上非常实用。第三生成策略本身会动态收敛。迭代到后期错误类型集中在少数几个高频问题上这时候不是无脑重生成而是针对高频错误专门调整该环节的 Prompt补上负面样例。这个过程很像是把“考试错题本”持续回灌给生成器错一道补一道。3.3 反馈信号的构造三层校验体系自反馈系统的核心是反馈信号怎么构造。我的实践经验是把校验体系分成三层每层解决不同的问题第一层规则校验器。这一层处理的是“客观正确性”用程序化的方式检查输出是否符合工具 schema。具体来说用 pydantic 或 jsonschema 去校验生成的 JSON 是否满足参数类型、必填项、枚举值、日期格式等硬性约束。这一层最便宜、最稳定而且完全不依赖模型是整个质检体系的地基。第二层执行器验证。规则只能管格式管不了“参数值是否合理”。比如search_product(keyword保温杯, min_price120元, max_price80元)在规则层是合法的但语义上 min_price 和 max_price 矛盾了。所以我给每个工具都写了一个轻量级“确定性模拟器”它不需要真的调用外部 API而是基于一组预定义规则和候选数据集模拟返回工具结果。比如遇到价格区间不合法模拟器就直接返回一个parameter_error信号进入反馈。这一层能把大量“看着格式正确、实际语义混乱”的样本挡在门外。第三层LLM 评审。前两层解决不了的是开放式问题比如“这句用户指令是否明确表达了调用某个工具的意图”“工具调用顺序是否符合常识”“返回结果是否真的回答了用户的问题”。这类问题需要更高级别的语义理解。我会启动一个评审模型通常和生成器模型不同从一致性、完整性、合理性、可执行性四个维度打分子并输出结构化的错误类型。评审模型的 Prompt 也很关键必须强制它输出[ERROR_TYPE]和[FIX_SUGGESTION]否则它会说一堆漂亮话却给不出可操作的修正信息。3.4 迭代终止条件与代价控制动态自反馈不是无限转圈圈得有明确的止损线。我在设计流水线时设置了两个终止条件满足任意一个就停止迭代一是质量分数达到预设阈值比如 LLM 评审四项得分平均分超过 4.2/5二是迭代次数达到上限第一次迭代次数设成 3后来调到 5。每多迭代一轮要消耗的 token 是线性增长的所以这里必须做代价控制不能让它变成无底洞。这里有个非常重要的经验不要只把“终态样本”保存下来也要把“历史错误记录”保存下来。错误记录本身就是极有价值的训练数据比如“参数类型错误”和“修复方案”就是一个很好的错误修正样本对。我后来做 DPO 训练时这些负样本对起了大作用省了大量额外标注。所以自反馈不只服务于数据质量它顺便产出的副产品能让后续训练事半功倍。4. 实操流程从零构建工具调用合成数据的完整流水线4.1 总体流水线架构理论讲再多不如看一次完整流程。下面这套流程是我在真实项目里跑通的版本整体分六步工具集定义、场景库构建、分解式生成、动态自反馈、质量门控与去重、格式转换输出。每一步的输入输出都很明确我把它当成一条小型生产线在维护。整体架构的要点是每一个模块都要“可插拔”。你换一批工具集流水线还能跑你把动态自反馈层禁用掉先快速出一批粗数据做 demo也是允许的。不要把所有逻辑写死在一个大脚本里后期改一个环节就要重跑全部那是灾难。4.2 第一步工具集定义与场景库构建工具集定义要基于真实的 API 文档而不是凭空想象。每个工具需要记录五个字段工具名、功能描述、参数列表类型、必填、枚举、默认值、返回值结构、调用约束比如是否幂等、是否支持并发。这里有个小技巧是给每个工具写“典型用例”和“禁止用例”比如查天气工具禁止在没有城市名时直接发起调用应该在参数里标cityUNKNOWN等待追问。这些用例会作为 few-shot 样例在后续生成起着关键引导作用。场景库构建我严格按照“领域-任务-表达”三层来建。领域大方向比如出行、办公、电商、生活、数据库运维等每个领域下定义具体任务比如“查航班”“订酒店”“查库存”每个任务还要有若干种表达模板覆盖命令、疑问、叙述、隐含约束等。一个可用的场景库至少要保证每个工具出现 3-5 个不同场景每个场景有至少 3 个不同表达风格。这套配置很枯燥但后面生成数据的多样性全靠它打底。4.3 第三步分解式生成的 Prompt 实现这一步是代码实现的核心。我会维护四个独立的生成器意图生成器、规划生成器、参数生成器、回复生成器。每个生成器都是一个独立的 Prompt 任务这样可以单独调优。下面给一个意图生成器的 Prompt 骨架之后规划生成器类似只讲关键设计点。你是一名用户意图生成器。你的任务是为一个工具调用系统生成一段用户指令。 【工具信息】 {工具描述与 schema} 【场景要求】 场景类型{场景类型} 要求包含的约束卡片{约束卡片列表} 要求避免的内容{禁止出现的表达} 【硬性要求】 1. 指令必须自然用户不会直接说出工具名或参数名。 2. 约束必须隐含在表达里而不是显式列出“请查询某参数”。 3. 指令长度不超过 50 字。 4. 必须覆盖要求的约束卡片且不能出现禁止内容。 请直接输出指令文本不要输出任何解释。规划生成器的任务则是根据指令输出工具调用序列格式是 JSON 数组每个元素包含tool_name、arguments、reason。我会强制要求它先输出一段简短的“思考摘要”说明为什么选这个工具、顺序如何再输出 JSON方便后续校验器定位错误。参数生成器是单独从“用户指令规划序列”里抽参数的这一步专门负责把自然语言表达转成结构化的参数键值对。它最容易出错所以要配置最多的 few-shot 样例。回复生成器则需要基于“用户指令工具调用序列模拟执行结果”来生成最终回答要求回答必须引用工具返回的真实结果禁止编造数据。4.4 第四步动态自反馈的核心实现在代码层面动态自反馈是一个 while 循环。初版长这样def synthesize_with_feedback(scene, max_rounds3): draft generate_full_sample(scene) history [] for round_idx in range(max_rounds): rule_result rule_validator(draft) exec_result executor_validator(draft) llm_result llm_reviewer(draft, scene) errors collect_errors(rule_result, exec_result, llm_result) if not errors: return draft history accumulate(history, errors) draft regenerate(draft, history) # 带着历史错误重新生成 return None # 超轮次未通过废弃或降级这个循环里最关键的参数是history。错误描述一定要结构化错误类型、出错位置意图/规划/参数/回复、错误细节、修复建议。不要只丢一句“有错误请修正”模型记不住也修不准。比如参数错误要写成[PARAM_ERROR] in arguments.city: expected str, got int; suggestion: convert to str and ensure it is a valid city name from the candidate list。4.5 质量门控、去重与多样性控制经过自反馈的样本还要再过一道质量门控LLM 评审的综合分数必须达标且必须通过三层校验。没通过的样本不是直接丢弃而是进入“低质量池”留着做负样本或者分析错误分布。通过率通常在 40% 到 60% 之间如果你发现通过率异常高比如超过 80%别高兴太早很可能是生成器在“作弊”比如生成极其类似的模板化样本一招鲜吃遍天。去重的逻辑我建议用 Embedding 相似度和 MinHash 双保险。先说 Embedding 相似度把每条样本的“用户指令调用序列摘要”embedding 化计算两两相似度阈值设 0.85 以上的删掉。再说 MinHash因为 Embedding 对“参数值变了但结构完全一样”的样本不敏感它有 Jaccard 来捕捉结构级重复。两层叠加后多样性会有立竿见影的提升。多样性控制还有一个容易被忽略的细节按“工具结构类型”走分层采样。不要让并行多工具调用在数据集里只占 5%因为模型对它的学习会严重不足。我会在最终数据装配阶段做比例控制比如单一工具:顺序多工具:并行多工具:条件分支 4:3:2:1然后再结合场景分布做最终抽样。4.6 数据集指标怎么设评估合成数据集的质量不能只盯着“跑了一遍 benchmark 涨了几个点”这种终极指标。我习惯在数据构建阶段就跑三组轻量指标工具覆盖率每个工具名在数据集里出现的次数占对应预期次数的比例低于 80% 要补采。参数合法率通过规则校验器的参数占总参数的比例低说明生成器质量没过关。结构多样性不同调用结构类型的样本占比方差方差太大说明采样失衡。指令多样性用户指令之间的文本相似度分布峰值太集中说明生成器在重复套模板。这三组指标可以在流水线中间位置实时打印出来数据没出库就知道大致健康度比全部生成完再后悔强得多。5. 实操中的坑与排查技巧5.1 常见问题速查表把这几年跑合成数据流水线遇到的高频问题整理成了一张速查表基本覆盖了“为什么会出问题”和“怎么修”。症状可能原因排查思路解决方案生成结果千篇一律场景库太薄 / 约束卡片太少统计指令相似度分布扩充场景库增加负面约束用多温度采样参数幻觉严重工具 schema 描述不清晰 / few-shot 不够检查参数生成器的错误类型分布增加参数候选集约束强制从候选取值反馈循环震荡不收敛一次修改范围太大 / 多条错误互相抵触打印每一轮 Round 的错误清单对比限制每轮只修一类错误错误历史累积后去重LLM 评审“睁眼说瞎话”评审 Prompt 太宽松 / 与生成器同模型抽 50 条抽样人工复核换不同模型做评审强制出结构化结果样本很真实但工具调用错误百出场景表达与工具能力不匹配检查场景库中任务定义是否越界每个任务在进入生成前先跑 5 条人工验证基线通过率虚高模板化生成 / 生成器钻评审空子看结构多样性指标去重阈值调低加入模板检测正则5.2 避免“千篇一律”的实战经验“看上去每条都不一样实际上同质化几乎为零”是合成数据最常见的问题。有个很典型的例子用户指令都是“帮我查一下今天的天气”“帮我查一下明晚的天气”“帮我查一下后天的天气”虽然日期在变但意图结构完全没有区分度模型学到的是“天气查询调用一个固定模板”遇到真正的复杂表达立刻歇菜。要解决这个问题我的建议是给意图生成器喂一个“正负约束池”。正向约束池里有“包含预算上限”“包含时间区间”“包含地点偏好”“包含历史依赖”“包含否定意向”等等每次随机采 2-3 个组合负向约束池里放“禁止出现工具名”“禁止出现参数名”“禁止使用命令句式”等等每次随机采 1-2 个来强制生成器换风格。同时温度参数我这里一般设置在 0.8 到 1.0 之间太低容易收敛到平均太高会产出混乱文本。5.3 反馈循环震荡的深度解析动态自反馈迭代轮次多了之后会出现一个特别磨人的现象修好了参数错误又引入了工具选择错误修好了工具选择又把指令语义改偏了。这种循环震荡的本质是生成器在改写时没有历史全局视角每一轮只盯着“当前最重要的是修错”结果按下葫芦浮起瓢。我踩过几次坑之后的解决办法是双管齐下。第一每一轮迭代最多只修一个错误类型不要贪多。比如这一轮只修参数类型错误下一轮再修工具选择错误。可能要多转几轮但整体收敛性会明显变好。第二把历史错误记录用一种“累积式提示”的方式传递给生成器把前几轮的错误类型、修复状态整理成一个清单让模型知道自己已经改过哪一类新增了一类什么错误避免重复劳动。5.4 与真实数据分布偏移的问题最后必须说一个更宏观的坑合成数据天然会偏离真实分布。因为合成数据是我们“想象中”的用户需求而真实用户问法千奇百怪同一个意思可能有十种表达。直接用纯合成数据训练出来的模型放上线之后会遇到大量“没见过”的长尾表达。我的经验做法是“锚点混合训练”哪怕只搞到 500 到 1000 条人工标注的真实数据也一定把这批真实数据作为锚点和合成数据按体积比 1:5 到 1:10 混合再进入训练。这套做法在多个实验里都有稳定收益。另外在生成合成数据时故意掺入一些“口语化、带错别字、句子不通顺”的指令也可以拉近合成数据与真实分布的鸿沟。这个听起来有点反直觉但实测下来效果很好因为真实用户是真的会打错字。我自己的体会是合成数据项目的成败从来不取决于“模型多强”而取决于你对生成空间的定义有多大、质量校验的门控有多严。分解生成负责把空间画出来动态自反馈负责把残次品挑出去回炉两者配合才能低成本地造出又宽又稳的工具调用数据。最后再分享一个很具体的小技巧如果你打算长期维护一套工具调用合成数据流水线建议每次都把“错误类型分布”打印成一份报告存下来。随着工具集升级、场景库扩充你会清楚地看到生成器薄弱点在哪里未来扩数据、写论文、调模型都用得到这份报告。我自己就是靠这个思路把一个不起眼的数据合成脚本慢慢做成了团队里所有人都在用的基础设施。
返回列表