Appearance
SkillOpt 自学习机制详解
用费曼技巧讲清楚:SkillOpt 到底在做什么、为什么这样做、每一步发生了什么。
一句话概括
SkillOpt 是一个「先考试、再补课、再考试」的闭环 —— 用旧 Skill 做一次模拟考试拿到基线分,分析答错的题总结补课方案,改完 Skill 后再考一次,只有新分数严格高于旧分数才采纳,否则保留旧 Skill 不动。
为什么需要 SkillOpt
想象一个场景:用户跟 AI 聊了很多轮,AI 的表现有好有坏。我们想知道:
"能不能从这些对话里学到东西,让 AI 下次表现得更好?"
最直觉的做法是让 AI 自己看历史对话、总结规则、写一份新的 Skill 文档。但这样做有一个致命问题 —— 你怎么知道新写的 Skill 真的比旧的好? 也许它改了一堆,结果反而变差了。
SkillOpt 的回答是:我不盲信改进,我用分数说话。 改完之后必须重新跑一遍,分数严格提升了才采纳,否则不动。
整体流程
一步步拆解
第一步:数据集构造
做什么:把用户的聊天记录变成「考题」。
用户的对话是一问一答的形式。我们把每一对「用户提问 → AI 回答」配对起来,每对就是一道「考题」—— 题目是用户的话,参考答案是当时 AI 的回复。
然后把这些考题分成两份:
- 训练集(train):用来「补课」,即分析答错的原因、提出改进方案
- 验证集(selection):用来「考试」,即检验改完的 Skill 是否真的变好
分法很简单:最后 30% 的对话对作为验证集,前面的作为训练集。
为什么分两份? 如果用同一份题既补课又考试,就像「开卷考自己练过的题」—— 你永远不知道是真的学会了还是只是背了答案。分两份才能测出真正的提升。
代码位置:backend/services/review/pipeline/skillopt/dataset.py → DatasetProvider.build_split()
第二步:Baseline Rollout(旧 Skill 考试)
做什么:用旧 Skill 在验证集上做一次「模拟考试」,拿到基线分数。
具体来说,把旧 Skill 的内容作为系统提示词,把验证集里每道题的用户提问发给 LLM,让它生成回答。然后给每个回答打分。
这一步的意义:先知道现在有多好。后面所有改进都要跟这个分数比。
代码位置:backend/services/review/pipeline/skillopt/rollout.py → RolloutRunner.rollout()
沙箱模式
Rollout 在「沙箱」中运行 —— LLM 被告知:
- 你在沙箱里,不要调用真实工具
- 不要写入用户会话或素材库
- 只需要按 Skill 规则生成输出
这样 rollout 不会产生任何用户可见的副作用。
第三步:Train Rollout + 反思(分析错题)
做什么:用旧 Skill 在训练集上也跑一遍,然后让另一个 LLM(优化器)看结果,分析哪里做得不好,提出改进建议。
分两个子步骤:
3a. Train Rollout
和第二步一样,只是用的是训练集。这次跑的目的是收集「哪里答错了」的证据。
3b. 反思(Reflect)
把 rollout 结果(每道题的分数、失败原因、输出摘要)交给优化器 LLM,让它分析:
- 哪些题答错了?错在哪?
- Skill 里缺了什么规则?
- 应该怎么补?
优化器返回一组「补丁」(patches),每个补丁包含:
- reasoning:为什么要这样改(推理过程)
- edits:具体的编辑操作列表
如果 LLM 反思失败怎么办? 系统有确定性回退:把所有答错题目的失败原因整理成列表,追加到 Skill 末尾。这样即使 LLM 挂了,也能留下「哪些题做错了」的记录。
代码位置:backend/services/review/pipeline/skillopt/reflect.py → LLMSkillPatchReflector.reflect()
编辑操作类型
优化器可以提议 4 种编辑操作:
| 操作 | 比喻 | 说明 |
|---|---|---|
append | 在书末尾加一页 | 在 Skill 末尾追加新内容 |
insert_after | 在某段后面插一句话 | 在指定位置后面插入新内容 |
replace | 把某段话改写 | 替换指定内容 |
delete | 删掉某段话 | 删除指定内容(有长度限制,防止误删大片) |
第四步:聚合 + 筛选
做什么:多个 patch 中可能有重复的编辑建议。先合并,再取最重要的。
- 聚合:把相同操作(op、target、content 都一样)的编辑合并,每合并一次
support_count加 1。然后按 support 从高到低排序。 - 筛选:只取 support 最高的前 N 条(N 由
REVIEW_SKILLOPT_EDIT_BUDGET控制,默认 2)。
为什么要限制编辑数量? 一次改太多东西,万一改坏了很难定位问题。小步迭代更安全。
代码位置:backend/services/review/pipeline/skillopt/strategy.py → _aggregate_patches() + _select_by_budget()
第五步:应用编辑
做什么:把筛选后的编辑逐一应用到当前 Skill 上。
每应用一条编辑后,系统会检查 Skill 的结构完整性 —— 必须包含 6 个必要章节:
trigger— 触发条件workflow— 工作流程tool_policy— 工具使用策略output_schema— 输出格式quality_rubric— 质量标准failure_recovery— 失败恢复
如果某条编辑导致必要章节丢失,这条编辑会被回退(拒绝),跳过继续下一条。
如果当前 Skill 是空的,会用一份内置模板作为起始点。
代码位置:backend/services/review/pipeline/skillopt/update.py → MarkdownSkillUpdater.apply()
第六步:Gate 验证(新 Skill 再考试)
做什么:用候选 Skill 在验证集上重新跑一次 rollout,算出候选分数。只有候选分数严格大于基线分数,才算通过。
为什么要求严格大于? 等于意味着「改了跟没改一样」。既然没有改善,就不应该冒风险采纳新的。这是 SkillOpt 的核心安全机制 —— 宁可不变,也不变差。
代码位置:backend/services/review/pipeline/skillopt/strategy.py → _validate_candidate()
评分公式
每道题的打分是确定性的(不依赖 LLM 评分),公式如下:
soft = token重叠率 × 0.55 + 质量关键词覆盖率 × 0.3 + 0.15
hard = 1(如果 soft ≥ 0.62),否则 0- token 重叠率:LLM 输出和参考答案的词语重叠比例。重叠越多越像「答对了」
- 质量关键词覆盖率:输出中包含项目类型特定质量关键词的比例
不同项目类型的关键词:
| 聊天 (chat) | 图片 (image) | 视频 (video) |
|---|---|---|
| 步骤、格式、检查、偏好、风险、结论 | 主体、构图、光线、风格、参考图、水印、文字 | 主体、动作、镜头、时长、比例、稳定、首帧 |
最终的混合分数 = hard通过率 × 0.5 + soft均值 × 0.5
为什么用弱评分而不是让 LLM 打分? 因为让 LLM 评估另一个 LLM 的输出,本身就不稳定。用确定性的公式,同样的输入永远得到同样的分数,才有可比性。
代码位置:backend/services/review/pipeline/skillopt/rollout.py → RolloutRunner._score_output()
完整时序图
策略选择
SkillOpt 是自学习的策略之一。每个用户的自学习策略由 user 表的 review_learning_strategy 字段控制:
| 字段值 | 策略 |
|---|---|
distillation(默认) | 蒸馏:从历史对话中提炼规则 |
skillopt | 闭环 Skill 优化:本篇所述机制 |
运营人员通过直接修改数据库 user 表的 review_learning_strategy 字段切换用户策略。
支持的策略类型由 ReviewLearningStrategyType 枚举集中管理(backend/services/review/pipeline/contracts.py),新增策略只需扩展该枚举。
文件清单
| 文件 | 职责 |
|---|---|
backend/services/review/pipeline/skillopt/strategy.py | 主策略编排:数据集 → rollout → 反思 → 编辑 → 门控 |
backend/services/review/pipeline/skillopt/dataset.py | 数据集构造:消息配对 → train/selection 分片 |
backend/services/review/pipeline/skillopt/rollout.py | 沙箱 Rollout + 确定性弱评分 |
backend/services/review/pipeline/skillopt/reflect.py | LLM 反思 + JSON 解析 + 确定性回退 |
backend/services/review/pipeline/skillopt/update.py | Markdown 编辑应用 + 结构校验 |
backend/services/review/pipeline/skillopt/types.py | 数据模型(DTO) |
backend/services/review/pipeline/factory.py | 策略工厂(按用户策略类型缓存实例) |
backend/services/review/pipeline/processor.py | 三阶段编排器(加载 → 策略执行 → 注册) |
backend/services/review/pipeline/contracts.py | 策略 Protocol、枚举、上下文 DTO |