# Prompt Builder 分层注入系统 实施计划

> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.

**Goal:** 新建 prompt_builder + prompt_guidance 模块，将 5 个调用点的手动提示词拼接替换为统一的 8 层分层注入，并在前端提供预览面板。

**Architecture:** 纯函数 `buildSystemPrompt()` 接收 Agent 配置、模型名、可用工具列表等参数，按 8 层顺序组装系统提示词。所有指导文本常量集中在 `prompt_guidance.ts`。5 个调用点（gateway、agent_executor、crew_orchestrator、crew_delegate、controller preview）统一调用 builder，移除各自的手动拼接逻辑。

**Tech Stack:** TypeScript, Midway.js, Vue 3 + Element Plus

**Spec:** `docs/superpowers/specs/2026-04-15-prompt-builder-design.md`

---

## 文件结构

### 新建文件

| 文件 | 职责 |
|------|------|
| `packages/backend/src/modules/netaclaw/runtime/prompt_guidance.ts` | 所有指导文本常量（工具纪律、模型指导、工具策略、统一 MEMORY_SYSTEM_PROMPT） |
| `packages/backend/src/modules/netaclaw/runtime/prompt_builder.ts` | `buildSystemPrompt()` 纯函数 + `collectAvailableToolNames()` 辅助函数 |
| `packages/backend/test/prompt_builder.test.ts` | prompt_builder 纯函数单元测试 |

### 修改文件

| 文件 | 变更 |
|------|------|
| `packages/backend/src/modules/netaclaw/service/skill_loader.ts:143-161` | `buildSkillsPrompt()` 增强索引格式（category + tags + 强制措辞） |
| `packages/backend/src/modules/netaclaw/gateway/server.ts:25-42,167-209` | 移除 MEMORY_SYSTEM_PROMPT，替换手动拼接为调用 builder |
| `packages/backend/src/modules/netaclaw/service/agent_executor.ts:20-24,98-136` | 移除 MEMORY_SYSTEM_PROMPT，替换手动拼接为调用 builder |
| `packages/backend/src/modules/netaclaw/runtime/agent.ts:64-66` | 移除 memoryContext 拼接（已由 builder 处理） |
| `packages/backend/src/modules/netaclaw/service/crew_orchestrator.ts:224-257` | `buildEnhancedPrompt()` 改为调用 builder + crewContext |
| `packages/backend/src/modules/netaclaw/service/crew_delegate.ts:47-53` | `buildSubAgentPrompt()` 改为调用 builder |
| `packages/backend/src/modules/netaclaw/controller/agent.ts:6-41` | 新增 `previewPrompt` 接口 |
| `packages/frontend/src/modules/agent/views/agent-edit.vue:94-102,211-480` | 系统提示词 Tab 下方新增折叠预览面板 |

---

## Task 1: 创建 prompt_guidance.ts — 指导文本常量

**Files:**
- Create: `packages/backend/src/modules/netaclaw/runtime/prompt_guidance.ts`

- [ ] **Step 1: 创建 prompt_guidance.ts 文件（第一部分：MEMORY + TOOL_USE_ENFORCEMENT + MODEL_GUIDANCE_RULES 前半）**

```typescript
// packages/backend/src/modules/netaclaw/runtime/prompt_guidance.ts

/**
 * Prompt Builder 指导文本常量
 * 所有注入到系统提示词的指导文本集中管理于此文件。
 */

// ─── 统一的 MEMORY_SYSTEM_PROMPT（合并 gateway 详细版 + agent_executor 简版） ───

export const MEMORY_SYSTEM_PROMPT = `

## 记忆系统
你拥有长期记忆能力。使用 memory_save 工具存储重要信息，使用 memory_recall 工具检索过往记忆。

记忆类型：
- user: 用户画像（偏好、角色、习惯）
- project: 项目知识（进展、决策、约束）
- feedback: 行为反馈（用户对你行为的纠正或确认）
- reference: 引用（外部资源链接、文档地址）

存储原则：
- 当用户透露个人偏好、角色、习惯时，存为 user 类型
- 当了解到项目进展、决策、约束时，存为 project 类型
- 当用户纠正或确认你的行为时，存为 feedback 类型
- 当提到外部资源链接时，存为 reference 类型
- 更新已有记忆而非创建重复条目
- 只存储对未来对话有价值的信息`;

// ─── Layer 2: 工具使用纪律（通用） ───

export const TOOL_USE_ENFORCEMENT = `# 工具使用规范
你必须通过工具采取行动 - 不要只描述你打算做什么。当你说"我来检查一下"、"让我执行"时，必须在同一回复中立即发起对应的工具调用。不要以"下一步我会..."结束回复 - 现在就执行。

持续工作直到任务真正完成。不要停在计划阶段。如果你有可用的工具能完成任务，使用它们，而不是告诉用户你会怎么做。

每条回复要么 (a) 包含推进任务的工具调用，要么 (b) 向用户交付最终结果。仅描述意图而不行动的回复是不可接受的。`;

// ─── Layer 3: 模型特定指导 ───

interface ModelGuidanceRule {
  patterns: string[];
  guidance: string;
}

const MODEL_GUIDANCE_RULES: ModelGuidanceRule[] = [
  {
    patterns: ['claude'],
    guidance: '', // Claude 工具调用能力强，不需要额外指导
  },
  {
    patterns: ['gpt', 'o1', 'o3', 'o4'],
    guidance: `# 执行纪律
<tool_persistence>
- 工具能提升正确性时必须使用，不要提前停止
- 工具返回空结果时换一种查询策略重试
- 持续调用工具直到：(1) 任务完成 且 (2) 已验证结果
</tool_persistence>
<mandatory_tool_use>
以下问题绝不能凭记忆回答，必须使用工具：
- 算术计算 → bash
- 文件内容、大小 → read_file / list_dir
- 当前时间日期 → bash
</mandatory_tool_use>`,
  },
  {
    patterns: ['deepseek'],
    guidance: `# 操作规范
- 支持并行工具调用：多个独立操作可在同一回复中发起
- 工具参数必须是合法 JSON，注意字符串转义
- 不要在工具调用的同时输出大段解释，先执行再说明`,
  },
  {
    patterns: ['minimax', 'abab'],
    guidance: `# 操作规范
- 算术、日期、文件内容等事实性问题必须使用工具获取，不要凭记忆回答
- 调用工具时严格按照参数 schema 传参，不要添加 schema 中不存在的字段
- 一次只调用一个工具，等待结果后再决定下一步
- 如果工具返回错误，换一种方式重试，不要放弃`,
  },
  {
    patterns: ['doubao', 'skylark'],
    guidance: `# 操作规范
- 工具参数必须严格匹配 schema 定义的类型
- 使用 bash 工具时加 -y 等非交互标志，避免命令挂起
- 先用 read_file/list_dir 了解现状，再做修改操作`,
  },
  {
    patterns: ['qwen'],
    guidance: `# 操作规范
- 工具调用参数必须严格匹配 schema，不要添加额外字段
- 使用 bash 工具时加 -y 等非交互标志，避免命令挂起
- 先用 read_file/list_dir 了解现状，再做修改操作`,
  },
  {
    patterns: ['glm', 'chatglm'],
    guidance: `# 操作规范
- 工具调用参数必须严格匹配 schema 定义的类型（string/number/boolean）
- 文件路径使用绝对路径
- 每次工具调用后检查返回结果，确认操作成功后再继续`,
  },
];

const DEFAULT_MODEL_GUIDANCE = `# 操作规范
- 事实性问题（算术、日期、文件内容）必须使用工具获取，不要凭记忆回答
- 调用工具时严格按照参数 schema 传参
- 一次只调用一个工具，等待结果后再决定下一步
- 如果工具返回错误，换一种方式重试`;

export function getModelGuidance(modelId: string): string {
  const lower = modelId.toLowerCase();
  for (const rule of MODEL_GUIDANCE_RULES) {
    if (rule.patterns.some(p => lower.includes(p))) {
      return rule.guidance;
    }
  }
  return DEFAULT_MODEL_GUIDANCE;
}

// ─── PLACEHOLDER_TOOL_BEHAVIOR ───
```

- [ ] **Step 2: 追加 prompt_guidance.ts 第二部分（Layer 4 工具行为策略）**

在 `// ─── PLACEHOLDER_TOOL_BEHAVIOR ───` 处替换为：

```typescript
// ─── Layer 4: 工具行为策略（条件注入） ───

const TOOL_BEHAVIOR_GUIDANCE: Record<string, string> = {
  memory_save: `## 记忆使用策略
使用 memory_save 存储对未来对话有价值的持久信息：用户偏好、环境细节、项目约束。
保持记忆紧凑，聚焦于减少用户未来重复纠正的事实。
不要存储任务进度、会话结果、临时 TODO 状态。更新已有记忆而非创建重复条目。`,

  todo: `## 任务规划策略
面对3步以上的复杂任务或用户提供多个任务时，先用 todo 工具创建任务列表。
同一时间只标记一个任务为 in_progress。完成后立即标记 completed。`,

  skill_manage: `## 技能管理策略
完成复杂任务（5步以上工具调用）后，考虑用 skill_manage 将方法保存为技能以便复用。
使用技能时如发现过时或错误，立即修补，不要等用户要求。`,
};

export function getToolBehaviorGuidance(toolNames: string[]): string {
  const parts: string[] = [];
  for (const [toolName, guidance] of Object.entries(TOOL_BEHAVIOR_GUIDANCE)) {
    if (toolNames.includes(toolName)) {
      parts.push(guidance);
    }
  }
  return parts.join('\n\n');
}
```

- [ ] **Step 3: 提交**

```bash
git add packages/backend/src/modules/netaclaw/runtime/prompt_guidance.ts
git commit -m "feat(netaclaw): 新建 prompt_guidance.ts — 集中管理所有指导文本常量"
```

---

## Task 2: 创建 prompt_builder.ts — 核心构建函数

**Files:**
- Create: `packages/backend/src/modules/netaclaw/runtime/prompt_builder.ts`

- [ ] **Step 1: 创建 prompt_builder.ts 文件（接口定义 + collectAvailableToolNames）**

```typescript
// packages/backend/src/modules/netaclaw/runtime/prompt_builder.ts

import { SkillLoaderService } from '../service/skill_loader.js';
import {
  TOOL_USE_ENFORCEMENT,
  MEMORY_SYSTEM_PROMPT,
  getModelGuidance,
  getToolBehaviorGuidance,
} from './prompt_guidance.js';

/** 基础内置工具（始终可用） */
const BASE_TOOLS = ['bash', 'read_file', 'write_file', 'list_dir', 'todo'];

/** 构建完整的可用工具名列表 */
export function collectAvailableToolNames(opts: {
  memoryEnabled?: boolean;
  hasSkills?: boolean;
  crewRole?: 'master' | 'sub';
}): string[] {
  const names = [...BASE_TOOLS];
  if (opts.memoryEnabled) names.push('memory_save', 'memory_recall');
  if (opts.hasSkills) names.push('read_skill', 'read_skill_file', 'skill_manage');
  if (opts.crewRole === 'master') names.push('delegate_task', 'delegate_parallel', 'escalate');
  return names;
}

export interface BuildSystemPromptParams {
  agentSystemPrompt: string;
  modelId: string;
  availableToolNames: string[];
  skills: string[];
  skillLoader: SkillLoaderService;
  memoryEnabled: boolean;
  memoryContext?: string;
  crewContext?: string;
}

export interface PromptLayer {
  name: string;
  content: string;
}

export interface BuildResult {
  prompt: string;
  layers: PromptLayer[];
}

// ─── PLACEHOLDER_BUILD_FN ───
```

- [ ] **Step 2: 追加 buildSystemPrompt 函数实现**

在 `// ─── PLACEHOLDER_BUILD_FN ───` 处替换为：

```typescript
/** 8 层分层组装系统提示词 */
export function buildSystemPrompt(params: BuildSystemPromptParams): BuildResult {
  const {
    agentSystemPrompt,
    modelId,
    availableToolNames,
    skills,
    skillLoader,
    memoryEnabled,
    memoryContext,
    crewContext,
  } = params;

  const layers: PromptLayer[] = [];

  // Layer 1: Agent 身份（用户写的 systemPrompt，原样保留）
  if (agentSystemPrompt) {
    layers.push({ name: 'Agent 身份', content: agentSystemPrompt });
  }

  // Layer 2: 工具使用纪律（通用，所有有工具的 Agent 都注入）
  if (availableToolNames.length > 0) {
    layers.push({ name: '工具使用纪律', content: TOOL_USE_ENFORCEMENT });
  }

  // Layer 3: 模型特定指导（按模型名称子串匹配）
  const modelGuidance = getModelGuidance(modelId);
  if (modelGuidance) {
    layers.push({ name: '模型特定指导', content: modelGuidance });
  }

  // Layer 4: 工具行为策略（按可用工具条件注入）
  const toolBehavior = getToolBehaviorGuidance(availableToolNames);
  if (toolBehavior) {
    layers.push({ name: '工具行为策略', content: toolBehavior });
  }

  // Layer 5: Skill 索引（增强版）
  if (skills.length > 0) {
    const skillPrompt = skillLoader.buildSkillsPrompt(skills, availableToolNames);
    if (skillPrompt) {
      layers.push({ name: 'Skill 索引', content: skillPrompt });
    }
  }

  // Layer 6: 记忆系统
  if (memoryEnabled) {
    let memoryContent = MEMORY_SYSTEM_PROMPT;
    if (memoryContext) {
      memoryContent += `\n\n<memory-context>\n${memoryContext}\n</memory-context>`;
    }
    layers.push({ name: '记忆系统', content: memoryContent });
  }

  // Layer 7: Crew 编排上下文（仅 Crew 场景）
  if (crewContext) {
    layers.push({ name: 'Crew 编排上下文', content: crewContext });
  }

  // Layer 8: 元信息
  const meta = `\n---\n当前时间: ${new Date().toISOString()}\n模型: ${modelId}`;
  layers.push({ name: '元信息', content: meta });

  const prompt = layers.map(l => l.content).join('\n\n');
  return { prompt, layers };
}
```

- [ ] **Step 3: 提交**

```bash
git add packages/backend/src/modules/netaclaw/runtime/prompt_builder.ts
git commit -m "feat(netaclaw): 新建 prompt_builder.ts — 8层分层系统提示词组装引擎"
```

---

## Task 3: 单元测试 prompt_builder + prompt_guidance

**Files:**
- Create: `packages/backend/test/prompt_builder.test.ts`

项目已有 Jest 基础设施（`jest.config.js` + `ts-jest`），但无实际测试文件。prompt_builder 和 prompt_guidance 是纯函数，不依赖 Midway 框架，可以直接用 Jest 测试。

- [ ] **Step 1: 编写测试文件（第一部分：collectAvailableToolNames + getModelGuidance）**

```typescript
// packages/backend/test/prompt_builder.test.ts

import { collectAvailableToolNames, buildSystemPrompt } from '../src/modules/netaclaw/runtime/prompt_builder.js';
import { getModelGuidance, getToolBehaviorGuidance, TOOL_USE_ENFORCEMENT, MEMORY_SYSTEM_PROMPT } from '../src/modules/netaclaw/runtime/prompt_guidance.js';

// ─── collectAvailableToolNames ───

describe('collectAvailableToolNames', () => {
  test('基础工具始终包含 todo', () => {
    const names = collectAvailableToolNames({});
    expect(names).toContain('bash');
    expect(names).toContain('read_file');
    expect(names).toContain('write_file');
    expect(names).toContain('list_dir');
    expect(names).toContain('todo');
  });

  test('memoryEnabled 追加记忆工具', () => {
    const names = collectAvailableToolNames({ memoryEnabled: true });
    expect(names).toContain('memory_save');
    expect(names).toContain('memory_recall');
  });

  test('hasSkills 追加 skill 工具', () => {
    const names = collectAvailableToolNames({ hasSkills: true });
    expect(names).toContain('read_skill');
    expect(names).toContain('read_skill_file');
    expect(names).toContain('skill_manage');
  });

  test('crewRole=master 追加委派工具', () => {
    const names = collectAvailableToolNames({ crewRole: 'master' });
    expect(names).toContain('delegate_task');
    expect(names).toContain('delegate_parallel');
    expect(names).toContain('escalate');
  });

  test('crewRole=sub 不追加委派工具', () => {
    const names = collectAvailableToolNames({ crewRole: 'sub' });
    expect(names).not.toContain('delegate_task');
  });
});

// ─── getModelGuidance ───

describe('getModelGuidance', () => {
  test('Claude 返回空字符串', () => {
    expect(getModelGuidance('claude-sonnet-4-20250514')).toBe('');
  });

  test('MiniMax 匹配 minimax 规则', () => {
    const g = getModelGuidance('MiniMax-M2.7-highspeed');
    expect(g).toContain('一次只调用一个工具');
  });

  test('GPT 匹配 gpt 规则', () => {
    const g = getModelGuidance('gpt-4o-2024-08-06');
    expect(g).toContain('tool_persistence');
  });

  test('DeepSeek 匹配 deepseek 规则', () => {
    const g = getModelGuidance('deepseek-chat');
    expect(g).toContain('并行工具调用');
  });

  test('未知模型返回默认指导', () => {
    const g = getModelGuidance('some-unknown-model-v1');
    expect(g).toContain('事实性问题');
  });

  test('火山引擎跑 doubao 模型匹配 doubao 规则', () => {
    const g = getModelGuidance('doubao-pro-32k');
    expect(g).toContain('非交互标志');
  });
});

// ─── getToolBehaviorGuidance ───

describe('getToolBehaviorGuidance', () => {
  test('包含 todo 时注入任务规划策略', () => {
    const g = getToolBehaviorGuidance(['bash', 'todo']);
    expect(g).toContain('任务规划策略');
  });

  test('包含 memory_save 时注入记忆策略', () => {
    const g = getToolBehaviorGuidance(['memory_save', 'memory_recall']);
    expect(g).toContain('记忆使用策略');
  });

  test('无匹配工具时返回空字符串', () => {
    const g = getToolBehaviorGuidance(['bash', 'read_file']);
    expect(g).toBe('');
  });
});
```

- [ ] **Step 2: 追加测试文件第二部分（buildSystemPrompt 集成测试）**

在测试文件末尾追加：

```typescript
// ─── buildSystemPrompt ───

// 模拟 SkillLoaderService（只需 buildSkillsPrompt 方法）
function createMockSkillLoader(returnValue: string) {
  return {
    buildSkillsPrompt: jest.fn().mockReturnValue(returnValue),
  } as any;
}

describe('buildSystemPrompt', () => {
  test('基础场景：无 skill、无记忆、无 crew', () => {
    const result = buildSystemPrompt({
      agentSystemPrompt: '你是电商助手',
      modelId: 'MiniMax-M2.7-highspeed',
      availableToolNames: ['bash', 'read_file', 'write_file', 'list_dir', 'todo'],
      skills: [],
      skillLoader: createMockSkillLoader(''),
      memoryEnabled: false,
    });

    // Layer 1: Agent 身份
    expect(result.prompt).toContain('你是电商助手');
    // Layer 2: 工具纪律
    expect(result.prompt).toContain('工具使用规范');
    // Layer 3: MiniMax 模型指导
    expect(result.prompt).toContain('一次只调用一个工具');
    // Layer 4: todo 工具策略
    expect(result.prompt).toContain('任务规划策略');
    // Layer 8: 元信息
    expect(result.prompt).toContain('模型: MiniMax-M2.7-highspeed');
    // 不含记忆
    expect(result.prompt).not.toContain('记忆系统');
    // layers 数组正确
    expect(result.layers.map(l => l.name)).toEqual([
      'Agent 身份', '工具使用纪律', '模型特定指导', '工具行为策略', '元信息',
    ]);
  });

  test('完整场景：skill + 记忆 + memoryContext', () => {
    const mockLoader = createMockSkillLoader('<available_skills>...</available_skills>');
    const result = buildSystemPrompt({
      agentSystemPrompt: '你是运营助手',
      modelId: 'gpt-4o',
      availableToolNames: ['bash', 'todo', 'memory_save', 'memory_recall', 'read_skill', 'read_skill_file', 'skill_manage'],
      skills: ['playwright-cli'],
      skillLoader: mockLoader,
      memoryEnabled: true,
      memoryContext: '用户偏好：简洁回复',
    });

    expect(result.prompt).toContain('你是运营助手');
    expect(result.prompt).toContain('tool_persistence'); // GPT 指导
    expect(result.prompt).toContain('available_skills');
    expect(result.prompt).toContain('记忆系统');
    expect(result.prompt).toContain('<memory-context>');
    expect(result.prompt).toContain('用户偏好：简洁回复');
    expect(mockLoader.buildSkillsPrompt).toHaveBeenCalledWith(['playwright-cli'], expect.any(Array));
  });

  test('Crew 场景：注入 crewContext', () => {
    const result = buildSystemPrompt({
      agentSystemPrompt: '你是主 Agent',
      modelId: 'claude-sonnet-4-20250514',
      availableToolNames: ['bash', 'todo', 'delegate_task', 'delegate_parallel', 'escalate'],
      skills: [],
      skillLoader: createMockSkillLoader(''),
      memoryEnabled: false,
      crewContext: '## 你的团队成员\n- 小明: 数据分析',
    });

    expect(result.prompt).toContain('你的团队成员');
    expect(result.layers.find(l => l.name === 'Crew 编排上下文')).toBeTruthy();
    // Claude 模型指导为空，不应有 Layer 3
    expect(result.layers.find(l => l.name === '模型特定指导')).toBeFalsy();
  });

  test('空 agentSystemPrompt 不生成 Layer 1', () => {
    const result = buildSystemPrompt({
      agentSystemPrompt: '',
      modelId: 'deepseek-chat',
      availableToolNames: ['bash', 'todo'],
      skills: [],
      skillLoader: createMockSkillLoader(''),
      memoryEnabled: false,
    });

    expect(result.layers.find(l => l.name === 'Agent 身份')).toBeFalsy();
    expect(result.layers[0].name).toBe('工具使用纪律');
  });
});
```

- [ ] **Step 3: 运行测试确认全部通过**

```bash
cd packages/backend
npx jest test/prompt_builder.test.ts --verbose
```

预期输出：所有 test case PASS。如果 ts-jest 的 ESM 导入有问题（`.js` 后缀），需要在 jest.config.js 中添加 `moduleNameMapper` 将 `.js` 映射到 `.ts`。

如果遇到 ESM 问题，在 `jest.config.js` 中添加：
```javascript
moduleNameMapper: {
  '^(\\.{1,2}/.*)\\.js$': '$1',
},
```

- [ ] **Step 4: 提交**

```bash
git add packages/backend/test/prompt_builder.test.ts
git add packages/backend/jest.config.js  # 如果修改了
git commit -m "test(netaclaw): prompt_builder + prompt_guidance 纯函数单元测试"
```

---

## Task 4: 增强 skill_loader.ts — 索引格式升级

**Files:**
- Modify: `packages/backend/src/modules/netaclaw/service/skill_loader.ts:143-161`

- [ ] **Step 1: 修改 buildSkillsPrompt() 方法**

打开 `packages/backend/src/modules/netaclaw/service/skill_loader.ts`，将 `buildSkillsPrompt()` 方法（第 143-161 行）替换为：

```typescript
buildSkillsPrompt(skillNames: string[], availableTools: string[]): string {
  const filtered = this.filterByConditions(skillNames, availableTools);
  if (filtered.length === 0) return '';

  const lines: string[] = [];
  let totalChars = 0;
  for (const s of filtered) {
    const category = (s.metadata?.category as string) || '通用';
    const tags = Array.isArray(s.metadata?.tags) ? (s.metadata.tags as string[]).join(',') : '';
    const line = `  <skill name="${s.name}" category="${category}" tags="${tags}">\n    ${s.description}\n  </skill>`;
    if (totalChars + line.length > SkillLoaderService.MAX_SKILLS_PROMPT_CHARS) {
      this.logger.warn('[SkillLoader] Skill 索引超过 %d 字符限制，截断', SkillLoaderService.MAX_SKILLS_PROMPT_CHARS);
      break;
    }
    lines.push(line);
    totalChars += line.length;
  }
  if (lines.length === 0) return '';

  return `\n\n## 技能（必须扫描）\n回复前，扫描以下技能列表。如果某个技能明确匹配当前任务，用 read_skill 工具加载完整指令后严格遵循。\n\n<available_skills>\n${lines.join('\n')}\n</available_skills>`;
}
```

变更点：
1. 每个 `<skill>` 标签增加 `category` 和 `tags` 属性
2. 索引头部从被动措辞改为强制措辞（"必须扫描"）
3. 尾部说明从 `当你需要使用某个 skill 时...` 改为 `回复前，扫描以下技能列表...`

- [ ] **Step 2: 验证编译通过**

```bash
cd packages/backend
npx tsc --noEmit --pretty 2>&1 | head -20
```

预期：无新增编译错误。

- [ ] **Step 3: 提交**

```bash
git add packages/backend/src/modules/netaclaw/service/skill_loader.ts
git commit -m "feat(netaclaw): skill 索引增强 — category/tags 属性 + 强制扫描措辞"
```

---

## Task 5: 改造 gateway/server.ts — 替换手动拼接

**Files:**
- Modify: `packages/backend/src/modules/netaclaw/gateway/server.ts:1-42,131-135,167-177,192-210,226-229`

这是最核心的改造，WebSocket 对话入口。

- [ ] **Step 1: 添加 import 并移除 MEMORY_SYSTEM_PROMPT**

在文件顶部 import 区域添加：

```typescript
import { buildSystemPrompt, collectAvailableToolNames } from '../runtime/prompt_builder.js';
```

删除第 25-42 行的 `MEMORY_SYSTEM_PROMPT` 常量（整个 const 声明）。

- [ ] **Step 2: 替换 handleChat() 中的 systemPrompt 组装逻辑**

找到 `handleChat()` 方法中构建 `agentConfig` 的部分。

当前代码（约第 131-135 行）：
```typescript
const { skillPrompt, skillTools: agentSkillTools } = buildSkillContext(
  this.skillLoader,
  agentInfo.skills,
  builtinToolNames,
);
```

替换为：
```typescript
const { skillTools: agentSkillTools } = buildSkillContext(
  this.skillLoader,
  agentInfo.skills,
  builtinToolNames,
);
```

注意：仍然需要 `buildSkillContext` 来获取 `skillTools`（read_skill 等工具实例），但不再需要 `skillPrompt`（由 builder 内部处理）。

- [ ] **Step 3: 提前记忆系统代码 + 替换 agentConfig 构建**

当前 gateway 中记忆系统代码在第 192-210 行（agentConfig 构建之后），需要提前到 `buildSystemPrompt` 调用之前。同时消除冗余的 `agentInfo2` 重新查询。

找到第 192-210 行的记忆系统代码块，**整块删除**（包括 `agentInfo2` 查询）。

然后将第 167-177 行的 agentConfig 构建替换为以下代码（记忆处理 + builder 调用 + agentConfig 构建一体化）：

```typescript
// --- 记忆系统（从 cfg 直接取，不再重新查询 agentInfo2） ---
const memoryConfig: AgentMemoryConfig | undefined = cfg?.memory;
let memoryContext: string | undefined;
let memoryTools: AnyAgentTool[] = [];
if (memoryConfig?.enabled) {
  const provider = createMemoryProvider(memoryConfig, this.memoryRepo);
  const userId = 'anonymous';
  memoryContext = await prefetchMemory(provider, content, agentInfo.name, userId, memoryConfig.prefetchLimit);
  memoryTools = [
    createMemorySaveTool(provider, agentInfo.name, userId),
    createMemoryRecallTool(provider, agentInfo.name, userId),
  ];
}

// --- 分层组装系统提示词 ---
const toolNames = collectAvailableToolNames({
  memoryEnabled: !!memoryConfig?.enabled,
  hasSkills: !!agentInfo.skills?.length,
});
const { prompt: systemPrompt } = buildSystemPrompt({
  agentSystemPrompt: agentInfo.systemPrompt || '',
  modelId: mc.modelId || '',  // 纯模型名，不含 provider: 前缀
  availableToolNames: toolNames,
  skills: agentInfo.skills || [],
  skillLoader: this.skillLoader,
  memoryEnabled: !!memoryConfig?.enabled,
  memoryContext,
});

agentConfig = {
  name: agentInfo.name,
  systemPrompt,
  model,
  apiKey,
  baseUrl,
  channelId,
  channelName,
  channelSupplier,
  maxToolRounds: cfg?.middleware?.maxToolRounds ?? cfg?.middleware?.maxToolCalls ?? 20,
  temperature: cfg?.temperature,
  maxTokens: cfg?.maxTokens,
  defaultThinkLevel: cfg?.thinking?.defaultLevel,
};
```

注意事项：
- `modelId: mc.modelId || ''` 传纯模型名（如 `MiniMax-M2.7-highspeed`），不是 `model` 变量（`openai:MiniMax-M2.7-highspeed`）
- `memoryConfig` 从 `cfg?.memory` 取（`cfg` 在第 129 行已有），不再重新查询 `agentInfo2`
- 原第 193 行的 `const agentInfo2 = agentId ? await this.agentService.info(agentId) : null;` 删除（消除冗余数据库查询）

- [ ] **Step 4: 移除 runAgent 调用中的 memoryContext 参数**

找到 `runAgent()` 调用（约第 229 行），移除 `memoryContext` 参数：

```typescript
const result = await runAgent({
  agentConfig,
  tools: [...this.defaultTools, ...memoryTools, ...agentSkillTools],
  userMessage: content,
  history: history.slice(0, -1),
  // memoryContext 已移除 — 由 builder 处理
  sessionThinkLevel: sessionThinkLevel ?? undefined,
  onToken: (token) => { /* ... */ },
  // ...
});
```

- [ ] **Step 5: 验证编译通过**

```bash
cd packages/backend
npx tsc --noEmit --pretty 2>&1 | head -30
```

- [ ] **Step 6: 提交**

```bash
git add packages/backend/src/modules/netaclaw/gateway/server.ts
git commit -m "refactor(netaclaw): gateway handleChat() 替换手动拼接为 prompt_builder"
```

---

## Task 6: 改造 agent_executor.ts — 替换手动拼接

**Files:**
- Modify: `packages/backend/src/modules/netaclaw/service/agent_executor.ts:1-24,77-100,116-141`

- [ ] **Step 1: 添加 import 并移除 MEMORY_SYSTEM_PROMPT**

在文件顶部 import 区域添加：

```typescript
import { buildSystemPrompt, collectAvailableToolNames } from '../runtime/prompt_builder.js';
```

删除第 20-24 行的 `MEMORY_SYSTEM_PROMPT` 常量。

- [ ] **Step 2: 替换 execute() 中的 systemPrompt 组装**

找到 `execute()` 方法中构建 skillPrompt 的部分（约第 72-83 行）。

当前代码：
```typescript
let skillTools: AnyAgentTool[] = [];
let skillPrompt = '';
// ...
const builtinToolNames = ['bash', 'read_file', 'write_file', 'list_dir'];
const skillCtx = buildSkillContext(this.skillLoader, agentEntity.skills, builtinToolNames);
skillPrompt = skillCtx.skillPrompt;
skillTools = skillCtx.skillTools;
```

替换为：
```typescript
let skillTools: AnyAgentTool[] = [];
// ...
const builtinToolNames = ['bash', 'read_file', 'write_file', 'list_dir'];
const skillCtx = buildSkillContext(this.skillLoader, agentEntity.skills, builtinToolNames);
skillTools = skillCtx.skillTools;
```

移除 `skillPrompt` 变量（不再需要）。

- [ ] **Step 3: 替换 agentConfig 构建和记忆拼接**

找到 agentConfig 构建（约第 98-105 行）和记忆拼接（约第 116-136 行）。

将 agentConfig 构建中的 `systemPrompt: (agentEntity.systemPrompt || '') + skillPrompt` 替换。

注意：`model` 变量在渠道解析后变成了 `provider:model` 格式（如 `openai:MiniMax-M2.7-highspeed`），但 builder 需要纯模型名。用 `mc.modelId` 而不是 `model`。

在记忆预取完成后，统一构建：

```typescript
// 记忆预取（保持原有逻辑获取 memoryContext 和 memoryTools）
let memoryContext: string | undefined;
let memoryTools: AnyAgentTool[] = [];
const memoryConfig: AgentMemoryConfig | undefined = cfg?.memory;
if (memoryConfig?.enabled) {
  const provider = createMemoryProvider(memoryConfig, this.memoryEntity);
  memoryContext = await prefetchMemory(provider, params.message, agentEntity.name, params.userId, memoryConfig.prefetchLimit);
  memoryTools = [createMemorySaveTool(provider), createMemoryRecallTool(provider)];
}

// 构建可用工具名列表
const toolNames = collectAvailableToolNames({
  memoryEnabled: !!memoryConfig?.enabled,
  hasSkills: !!agentEntity.skills?.length,
});

// 分层组装系统提示词
const { prompt: systemPrompt } = buildSystemPrompt({
  agentSystemPrompt: agentEntity.systemPrompt || '',
  modelId: mc.modelId || '',  // 纯模型名，不含 provider: 前缀
  availableToolNames: toolNames,
  skills: agentEntity.skills || [],
  skillLoader: this.skillLoader,
  memoryEnabled: !!memoryConfig?.enabled,
  memoryContext,
});

agentConfig = {
  name: agentEntity.name,
  systemPrompt,
  model,
  apiKey,
  baseUrl,
  maxToolRounds: cfg?.middleware?.maxToolRounds ?? cfg?.middleware?.maxToolCalls ?? 20,
};
```

同时移除后面的 `agentConfig.systemPrompt += MEMORY_SYSTEM_PROMPT;` 行。

- [ ] **Step 4: 移除 runAgent 调用中的 memoryContext 参数**

找到 `runAgent()` 调用（约第 141 行），移除 `memoryContext` 参数。

- [ ] **Step 5: 验证编译通过**

```bash
cd packages/backend
npx tsc --noEmit --pretty 2>&1 | head -30
```

- [ ] **Step 6: 提交**

```bash
git add packages/backend/src/modules/netaclaw/service/agent_executor.ts
git commit -m "refactor(netaclaw): agent_executor execute() 替换手动拼接为 prompt_builder"
```

---

## Task 7: 改造 runtime/agent.ts — 移除 memoryContext 拼接

**Files:**
- Modify: `packages/backend/src/modules/netaclaw/runtime/agent.ts:26-40,64-66`

- [ ] **Step 1: 从 AgentRunParams 接口移除 memoryContext**

打开 `packages/backend/src/modules/netaclaw/runtime/agent.ts`。

找到 `AgentRunParams` 接口（第 26-40 行），移除 `memoryContext?: string;` 字段。

- [ ] **Step 2: 简化 systemContent 构建**

找到第 64-66 行：

```typescript
const systemContent = memoryContext
  ? `${agentConfig.systemPrompt}\n\n<memory-context>\n${memoryContext}\n</memory-context>`
  : agentConfig.systemPrompt;
```

替换为：

```typescript
const systemContent = agentConfig.systemPrompt;
```

同时从第 43 行的解构中移除 `memoryContext`：

```typescript
// 之前
const { agentConfig, tools, userMessage, history = [], memoryContext,
  onToken, onThinking, onToolCall, onToolResult } = params;

// 之后
const { agentConfig, tools, userMessage, history = [],
  onToken, onThinking, onToolCall, onToolResult } = params;
```

- [ ] **Step 3: 验证编译通过**

```bash
cd packages/backend
npx tsc --noEmit --pretty 2>&1 | head -30
```

注意：此步骤可能会在 gateway/server.ts 和 agent_executor.ts 中报错（如果它们还在传 memoryContext 参数）。确保 Task 5 和 Task 6 已完成。

- [ ] **Step 4: 提交**

```bash
git add packages/backend/src/modules/netaclaw/runtime/agent.ts
git commit -m "refactor(netaclaw): runAgent 移除 memoryContext — 已由 prompt_builder 统一处理"
```

---

## Task 8: 改造 crew_orchestrator.ts — 主 Agent 提示词

**Files:**
- Modify: `packages/backend/src/modules/netaclaw/service/crew_orchestrator.ts:1-23,224-257`

- [ ] **Step 1: 添加 import**

在文件顶部 import 区域添加：

```typescript
import { buildSystemPrompt, collectAvailableToolNames } from '../runtime/prompt_builder.js';
```

- [ ] **Step 2: 替换 buildEnhancedPrompt() 方法**

找到 `buildEnhancedPrompt()` 方法（第 224-257 行），替换为：

```typescript
private buildEnhancedPrompt(
  masterAgent: NetaClawAgentEntity,
  ctx: CrewRunContext,
  crew: NetaClawCrewEntity,
  resolvedModelId: string,
): string {
  // 构建 Crew 专属上下文（Layer 7）
  const crewParts: string[] = [];

  // 团队成员信息（含技能）
  const memberList = ctx.memberAgents
    .map(m => {
      const skills = m.agent?.skills?.length
        ? `，已具备技能: ${m.agent.skills.join('、')}`
        : '';
      const desc = m.agent?.description ? `，简介: ${m.agent.description}` : '';
      return `- ${m.name} (${m.label}): ${m.role || '无角色描述'}${desc}${skills}`;
    })
    .join('\n');
  crewParts.push(`## 你的团队成员\n${memberList}\n\n注意：委派任务时无需让成员安装工具或环境，他们已具备所列技能，直接描述要完成的目标即可。`);

  // 委派提示
  if (crew.delegateHints?.hints) {
    crewParts.push(`## 调度建议\n${crew.delegateHints.hints}`);
  }

  // 工具说明
  crewParts.push(`## 编排工具说明
- delegate_task: 将任务委派给指定子 Agent 串行执行
- delegate_parallel: 同时委派多个任务给不同子 Agent 并行执行
- escalate: 遇到无法解决的问题时升级给人工处理

请根据任务需求合理分配工作给团队成员，充分利用并行执行提高效率。`);

  // 通过 builder 统一组装
  const toolNames = collectAvailableToolNames({
    hasSkills: !!masterAgent.skills?.length,
    crewRole: 'master',
  });
  const { prompt } = buildSystemPrompt({
    agentSystemPrompt: masterAgent.systemPrompt || '',
    modelId: resolvedModelId,
    availableToolNames: toolNames,
    skills: masterAgent.skills || [],
    skillLoader: this.skillLoader,
    memoryEnabled: false,
    crewContext: crewParts.join('\n\n'),
  });
  return prompt;
}
```

- [ ] **Step 3: 更新 buildEnhancedPrompt 的调用处（第 139 行）**

当前代码（`crew_orchestrator.ts` 第 138-139 行）：
```typescript
// 构建增强系统提示词
const enhancedPrompt = this.buildEnhancedPrompt(masterAgent, ctx, crew);
```

问题：模型解析在第 141-157 行，在 `buildEnhancedPrompt` 调用之后。需要将调用移到模型解析之后。

将第 138-161 行改为：
```typescript
// 解析主 Agent 模型配置（移到 buildEnhancedPrompt 之前）
const mc = (masterAgent.modelConfig || {}) as any;
let model: string, apiKey: string, baseUrl: string | undefined;
if (mc.apiKey && mc.modelId) {
  model = mc.modelId;
  apiKey = mc.apiKey;
  baseUrl = mc.apiUrl;
} else if (mc.channelId) {
  const resolved = await this.modelChannelService.resolveForAgent(mc.channelId, mc.modelId || '');
  model = `${resolved.provider}:${resolved.model}`;
  apiKey = resolved.apiKey;
  baseUrl = resolved.baseUrl;
} else {
  model = mc.modelId || (process.env.NETACLAW_MODEL ?? 'anthropic:claude-sonnet-4-20250514');
  apiKey = process.env.NETACLAW_API_KEY ?? '';
  baseUrl = mc.apiUrl;
}

// 提取纯 modelId（去掉 provider: 前缀）用于 prompt_builder 模型匹配
const pureModelId = model.includes(':') ? model.split(':')[1] : model;

// 构建增强系统提示词（现在有 modelId 了）
const enhancedPrompt = this.buildEnhancedPrompt(masterAgent, ctx, crew, pureModelId);

const agentConfig: AgentConfig = {
  name: masterAgent.name,
  systemPrompt: enhancedPrompt,
  model,
  apiKey,
  baseUrl,
  // ... 其余字段保持不变
```

- [ ] **Step 4: 验证编译通过**

```bash
cd packages/backend
npx tsc --noEmit --pretty 2>&1 | head -30
```

- [ ] **Step 5: 提交**

```bash
git add packages/backend/src/modules/netaclaw/service/crew_orchestrator.ts
git commit -m "refactor(netaclaw): crew_orchestrator buildEnhancedPrompt 改用 prompt_builder"
```

---

## Task 9: 改造 crew_delegate.ts — 子 Agent 提示词

**Files:**
- Modify: `packages/backend/src/modules/netaclaw/service/crew_delegate.ts:1-13,47-53`

- [ ] **Step 1: 添加 import**

在文件顶部 import 区域添加：

```typescript
import { buildSystemPrompt, collectAvailableToolNames } from '../runtime/prompt_builder.js';
```

- [ ] **Step 2: 替换 buildSubAgentPrompt() 方法**

找到 `buildSubAgentPrompt()` 方法（第 47-53 行），替换为：

```typescript
private buildSubAgentPrompt(
  systemPrompt: string,
  role: string,
  agentSkills?: string[],
  modelId?: string,
): string {
  const rolePrefix = role
    ? `${systemPrompt}\n\n你在团队中的角色: ${role}`
    : systemPrompt;

  const toolNames = collectAvailableToolNames({
    hasSkills: !!agentSkills?.length,
    crewRole: 'sub',
  });
  const { prompt } = buildSystemPrompt({
    agentSystemPrompt: rolePrefix,
    modelId: modelId || '',
    availableToolNames: toolNames,
    skills: agentSkills || [],
    skillLoader: this.skillLoader,
    memoryEnabled: false,
  });
  return prompt;
}
```

- [ ] **Step 3: 更新 buildSubAgentPrompt 的调用处（第 111 行）**

当前代码（`crew_delegate.ts` 第 110-111 行）：
```typescript
const modelInfo = await this.resolveModelConfig(agent);
const systemPrompt = this.buildSubAgentPrompt(agent.systemPrompt || '', member.role, agent.skills);
```

`modelInfo.model` 格式为 `provider:modelId`（如 `openai:MiniMax-M2.7-highspeed`），需要提取纯 modelId。

替换为：
```typescript
const modelInfo = await this.resolveModelConfig(agent);
// 提取纯 modelId（去掉 provider: 前缀）用于 prompt_builder 模型匹配
const pureModelId = modelInfo.model.includes(':') ? modelInfo.model.split(':')[1] : modelInfo.model;
const systemPrompt = this.buildSubAgentPrompt(agent.systemPrompt || '', member.role, agent.skills, pureModelId);
```

- [ ] **Step 4: 验证编译通过**

```bash
cd packages/backend
npx tsc --noEmit --pretty 2>&1 | head -30
```

- [ ] **Step 5: 提交**

```bash
git add packages/backend/src/modules/netaclaw/service/crew_delegate.ts
git commit -m "refactor(netaclaw): crew_delegate buildSubAgentPrompt 改用 prompt_builder"
```

---

## Task 10: 后端预览接口 — controller/agent.ts

**Files:**
- Modify: `packages/backend/src/modules/netaclaw/controller/agent.ts:1-41`

- [ ] **Step 1: 添加 import 和注入**

在文件顶部添加 import：

```typescript
import { buildSystemPrompt, collectAvailableToolNames } from '../runtime/prompt_builder.js';
import { SkillLoaderService } from '../service/skill_loader.js';
import { NetaClawModelChannelService } from '../service/model_channel.js';
```

在 `NetaClawAgentAdminController` 类中添加注入：

```typescript
@Inject()
skillLoader: SkillLoaderService;

@Inject()
channelService: NetaClawModelChannelService;
```

- [ ] **Step 2: 添加 previewPrompt 接口**

在 `NetaClawAgentAdminController` 类中（info() 方法之后）添加：

```typescript
@Post('/previewPrompt', { summary: '预览最终系统提示词' })
async previewPrompt(@Body() body: {
  systemPrompt: string;
  skills: string[];
  modelConfig: { channelId?: number; modelId?: string };
}) {
  let modelId = body.modelConfig?.modelId || 'unknown';

  // 如果有 channelId，解析渠道获取模型信息
  if (body.modelConfig?.channelId) {
    const resolved = await this.channelService.resolveForAgent(
      body.modelConfig.channelId,
      modelId,
    );
    modelId = resolved.model;
  }

  const toolNames = collectAvailableToolNames({
    hasSkills: !!body.skills?.length,
  });

  const result = buildSystemPrompt({
    agentSystemPrompt: body.systemPrompt || '',
    modelId,
    availableToolNames: toolNames,
    skills: body.skills || [],
    skillLoader: this.skillLoader,
    memoryEnabled: false, // 预览时不含记忆上下文
  });

  return this.ok(result);
}
```

- [ ] **Step 3: 验证编译通过**

```bash
cd packages/backend
npx tsc --noEmit --pretty 2>&1 | head -30
```

- [ ] **Step 4: 提交**

```bash
git add packages/backend/src/modules/netaclaw/controller/agent.ts
git commit -m "feat(netaclaw): 新增 previewPrompt 接口 — 前端预览最终系统提示词"
```

---

## Task 11: 前端预览面板 — agent-edit.vue

**Files:**
- Modify: `packages/frontend/src/modules/agent/views/agent-edit.vue:94-102,211-480`

- [ ] **Step 1: 在系统提示词 Tab 下方添加折叠预览面板**

打开 `packages/frontend/src/modules/agent/views/agent-edit.vue`。

找到系统提示词 Tab（第 94-102 行），在 `</el-tab-pane>` 闭合标签之前（第 102 行之前），添加折叠面板：

```vue
    <!-- 最终提示词预览 -->
    <el-collapse v-if="modelConfig.channelId" style="margin-top: 16px">
      <el-collapse-item>
        <template #title>
          <span>预览最终提示词</span>
          <el-tag size="small" type="info" style="margin-left: 8px">
            运行时实际发送给模型的内容
          </el-tag>
        </template>
        <el-button size="small" @click="loadPreview" :loading="previewLoading" style="margin-bottom: 12px">
          刷新预览
        </el-button>
        <div v-if="previewLayers.length" class="prompt-preview">
          <div v-for="layer in previewLayers" :key="layer.name" class="layer-block">
            <div class="layer-label">{{ layer.name }}</div>
            <pre class="layer-content">{{ layer.content }}</pre>
          </div>
        </div>
      </el-collapse-item>
    </el-collapse>
```

- [ ] **Step 2: 在 script 中添加预览相关的响应式变量和方法**

在 `<script>` 部分的 `setup()` 或 `<script setup>` 中添加：

```typescript
const previewLoading = ref(false);
const previewLayers = ref<Array<{ name: string; content: string }>>([]);

async function loadPreview() {
  previewLoading.value = true;
  try {
    const res = await service.request({
      url: '/admin/netaclaw/agent/previewPrompt',
      method: 'POST',
      data: {
        systemPrompt: form.systemPrompt,
        skills: form.skills,
        modelConfig: {
          channelId: modelConfig.channelId,
          modelId: modelConfig.modelId,
        },
      },
    });
    previewLayers.value = res?.layers || [];
  } catch (e) {
    console.error('预览加载失败', e);
    previewLayers.value = [];
  } finally {
    previewLoading.value = false;
  }
}
```

- [ ] **Step 3: 添加样式**

在 `<style scoped>` 中添加：

```css
.prompt-preview {
  max-height: 500px;
  overflow-y: auto;
}

.layer-block {
  margin-bottom: 12px;
  border: 1px solid var(--el-border-color-lighter);
  border-radius: 4px;
}

.layer-label {
  padding: 6px 12px;
  background: var(--el-fill-color-light);
  font-weight: 600;
  font-size: 13px;
  border-bottom: 1px solid var(--el-border-color-lighter);
}

.layer-content {
  padding: 12px;
  margin: 0;
  white-space: pre-wrap;
  word-break: break-word;
  font-size: 12px;
  line-height: 1.6;
  max-height: 300px;
  overflow-y: auto;
}
```

- [ ] **Step 4: 在浏览器中验证**

1. 启动前端开发服务器（用户手动执行 `pnpm --filter @neta/frontend dev`）
2. 打开 Agent 编辑页面
3. 选择一个模型渠道
4. 切换到"系统提示词"Tab
5. 确认折叠面板出现
6. 点击"刷新预览"按钮
7. 确认各层内容正确显示

- [ ] **Step 5: 提交**

```bash
git add packages/frontend/src/modules/agent/views/agent-edit.vue
git commit -m "feat(agent): Agent 编辑页新增最终提示词预览面板"
```

---

## Task 12: 全量编译验证 + 运行测试

**Files:** 无新文件

- [ ] **Step 1: 全量 TypeScript 编译检查**

```bash
cd packages/backend
npx tsc --noEmit --pretty
```

预期：无编译错误。如有错误，逐一修复。

- [ ] **Step 2: 运行单元测试**

```bash
cd packages/backend
npx jest test/prompt_builder.test.ts --verbose
```

预期：所有测试 PASS。

- [ ] **Step 3: 检查前端编译**

```bash
cd packages/frontend
npx vue-tsc --noEmit 2>&1 | head -30
```

预期：无新增编译错误。

- [ ] **Step 4: 最终提交（如有修复）**

```bash
git add -A
git commit -m "fix(netaclaw): prompt_builder 全量编译修复"
```

---

## 实施顺序与依赖关系

```
Task 1 (prompt_guidance.ts)
    ↓
Task 2 (prompt_builder.ts) ← 依赖 Task 1
    ↓
Task 3 (单元测试) ← 依赖 Task 1 + 2
    ↓
Task 4 (skill_loader 增强) ← 独立，但 builder 调用它
    ↓
Task 5 (gateway/server.ts) ← 依赖 Task 1 + 2 + 4
Task 6 (agent_executor.ts) ← 依赖 Task 1 + 2 + 4
Task 7 (runtime/agent.ts) ← 依赖 Task 5 + 6（移除 memoryContext 后调用方不再传）
    ↓
Task 8 (crew_orchestrator.ts) ← 依赖 Task 1 + 2
Task 9 (crew_delegate.ts) ← 依赖 Task 1 + 2
    ↓
Task 10 (controller previewPrompt) ← 依赖 Task 1 + 2
Task 11 (前端预览面板) ← 依赖 Task 10
    ↓
Task 12 (全量验证) ← 依赖所有 Task
```

Task 5/6 可并行，Task 8/9 可并行，Task 10/11 串行。









