GPU_GUARD_MONOREPO/docs/superpowers/specs/2026-04-13-skill-evolution-design.md

387 lines
14 KiB
Markdown
Raw Normal View History

2026-05-20 21:39:12 +08:00
# 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 模块。