14 KiB
Skill 自进化系统设计
日期: 2026-04-13 模块: netaclaw/skill-evolution 状态: 设计阶段 依赖: P0 长期记忆系统(共享 background review 模式)
1. 目标
为 NetaClaw Agent 添加 Skill 自进化能力:Agent 在对话中积累经验后,后台 review agent 自动提炼可复用的方法论为 Skill,或更新已有 Skill。同时提供 skill_manage 工具允许 Agent 在对话中主动创建/编辑 Skill。所有 Agent 创建的 Skill 经过安全扫描后才写入文件系统。
2. 核心决策
| 决策项 | 选择 | 理由 |
|---|---|---|
| Review 触发方式 | 迭代计数(默认每 10 轮工具调用) | Hermes 验证过的模式,平衡频率和成本 |
| Review Agent 模型 | 复用主 Agent 同模型 | 保证提炼质量 |
| 安全校验 | 完整正则扫描(~84 条规则) | Agent 创建内容需要严格审查 |
| Skill 存储 | 文件系统 + DB 元数据 | 与现有架构一致 |
| Review 执行方式 | 异步不阻塞主对话 | 不影响用户体验 |
3. 系统架构
[Agent 对话循环 (attempt.ts)]
↓ toolCallCount 累计 >= N
[chat.ts 检测触发条件]
↓ 异步(不阻塞响应)
[spawnSkillReview()] → 独立 runAgent() 调用
↓ review agent 拥有 skill_manage + skill_list 工具
[skill_manage 工具]
↓ 写入前
[SkillGuard 安全扫描]
↓ 通过 → 原子写入
[文件系统 skills/{name}/SKILL.md] + [DB netaclaw_skill]
↓ 写入后
[SkillLoader.reloadSkill()] → 热更新内存缓存
↓ 下次对话
[system prompt 自动包含新 skill]
关键约束:
- Review agent 是一次性的:spawn → 分析 → 写入 → 结束
- Review agent 禁用自身的 nudge interval(防止递归 review)
- Review agent 最多 8 轮工具调用
- Skill 写入是原子的:先写临时文件,扫描通过后 rename
- Review agent 不产生用户可见输出
4. Skill Manager 工具
4.1 skill_manage 工具
// tools/builtin/skill_manage.ts
interface SkillManageParams {
action: 'create' | 'edit' | 'patch' | 'delete' | 'write_file' | 'remove_file';
name: string; // skill 名称
content?: string; // SKILL.md 完整内容(create/edit)
old_string?: string; // patch: 要替换的文本
new_string?: string; // patch: 替换后的文本
replace_all?: boolean; // patch: 替换所有匹配
category?: string; // 可选分类目录
file_path?: string; // 支持文件路径(write_file/remove_file)
file_content?: string; // 支持文件内容(write_file)
}
| action | 用途 | 必需参数 |
|---|---|---|
| create | 创建新 skill | name, content |
| edit | 完整重写 SKILL.md | name, content |
| patch | 局部替换 | name, old_string, new_string |
| delete | 删除 skill 目录 | name |
| write_file | 写入支持文件 | name, file_path, file_content |
| remove_file | 删除支持文件 | name, file_path |
4.2 skill_list 工具
// 只读工具,返回已有 skill 列表
interface SkillListParams {
category?: string; // 可选:按分类过滤
}
// 返回: [{ name, description, category }]
4.3 验证规则
| 规则 | 限制 |
|---|---|
| name | 最长 64 字符,/^[a-z0-9][a-z0-9._-]*$/ |
| category | 单层目录,同 name 命名规则 |
| SKILL.md content | 最大 100,000 字符 |
| 支持文件 | 最大 1MB/文件 |
| 支持文件目录 | 仅 references/, templates/, scripts/, assets/ |
| frontmatter | 必须包含 name 和 description 字段 |
| description | 最长 1024 字符 |
| 总文件数 | 每个 skill 最多 50 个文件 |
| 总大小 | 每个 skill 最大 1MB |
4.4 原子写入流程
1. 验证参数 → 失败则返回错误
2. 写入临时文件 skills/{name}/.tmp_SKILL.md
3. 调用 SkillGuard.scan() 扫描临时文件
4. 如果 verdict === 'dangerous' → 删除临时文件,返回错误
5. rename 临时文件 → SKILL.md
6. 同步 DB 元数据(netaclaw_skill 表)
7. 调用 SkillLoader.reloadSkill(name) 热更新缓存
8. 清除 skill prompt 缓存
5. Skill Guard 安全扫描
5.1 威胁模式分类
参考 Hermes skills_guard.py 的 84 条正则规则,按类别组织:
| 类别 | 规则数 | severity | 示例模式 |
|---|---|---|---|
| 数据泄露 (exfiltration) | 18 | critical | process\.env, \.ssh/, credentials, DNS exfil |
| 提示注入 (prompt_injection) | 16 | critical | ignore.*instructions, you are now, system prompt |
| 破坏性操作 (destructive) | 8 | critical | rm\s+-rf\s+/, mkfs, dd\s+if=, chmod\s+777 |
| 持久化 (persistence) | 10 | high | crontab, authorized_keys, systemd, sudoers |
| 网络 (network) | 9 | high | reverse shells, tunnels, hardcoded IPs |
| 混淆 (obfuscation) | 14 | high | eval\(, Buffer\.from.*base64, hex encoding |
| 代码执行 (execution) | 6 | high | child_process, exec\(, spawn\( |
| 路径穿越 (path_traversal) | 5 | high | \.\.\/, /etc/passwd, /proc/ |
| 加密挖矿 (crypto_mining) | 2 | critical | stratum+tcp, xmrig |
| 供应链 (supply_chain) | 6 | critical | curl.*|.*bash, unpinned deps |
| 权限提升 (privilege_escalation) | 5 | high | sudo, setuid, NOPASSWD |
| 凭证暴露 (credential_exposure) | 5 | critical | hardcoded secrets, private keys |
5.2 扫描接口
// skill_evolution/guard.ts
interface ScanFinding {
category: string;
severity: 'critical' | 'high' | 'medium';
pattern: string;
match: string;
file: string;
line: number;
}
interface ScanResult {
verdict: 'safe' | 'caution' | 'dangerous';
findings: ScanFinding[];
summary: string;
}
function scanSkill(skillPath: string): ScanResult;
5.3 判定逻辑
findings 中有 critical → verdict = 'dangerous'
findings 中有 high → verdict = 'caution'
无 findings → verdict = 'safe'
Agent 创建的 skill 策略:
- safe → 允许
- caution → 允许(记录日志)
- dangerous → 阻止,回滚,返回错误信息给 agent
5.4 二进制文件拦截
禁止的文件扩展名:.exe, .dll, .so, .dylib, .bin, .dat, .com, .msi, .dmg, .app, .deb, .rpm
6. Background Review 机制
6.1 触发条件
在 controller/chat.ts 中,runAgent() 返回后:
// 从 Agent 配置读取
const evoConfig = agentEntity?.config?.skillEvolution;
const skillEvolutionEnabled = evoConfig?.enabled ?? false;
const nudgeInterval = evoConfig?.nudgeInterval ?? 10;
// 累计工具调用计数(跨同一会话的多次请求)
const totalCalls = (sessionMeta.toolCallsSinceReview ?? 0) + result.toolCallCount;
const shouldReview = (
skillEvolutionEnabled &&
totalCalls >= nudgeInterval &&
result.toolCallCount > 0
);
if (shouldReview) {
sessionMeta.toolCallsSinceReview = 0;
// 异步触发,不阻塞响应
spawnSkillReview({
conversationHistory: history,
parentAgentConfig: agentConfig,
skillLoader: this.skillLoader,
linkedSkills: agentEntity?.skills ?? [],
allowOptimize: evoConfig?.allowOptimizeLinked ?? true,
allowCreateNew: evoConfig?.allowCreateNew ?? true,
}).catch(err => logger.warn('Skill review failed:', err));
} else {
sessionMeta.toolCallsSinceReview = totalCalls;
}
6.2 Review Agent 配置
// skill_evolution/review.ts
interface SkillReviewContext {
conversationHistory: LLMMessage[];
parentAgentConfig: AgentConfig;
skillLoader: SkillLoaderService;
skillRepo: Repository<NetaClawSkillEntity>; // DB 同步用
agentRepo: Repository<NetaClawAgentEntity>; // 自动关联新 skill 用
agentName: string; // 当前 Agent 名称
linkedSkills: string[]; // Agent 已勾选的 skill 名称
allowOptimize: boolean;
allowCreateNew: boolean;
}
async function spawnSkillReview(ctx: SkillReviewContext): Promise<void> {
const reviewPrompt = buildSkillReviewPrompt(
ctx.linkedSkills, ctx.allowOptimize, ctx.allowCreateNew,
);
const reviewConfig: AgentConfig = {
...ctx.parentAgentConfig,
name: `${ctx.parentAgentConfig.name}_skill_reviewer`,
systemPrompt: SKILL_REVIEW_SYSTEM_PROMPT,
maxToolRounds: 8,
};
// skill_manage 工具接收 SkillWriter 实例(非 SkillLoader)
const writer = new SkillWriter(ctx.skillLoader.getSkillsDir(), ctx.skillRepo, ctx.skillLoader);
const tools = [
createSkillManageTool(writer, {
allowedEditSkills: ctx.allowOptimize ? ctx.linkedSkills : [],
allowCreateNew: ctx.allowCreateNew,
}),
createSkillListTool(ctx.skillLoader),
];
await runAgent({
agentConfig: reviewConfig,
tools,
userMessage: reviewPrompt,
history: ctx.conversationHistory,
});
}
6.3 Review Prompt
Review prompt 是动态生成的,根据 Agent 的 skillEvolution 配置和已勾选 skill 列表拼接:
function buildSkillReviewPrompt(
linkedSkills: string[], // Agent 已勾选的 skill 名称列表
allowOptimize: boolean,
allowCreateNew: boolean,
): string {
let prompt = '回顾上面的对话,考虑是否需要保存或更新 skill。\n\n';
prompt += '关注:\n';
prompt += '1. 是否使用了非显而易见的方法来完成任务?\n';
prompt += '2. 是否经历了试错或因实际发现而改变了方案?\n';
prompt += '3. 用户是否期望或希望不同的方法或结果?\n\n';
if (allowOptimize && linkedSkills.length > 0) {
prompt += `当前 Agent 已关联以下 skill:${linkedSkills.join(', ')}\n`;
prompt += '请优先检查这些已有 skill 是否可以根据本次对话的经验进行优化(用 patch 更新)。\n\n';
}
if (allowCreateNew) {
prompt += '如果发现了全新的可复用方法论且没有相关 skill,请创建新 skill。\n\n';
}
prompt += '操作指南:\n';
prompt += '- 先用 skill_list 查看已有 skill,避免重复创建\n';
prompt += '- 优化已有 skill 时优先用 patch(局部替换),避免 edit 全量重写\n';
prompt += '- Skill 内容应聚焦于可复用的方法论,而非具体的业务数据\n';
prompt += '- 如果没有值得保存的内容,直接说"无需保存"并停止\n';
return prompt;
}
### 6.4 Review System Prompt
你是一个 Skill 提炼专家。你的任务是分析对话历史,提取可复用的方法论并保存为 Skill。
Skill 格式要求:
- SKILL.md 必须包含 YAML frontmatter(name + description)
- 正文用 Markdown,包含:触发条件、工作流程、规则约束
- name 使用小写字母、数字、连字符(如 nginx-deploy-config)
- description 一句话描述 skill 的用途
你只有 8 轮工具调用机会,请高效行动。
## 7. Agent 配置扩展
### 7.1 AgentEntity.config 扩展
```typescript
interface AgentConfig {
// ...现有字段
skillEvolution?: {
enabled: boolean;
nudgeInterval?: number; // 触发 review 的工具调用间隔,默认 10
allowOptimizeLinked?: boolean; // 是否允许优化已勾选的 skill,默认 true
allowCreateNew?: boolean; // 是否允许创建全新 skill,默认 true
};
}
7.2 Skill 进化范围
Agent 的 skill 进化有两种模式,均可在 Agent 编辑页面独立开关:
模式 1:优化已勾选 Skill(allowOptimizeLinked)
- Agent 配置中已关联的 skill(
agent.skills[]列表)可以被 review agent 通过 patch/edit 更新 - 适用场景:电商运营 Agent 反复执行"上架商品"skill,在实践中发现更好的标题写法或定价策略,自动优化该 skill
- Review agent 在分析对话时,优先检查已勾选 skill 是否有改进空间
模式 2:创建全新 Skill(allowCreateNew)
- Review agent 可以发现对话中的新方法论并创建全新 skill
- 新创建的 skill 自动关联到当前 Agent(加入
agent.skills[]) - 适用场景:投流 Agent 在实践中摸索出一套新的出价策略,提炼为独立 skill
7.3 多 Agent 协作场景考虑
电商运营系统中多个 Agent 各司其职(上架、下架、投流、产品管理等),skill 进化需要考虑:
- Skill 共享:一个 Agent 优化的 skill 如果被其他 Agent 也勾选了,优化会自动生效(因为 skill 存在文件系统,所有 Agent 共享同一份)
- 写入冲突:多个 Agent 同时优化同一个 skill 时,用文件锁(lockfile)保证原子性,后写入的 patch 基于最新内容
- 进化隔离:如果某个 Agent 的场景特殊,不希望它的优化影响其他 Agent,可以关闭
allowOptimizeLinked,只允许allowCreateNew(新 skill 只关联到自己)
7.4 Agent 编辑页面配置
在 Agent 编辑页面新增"Skill 进化"配置区域:
- 总开关:启用/禁用 Skill 进化
- 子开关:允许优化已勾选 Skill(默认开)
- 子开关:允许创建新 Skill(默认开)
- 数字输入:Review 触发间隔(高级选项,默认 10)
7.5 SkillLoader 扩展
新增方法:
// 热更新单个 skill(skill_manage 写入后调用)
async reloadSkill(name: string): Promise<void>;
// 获取 skill 列表摘要(给 review agent 用)
getSkillSummaries(): { name: string; description: string; category?: string }[];
8. 新增文件清单
src/modules/netaclaw/
├── skill_evolution/
│ ├── guard.ts # SkillGuard 安全扫描(正则模式匹配)
│ ├── guard_patterns.ts # 威胁模式定义(84 条规则)
│ ├── review.ts # spawnSkillReview() 后台 review 逻辑
│ └── skill_writer.ts # 原子写入 + 验证 + DB 同步
├── tools/builtin/
│ ├── skill_manage.ts # skill_manage 工具
│ └── skill_list.ts # skill_list 工具
9. 修改文件清单
| 文件 | 修改内容 |
|---|---|
controller/chat.ts |
runAgent 返回后检测触发条件,异步调用 spawnSkillReview,通过 sessionRepo 直接更新 metadata |
service/skill_loader.ts |
新增 reloadSkill()、getSkillSummaries()、getSkillsDir() 方法 |
10. 依赖
无新增外部依赖。安全扫描用纯正则实现,skill 文件读写用 Node.js fs 模块。