1. 为什么 Agentic 小模型需要一个可观测的数据飞轮
Agentic 小模型和普通对话模型的差别,不在于参数量,而在于它要在一个循环里反复做三件事:读环境状态、选工具、根据返回结果修正下一步。这意味着它的能力上限很大程度上由训练数据的“闭环质量”决定——如果数据只是静态的一问一答,模型学不会多轮纠错;如果数据来自真实 API,成本高、反馈不稳定,很难规模化迭代。
数据飞轮的核心思路是:让每一轮训练后模型暴露的失败样本,自动变成下一轮更有信息量的训练数据。推理任务里表现为错题扩增,虚拟任务里表现为行为树分支扩展,最终形成“错题 → 扩增 → 过滤 → 训练 → 新错题”的闭环。这个闭环要跑起来,本地开发环境需要一套可复制的配置骨架,把模型接入、工具调用、数据回流三件事串起来。
这篇面向本地开发环境,交付一份可直接复制的config.toml与settings.json骨架,演示如何通过 TaoToken 统一 Key/API 通道接入 AI 工具,并给出验证数据回流与模型迭代是否生效的具体动作。适合正在做 Agentic 小模型微调、想搭建可观测进化闭环的开发者。
2. TaoToken 前置:统一 Key 与 API 通道
在搭建飞轮之前,先解决一个工程问题:本地开发时,模型调用、工具调用、数据合成往往走不同供应商,Key 分散、计费混乱、切换成本高。TaoToken 的作用是提供一个统一的 API 通道,把模型对话、编码 Agent、数据合成脚本的调用收敛到一套 Key 上。
你需要先拿到 API Key。访问控制台创建:
- 控制台入口:https://taotoken.net/console?utm_source=taotoken_aicg_blog_end&utm_content=console&utm_campaign=rewrite
- API Key 管理:https://taotoken.net/api-keys?utm_source=taotoken_aicg_blog_end&utm_content=api-keys&utm_campaign=rewrite
拿到 Key 后,API 基地址统一用https://taotoken.net/api(注意:API 地址不加 UTM 参数,直接写进配置文件即可)。如果你用的是 Claude Code 这类编码 Agent 做数据合成脚本的辅助开发,可以走 Anthropic 兼容通道:
- Claude Code 接入说明:https://taotoken.net/ClaudeCodeAnthropic?utm_source=taotoken_aicg_blog_end&utm_content=ClaudeCodeAnthropic&utm_campaign=rewrite
注意:不要把 Key 硬编码进提交到 Git 的配置文件。本地用环境变量注入,配置文件里只写占位符。
3. 可复制配置:config.toml 与 settings.json 骨架
下面这份骨架分两部分:config.toml负责飞轮主流程(任务合成、环境模拟、奖励计算、数据回流),settings.json负责工具与模型通道。两者配合,构成一个最小可运行的迭代闭环。
3.1 config.toml:飞轮主流程配置
# config.toml - Agentic 小模型数据飞轮骨架 [project] name = "agentic-flywheel" version = "0.1.0" work_dir = "./workspace" seed = 42 [model] # 统一走 TaoToken API 通道 base_url = "https://taotoken.net/api" api_key_env = "TAOTOKEN_API_KEY" # 从环境变量读取,不写明文 teacher_model = "claude-sonnet" # 用于任务合成与 rubric 生成 student_model = "qwen3-8b" # 待迭代的小模型 max_tokens = 4096 temperature = 0.7 [flywheel] # 数据飞轮轮次控制 max_rounds = 5 hard_sample_ratio = 0.6 # 每轮从失败样本中采样的比例 min_new_samples = 200 # 每轮至少新增样本数 consistency_filter = true # 多模型一致性过滤 consistency_models = ["claude-sonnet", "qwen3-30b"] [task_synthesis] # 任务合成:信息差注入 enable_information_gap = true rewrite_user_prompt = true # 把完整流程重写为信息不充分的指令 hide_critical_details = true # 关键细节藏进用户私有上下文 [environment] # 环境模拟:mock user + mock tool mock_user = true mock_tool = true task_level_consistency = true # 同一任务内相同调用返回一致结果 tool_response_cache = "./cache/tool_responses.jsonl" [reward] # 基于执行的 rubric 奖励 type = "rubric_based" use_workflow_reference = true penalize_forbidden_actions = true require_user_interaction = true [data_flow] # 数据回流路径 raw_trajectory_dir = "./data/trajectories" hard_sample_dir = "./data/hard_samples" filtered_dir = "./data/filtered" train_set = "./data/train.jsonl" eval_set = "./data/eval.jsonl"3.2 settings.json:工具与模型通道
{ "api": { "base_url": "https://taotoken.net/api", "api_key": "${TAOTOKEN_API_KEY}", "timeout_seconds": 120, "max_retries": 3 }, "tools": { "mock_tool_enabled": true, "tool_schema_dir": "./schemas", "consistency_cache": "./cache/tool_responses.jsonl", "forbidden_actions": [ "write_without_confirmation", "skip_identity_check", "guess_missing_param" ] }, "mock_user": { "enabled": true, "adversarial_ratio": 0.3, "private_context_dir": "./data/private_context", "reveal_only_if_asked": true }, "rubric": { "require_subgoal_completion": true, "require_user_interaction": true, "penalize_forbidden_actions": true, "workflow_reference_dir": "./data/workflows" }, "logging": { "level": "info", "trajectory_log": "./logs/trajectory.jsonl", "reward_log": "./logs/reward.jsonl" } }把这两份文件放进项目根目录,设置环境变量后即可启动第一轮:
export TAOTOKEN_API_KEY="你的Key" python -m flywheel.run --config config.toml --settings settings.json --round 14. 验证请求:数据回流与迭代是否生效
配置写完不代表飞轮转起来了。你需要用具体动作验证三件事:任务合成是否真的注入了信息差、环境模拟是否稳定、奖励是否基于执行而非主观打分。
4.1 验证任务合成的信息差
跑一轮合成后,检查生成的用户指令是否“信息不充分”。如果模型能从初始指令直接推出完整流程,说明信息差没生效。
import json with open("./data/train.jsonl", "r", encoding="utf-8") as f: samples = [json.loads(line) for line in f] # 检查前 5 条样本的初始指令长度与隐藏字段数 for s in samples[:5]: prompt_len = len(s["user_prompt"]) hidden = s.get("task_background", {}).get("only_reveal_if_asked", []) print(f"prompt_len={prompt_len}, hidden_fields={len(hidden)}")预期结果:hidden_fields大于 0,且user_prompt里不包含这些字段的具体值。如果hidden_fields为 0,回到config.toml确认hide_critical_details = true是否被下游脚本读取。
4.2 验证环境模拟的一致性
同一任务内,相同语义的工具调用应返回一致结果,否则 RL 训练会因反馈抖动而无法收敛。
import json from collections import defaultdict cache = defaultdict(list) with open("./cache/tool_responses.jsonl", "r", encoding="utf-8") as f: for line in f: rec = json.loads(line) key = (rec["task_id"], rec["tool_name"], rec["args_hash"]) cache[key].append(rec["response"]) inconsistent = {k: v for k, v in cache.items() if len(set(map(str, v))) > 1} print(f"inconsistent_calls={len(inconsistent)}")预期结果:inconsistent_calls接近 0。如果数量偏高,检查task_level_consistency是否开启,以及args_hash的计算是否把语义相同的参数归一化了。
4.3 验证奖励是否基于执行
奖励日志里应该能看到每个子目标的完成情况,而不是一个笼统的分数。
python -m flywheel.inspect_reward --log ./logs/reward.jsonl --task-id task_0001输出应包含类似结构:
{ "task_id": "task_0001", "subgoals": [ {"name": "verify_identity", "completed": true}, {"name": "ask_clarifying_question", "completed": true}, {"name": "confirm_before_write", "completed": false} ], "forbidden_actions_hit": [], "final_reward": 0.67 }如果subgoals为空或只有final_reward,说明 rubric 没有正确加载,检查workflow_reference_dir下是否有对应任务的 workflow 文件。
4.4 验证迭代是否真的在进化
跑完两轮后,对比失败样本的分布变化。早期失败集中在格式与参数缺失,中后期应转向复杂状态下的决策错误。
import json from collections import Counter def load_failures(path): with open(path, "r", encoding="utf-8") as f: return [json.loads(line) for line in f] r1 = load_failures("./data/hard_samples/round_1.jsonl") r2 = load_failures("./data/hard_samples/round_2.jsonl") def categorize(samples): c = Counter() for s in samples: if s.get("miss_func"): c["miss_func"] += 1 elif s.get("miss_param"): c["miss_param"] += 1 elif s.get("wrong_branch"): c["wrong_branch"] += 1 else: c["other"] += 1 return c print("round1:", categorize(r1)) print("round2:", categorize(r2))预期结果:miss_func和miss_param占比下降,wrong_branch占比上升。这说明模型已经过了“能跑通”阶段,进入“复杂状态下做对决策”阶段,飞轮在起作用。
5. 本篇常见错排查
5.1 报错401 Unauthorized或invalid api key
最常见的原因是环境变量没注入,或者配置文件里写了明文 Key 但被 Git 忽略后本地也没同步。检查:
echo $TAOTOKEN_API_KEY如果为空,重新导出。如果用的是settings.json里的${TAOTOKEN_API_KEY}占位符,确认你的加载脚本支持环境变量替换。TaoToken 的 Key 在控制台创建后只显示一次,丢了就重新生成一个。
5.2 任务合成后模型仍然“照着步骤执行”
说明信息差没生效。检查task_synthesis段:rewrite_user_prompt和hide_critical_details必须同时为true。另外,教师模型生成完整流程后,重写指令的 prompt 里要明确要求“移除所有决定性细节”,否则教师模型会偷懒保留关键参数。
5.3 工具调用返回不一致导致训练抖动
如果inconsistent_calls偏高,先确认args_hash的计算逻辑。常见坑是把时间戳、随机 ID 这类非语义字段也算进了 hash,导致相同语义的调用被当成不同调用。归一化时只保留工具名和业务参数。
5.4 奖励全是 0 或全是 1
全是 0 通常是 rubric 里的子目标名称和 workflow 文件里的名称对不上,检查大小写和下划线。全是 1 通常是forbidden_actions没生效,模型走了 hack path 但没被扣分。确认penalize_forbidden_actions = true且forbidden_actions列表里的动作名和工具 schema 里定义的一致。
5.5 第二轮训练后指标不升反降
先看失败样本分布。如果wrong_branch突然暴增,可能是行为树扩展时分支条件构造得太激进,导致任务难度跳变。把hard_sample_ratio从 0.6 降到 0.4,让每轮新增样本里保留更多中等难度样本,给模型一个过渡。
6. 把飞轮接到你的日常开发流
配置骨架跑通后,下一步是把它接到你已有的开发流程里。如果你主要用编码 Agent 做数据合成脚本的辅助开发,可以走 Coding Plan 通道,把模型调用和脚本开发收敛到同一套 Key:
- Coding Plan:https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_content=coding-plan&utm_campaign=rewrite
如果你需要快速验证某个教师模型在任务合成上的表现,直接用模型对话入口试几条 prompt,比改配置快:
- 模型对话:https://taotoken.net/?utm_source=taotoken_aicg_blog_end&utm_content=model-chat&utm_campaign=rewrite
接入文档里有完整的 API 参数说明和兼容通道细节,遇到通道问题时对照排查:
- 接入文档:https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_content=doc&utm_campaign=rewrite
我自己的习惯是:每轮飞轮跑完后,先看reward.jsonl里子目标完成率的变化,再看hard_samples的类别分布。如果连续两轮wrong_branch占比没有上升,说明任务难度梯度太平,需要手动往行为树里注入更激进的分支条件。这个判断比看最终 benchmark 分数更早、更准,能让你在指标掉之前就调整数据分布。