【企业级龙虾】OpenClaw Skills 动态加载架构深度解析:几百个 Skills 如何可控挂载到上下文
关键词:OpenClaw、Skills、动态加载、System Prompt、上下文预算、架构设计
适读人群:想看懂 OpenClaw skill 机制、准备做大规模 skills 管理、想做性能与成本优化的开发者
一、先说结论
本文基于 OpenClaw 官方 Github 源码 20260304 进行分析,所有分析均有源码引用,确保100%准确。
OpenClaw 对 skills 的处理不是“全量塞给模型”,而是一个分层漏斗:
- 多来源发现(workspace / managed / bundled / extra / plugin 等)
- 来源内限流(默认每次加载上限 200)
- 资格过滤(enabled、allowlist、OS/bin/env/config 条件)
- 优先级合并覆盖(同名 skill 后者覆盖前者)
- 模型挂载预算(默认最多 150 个 + 30k chars)
- 动态刷新(文件监听 + snapshot version)
一句话:先把“能用的”筛出来,再把“该给模型看的”压到预算内。
二、为什么这个架构很重要
如果 workspace/skills 下有几百个目录,直接全塞进上下文会出 3 个问题:
- Token 爆炸(成本高、上下文拥堵)
- 模型决策噪音变大(“技能过载”)
- 文件扫描成本高(启动/首轮响应慢)
OpenClaw 这套机制本质是:
把技能系统从“文件目录问题”升级成“运行时策略问题”。
三、源码入口
- 核心加载:
src/agents/skills/workspace.ts - 可用性判定:
src/agents/skills/config.ts - 监听与版本:
src/agents/skills/refresh.ts - 会话更新挂钩:
src/auto-reply/reply/session-updates.ts - 运行时挂载:
src/agents/pi-embedded-runner/run/attempt.ts - 系统提示拼装:
src/agents/system-prompt.ts - 插件技能来源:
src/agents/skills/plugin-skills.ts
四、加载来源全景图(不是只有 workspace/skills)
OpenClaw 的 skill 来源包括:
- 内置:bundled skills
- 托管:
CONFIG_DIR/skills - 工作区:
<workspace>/skills - 个人 agents:
~/.agents/skills - 项目 agents:
<workspace>/.agents/skills - 扩展目录:
skills.load.extraDirs - 插件声明目录:plugin manifest 中声明的 skills 路径
五、优先级覆盖规则(同名 skill 谁生效)
合并顺序(低 -> 高):
extra < bundled < managed < agents-personal < agents-project < workspace
即:workspace 同名 skill 最终优先级最高。
这让“项目本地覆写公共 skill”成为可能,非常实用。
六、几百个 skills 时,怎么防上下文爆炸
1)来源扫描限流(第一层)
在 workspace.ts 中默认限制:
maxCandidatesPerRoot = 300maxSkillsLoadedPerSource = 200maxSkillFileBytes = 256KB
这意味着它不会无限递归地扫目录,也不会吃下超大 SKILL.md。
注意一个细节:
“每源 200”对单目录来源非常直观;但extraDirs可能是多个目录,每个目录都可触发一次加载上限。
2)可用性过滤(第二层)
即使被发现,也未必进入候选:
skills.entries.<key>.enabled === false-> 直接排除- bundled allowlist 不通过 -> 排除
- metadata 条件不满足(OS/bin/env/config)-> 排除
- 可结合远程节点能力(如 remote macOS)判定
3)模型挂载预算(第三层)
进入 system prompt 前再次压缩:
maxSkillsInPrompt = 150(数量上限)maxSkillsPromptChars = 30_000(字符预算)
先按数量截断,再按字符预算二分查找“最大可放前缀”。
这一步就是你问的“怎么丢弃不用的 skills 到上下文”:预算外直接不挂载。
七、“合适 skills”是语义检索吗
当前不是“按用户问题向量检索 Top-K skill”的路线。
目前更偏策略驱动筛选:
- 能不能用(依赖/配置/平台)
- 是否允许(enabled/allowlist/filter)
- 能不能塞进预算(数量 + 字符)
所以“合适”指的是运行时可用且预算友好,不是“语义最像”。
八、运行时挂载路径
时序图(首轮,通常无 snapshot)
User Message
-> initSessionState
-> ensureSkillSnapshot
-> ensureSkillsWatcher
-> buildWorkspaceSkillSnapshot
-> loadSkillEntries(多来源)
-> shouldIncludeSkill(资格过滤)
-> applySkillsPromptLimits(150 + 30k预算)
-> session.skillsSnapshot = { prompt, skills, version }
-> runEmbeddedAttempt
-> resolveSkillsPromptForRun(snapshot优先)
-> buildEmbeddedSystemPrompt(skillsPrompt挂载)
-> LLM 执行
时序图(后续轮次,动态刷新)
SKILL.md add/change/unlink
-> watcher(chokidar)
-> bumpSkillsSnapshotVersion(workspace)
Next Message
-> ensureSkillSnapshot
-> version变了 => 重建snapshot
-> version没变 => 复用snapshot
-> runEmbeddedAttempt(直接复用snapshot.prompt)
九、上下文“丢弃”到底发生在哪
很多人以为“没用到就即时丢弃”。实际上不是单点,而是多阶段:
- 扫描阶段:目录过大、文件过大、无
SKILL.md被丢弃 - 资格阶段:不满足 metadata / config 条件被丢弃
- 挂载阶段:超过
maxSkillsInPrompt或maxSkillsPromptChars被丢弃 - 调用阶段:
disable-model-invocation的 skill 不进入模型技能块
所以它更像分层减压系统,不是“模型先看全量再自己忽略”。
十、实战调参建议(200+ skills 场景)
如果你已经进入“技能很多”的阶段,建议按这个顺序调:
- 先按业务域拆目录,减少单 root 混杂度
- 调
skills.limits.maxSkillsLoadedPerSource(慎增) - 调
skills.limits.maxSkillsInPrompt(通常 80~150 区间更稳) - 维持
maxSkillsPromptChars的预算意识,避免 system prompt 过重 - 用
skillFilter(agent/channel 级)做软隔离:让不同入口只看相关技能 - 对“不能被模型直接触发”的技能加
disable-model-invocation - skills 不是越多越好,越多越会加重模型认知负担,skills 是需要精简的
十一、常见误区
-
误区1:skills 目录里有,就一定进上下文
-> 错,至少要过资格过滤 + prompt 预算。 -
误区2:同名 skill 会并存
-> 错,会按优先级覆盖,最终 Map 只留一个。 -
误区3:改了 SKILL.md 必须重启
-> 错,有 watcher + version,下一轮可刷新。 -
误区4:包含语义检索层(比如 Top-K 策略)
-> 错,没有语义检索和全文检索层,这点比较反常识。
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐

所有评论(0)