SkillClaw 技能集体进化框架深度代码解析

摘要

SkillClaw(arXiv:2604.08377)提出了一种让 LLM Agent 的技能在多用户环境中持续进化的框架,核心洞察是:当前 Agent 系统(如 OpenClaw)的技能在部署后基本静态,不同用户重复踩同样的坑、重复发现相似工作流。SkillClaw 把跨用户、跨时间的交互轨迹作为信号,通过"自主 evolver"自动识别模式并更新共享技能库,实现"一次改进,全系统受益"的累积式能力提升。

本文不重复论文摘要,而是直接对照项目源码,回答一篇针对该论文的评审所提出的若干关键疑问:evolver 的算法到底是什么?是否引入了外部验证器作为"认识论锚点"?更新频率和粒度如何控制?技能冲突如何仲裁?错误累积与隐私隔离如何处理?技能库规模增大后检索效率如何保障?

读完本文你会发现:代码基本印证了评审基于摘要的推测,并把其中几个"摘要未说明"的点坐实了——evolver 是 LLM 反思式自举而非 RL,默认路径无外部锚点,冲突仲裁极浅,无 PII 脱敏,检索无工程化保障。但版本化与发布门设计相对扎实。

项目地址:https://github.com/AMAP-ML/SkillClaw
论文:https://arxiv.org/abs/2604.08377

1. 整体架构:两个组件,两套引擎

SkillClaw 由两个解耦组件构成,二者只通过共享存储(Alibaba OSS / S3 / 本地文件系统)和统一的技能格式(SKILL.md 目录)交互。

graph LR
    U[用户 + Agent
Hermes/Codex/Claude Code/OpenClaw] -->|OpenAI 兼容 API| P[Client Proxy
skillclaw/] P -->|自动记录轨迹| S[(共享存储
OSS/S3/local)] S -->|读 sessions| E[Evolve Server
evolve_server/] E -->|写 evolved SKILL.md| S S -->|pull/reload| P P -->|注入技能 catalog| U
  • Client Proxyskillclaw/):本地 API 代理(/v1/chat/completions/v1/messages/v1/responses),拦截 agent 请求、记录会话产物、管理本地技能库。这是单用户起步所需的全部。
  • Evolve Serverevolve_server/):可选服务,从共享存储读 session 数据,演化或创建技能再写回。它支持两种引擎:
    • workflow(默认):固定 3 阶段 LLM 管线(Summarize → Aggregate → Execute)
    • agent:OpenClaw 驱动的工作区,agent 直接编辑技能

部署模型是渐进的:先单机用客户端,需要自动演化时加一台 evolve server,团队共享时多个客户端指向同一共享存储 + 一台 server。客户端与服务端只在共享存储里相遇

2. Evolver 算法剖析:LLM 反思式自举,不是 RL

这是评审最关心的一个问题——“摘要未说明 evolver 的具体算法(是基于 LLM 的反思?规则引擎?还是 RL?)”。代码给出的答案非常明确:本质是 LLM 反思式自举 + 薄层规则记账,没有任何 RL。

workflow 引擎的编排位于 evolve_server/engines/workflow.py:894run_once(),每个"认知"步骤都是一次 OpenAI 兼容 API 调用(客户端 evolve_server/core/llm_client.py:19):

阶段 文件:行 机制 温度
轨迹构建 summarizer.py:114 规则(字符串格式化)
元数据抽取 summarizer.py:349 规则(字典扫描,提取 read/modified skills、prm 分、tool 错误)
摘要 summarizer.py:390 LLM 调用(每 session 一次) 0.2
会话裁判 session_judge.py:362 LLM 调用,固定权重打分 0.1
聚合 aggregation.py:16 规则(按技能名分桶)
演化(决策+产出合一) execution.py:366 单次 LLM 调用,同时决定动作并产出新技能 0.4
冲突合并 execution.py:265 LLM 调用 0.3
技能验证器 skill_verifier.py:154 LLM 调用(门禁) 0.1

关键点:决策与产出在同一次 LLM 调用里完成evolve_skill_from_sessionsexecution.py:366),动作枚举只有四种(evolve_server/core/constants.py:35-41):

1
2
3
4
CREATE      = "create_skill"
IMPROVE = "improve_skill"
OPTIMIZE_DESC = "optimize_description"
SKIP = "skip"

演化系统提示词反复强调"做针对性编辑,不要重写"“宁可 skip 也别投机性编辑”,并有一条很关键的反模式警告(execution.py:125):

如果技能里已有正确 API 信息而 agent 没用上,那是 agent 的问题不是技能的问题,不要删掉正确信息

会话裁判的打分权重是硬编码的(session_judge.py:90-95):task_completion 0.55 / response_quality 0.30 / efficiency 0.05 / tool_usage 0.10,且 session_judge.py:137 会用固定权重重算 overall 而不信任模型自报的 overall——这是个小的确定性保护。

关于 RL——完全没有。 PRM/裁判打出的分数只是存进元数据、作为文本喂给后续 prompt,不存在任何梯度、参数更新或策略优化。准确表述是:LLM-as-judge 驱动的离线反思式技能编辑,规则只负责记账(SHA-256、版本号、slug 正则),不负责认知。

3. 认识论锚点之问:是 LLM 自举,还是引入了外部验证器?

这是评审最尖锐的问题,代码层面的答案是:默认配置下纯 LLM 自举,没有任何外部锚点。 系统里三层所谓"验证"都是 LLM 判 LLM:

graph TD
    T[session 轨迹] --> SUM[LLM 摘要]
    SUM --> EV[LLM 演化
决策+产出] EV --> V1{skill_verifier?} V1 -->|默认关| PUB1[直接发布] V1 -->|opt-in 开| LLMJ[LLM-as-judge 门禁] LLMJ --> PUB2[发布/拒绝] EV --> V2{publish_mode?} V2 -->|validated opt-in| REPLAY[重放 A/B
LLM 重答+PRM 判分] REPLAY --> PUB3[≥baseline 才发布]

3.1 PRM Scorer

skillclaw/prm_scorer.py:116:让模型对每轮回答投票 +1/-1/0,多数表决(prm_m 路并行,默认 3)。纯 LLM judge,无 ground-truth、无程序化正确性校验。

3.2 Skill Verifier

evolve_server/pipeline/skill_verifier.py:154:又一个 LLM 调用,作为"发布门禁"。但 use_skill_verifier 默认 Falseconfig.py:105)。开启后是"LLM 判 LLM 产物是否值得发布",唯一确定性的部分是 min_score=0.75 的数值阈值比较(skill_verifier.py:228)。verifier 提示词明确:“你的工作不是改进技能,只是判断候选是否安全且值得发布”,并要求 fail-closed——verifier 调用失败/JSON 非法时也判 reject(skill_verifier.py:197-220)。

3.3 重放 A/B 验证

这是最接近"锚点"的东西,但也是 opt-in(publish_mode="validated",默认 directconfig.py:107)。做法(skillclaw/validation_worker.py:170):

  1. 拿候选技能和当前技能分别注入 prompt,让 LLM 重答同一指令(_run_replay_branchvalidation_worker.py:142);
  2. 用 PRM judge 给两个回答打分;
  3. 候选须 candidate_mean >= baseline_mean 且 >= 0.75 才算赢(validation_worker.py:208-210)。

问题在于:重放的"环境"只是 LLM 重新回答原始 user 指令,不重跑 agent 的工具、不对照 ground-truth 输出校验程序化正确性。更糟的是,重放案例还刻意偏好"无工具调用"的纯文本轮次(workflow.py:514)——即恰恰是工具/事实正确性无法校验的那类轮次。重放 worker 只在opted-in 的空闲客户端上跑(validation_worker.py:80/94-103,要求 idle + sharing + group_id)。

3.4 结论:默认路径欠债

  • 默认publish_mode="direct" + use_skill_verifier=False):LLM 演化技能 → 直接写共享库,非 LLM 校验。纯 LLM 自举,且形成闭环:LLM 写技能 → 写进共享库 → 该 LLM 未来又读这些技能
  • 开 verifier:多一道 LLM-as-judge 门,仍无外部锚点。
  • validated 模式:LLM 重放 A/B + LLM PRM 判分,最接近 grounding,但仍全 LLM 判、偏好无工具轮次。

按"信任被验证取代、需要异源锚点"的视角,SkillClaw 默认路径正是评审担心的"LLM 自举无锚点"形态。确定性逻辑只存在于记账层(SHA-256 去重 workflow.py:338、版本自增 skill_registry.py:123、slug 正则),都不校验语义正确性。agent 引擎的 “self-validation” 也只要求 agent 写一份 history/vN_evidence.md,是 prompt 约束不是代码约束——测试只断言 markdown 里含规定字符串。

顺带回答"是否需要用户显式反馈":不需要。proxy 自动记录每轮(prompt、响应、工具调用/结果/错误),按 user-turn modulo 自动上传 session(api_server.py:3055),PRM 打分也是后台自动。摘要说的"limited interaction and feedback"成立——它从原始轨迹工作,不向用户索要 thumbs-up。

4. 更新频率与粒度控制:多层节流,但默认偏激进

频率是多层独立节流叠加,粒度由 LLM 每组自选。评审问"过于激进导致不稳定"——确实存在缺口。

4.1 触发与节流(多层 Gate)

Gate 文件:行 作用 默认
A. user-turn 取模 api_server.py:3104-3114 主防抖门,只有 final 轮计入计数 sharing_session_upload_interval=0(禁用)
B. 定时轮询 workflow.py:1015-1023 server 周期 run_once interval_seconds=600s
C. 技能重载轮询 api_server.py:2005-2028 proxy 拉取技能更新 30s + 10% 抖动 + 失败退避封顶 60s
D. 内容 SHA 去重 workflow.py:338-352 内容未变直接 skip
E. fail-closed 验证器 + validated 门 见第 3 节 发布前质量门 均默认关

Gate A 是主防抖:设为 2 即每 2 个用户轮上传一次快照并触发 evolve(commit 064de4a);只有 final 轮计入,内部 tool_use 轮不推进(api_server.py:239-241),避免每步上传。上传成功后才 POST {evolve_server}/trigger,且若响应报 uploaded_skills > 0 则立即拉新技能并 reload(commit 0995fc9)。

4.2 缺口:无 cooldown、无 min-sessions

没有显式 cooldown 计时器没有"至少 N 个 session 才演化"的阈值run_once 会处理单个 drain 出来的 session;一个 skill group 可以只由一个 session 构成。唯一上游上限是 evolve_batch_size(默认 20,config.py:102),限制每组喂给 summarizer 的 session 数。

4.3 粒度与工作版/发布版

LLM 每组在四种动作里选(create/improve/optimize_desc/skip)。Nacos 后端有明确的工作版 vs 发布版区分(测试 test_nacos_working_version_push.pytest_nacos_skill_lifecycle.py 确认):

  • 发布版 = latest 标签指向的版本;运行时 pull 只取发布版(nacos_skill_hub.py:529-536)。
  • 工作版 = editing/reviewing 状态、未打 latest对运行时客户端不可见
  • nacos_publish_mode 默认 reviewconfig.py:123):上传+submit 后停在 uploaded_pending_review,要 Nacos 发布管线/人工 approve 才上线。direct 才直接打 latest
  • 硬安全规则:同名 reviewing 工作版内容不同则抛 RuntimeError 拒绝覆盖nacos_skill_hub.py:439-443)。

4.4 回滚

  • Nacos:保留所有版本 + skillclaw skills publish NAME VERSION 手动重指 latestcli.py:868-885)。
  • 对象存储:版本历史(skill_registry.py:133-149,history 封顶 20 条)+ pull_skills整目录备份、失败即从备份恢复(skill_hub.py:700-796,保留 3 份备份)。
  • 注意:Nacos 后端的 pull 没有备份/恢复逻辑,安全性完全依赖"只拉 published 版本"。没有自动回滚已发布远端版本的命令。

5. 技能冲突解决:很浅,且只在同名哈希碰撞时触发

评审担心"不同用户经验推出矛盾更新如何仲裁"。代码给出的答案是:没有真正的仲裁机制,只有 LLM 自行裁量 + 哈希判断是否需要调 LLM。

  • 演化端唯一的冲突检测是 byte 级 SHA-256 比较(workflow.py:338-352 _detect_conflict)。两个语义等价但措辞不同的技能不会被判重复;两个针对同名技能的矛盾编辑才进合并路径。
  • 合并由 LLM execute_merge 完成(execution.py:265),提示词(execution.py:25-56)让 LLM"矛盾时偏好更具体/具体的指引"——仲裁完全交给 LLM 判断,无确定性规则、无投票、无 per-user 权重。
  • 致命的 last-write-wins 兜底workflow.py:354-381 _resolve_and_upload):若 LLM merge 失败,直接用 incoming 版本覆盖(“keeping incoming version”),无拒绝、无重试、无人工门。
  • 聚合按技能名分桶(aggregation.py:16-46),不做跨用户去重、不做嵌入聚类、不检测"两个不同名字的技能其实是一回事"。新技能创建时靠 prompt 让 LLM 起不撞名的名字,若还是撞了,代码悄悄把 create_skill 降级成 improve_skillexecution.py:456-465)。
  • 嵌入去重在客户端注入时跑(skill_manager.py:433-477),演化端从不调用——不能阻止重复/重叠技能被写进共享库。

6. 错误累积风险:最强护栏默认关闭

评审担心"早期用户的错误模式被固化进共享技能库"。代码证实:默认路径下一个候选技能只经过一次 evolve_skill_from_sessions LLM 调用就直写共享库。 三层 gate 里只有 session judge 默认开,且它只打分不阻断:

Gate 默认 机制 是否阻断发布
Session judge LLM 打 overall 分,固定权重重算 否,只作为 evidence 文本
Skill verifier LLM-as-judge 门禁,min_score=0.75 开启后阻断
重放 A/B 验证 候选须 ≥ baseline 且 ≥ 0.75 开启后阻断

effectiveness 统计(skill_manager.py:244-284)会回写 skill_stats.json 并在检索排序里降权低效技能、push 时可过滤(skill_hub.py:350-363),但这只是客户端 push 过滤,不删除共享库里已有的坏技能。

reject 后无自动重试——同一 evidence 被消费删除(workflow.py:946-952 处理成功即删 session key),候选不再有第二次机会。所以"错误累积 → 系统性偏差"的风险是真实存在的,不是摘要没说清——是默认没护栏。

7. 隐私与隔离:无 PII 脱敏

代码里没有任何 PII 清洗/脱敏grep 'pii|redact|scrub|anonym|pseudonym|mask' 全仓零代码命中。

  • 原始用户内容被原样上传共享存储api_server.py:3073-3083):完整 turns(含 prompt_text 原始输入、response_texttool_calls 含参数、tool_resultstool_errors)verbatim 上传到 {group_id}/sessions/{session_id}.json。唯一身份字段是 user_alias——自填句柄,不是伪匿名哈希,两个都叫 “alice” 的人会撞名。
  • 写技能的 LLM 看到真实(裁剪过的)用户内容:summarizer 把每字段裁到 ~400 字(summarizer.py:29-33),产出 8-15 句 LLM 摘要,然后 _trajectory+_summaryexecution.py:302-349 _build_session_evidence 喂给演化 LLM。裁剪是主要(且非刻意的)抽象层,但不是隐私机制,trajectory 里仍含真实裁剪后的 prompt 和工具 I/O。
  • 技能本身没护栏防泄密:技能 prompt 让 LLM"压缩环境信息(API endpoint、端口、payload 格式)",但没有指令清洗 PII,也没有后处理扫描 SKILL.md 是否泄露用户内容。若 LLM 把用户的 API key/邮箱/文件路径原样写进技能,没有任何环节能拦住。verifier 的 “safe to publish” 指发布质量,不是隐私。
  • 隔离是 group 级、非 user 级:组内所有人能读所有人的 session 和技能,无 per-user ACL。唯一按 alias 隔离的是验证结果(validation_store.py:77-78),防一人多投,但候选技能内容本身组内共享。
  • 稍好一点的是 agent 引擎:agent_workspace.py:152-167 写 compact session(trajectory+summary,刻意排除原始 turns),这是唯一刻意的抽象边界——但默认 workflow 引擎不走这条路径。
  • session 处理后从队列删除(workflow.py:946-952),但若 had_processing_error 则保留重试;演化出的技能内容永久存于 {group_id}/skills/

评审"跨用户共享涉及隐私边界"在企业场景是真的踩雷——代码层面没有任何脱敏,靠的是"用户自觉别把敏感数据喂给 agent"。

8. 检索与规模:无工程化保障,RAG 是死代码

这是评审又一个被代码证伪的点。当前实现没有工程化检索,规模线性膨胀,且嵌入检索代码是死代码。

任务时 SkillClaw 不做任何检索/匹配:它把全部合格技能作为一个 XML catalog 注入 system prompt,让模型自己扫 <description> 决定读哪个 SKILL.mdskill_manager.py:650-669 build_injection_promptget_all_skills 全量)。匹配规则写在注入提示里(skill_manager.py:620-648):“扫 available_skills 的 description,至多读一个 SKILL.md”。

8.1 死代码证据

语义检索 retrieve()skill_manager.py:483)和 _embedding_retrieve(line 455)在全仓没有任何调用方——grep '\.retrieve(' 只命中它自己的定义。SentenceTransformer 嵌入、余弦相似、0.9 去重、effectiveness 加权打分(_weighted_score line 428-431)都写好了,但 request path 根本不走它。config.skill_top_k=6retrieval_mode 开关也是惰性的。

8.2 规模后果

关切 行为
top-k 检索 配置存在(skill_top_k=6)但 retrieve() 从不被调用,惰性
嵌入/RAG 已实现但不可达
catalog 上限 单一软上限 max_skills_prompt_chars(默认 30KB,config.py:37
超限降级 compact 模式丢掉 <description> 只留 name+locationskill_manager.py:592-618),模型只能按名字匹配
对话历史 _truncate_messagesapi_server.py:3181-3216)削到 max_context_tokens(默认 20K);catalog 在 system message 里永不被截断
排序 注入时无排序
缓存 embedding 缓存只在未用的嵌入模式下生效;refresh_if_changed() 靠 generation 指纹避免重解析

具体后果:catalog 在 system message 里无界增长,超 30KB 后丢 description 降级到 name-only 匹配,再往后 _truncate_messages 会越来越多地吃对话历史来压总长,而技能 catalog 仍占着 system message 不让。无分页、无 top-k、无索引、无 RAG,reload 时整目录重扫重解析 O(N) 非增量。可检索性几乎全压在 description 这一个短文本字段上。

9. 总评与思考

维度 评价
evolver 算法 LLM 反思式自举,非 RL;决策与产出在同一次调用
认识论锚点 默认无外部锚点,重放验证是 LLM 判 LLM 且 opt-in
更新频率/粒度 多层节流但默认偏激进;无 cooldown、无 min-sessions
冲突解决 极浅,SHA-only 触发,LLM 裁量 + last-write-wins 兜底
错误累积 真实风险,最强护栏默认关,reject 无重试
隐私隔离 无 PII 脱敏,group 级非 user 级隔离
检索规模 无工程化保障,RAG 死代码,30KB 后降级 name-only
版本化与发布门 扎实:工作版/发布版分离、review 模式默认、pull 备份恢复、SHA 去重

代码基本印证了评审基于摘要的推测,并坐实了几个"摘要未说明"的点。相对更扎实的设计集中在版本化与发布门——Nacos 工作版/发布版分离、review 模式默认、对象存储 pull 备份恢复、SHA 去重——这部分工程上能站住脚。

而从 Agent Infra 文章"验证债"的视角看,SkillClaw 最该追问的"认识论锚点"恰恰是它最薄弱的环节:默认配置下确实欠债。一个被污染的技能会被所有用户继承,而唯一的"异源锚点"候选(重放 A/B)本身仍是 LLM 判 LLM,且偏好无法校验工具正确性的纯文本轮次。

这给后续工作留下清晰方向:

  1. 引入真正的外部锚点:把技能里的代码/命令做可执行校验(编译、lint、dry-run),而非 LLM 自评。
  2. 冲突解决升级:从 SHA-only 升级到语义去重(激活已写的嵌入路径),引入投票/per-user 权重仲裁。
  3. 检索工程化:把 retrieve() 接入注入路径,让 catalog 不再全量塞 system prompt。
  4. 隐私层:在 session 上传前加脱敏,在 SKILL.md 发布前扫描泄露。
  5. 默认开启发布门:让 validated / use_skill_verifier 默认开,把"集体进化"的安全网做实。

SkillClaw 的"集体进化视角"理念是对的——把技能更新从单用户单会话扩展到多用户跨时间的生态层面,这在方向上区别于传统 RAG 和 DSPy。但理念落地到代码,目前的实现是"理念先行、护栏后置",留给安全/可靠性的债并不小。

附:关键文件索引

关注点 文件
编排主循环 evolve_server/engines/workflow.py 894
冲突检测(SHA only) evolve_server/engines/workflow.py 338-352
合并 + last-write-wins 兜底 evolve_server/engines/workflow.py 354-381
verifier reject 路径(无重试) evolve_server/engines/workflow.py 766-791
重放验证 finalize 阈值 evolve_server/engines/workflow.py 596-616
LLM 演化决策+产出 evolve_server/pipeline/execution.py 366
LLM 合并提示词 evolve_server/pipeline/execution.py 25-56
create→improve 降级 evolve_server/pipeline/execution.py 456-465
按技能名聚合 evolve_server/pipeline/aggregation.py 16-46
skill verifier(默认关) evolve_server/pipeline/skill_verifier.py 154-241
session judge(默认开) evolve_server/pipeline/session_judge.py 185-200, 362
配置默认值(verifier 关、direct 发布) evolve_server/core/config.py 104-111
动作枚举 evolve_server/core/constants.py 35-41
OpenAI 兼容 LLM 客户端 evolve_server/core/llm_client.py 19
版本/SHA 记账 evolve_server/core/skill_registry.py 105-151
PRM 多数表决 judge skillclaw/prm_scorer.py 116
重放 A/B(≥baseline 才赢) skillclaw/validation_worker.py 170-210
原始 session 上传(无脱敏) skillclaw/api_server.py 3073-3083
user-turn modulo 节流 skillclaw/api_server.py 3104-3114
技能重载轮询 skillclaw/api_server.py 2005-2028
全量注入 + catalog skillclaw/skill_manager.py 650-669
死代码 retrieve() skillclaw/skill_manager.py 483
嵌入去重(仅注入时) skillclaw/skill_manager.py 433-477
Nacos pull 只取发布版 skillclaw/nacos_skill_hub.py 529-536
Nacos 工作版 vs 发布版 skillclaw/nacos_skill_hub.py 249-299
pull 前备份/失败恢复 skillclaw/skill_hub.py 700-796