
1. 从「训完就冻住」说起Agent 持续学习到底难在哪如果你最近在折腾 Agent大概率遇到过这种场景同一个代码库同一个报错你纠正了模型三次第四次它还是踩进同一个坑。你翻聊天记录、贴上下文、写 system prompt它当场学会了换个会话又忘得一干二净。这不是模型笨是它的能力在训练结束那一刻就被冻住了。预训练时代模型从人类已有的文本里学到了 Agent 时代真正值钱的能力来自它在真实环境里跑任务、拿反馈、改策略。问题在于绝大多数团队把「训练」和「部署」当成两件事训练时用一套工具接口上线后是另一套环境模型在实验室里学到的经验到了生产环境直接打折。更麻烦的是就算你想让它边用边学全参微调的成本和风险也扛不住——万亿参数的模型动一次权重就是几十张卡起步训崩了还回不去。这就是为什么「持续学习」被反复提起却很少有人真正跑通。它要同时解决三件事一是让更新足够轻轻到能在生产环境里频繁做二是让更新可回滚、可隔离不能一次学坏污染整个模型三是让训练环境和部署环境对齐学到的经验能直接用上。LoRA 在这里的角色变了。过去它是「穷人版全参微调」省钱但打折现在它更像一种可写入、可回滚、可独立演化的持久状态——基座承载通用知识LoRA 承载你的偏好、代码风格、业务逻辑。配合强化学习Agent 就能在真实任务里持续积累经验而不是每次从零开始。我试过用一套统一的 Key 把训练、评测、推理串起来省掉了在多个平台之间来回切账号的麻烦。下面这篇就按「LoRA 训练配置 → 强化学习奖励函数 → 持续学习验证脚本 → 统一 Key 接入评测」的顺序把可复制的部分全部交出来。适合正在做 Agent 后训练、想让模型越用越聪明的同学也适合想先跑通一条最小闭环再决定要不要投入的团队。2. TaoToken 前置统一 Key 接入评测流程在动手写训练脚本之前先把「评测入口」这件事解决掉。做 Agent 持续学习你一定会反复做同一件事拿一个 checkpoint 去跑一批真实任务看它比上一版强了多少。如果每次评测都要换平台、换 Key、换 SDK光是环境切换就能耗掉半天。TaoToken 在这里的作用是提供一个统一的 API 入口让你用同一套 Key 去调用不同模型做对照评测。官网在 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content API 地址是 https://taotoken.net/api 。注意 API 地址不带 UTM 参数配置的时候别把查询串带进去否则部分 SDK 会报 URL 解析错误。你需要准备三样东西我把它叫「三件套」后面所有配置都会用到配置项取值来源说明Base URLhttps://taotoken.net/api所有请求的前缀不要带末尾斜杠API Key控制台创建形如 sk- 开头只显示一次务必存好Model ID模型列表里选评测时用来指定对照模型创建 Key 的入口在控制台的 API Keys 页面路径是 https://taotoken.net/console/api-keys?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 。如果你只是想先验证模型能不能正常对话可以直接去模型对话页面 https://taotoken.net/chat?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 手动发几条消息确认 Key 和网络都通再去写脚本。这里有个容易踩的坑很多人把 Base URL 写成 https://taotoken.net/api/v1 或者带上一堆查询参数结果 SDK 拼接路径时变成 /api/v1/v1/chat/completions直接 404。正确做法是 Base URL 只写到 /api具体路径交给 SDK 拼。另外Key 不要硬编码在脚本里提交到 Git用环境变量或者 .env 文件后面配置片段我会写成读环境变量的形式。如果你打算长期做 Agent 训练和评测建议顺手看一下 Coding Plan它更适合需要持续跑任务、反复调用的场景入口在 https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 。接入文档在 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 遇到参数不确定的时候以文档为准。前置准备做完你手上应该有一个能用的 Key、一个确认可用的 Base URL、一个用来做对照的 Model ID。接下来进入真正的训练配置环节。3. 可复制配置LoRA 训练 强化学习奖励函数模板这一节是全文最硬的部分我会给出可以直接落地的 LoRA 训练配置、强化学习奖励函数模板以及 Agent 持续学习的验证脚本。所有配置都按「能跑通最小闭环」来写不追求花哨。3.1 LoRA 训练配置YAML先给一份 LoRA 微调的配置模板。核心思路是基座冻结只训练低秩适配器rank 不要开太大Agent 场景下 16 到 32 通常够用太大反而容易过拟合到某几个任务上。# lora_agent_config.yaml base_model: your-base-model-path # 基座模型路径或 HuggingFace ID output_dir: ./outputs/lora-agent-v1 lora: r: 32 # 低秩维度Agent 场景 16-32 起步 lora_alpha: 64 # 一般设为 r 的 2 倍 lora_dropout: 0.05 target_modules: # 按基座结构填MoE 模型注意只挂 attention 和 mlp - q_proj - k_proj - v_proj - o_proj - gate_proj - up_proj - down_proj bias: none task_type: CAUSAL_LM training: per_device_train_batch_size: 2 gradient_accumulation_steps: 8 learning_rate: 1.0e-4 lr_scheduler_type: cosine warmup_ratio: 0.03 num_train_epochs: 3 max_seq_length: 8192 bf16: true gradient_checkpointing: true logging_steps: 10 save_steps: 200 save_total_limit: 3 # 保留最近 3 个方便回滚 rl: algorithm: grpo # 组相对策略优化适合 Agent 多步任务 num_generations: 8 # 每个 prompt 采样 8 条轨迹做对比 max_new_tokens: 2048 temperature: 0.9 kl_coef: 0.02 # KL 惩罚防止策略跑偏太远几个参数的解释别照抄完事。r和lora_alpha的比例决定更新幅度alpha 太大训练不稳太小又学不动2 倍是个稳妥起点。target_modules一定要对着你的基座结构改MoE 模型如果乱挂 expert 层训推不一致会非常严重。save_total_limit别省持续学习最怕的就是某一轮学坏之后回不去保留多个 checkpoint 是底线。3.2 强化学习奖励函数模板Agent 任务的奖励函数是成败关键。纯结果奖励任务成功给 1失败给 0在长程任务里信号太稀疏模型学不到中间步骤。我的做法是分层给奖励格式分、过程分、结果分三段。# reward_fn.py import re from typing import List def format_reward(response: str) - float: 格式奖励要求模型输出结构化的思考动作 score 0.0 if thought in response and /thought in response: score 0.2 if action in response and /action in response: score 0.2 # 动作必须是合法 JSON action_match re.search(raction(.*?)/action, response, re.S) if action_match: try: import json json.loads(action_match.group(1).strip()) score 0.1 except Exception: pass return score def process_reward(trajectory: List[dict], expected_steps: int) - float: 过程奖励鼓励合理步数惩罚无效重复 steps len(trajectory) if steps 0: return 0.0 # 步数接近预期给高分过多或过少都扣 ratio min(steps, expected_steps) / max(steps, expected_steps) # 检测重复动作 actions [t.get(action) for t in trajectory] unique_ratio len(set(map(str, actions))) / len(actions) return 0.3 * ratio 0.2 * unique_ratio def result_reward(task_result: dict) - float: 结果奖励任务是否真正完成 if task_result.get(success): return 1.0 # 部分完成给部分分 return 0.3 * task_result.get(partial_score, 0.0) def compute_reward(response: str, trajectory: List[dict], task_result: dict, expected_steps: int 5) - float: total ( format_reward(response) process_reward(trajectory, expected_steps) result_reward(task_result) ) return round(total, 4)这套奖励函数的好处是信号密集。格式分让模型先学会「怎么说话」过程分让它学会「怎么规划」结果分才是最终目标。三者权重可以按任务调代码类任务可以把结果分权重拉高对话类任务可以适当提高格式分。3.3 Agent 持续学习验证脚本训练完不是看 loss 曲线就完事要拿真实任务验证「它是不是真的比上一版强」。下面这个脚本做两件事跑一批固定任务对比新旧两个 checkpoint 的成功率。# eval_agent.py import os import json import requests from concurrent.futures import ThreadPoolExecutor BASE_URL os.environ[TAOTOKEN_BASE_URL] # https://taotoken.net/api API_KEY os.environ[TAOTOKEN_API_KEY] MODEL_ID os.environ.get(EVAL_MODEL_ID, your-model-id) def call_model(prompt: str, temperature: float 0.2) - str: resp requests.post( f{BASE_URL}/v1/chat/completions, headers{ Authorization: fBearer {API_KEY}, Content-Type: application/json, }, json{ model: MODEL_ID, messages: [{role: user, content: prompt}], temperature: temperature, }, timeout120, ) resp.raise_for_status() data resp.json() # 注意部分返回结构里 choices 可能为空要做保护 choices data.get(choices) or [] if not choices: raise ValueError(fempty choices: {json.dumps(data)[:200]}) return choices[0][message][content] def run_task(task: dict) - dict: try: output call_model(task[prompt]) success task[checker](output) return {id: task[id], success: success, output: output[:200]} except Exception as e: return {id: task[id], success: False, error: str(e)} def evaluate(tasks: list, workers: int 4) - dict: with ThreadPoolExecutor(max_workersworkers) as pool: results list(pool.map(run_task, tasks)) total len(results) passed sum(1 for r in results if r[success]) return { total: total, passed: passed, pass_rate: round(passed / total, 4) if total else 0.0, details: results, } if __name__ __main__: with open(tasks.json, r, encodingutf-8) as f: tasks json.load(f) report evaluate(tasks) print(json.dumps(report, ensure_asciiFalse, indent2))跑的时候把新旧两个 checkpoint 分别挂到不同的 Model ID 上各跑一遍对比pass_rate。如果新版在固定任务集上稳定高出几个点说明持续学习确实带来了增量如果持平甚至下降先检查奖励函数是不是把模型带偏了。4. 验证请求与成功结果从单条调用到批量评测配置写完先别急着上大批量任务用一条最小请求确认链路是通的。这一步能帮你快速区分「是配置错了」还是「是模型能力问题」。4.1 单条请求验证用 curl 发一条最简单的请求确认 Base URL、Key、Model ID 三件套都对curl -s https://taotoken.net/api/v1/chat/completions \ -H Authorization: Bearer $TAOTOKEN_API_KEY \ -H Content-Type: application/json \ -d { model: your-model-id, messages: [{role: user, content: 用一句话说明什么是持续学习}], temperature: 0.2 }正常返回的结构里choices[0].message.content就是模型输出。如果这一步就报错先看错误码别往下走。4.2 成功结果的判断标准单条通了之后跑批量评测。一个健康的持续学习结果应该满足三个条件第一固定任务集上的通过率比上一版有提升哪怕只提升 2 到 3 个点也算有效。第二提升不是靠某几个任务刷出来的要看details里失败任务的分布有没有变化。第三模型输出格式的合规率要稳定不能为了提分把格式学崩了。我实测下来比较稳的节奏是每积累 200 到 500 条真实任务轨迹做一次 LoRA 更新然后跑一次全量评测。更新太频繁容易震荡太久不更新又学不动。4.3 把评测结果落盘评测报告一定要存下来按时间戳命名方便回溯python eval_agent.py reports/eval_$(date %Y%m%d_%H%M).json有了历史报告你才能画出「任务量 vs 通过率」的曲线判断持续学习是不是真的在起作用。如果曲线早早平了说明要么任务集太简单要么奖励函数没提供新信号。5. 本篇常见错排查401、local proxy failed、reading choices、OAuth这一节按真实报错来遇到问题直接对号入座。401 Unauthorized最常见的原因是 Key 没读到或者读错了。先确认环境变量真的注入了echo $TAOTOKEN_API_KEY看有没有值。如果值存在但还是 401检查 Key 是不是被复制时带了空格或者用了已经删除的旧 Key。还有一种情况是 Base URL 写错请求打到了别的域名认证自然过不去。local proxy failed / connection refused这类报错通常是本地网络配置问题。检查你的 HTTP_PROXY、HTTPS_PROXY 环境变量是不是指向了一个已经关掉的本地端口。很多工具会默认读这两个变量如果之前设过又没清理请求就会往一个不存在的代理发。清掉这两个变量再试。reading choices 报错 / empty choices这个我在验证脚本里专门做了保护。返回体里choices为空或者字段缺失常见于请求被截断、模型名写错、或者返回的是错误结构但 HTTP 状态码是 200。处理方式是先把完整返回打出来看别直接取choices[0]。如果模型名不对有些服务会返回一个带 error 字段的 JSON而不是抛 4xx。OAuth 相关报错如果你用的是某些 CLI 工具比如 Claude Code 这类它可能走的是 OAuth 流程而不是纯 API Key。这时候要确认你填的是 API Key 模式还是 OAuth 模式两者不能混。用 API Key 接入时Base URL、Key、Model ID 三件套必须同时正确缺一个都会在鉴权阶段失败。如果工具支持 settings 文件把配置写进去比每次命令行传参更稳{ baseUrl: https://taotoken.net/api, apiKey: sk-your-key-here, model: your-model-id }注意这个 JSON 只是示例结构实际字段名以你所用工具的文档为准。写配置文件的时候路径要和工具读取的路径一致放错目录等于没配。训练侧报错loss 不收敛 / 训推不一致如果 LoRA 训练 loss 一直震荡先降学习率再检查target_modules有没有挂到不该挂的层。MoE 模型尤其要注意训练精度和推理精度不一致会让梯度漂移。解决办法是固定精度配置训练和推理用同一套 dtype别一边 bf16 一边 fp16。评测结果忽高忽低先固定 temperature评测时用 0.1 到 0.2别用默认值。采样温度太高同一批任务每次结果都不一样根本没法对比。另外任务集要固定别每次评测换题那样比出来的差异没有意义。6. 语义一致 CTA把这条闭环跑起来到这里一条最小可用的 Agent 持续学习闭环就齐了LoRA 训练配置负责「怎么学」奖励函数负责「学什么」验证脚本负责「学得怎么样」统一 Key 负责「评测入口不折腾」。如果你现在就想动手建议的顺序是先去控制台把 Key 建好用单条 curl 确认链路通然后把 LoRA 配置里的基座路径换成你自己的模型先跑一个很小的数据集确认训练能起来接着把奖励函数接到你的任务环境里跑一轮 GRPO最后用验证脚本对比新旧 checkpoint。需要反复调用做评测的话Coding Plan 会比按次调用更省心入口在 https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 。只想先手动验证模型表现的去模型对话页面发几条消息最快https://taotoken.net/chat?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 。参数拿不准就翻接入文档https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 。最后留一个我踩过的坑持续学习最容易失败的地方不是训练本身而是评测集不固定。你今天用这批任务测明天换一批得出的「提升」根本不可比。把任务集冻结下来每次更新只改模型不改题你才能看清 LoRA 到底有没有让 Agent 越用越聪明。