MidTool:通过中期数据合成提升大模型工具调用能力的工程实践
在实际 AI 应用开发中让大语言模型LLM学会稳定、可靠地使用外部工具如 API、数据库、计算器等是一个关键挑战。模型在预训练阶段学习了海量文本知识但“知道”某个工具的存在和“会使用”这个工具是两回事。传统的指令微调Instruction Tuning或强化学习人类反馈RLHF虽然能提升模型的指令遵循能力但在工具使用的泛化性、鲁棒性和多步推理上仍有不足。一个核心问题是高质量、多样化的工具使用训练数据从何而来人工标注成本高昂且难以覆盖所有可能的工具组合与复杂场景。MidTool 正是为了解决这一数据瓶颈而提出的方法。其核心思想是在模型训练的“中期”Mid-training通过合成Synthesis高质量的工具使用数据来系统性提升模型的工具调用Tool Use能力。这种方法不是简单的数据增强而是通过精心设计的合成策略让模型在学习过程中“见多识广”从而在面对新工具或复杂指令时能更准确地理解意图、规划步骤并执行调用。本文将从工程实践的角度深入解析 MidTool 的核心机制、数据合成流程并提供一个从零构建工具使用数据合成管道的实战指南。我们将探讨如何设计合成规则、生成高质量的数据对以及如何将这些数据有效地整合到模型训练流程中。无论你是希望提升现有模型工具调用能力的算法工程师还是正在构建基于 LLM 的智能体Agent系统的开发者理解并实践 Mid-training Data Synthesis 的思路都将大有裨益。1. 理解 Mid-training Data Synthesis 的核心价值在深入技术细节之前我们需要明确为什么要在“中期”进行数据合成以及它解决了传统方法的哪些痛点。1.1 工具使用能力训练的挑战大语言模型的工具使用能力可以分解为几个子任务工具识别与选择理解用户指令判断是否需要调用工具并从工具库中选择最合适的一个或多个工具。参数解析与构建根据指令和工具的描述如函数签名、API 文档正确提取或推导出调用工具所需的参数。多步规划与执行对于复杂任务需要将任务分解为多个子步骤并按顺序调用不同的工具同时处理中间结果。结果解释与响应生成将工具返回的原始结果如 JSON、数字、文本整合到自然语言回复中回应用户。传统的指令微调数据如(instruction, response)对往往只展示了最终的正确调用和结果。模型缺乏对“为什么选这个工具”、“参数怎么来的”、“如果调用失败怎么办”等决策过程的学习。这导致模型在面对分布外OOD指令或新工具时泛化能力弱容易产生幻觉或错误调用。1.2 Mid-training 与 Fine-tuning 的定位差异预训练Pre-training在海量无标注文本上学习语言建模获得通用语言理解和生成能力。此时模型对“工具”没有明确概念。中期训练Mid-training这是一个介于预训练和下游任务微调之间的阶段。在此阶段我们向模型注入特定领域或能力如工具使用、代码生成、数学推理的知识。数据通常是合成的或从特定来源收集的目标是在不损害通用能力的前提下塑造模型的特定行为模式。Mid-training 的数据量通常远小于预训练但远大于特定任务的微调。微调Fine-tuning在特定、高质量的任务数据如客服对话、代码补全上进行训练使模型高度适应该任务。数据通常要求精准但多样性和数量有限。MidTool 的“Mid-training”定位意味着它旨在为模型打下坚实的工具使用“基础”而不是针对某个具体 API 进行“特化”。这好比在教一个人“如何阅读说明书并操作机器”的通用方法而不是只教他操作某一台特定的打印机。1.3 数据合成的优势相比于人工标注合成数据具有显著优势成本与规模可以自动化、低成本地生成海量数据。多样性与可控性可以系统地覆盖边缘情况、错误输入、复杂组合这是人工标注难以穷尽的。知识注入可以通过规则或模板将工具使用的“最佳实践”如参数检查、错误处理直接编码到训练数据中。MidTool 的关键在于设计一套有效的合成策略Synthesis Strategy确保生成的数据既能教会模型正确的工具使用模式又能提升其泛化能力和鲁棒性。2. 构建工具使用数据合成管道一个完整的 MidTool 数据合成管道包含几个核心组件工具定义库、指令生成器、思维链Chain-of-Thought合成器、回复生成器以及质量过滤器。下面我们逐步拆解每个部分。2.1 环境与依赖准备我们将使用 Python 作为主要实现语言。虽然 MidTool 是一个方法论不依赖特定框架但我们会利用一些常用库来辅助数据生成和处理。# 建议创建一个新的虚拟环境 python -m venv midtool-env source midtool-env/bin/activate # Linux/macOS # midtool-env\Scripts\activate # Windows # 安装核心依赖 pip install openai1.0.0 # 用于调用大模型生成指令和回复示例用可替换 pip install pydantic2.0 # 用于定义严谨的工具模式和数据模型 pip install jinja23.0 # 用于模板化生成 pip install datasets # 用于处理和保存生成的数据集 pip install loguru # 用于日志记录如果使用其他 LLM 服务如 Anthropic Claude、本地部署模型请安装相应的 SDK。本文示例将使用 OpenAI 格式的 API 进行说明。2.2 定义工具库Tool Library工具库是所有合成数据的源头。我们需要用一种结构化的方式定义每个工具。这里使用 Pydantic 模型来确保规范性。# tool_definition.py from enum import Enum from typing import List, Optional, Any, Dict from pydantic import BaseModel, Field class ParamType(str, Enum): STRING string INTEGER integer NUMBER number BOOLEAN boolean ARRAY array OBJECT object class ToolParameter(BaseModel): 工具参数定义 name: str Field(..., description参数名称) type: ParamType Field(..., description参数类型) description: str Field(..., description参数含义和约束的详细描述) required: bool Field(defaultTrue, description是否必须) # 可以添加枚举值示例等 examples: Optional[List[Any]] Field(defaultNone, description参数值示例) class ToolDefinition(BaseModel): 工具定义 name: str Field(..., description工具的唯一名称如 get_weather) description: str Field(..., description工具功能的自然语言描述) parameters: List[ToolParameter] Field(..., description工具参数列表) returns: str Field(..., description返回值的描述如 JSON对象包含温度、湿度和天气状况) # 可选的执行函数模拟或占位用于后续验证 execute: Optional[Any] Field(defaultNone, description模拟执行函数) # 示例定义几个常用工具 WEATHER_TOOL ToolDefinition( nameget_weather, description获取指定城市当前或未来的天气信息。, parameters[ ToolParameter(namecity, typeParamType.STRING, description城市名称例如北京、New York, requiredTrue), ToolParameter(namedate, typeParamType.STRING, description查询日期格式为YYYY-MM-DD。默认为今天。, requiredFalse), ], returns一个JSON对象包含温度摄氏度、湿度百分比、天气状况如晴朗、多云、下雨和风速。 ) CALCULATOR_TOOL ToolDefinition( namecalculator, description执行数学计算。支持加、减、乘、除、幂运算。, parameters[ ToolParameter(nameexpression, typeParamType.STRING, description数学表达式例如(12 5) * 3 / 2, requiredTrue), ], returns计算结果的数值浮点数或整数。 ) DB_QUERY_TOOL ToolDefinition( namequery_database, description根据给定的查询条件从用户数据库中检索信息。, parameters[ ToolParameter(nametable_name, typeParamType.STRING, description要查询的表名, requiredTrue), ToolParameter(namecolumns, typeParamType.ARRAY, description要返回的列名列表, requiredFalse), ToolParameter(nameconditions, typeParamType.OBJECT, description查询条件键为列名值为匹配值, requiredFalse), ToolParameter(namelimit, typeParamType.INTEGER, description返回结果的最大条数, requiredFalse), ], returns一个包含查询结果的JSON数组每条结果是一个对象。 ) TOOL_LIBRARY: Dict[str, ToolDefinition] { tool.name: tool for tool in [WEATHER_TOOL, CALCULATOR_TOOL, DB_QUERY_TOOL] }这个工具定义库是合成的基石。每个工具的description和参数的description至关重要它们将是模型学习如何理解和调用工具的主要文本依据。2.3 设计合成策略与指令生成指令生成是合成的第一步。我们的目标是生成多样化的用户请求这些请求有的需要单工具调用有的需要多工具组合有的包含模糊或错误信息以训练模型的鲁棒性。我们可以设计一个基于模板和 LLM 的混合生成器。# instruction_generator.py import random from typing import List, Dict, Any from jinja2 import Template from .tool_definition import TOOL_LIBRARY, ToolDefinition class InstructionGenerator: def __init__(self, tool_library: Dict[str, ToolDefinition]): self.tool_library tool_library self.tools list(tool_library.values()) def generate_by_template(self, pattern: str) - str: 使用预定义模板生成指令 templates { direct_single: [ 帮我查一下{{city}}的天气。, 计算一下{{expression}}等于多少, 从{{table}}表中找出{{condition}}的记录。 ], conversational_single: [ 我打算明天去{{city}}不知道天气怎么样你能告诉我吗, 这个式子{{expression}}的结果有点复杂你能算算吗, ], multi_tool: [ 我先要知道{{city1}}和{{city2}}的天气然后比较一下哪里更暖和。, 帮我算一下{{expr1}}和{{expr2}}然后把两个结果相加。, ], ambiguous: [ 今天天气如何, # 缺少城市参数 算个数。, # 缺少表达式 查一下用户信息。 # 缺少表名和条件 ], error_prone: [ 查一下城市叫New York注意有空格的天气。, # 参数格式可能有问题 计算123除以0。, # 数学错误 查询一个不存在的表‘non_existent_table’。, # 资源不存在 ] } # 这里需要实现具体的模板变量填充逻辑例如从列表中随机选择城市、生成随机表达式等。 # 为简化示例我们返回一个占位指令。 selected_template random.choice(templates.get(pattern, [这是一个测试指令。])) # 实际应用中需要用真实数据替换 {{}} 中的变量。 return selected_template def generate_by_llm(self, tool_def: ToolDefinition, complexity: str simple) - str: 使用LLM根据工具定义生成更自然、多样的指令 # 这是一个调用LLM API的示例提示词模板 prompt_template 你是一个帮助生成训练数据的助手。请根据以下工具的描述生成一条用户可能提出的、需要调用此工具的自然语言请求。 工具名称{{tool_name}} 工具描述{{tool_description}} 工具参数{% for param in parameters %}- {{param.name}} ({{param.type}}): {{param.description}}{% if not param.required %} (可选){% endif %} {% endfor %} 请生成一条{{complexity}}的指令。指令应尽可能自然、口语化并且必须包含调用该工具所需的必要信息参数值可以合理虚构。 只输出指令本身不要输出任何解释。 生成的指令 from jinja2 import Template tmpl Template(prompt_template) prompt tmpl.render( tool_nametool_def.name, tool_descriptiontool_def.description, parameterstool_def.parameters, complexitycomplexity ) # 调用LLM API (示例需替换为实际调用) # instruction call_llm_api(prompt, modelgpt-3.5-turbo) # 此处为模拟返回 instruction f请告诉我{tool_def.name}工具的使用示例。 return instruction def generate_batch(self, num_instructions: int, patterns: List[str] None) - List[Dict[str, Any]]: 批量生成指令及其元数据 if patterns is None: patterns [direct_single, conversational_single, multi_tool, ambiguous, error_prone] data [] for _ in range(num_instructions): pattern random.choice(patterns) if random.random() 0.7: # 70% 使用LLM生成30%使用模板 tool random.choice(self.tools) complexity random.choice([simple, medium, complex]) instr self.generate_by_llm(tool, complexity) required_tools [tool.name] else: instr self.generate_by_template(pattern) # 模板生成需要后处理来关联具体工具这里简化处理 required_tools self._infer_tools_from_instruction(instr) data.append({ instruction: instr, pattern: pattern, required_tools: required_tools, source: llm if llm in locals() else template }) return data def _infer_tools_from_instruction(self, instruction: str) - List[str]: 一个简单的启发式方法根据关键词推断可能需要的工具实际应用需要更复杂的NLP inferred [] if 天气 in instruction or weather in instruction.lower(): inferred.append(get_weather) if 算 in instruction or 计算 in instruction or calculate in instruction.lower(): inferred.append(calculator) if 查询 in instruction or 查 in instruction or find in instruction.lower() or search in instruction.lower(): inferred.append(query_database) # 这是一个粗略的推断 return inferred if inferred else [unknown]2.4 合成思维链与回复对于每条指令我们需要合成模型在响应时应该展现的“思考过程”Chain-of-Thought, CoT和最终回复。这是 MidTool 训练的关键它教会模型如何推理。# response_synthesizer.py from typing import List, Dict, Any from .tool_definition import TOOL_LIBRARY, ToolDefinition import json import random class ResponseSynthesizer: def __init__(self, tool_library: Dict[str, ToolDefinition]): self.tool_library tool_library def synthesize_cot_and_response(self, instruction: str, required_tools: List[str]) - Dict[str, Any]: 为给定指令和所需工具合成思维链和最终回复。 这是一个模拟的、基于规则的合成器。在实际项目中可以结合规则和LLM来生成更高质量的CoT。 # 步骤1 解析指令确定工具调用序列这里简化假设指令只对应一个工具 # 实际中这里应该有一个更复杂的规划模块。 tool_to_use required_tools[0] if required_tools else None if not tool_to_use or tool_to_use unknown: # 如果无法确定工具生成一个拒绝或澄清的回复 cot 用户的问题不明确无法确定要使用哪个工具。我需要请求澄清。 response 抱歉我不太确定您想让我做什么。您能提供更具体的信息吗例如您想查询哪个城市的天气或者您想计算什么 return {chain_of_thought: cot, response: response, tool_calls: []} tool_def self.tool_library.get(tool_to_use) if not tool_def: cot f用户可能需要使用工具‘{tool_to_use}’但该工具不在我的知识库中。我需要告知用户我无法处理。 response f抱歉我目前不支持‘{tool_to_use}’功能。 return {chain_of_thought: cot, response: response, tool_calls: []} # 步骤2 从指令中提取参数模拟一个简单的提取逻辑 # 这是一个非常简化的模拟真实场景需要NLP解析或调用一个小的参数提取模型。 extracted_params self._mock_extract_parameters(instruction, tool_def) # 步骤3 构建思维链 cot_parts [] cot_parts.append(f用户指令是‘{instruction}’。) cot_parts.append(f这涉及到‘{tool_def.name}’工具其功能是{tool_def.description}。) if extracted_params: param_desc , .join([f{k}{v} for k, v in extracted_params.items()]) cot_parts.append(f我从指令中提取出了参数{param_desc}。) else: # 参数缺失需要反问 missing_params [p.name for p in tool_def.parameters if p.required] cot_parts.append(f指令中缺少必要参数{missing_params}。我需要向用户询问这些信息。) response f为了使用‘{tool_def.name}’我需要您提供{, .join(missing_params)}。 return {chain_of_thought: \n.join(cot_parts), response: response, tool_calls: []} # 步骤4 模拟工具调用并生成结果 cot_parts.append(f现在我将使用参数 {extracted_params} 调用‘{tool_def.name}’。) mock_result self._mock_execute_tool(tool_def, extracted_params) cot_parts.append(f工具调用成功返回结果{mock_result}。) # 步骤5 根据结果生成面向用户的回复 response self._generate_user_response(tool_def, extracted_params, mock_result) cot_parts.append(f最后我将结果转化为自然语言回复给用户。) # 步骤6 构建最终的训练数据格式 # 训练时思维链CoT可以作为模型的“思考”部分进行学习。 # 一种常见格式是将CoT和回复拼接或在特定标记中。 final_cot \n.join(cot_parts) # 构建工具调用结构例如用于Function Calling格式 tool_call { name: tool_def.name, arguments: extracted_params } return { chain_of_thought: final_cot, response: response, tool_calls: [tool_call], tool_results: [mock_result] } def _mock_extract_parameters(self, instruction: str, tool_def: ToolDefinition) - Dict[str, Any]: 模拟参数提取。实际项目应使用更可靠的方法。 params {} # 这里只是一个极其简单的关键字匹配示例 if tool_def.name get_weather: if 北京 in instruction: params[city] 北京 params[date] 2023-10-27 elif 上海 in instruction: params[city] 上海 params[date] 2023-10-27 elif tool_def.name calculator: import re # 简单查找数字和运算符 match re.search(r(\d[\\-\*\/]\d), instruction) if match: params[expression] match.group(1) # ... 其他工具的模拟提取 return params def _mock_execute_tool(self, tool_def: ToolDefinition, params: Dict[str, Any]) - Any: 模拟工具执行返回一个假的结果。 if tool_def.name get_weather: return {temperature: random.randint(15, 30), humidity: random.randint(40, 80), condition: random.choice([晴朗, 多云, 小雨])} elif tool_def.name calculator: try: # 警告实际中不要用eval这里仅用于演示 result eval(params.get(expression, 0)) return result except: return 计算错误表达式无效或除零错误。 elif tool_def.name query_database: return [{id: 1, name: 模拟用户, age: 25}] return None def _generate_user_response(self, tool_def: ToolDefinition, params: Dict, result: Any) - str: 根据工具结果生成自然语言回复。 if tool_def.name get_weather: return f{params.get(city)}的天气是{result[condition]}气温{result[temperature]}摄氏度湿度{result[humidity]}%。 elif tool_def.name calculator: return f计算结果为{result}。 elif tool_def.name query_database: return f查询到了{len(result)}条记录。 return f操作‘{tool_def.name}’已完成结果为{result}。2.5 数据组装与格式转换生成的数据需要转换成模型训练所需的格式。常见的格式有对话格式如 ShareGPT、指令跟随格式如 Alpaca或带有特殊标记的格式。# data_formatter.py from datasets import Dataset from typing import List, Dict, Any def format_for_sft(instruction_data: List[Dict], response_data: List[Dict]) - Dataset: 格式化为标准监督微调SFT格式例如 Alpaca 格式。 formatted_data [] for instr_item, resp_item in zip(instruction_data, response_data): # 将思维链CoT作为模型输入的一部分或者作为多轮对话中的“思考”轮次。 # 这里我们采用一种简单格式将CoT和回复合并作为输出。 full_output f{resp_item[chain_of_thought]}\n\n最终回复{resp_item[response]} formatted_example { instruction: instr_item[instruction], input: , # 可以为空或放入工具定义等信息 output: full_output, # 可以保留元数据供后续分析 metadata: { pattern: instr_item[pattern], required_tools: instr_item[required_tools], tool_calls: resp_item.get(tool_calls, []), tool_results: resp_item.get(tool_results, []) } } formatted_data.append(formatted_example) return Dataset.from_list(formatted_data) def format_for_function_calling(instruction_data: List[Dict], response_data: List[Dict]) - List[Dict]: 格式化为函数调用Function Calling训练格式。 formatted_data [] for instr_item, resp_item in zip(instruction_data, response_data): # 假设训练数据期望模型输出一个包含工具调用的JSON。 # 这需要模型本身支持输出结构化内容。 example { messages: [ {role: user, content: instr_item[instruction]}, {role: assistant, content: json.dumps({ thought: resp_item[chain_of_thought], tool_calls: resp_item.get(tool_calls, []), response: resp_item[response] }, ensure_asciiFalse)} ] } formatted_data.append(example) return formatted_data2.6 运行合成管道最后我们将所有组件串联起来形成一个完整的数据合成脚本。# main_synthesis_pipeline.py from tool_definition import TOOL_LIBRARY from instruction_generator import InstructionGenerator from response_synthesizer import ResponseSynthesizer from data_formatter import format_for_sft from datasets import Dataset import json def main(): # 1. 初始化组件 instr_gen InstructionGenerator(TOOL_LIBRARY) resp_synth ResponseSynthesizer(TOOL_LIBRARY) # 2. 生成指令 print(正在生成指令...) instructions instr_gen.generate_batch(num_instructions100, patterns[direct_single, conversational_single, ambiguous]) print(f生成了 {len(instructions)} 条指令。) # 3. 为每条指令合成回复含CoT print(正在合成回复与思维链...) all_responses [] for i, instr_item in enumerate(instructions): if i % 10 0: print(f处理进度: {i}/{len(instructions)}) response_item resp_synth.synthesize_cot_and_response( instr_item[instruction], instr_item[required_tools] ) all_responses.append(response_item) # 4. 格式化数据 print(正在格式化数据...) dataset format_for_sft(instructions, all_responses) # 5. 保存数据 output_path midtool_synthetic_data.jsonl dataset.to_json(output_path, orientrecords, linesTrue) print(f数据已保存至: {output_path}) print(f数据样例:) print(json.dumps(dataset[0], indent2, ensure_asciiFalse)) if __name__ __main__: main()运行此脚本你将得到一个包含 100 条合成训练数据的 JSONL 文件。每条数据都包含了用户指令、模型内部的思考过程CoT以及最终回复。3. 将合成数据用于模型训练生成了数据之后下一步就是将其用于模型训练。Mid-training 通常意味着在一个基础预训练模型上用这批合成数据可能混合其他数据进行继续预训练或指令微调。3.1 训练配置要点假设我们使用 Hugging Face Transformers 库和 PyTorch 进行训练。# training_config_example.py from transformers import AutoTokenizer, AutoModelForCausalLM, TrainingArguments, Trainer from datasets import load_dataset import torch # 1. 加载模型和分词器 model_name meta-llama/Llama-3.2-3B-Instruct # 示例模型请替换为你有权使用的模型 tokenizer AutoTokenizer.from_pretrained(model_name) model AutoModelForCausalLM.from_pretrained(model_name) # 设置填充令牌如果分词器没有 if tokenizer.pad_token is None: tokenizer.pad_token tokenizer.eos_token # 2. 加载并预处理合成数据 def preprocess_function(examples): # 将指令和输出拼接成模型输入的格式 # 例如使用 ChatML 格式 texts [] for instr, out in zip(examples[instruction], examples[output]): message [ {role: user, content: instr}, {role: assistant, content: out} ] # 使用分词器的 apply_chat_template 方法如果支持 text tokenizer.apply_chat_template(message, tokenizeFalse, add_generation_promptFalse) texts.append(text) # 对文本进行分词 model_inputs tokenizer(texts, truncationTrue, paddingmax_length, max_length1024) # 将标签设置为输入ID对于因果语言建模 model_inputs[labels] model_inputs[input_ids].copy() return model_inputs dataset load_dataset(json, data_filesmidtool_synthetic_data.jsonl, splittrain) tokenized_dataset dataset.map(preprocess_function, batchedTrue) # 3. 定义训练参数 training_args TrainingArguments( output_dir./midtool-finetuned-model, num_train_epochs3, # Mid-training 轮次可以较少 per_device_train_batch_size4, per_device_eval_batch_size4, warmup_steps100, weight_decay0.01, logging_dir./logs, logging_steps10, save_steps500, eval_strategysteps, eval_steps500, save_total_limit2, load_best_model_at_endTrue, report_tonone, # 或 tensorboard fp16True, # 如果硬件支持 ) # 4. 创建 Trainer 并开始训练 trainer Trainer( modelmodel, argstraining_args, train_datasettokenized_dataset, # eval_datasettokenized_dataset_eval, # 如果有验证集 tokenizertokenizer, # data_collator..., # 如果需要自定义数据整理器 ) trainer.train() trainer.save_model() tokenizer.save_pretrained(./midtool-finetuned-model)关键配置说明学习率Mid-training 的学习率通常比预训练大但比下游任务微调小。可以从5e-5到1e-4开始尝试。训练轮次由于合成数据量可能远小于预训练数据3-5 个 epoch 通常足够需监控验证集损失防止过拟合。数据混合为了提高通用性建议将工具使用合成数据与一部分通用指令数据如 Alpaca、ShareGPT混合训练比例可以根据目标调整例如 7:3工具数据占 70%。损失函数标准的下一个词预测Causal LM损失即可。如果希望模型更关注工具调用部分可以在计算损失时对 CoT 和工具调用相关的 token 给予更高权重。3.2 评估与迭代训练完成后必须对模型的工具使用能力进行评估。评估不应只看验证集损失而应设计专门的评估集Benchmark。构建评估集包含未见过的指令、新工具但定义类似、复杂多步任务、有歧义或包含错误的指令。评估指标工具选择准确率模型是否选择了正确的工具。参数提取准确率/F1提取的参数是否与标准答案匹配。端到端任务成功率从指令到最终正确回复的比例。幻觉率模型是否编造了不存在的工具或参数。迭代合成策略根据评估结果反推合成数据的不足。例如如果模型在多工具规划上表现差就在下一轮合成中增加更多、更复杂的多工具组合数据。4. 常见问题与排查路径在实践中从数据合成到模型训练可能会遇到各种问题。下表列出了一些典型问题及其排查思路。问题现象可能原因检查点与排查方式处理建议模型完全忽略工具只用内部知识回答1. 合成数据中工具调用模式不够突出。2. 训练数据中通用对话数据比例过高。3. 损失函数未对工具调用部分给予足够关注。4. 模型容量太小无法同时掌握通用知识和工具使用。1. 检查合成数据样本看“工具调用”部分是否清晰、格式统一。2. 检查训练数据混合比例。3. 在评估集上做消融实验看模型是否看到了工具定义。4. 查看训练日志损失是否正常下降。1. 强化合成数据中工具调用的格式如使用特殊标记[TOOL_CALL]...[/TOOL_CALL]。2. 调整数据混合比例增加工具数据权重。3. 尝试在计算损失时对工具调用相关的 token 进行加权。4. 考虑使用更大规模的模型或在已有工具使用能力的模型上进行微调。模型能选择工具但参数解析错误1. 合成数据中参数提取的多样性不足。2. 指令生成器生成的指令与参数关联性弱。3. 思维链合成器中参数提取逻辑有缺陷导致训练数据本身就有噪声。1. 人工检查一批合成数据对比“指令”和“提取的参数”是否合理。2. 分析错误案例看是参数类型错误、值错误还是缺失。1. 改进指令生成器使其能生成与工具参数强相关的、多样的指令。2. 加强思维链合成器中的参数提取模块可以引入一个小型的NER或信息抽取模型。3. 在合成数据中增加“参数缺失-反问”的样本训练模型主动澄清的能力。模型在多步工具规划上表现差1. 合成数据中多步任务的比例和复杂度不够。2. 思维链合成器对多步任务的规划逻辑过于简单或模式单一。3. 模型在生成长序列的规划步骤时容易出错。1. 统计合成数据中多步任务的占比和步骤数分布。2. 检查多步任务样本的思维链是否逻辑清晰、步骤完整。1. 显著增加多工具、多步骤合成数据的比例和复杂度。2. 使用更强大的规划器如基于规则的规划器或调用LLM来生成高质量的多步思维链。3. 在训练时可以考虑使用课程学习Curriculum Learning先从单步任务开始逐步增加步骤数。训练后模型通用能力下降1. 工具数据与通用数据分布差异过大。2. 训练轮次过多在工具数据上过拟合。3. 学习率设置不当。1. 在通用指令基准如 MT-Bench 的部分问题上测试模型表现。2. 检查验证集如果包含通用数据的损失曲线是否在后期上升。1. 确保数据混合并在通用任务上保留一部分验证集。2. 使用更小的学习率或采用余弦退火等学习率调度策略。3. 早停Early Stopping根据通用能力验证集上的表现来决定停止时机。合成数据质量低包含大量错误或矛盾1. 指令生成模板过于随机产生无意义指令。2. LLM生成的指令或思维链未经过滤。3. 参数提取和结果模拟逻辑有bug。1. 随机采样并人工审查100条合成数据。2. 建立自动化的数据质量检查规则如工具名是否在库中参数类型是否匹配。1. 引入数据过滤和后处理步骤剔除明显错误的数据。2. 对LLM生成的内容可以设计自洽性检查如用另一个LLM或规则进行验证。3. 采用迭代式数据合成用小规模高质量数据训练一个初始模型用这个模型来帮助生成或筛选更多数据。5. 最佳实践与扩展方向5.1 合成数据质量保障清单在启动大规模合成前请对照此清单检查[ ]工具定义清晰每个工具的name,description,parameters的描述是否准确、无歧义[ ]指令多样性是否覆盖了直接指令、对话式指令、模糊指令、错误指令、多轮对话上下文[ ]参数覆盖度合成数据是否覆盖了每个参数的各种可能取值包括边界值、错误值[ ]思维链合理性合成的思考过程是否符合人类推理逻辑是否清晰展示了“识别工具 - 提取参数 - 调用 - 解释结果”的步骤[ ]结果真实性模拟的工具返回结果是否合理例如计算器工具不会返回文本天气工具返回的数据在合理范围内。[ ]格式一致性训练数据的格式如对话模板、工具调用标记是否在整个数据集中保持一致[ ]噪声控制是否有机制过滤掉包含事实性错误、逻辑矛盾或低质量语言的数据5.2 扩展方向从规则合成到模型合成初期可以使用规则和模板快速生成基础数据。随着迭代可以训练一个小型“数据合成模型”让它来生成更自然、更多样的指令和思维链。这类似于使用 LLM 来自动标注Self-Instruct 的变种。引入真实交互数据在智能体系统上线后收集真实的用户与模型的成功/失败交互日志。这些数据极其宝贵可以用来进一步微调模型或作为种子来生成更多类似的合成数据。支持动态工具库当前的合成管道假设工具库是静态的。可以扩展系统使其能够根据新加入的工具描述自动生成相应的训练数据实现工具的“即插即用”式学习。多模态工具使用除了 API 调用工具还可以是图像处理、音频生成等。合成数据需要包含对多模态输入/输出的描述和处理逻辑。强化学习整合将 Mid-training 与强化学习RL结合。用合成数据初始化模型然后在模拟环境或真实环境中通过 RL 进一步优化其工具使用策略例如学习何时不调用工具、如何从失败中恢复。Mid-training Data Synthesis 是一个强大的范式它通过创造性的数据生成将人类的先验知识如何正确使用工具系统地注入模型。其成功的关键不在于合成数据的绝对数量而在于其质量和多样性能否有效覆盖目标能力空间。从定义清晰的结构化工具开始精心设计合成策略并建立持续的质量评估与迭代循环你就能为你的 AI 智能体打造出坚实可靠的工具使用基本功。