← HowTo HT · 0003 · 2026-09-03
HOWTO / [论文复现实测] · WikiSkill v1 · 2026 · SEP 03
PAPER REPRO ARXIV:2608.27454 GOOGLE RESEARCH · NO OFFICIAL CODE

WikiSkill:会自己写
Wiki 的 Skill(复现实测)

WikiSkill(Google Research,2026-08-27)在"原始执行记录"和"可执行的 skill"之间插一层永不回滚的 wiki。 Google 没开源代码,第三方复现有报告说 gate 一个提议都没通过。 三层工作区 + 四组件全套按论文重写,自建任务集真跑三组配置——过程里撞上了一个自己写的 bug,还有一次教科书式的过拟合。
跑的配置组
3
编排侧 Token 差
3.3×
实测环境
glm-4.5-flash
+ glm-4.6
自己踩的 Bug
1 处审计链
TL;DR
  1. 第一版实现里,写给 proposer 看的审计记录用错了变量(上一轮 val 而非 R_best)——gate 逻辑本身没错,只因为这一个数字写错,6 轮接受数从 0 变成 1。这解释了第三方复现"一个都没通过"的报告。
  2. 修完之后复现出一次教科书式过拟合:被接受的 skill 让 val 60%→90%,但 test 60%→40%R_best 是只升不降的棘轮,之后两次更健康的修复方案都因为当期分数更低被 gate 拒绝,系统被锁在已知有害的局部最优里。
  3. 先测过评测噪声:空 skill 集把 test 集跑 5 遍,波动 20 个百分点——本文所有 test 差异都要对着这个数字打个问号。
§ 01 / ARCHITECTURE

三层工作区 + 四组件

思路来源明写在论文里:Karpathy 提出的 LLM Wiki——与其让经验散落在优化历史里,不如把它编译成持续复利的持久知识。现有的 skill 自进化方法(EvoSkill、Trace2Skill、SkillOpt)都没有把"学到了什么"维护成一份独立演化的知识表示。

workspace/
├── raw/                    # 不可变。每轮 rollout 的完整执行轨迹
├── wiki/                   # 永不回滚。跨轮累积、只增不减
│   ├── patterns/           #   每个失败模式或成功策略一个 .md
│   ├── index.md            #   pattern 目录
│   ├── logs.md             #   演化编年史
│   └── skill-impact.md     #   审计链:每次提议改了什么、验证分多少、接受还是拒绝
└── skills/                 # 可回滚。当前生效的 skill 集
    └── <name>/
        ├── SKILL.md        #   注入给 inference agent 的正文
        └── PURPOSE.md      #   这个 skill 对应哪几个 wiki pattern

每一轮 k,四个组件依次工作:

  1. Inference Agent 用当前 skill 集 S_k-1 跑训练集。训练 rollout 阶段禁止访问 wiki——论文 §5.1 消融显示,让 inference agent 也能看 wiki 会让最终 skill 质量下降(63.7%→60.9%),因为它会直接从 wiki 抄答案,轨迹失去信息量。
  2. Wiki Maintainer 采样最多 8 条轨迹(最多 5 条失败 + 3 条成功,单条截断 15,000 字符),做根因分析,更新 wiki/patterns/
  3. Skill Proposer 以 ReAct 方式工作:先拿 wiki index + skill-impact.md + 训练结果摘要,按需 read_file 看具体 pattern 页和原始轨迹,最后产出一个原子提议——新建或打补丁,只能选一个。
  4. Gating and Rollback:候选 skill 在验证集打分,严格大于历史最好分才接受,否则回滚到上一版。
唯一的硬约束

skill 可以回滚,wiki 永远不回滚。无论接受还是拒绝,提议的元数据、unified diff、验证分、接受结果都要写进 skill-impact.md,给 proposer 提供客观审计链,避免重复提议已经失败的干预。

这条设计在实测里是整套机制最脆的一环,§07 详说。

§ 02 / TASKS

环境与任务集

角色模型理由
Inference Agentglm-4.5-flash故意选弱模型,留出进化空间
Wiki Maintainer / Skill Proposerglm-4.6编排侧需要更强的分析能力

论文用 Gemini-3.5-Flash 和 Qwen/Gemma 系列。用不同模型族是这次复现和论文最大的偏离,结论对比时要记住这一点。

任务集怎么设计(比代码更重要的一步)

复现这类论文最容易翻车的不是实现,是任务集选得不对,导致 skill 根本没东西可学。论文的五个 benchmark(LiveMath / SealQA / SpreadsheetBench / OfficeQA / ALFWorld)共同点是:失败原因是程序性的——步骤漏了、边界约定错了、格式不对——而不是知识性的。

对标 LiveMath 那一类,造了 30 道单步题,五种题型各 6 道,按 2/2/2 切成 train/val/test:

题型内容弱模型的典型错法
A cal_days含首含尾的日历天数差一天
B biz_days工作日数(排除周末+节假日),含首含尾漏掉连续节假日、边界日误判星期
C time_add跨天的 HH:MM 时长加法对起始时间取模而不是对总分钟取模
D pct_change百分比变化,带符号、1 位小数、half-up舍入方式、正号丢失
E biz_offsetN 个工作日之后是哪天(不含起始日)把起始日算进去

规则全部写在题面里,不存在"必须靠猜"的隐藏约定——否则这就成了作弊 benchmark。弱模型仍然会系统性做错,这正是 skill 该修的东西。

§ 03 / NOISE

先量评测噪声

这一步千万别跳过。在跑任何进化循环之前,先用空 skill 集把验证集和测试集各跑 5 遍:

python3 noise.py 5
VAL:  60.0% × 5 次,失败模式每次完全相同 (..XX....XX)   波动 0pp
TEST: 50 / 50 / 70 / 70 / 60                             波动 20pp

验证集在 temperature 0 下完全确定,测试集却有 20 个百分点的波动。这个数字直接决定了:任何小于 20pp 的 test 差异,都无权解释。后面所有结论都要跟这条对齐。

论文自己也意识到了这个问题(Appendix "Evaluation robustness with small validation sets"),它的应对是:整条进化流程独立跑 3 次取平均 + 1,000 次配对 bootstrap 显著性检验,而且 test split 是 85–280 题,不是 10 题。

§ 04 / MAINTAINER

落地:Wiki Maintainer

给它的 prompt 里,有一句话对产出质量的影响远超其他所有要求:

"The wiki is never rolled back: everything you write compounds across iterations. Write knowledge that will still be useful ten iterations from now."

配合三条硬性要求:对每条失败轨迹做根因分析(不许停在"模型答错了",要指名具体的程序性错误);从成功轨迹提取有效策略防止回归;每个 pattern 页必须包含症状 / 根因 / 可操作的绕过方法三段。

产出质量比预期高很多

这是它在第 4 轮自动写出来的一页(未经任何编辑):

# Pattern: Calendar Day Business Confusion

## Symptom
The agent incorrectly applies business day filtering logic (removing
weekends and holidays) to questions asking for a simple count of
calendar days.

## Root Cause
The agent has over-fitted on the explicit step-by-step procedure for
calculating business days. When presented with a calendar day query,
it defaults to this complex procedure instead of using the simpler
date difference logic.

## Workaround
Strictly distinguish between "calendar days" and "business days":
- calendar days (inclusive): use (end_date - start_date) + 1.
- business days: apply the explicit listing and filtering steps.
Never apply weekend or holiday filters to a calendar day calculation.

这页 pattern 诊断的是它自己上一轮刚刚被接受的那个 skill 造成的副作用。wiki 层确实在做它该做的事——把跨轮的因果关系沉淀下来。这是整个复现里最让人意外的部分。

§ 05 / PROPOSER

落地:Skill Proposer

ReAct 循环,最多 4 轮,每轮可以回一句 READ: <path> 去看某个 pattern 页或原始轨迹:

for turn in range(4):
    out = llm(convo, ORCH_MODEL)
    m = re.match(r"\s*READ:\s*(\S+)", out)
    if m and turn < 3:
        body = ws.read(m.group(1))[:15000]
        convo += f"\n\nREAD: {...}\n<content>\n{body}\n</content>\n\nContinue."
        continue
    return out

论文强调提议必须是原子的:一轮只碰一个 skill,要么新建、要么打补丁。这不是洁癖——gate 只给一个标量分数,一次改两个地方就无法归因。

§ 06 / RESULTS

实测结果:三组配置

配置说明接受val 最好test 无skilltest 有skillskillspatterns
wiki论文默认(严格 >)1/690%60%40%19
nowiki消融:去掉 Wiki 层0/660%60%70%00
scoped显式适用边界 + gate 放宽为 >=2/660%70%80%27

先把话说死:这三个 test 差值全都在噪声里。20pp 的测量噪声 vs 最大 20pp 的差值。最有力的证据是 nowiki 那一行——它的 skill 集是空的,理论上 60% 和 70% 应该是同一个数,实测差了 10pp。所以本节的 test 列只能用来说明"测不出来",不能用来排名。有信息量的是过程,不是终点。

wiki 组:gate 锁死了一个过拟合的 skill

iter提议目标val门槛结果
0biz_day_holiday_check60%60%REJECTED(持平被严格 > 拒掉)
1biz_day_explicit_steps90%60%ACCEPTED(+30pp,看起来是大胜)
2cal_day_logic70%90%REJECTED
3time-add-minute-overflow40%90%REJECTED
4cal_day_logic70%90%REJECTED
5biz-offset-iterative70%90%REJECTED

it1 接受的 skill 写得没毛病,四步流程清晰。问题是它被无条件注入到每一道题的 prompt 里,于是开始污染日历天数题。证据链很干净:it1 接受前,训练集失败模式是 ..XX......(B 组业务日);接受后,it2 变成 XX........A 组日历天数,原本全对)。

Wiki Maintainer 立刻捕捉到了,写出了 §04 那页 pattern。Proposer 也据此在 it2 和 it4 两次尝试修复。但两次都被 gate 拒绝了,因为修复方案把 val 从 90% 拉回 70%。

关键发现

R_best 是个只升不降的棘轮。一旦某轮凭运气冲高,后面所有"整体更健康但当期分数更低"的修复都会被永久挡在门外——即便 wiki 明明已经诊断出了病因。最后的验收结果印证了这一点:val 60%→90%,test 60%→40%。这就是在 10 题验证集上过拟合的样子。

nowiki 组(消融):提议退化成复读机

6 轮里 5 轮提议同一个名字(biz_days_count)、同一个理由。没有 wiki,proposer 每轮都在从零重新发现同一个失败模式,写出实质相同的 skill,被拒,忘掉,再来一遍。对比 wiki 组 6 轮提出 5 个不同目标、覆盖 4 种题型——这是 wiki 层价值最直接的证据,但证明的不是"性能更好",是一个更朴素的东西:跨轮记忆能防止提议退化成复读机。代价也很清楚:编排侧输入 token 从 24,761 涨到 82,736,3.3 倍

scoped 组:加"适用边界"是唯一确实起作用的改动

针对 wiki 组暴露的越界触发,在 proposer prompt 里加了一段硬要求:每个 SKILL.md 必须以 ## Applies to 开头,写明"只在什么条件下用"和"绝不能用在哪些邻近题型上"。产出的 skill(runs/scoped/skills/time_add_overflow/SKILL.md,未经编辑):

## Applies to
Use this skill ONLY when: The user asks to add a duration (minutes or
hours) to a specific start time (HH:MM) and the duration is large
enough to potentially span more than 24 hours.
Do NOT use this skill when: The task involves date calculations,
business day counting, or percentage changes.

## Procedure
1. Convert to Minutes: 8:30 -> 510 minutes.
2. Add Duration to get total elapsed minutes.
3. Calculate Overflow: divide by 1440, remainder is minutes into final day.
4. Convert remainder back to HH:MM.
5. Verify: ensure result is not simply total minutes modulo 24 hours
   unless duration was explicitly less than 24 hours.

前四步干净,边界声明精确。但第 5 步是模型自己加的一句语义混乱的"校验"——步骤 3 做的就是模 1440,第 5 步却让它警惕"不要只是模 24 小时",自相矛盾。这类噪声句子 gate 拦不住:不影响验证分,就会一直留在 skill 里累积。自动生成的 skill 需要人工过一遍,这是自动化程度的上限。

另外要如实说明:这一组同时放宽了 gate 为 >=,两个变量一起动了,无法归因到单一改动;而且跑了两次,行为差别很大(第一次 6/6 全接受、val 全程 60%;第二次前 4 轮被拒、后 2 轮才接受)——编排模型在 temperature 0 下依然有明显的运行间方差。这一组只能算"值得试的方向",不能算"验证过的结论"。

§ 07 / PITFALLS

五个把这套东西跑坏的坑

1 · 审计链写错了,整个循环就空转(踩得最狠的一个)

第一版实现里,写进 skill-impact.md 的那行 "threshold to beat" 用的是上一轮的 val 分数,而不是 R_best

# 错的
f"(threshold to beat: {history[-1].get('val', 0):.1%})"
# 对的
threshold = r_best            # 判定当时的门槛,先存下来
accepted = r_val > threshold
f"(threshold to beat: {threshold:.1%})"

gate 的判定逻辑本身是对的,错的只是写给 proposer 看的那份审计记录。后果:

修复前修复后
6 轮接受数01
val 最好分60%(纹丝不动)90%
提议多样性6 轮里 5 轮重复同一个6 轮 5 个不同目标

同一份任务集、同一个模型、同一个 gate 逻辑,只因为审计链里一个数字写错,进化循环从"完全空转"变成"能跑"。这解释了为什么第三方复现会报告"gate 一个都没通过"——proposer 的全部历史感知都来自 skill-impact.md,这个文件一旦不可信,wiki 层就退化成了昂贵的装饰品。排查方法:跑完第一轮就把 wiki/skill-impact.md 打开,人工核对里面的 diff 和分数跟控制台日志是否一一对应。

2 · full-injection 是实验设定,不是生产设计

论文 §3.2.1 明确说:active skill 的全文直接注入 inference agent 的 system prompt,理由是"消除 skill 触发和检索失败这个混淆变量",Limitations 里也承认没有评估 skill 检索。这个选择在实验里是对的,在工程上是危险的——§06 那条完整因果链就是直接后果。真要用在真实场景,必须先解决触发问题:要么按 SKILL.md frontmatter 的 description 做检索,要么至少强制每个 skill 自带 Applies to 边界。

3 · 严格 > + 粗粒度指标 = 门槛过不去

在 10 题验证集上,最小可分辨的进步是10 个百分点——一整道题。wiki 组 it0 和整个 nowiki 组,全都是 60% 打平被拒。但把 gate 放宽成 >= 也不是免费的:scoped 组第一次运行直接接受了 6 个 val 分毫不动的提议,skill 集只是无脑膨胀。真正的解法是把验证集做大,让指标粒度变细,而不是在 gate 的比较符号上做文章。

4 · R_best 是只升不降的棘轮

一旦某轮靠运气冲上高分,之后所有"整体更健康但当期分数更低"的修复都被永久挡住。论文里 R_best 只在接受时更新、从不衰减,没有任何退火或重置机制。值得考虑:给 R_best 加衰减、或连续 N 轮全拒之后允许一次探索性接受——这属于论文之外的东西,只是从失败现象推出来的方向,没有实测验证。

5 · 限流会静默伪造实验结论

失败的 API 调用如果返回空串,会被判分函数打成"答错",最后得到一张漂亮但完全错误的准确率表。处理方式:全局最小请求间隔 1.5 秒 + 信号量限并发 2 + 指数退避重试 12 次,并且把硬失败次数单独统计。三组正式运行的 summary.json"errors": 0——这一行不是 0,整组数据作废。

§ 08 / SEQUENCE

想真的用起来,建议的顺序

1

先量噪声,再谈效果

空 skill 集把 val/test 各跑 5 遍。如果 test 波动比预期的 skill 收益还大,先去把评测规模做够,别急着写进化循环。

2

验证集要能分辨出你在乎的失败模式

训练集准确率长期在 80–90% 时,失败信号很薄,proposer 会反复在同一道题上打转。每种失败模式至少要有 3–5 个样本。

3

每个 skill 强制带适用边界

在 full-injection 下这是唯一能防越界触发的手段,成本只是 proposer prompt 里加一段话。

4

skill-impact.md 要当成一等公民测试

它是 proposer 唯一的历史感知来源,写错了整套机制归零,而且不会报错。

5

wiki 层单独也有价值

就算 gate 一个都不通过,wiki/patterns/ 里那些"症状/根因/绕过方法"的页子本身就是可读的资产——对多数团队来说这才是最先能兑现的部分,自动进化那半截是长期目标。

§ 09 / VS PAPER

和论文的对照

论文的结论本次复现
wiki 层带来 +15.0pp(48.7%→63.7%,消融)✗ 未复现,test 差值全在噪声内
持久知识对 skill 进化是关键的⚠ 部分支持:提议多样性显著更高,但换不成可测量的分数
Wiki Maintainer 能提炼出可操作的知识 完全成立,甚至能诊断出自己上一轮 skill 造成的副作用
skill 可回滚 / wiki 不回滚的分工 机制本身工作正常
严格 gate 会排除中性提议(论文自陈的 Limitation) 复现到了,且比预期更严重——还会锁死已知有害的高分 skill
skill 检索未评估(论文自陈的 Limitation) 复现到了 full-injection 导致的越界触发,有完整因果链

没有复现的部分:跨模型 skill 迁移(论文 Table 2)、模型规模与 skill 收益的关系、三次独立运行取平均、配对 bootstrap 显著性检验、五个真实 benchmark。坦白说,这套 10 题验证集 + 10 题测试集 + 6 轮的规模,不足以证伪论文的任何主结论。它能提供的是另一种价值:把这套架构真正跑起来会遇到什么,以及哪几处最容易做错。

§ 10 / REFS

参考

WikiSkill: Compiling Agent Experience into Persistent Knowledge for Skill Evolution — arXiv:2608.27454 论文全文 HTML 版 第三方复现(非官方):ashutoshsinghpr7/wikiskill 第三方复现(非官方):kenhuangus/wikiskill