GPU_GUARD_MONOREPO/docs/superpowers/specs/2026-04-13-skill-evolution-design.md
2026-05-20 21:39:12 +08:00

387 lines
14 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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 工具
```typescript
// 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 工具
```typescript
// 只读工具,返回已有 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 扫描接口
```typescript
// 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()` 返回后:
```typescript
// 从 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 配置
```typescript
// 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 列表拼接:
```typescript
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 frontmattername + 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优化已勾选 SkillallowOptimizeLinked**
- Agent 配置中已关联的 skill`agent.skills[]` 列表)可以被 review agent 通过 patch/edit 更新
- 适用场景:电商运营 Agent 反复执行"上架商品"skill在实践中发现更好的标题写法或定价策略自动优化该 skill
- Review agent 在分析对话时,优先检查已勾选 skill 是否有改进空间
**模式 2创建全新 SkillallowCreateNew**
- 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 扩展
新增方法:
```typescript
// 热更新单个 skillskill_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 模块。