第 7 章 · Skills 与能力模块化

04 · 技能系统工程化

把 skill 指令注入系统提示词,理解 skill 与 agent 的关系,以及可扩展的技能加载设计。

注入点:系统提示词的动态拼接

回顾 src/agent.ts 中系统提示词的构建方式。第 6 章把固定的字符串模板改成了动态拼接的数组,第 7 章利用这个灵活性注入 skill 指令:

const systemParts: string[] = [
  "你是一个代码仓库助手...",
  // 工具列表、工作方式、行为准则
];

// 项目规则(第 6 章)
if (options?.projectRules) {
  systemParts.push("", "## 项目规则", "", "...", options.projectRules);
}

// skill 指令(第 7 章)
if (options?.skill) {
  systemParts.push("", options.skill.instructions);
}

// 会话上下文(第 6 章)
if (contextText) {
  systemParts.push("", contextText);
}

systemParts.push("", `工作目录:${state.workingDir}`);

skill 的 instructions 直接追加到系统提示词的数组中。由于每个 skill 的 instructions 本身就是完整的 markdown 文本(带标题和分段),它自然地融入系统提示词的结构,不需要额外的包装。

RunAgentOptions 的变化

RunAgentOptions 新增了一个可选的 skill 字段:

export interface RunAgentOptions {
  maxIterations?: number;
  permissionGuard?: PermissionGuard;
  emitter?: AgentEmitter;
  contextManager?: ContextManager;
  projectRules?: string;
  /** 匹配到的 skill 指令(第 7 章新增) */
  skill?: Skill;
}

和之前新增的 permissionGuard、emitter、contextManager 一样,skill 是可选的。不传的话,agent 的行为和第 6 章完全一样。

Skill 与 Agent 的关系

理解 skill 在 agent 架构中的位置:

main.ts
  ├── SkillRegistry.match(input)  →  选择 skill
  ├── SkillRegistry.stripSkillPrefix(input)  →  剥离前缀
  └── runAgent(state, model, tools, { skill })
        ├── 系统提示词 = 基础指令 + skill.instructions + 项目规则 + 上下文
        ├── ReAct 循环(不变)
        └── 返回结果

skill 不改变 agent 的核心逻辑。 ReAct 循环、工具执行、权限检查、事件发射——这些都不受 skill 影响。skill 唯一做的事情是改变系统提示词的内容,让模型用不同的方式来使用同样的工具。

这就是 skill 系统的优雅之处:同一个 agent,不同的系统提示词,就能产生截然不同的工作方式。

可扩展设计

当前的 skill 都是在代码中内置的(builtInSkills)。但 SkillRegistry 的设计支持更灵活的扩展方式:

注册自定义 skill:

const customSkill: Skill = {
  name: "test",
  description: "编写测试",
  keywords: ["test", "测试", "spec"],
  instructions: "## 测试技能\n...",
};
registry.register(customSkill);

任何实现了 Skill 接口的对象都可以注册到 registry 中。将来可以从配置文件、远程 API 或者 skill 市场加载。

内置 skill + 自定义 skill 共存:

const registry = new SkillRegistry(); // 默认加载内置 skill
registry.register(customSkill);       // 追加自定义 skill

SkillRegistry 的构造函数接受可选的 skill 数组。不传就使用内置 skill,传了就使用传入的 skill 列表。这样测试时可以注入自定义 skill,不需要依赖内置定义。

测试覆盖

Skill 的测试文件在 test/skills/registry.test.ts 中,覆盖了:

  • 显式匹配: /debug xxx → debug skill, source = "explicit"
  • 自动匹配: "帮我修 bug" → debug skill, source = "auto"
  • 中文关键词匹配
  • 多关键词冲突时选最多的
  • 无匹配返回 null
  • 前缀剥离
  • 帮助文本格式化

测试时使用自定义的测试 skill(通过 makeSkill 辅助函数创建),不依赖内置 skill 的具体内容。

版本更新

main.ts 的版本号从 v0.6.0 升级到 v0.7.0

本章回顾

第 7 章实现了 skill 系统,让 agent 能根据任务类型切换工作方式:

新增文件作用
Skill 类型src/skills/types.ts定义 skill 的数据结构
内置 skillsrc/skills/builtins.tsdebug、review、refactor 三个基础技能
SkillRegistrysrc/skills/registry.ts注册、匹配、前缀剥离
skill 注入src/agent.ts把 skill 指令注入系统提示词
skill 集成src/main.ts匹配 skill 并传入 agent

加上之前章节的测试,整个项目现在有 155 个测试用例。

登录以继续阅读

解锁完整文档、代码示例及更多高级功能。

立即登录

On this page