Skip to content

10. 技能系统:可复用的专业能力

渐进式披露——让代理按需加载专业知识

引言

想象一个场景:你的代理需要掌握 100 种不同的编程框架知识。如果把所有知识都塞进系统提示词,会导致:

  • 上下文窗口被占满
  • 无关信息干扰判断
  • Token 成本急剧上升

技能系统解决了这个问题——通过渐进式披露(Progressive Disclosure),让代理只在需要时加载相关知识。

渐进式披露:代理按需从技能库加载知识

┌─────────────────────────────────────────────────────────────┐
│                      代理上下文                              │
│                                                             │
│   系统提示词(精简)                                         │
│   + 当前对话                                                 │
│   + 按需加载的技能 ←─────────────────────┐                  │
│                                          │                  │
└─────────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────────┐
│                       技能库                                 │
│                                                             │
│   ┌─────────┐  ┌─────────┐  ┌─────────┐  ┌─────────┐       │
│   │ React   │  │  Vue    │  │ Python  │  │  Go     │       │
│   │ SKILL   │  │ SKILL   │  │ SKILL   │  │ SKILL   │       │
│   └─────────┘  └─────────┘  └─────────┘  └─────────┘       │
│                                                             │
│   用户问 React 问题 → 只加载 React 技能                       │
│                                                             │
└─────────────────────────────────────────────────────────────┘

核心概念

什么是技能?

技能是一种特殊的 Markdown 文件(SKILL.md),包含:

  1. 元数据(Frontmatter):描述技能的属性
  2. 正文:具体的知识、指南、模板

渐进式披露

传统方式:
┌─────────────────────────────────────────┐
│  系统提示词                              │
│  + 所有框架知识(100个)                 │  ← 上下文爆炸
│  + 当前对话                              │
└─────────────────────────────────────────┘

技能系统:
┌─────────────────────────────────────────┐
│  系统提示词                              │
│  + 技能目录(100行描述)                 │  ← 轻量级
│  + 当前对话                              │
│  + 按需加载的技能(1-2个)               │  ← 只在需要时加载
└─────────────────────────────────────────┘

生活类比

技能系统就像图书馆

  • 代理知道图书馆里有哪些书(技能目录)
  • 但不会把所有书都搬到桌上
  • 需要时才去借阅相关的书(加载技能)

SKILL.md 文件格式

基础结构

markdown
---
name: react-component-generator
description: 生成 React 组件的技能
---

# React 组件生成技能

## 使用方法

当用户要求创建 React 组件时,遵循以下步骤:

1. 确认组件类型(函数组件/类组件)
2. 确认是否需要 TypeScript
3. 生成组件代码

## 代码模板

### 函数组件模板

\`\`\`tsx
interface ${ComponentName}Props {
  // props
}

export const ${ComponentName}: React.FC<${ComponentName}Props> = (props) => {
  return (
    <div>
      {/* content */}
    </div>
  );
};
\`\`\`

Frontmatter 字段详解

字段类型必填说明
namestring技能唯一标识符
descriptionstring技能描述(用于判断是否加载)
licensestring许可证
compatibilitystring[]兼容的框架/环境
metadataobject自定义元数据
allowed-toolsstring[]技能允许使用的工具

SKILL.md 文件结构:Frontmatter 元数据与正文内容

完整示例

markdown
---
name: nextjs-app-router
description: Next.js 14 App Router 开发指南,包含路由、布局、数据获取等最佳实践
license: MIT
compatibility:
  - Next.js 14+
  - React 18+
  - TypeScript
metadata:
  version: "1.0.0"
  author: "DeepAgents Team"
  updated: "2024-01-15"
allowed-tools:
  - read_file
  - write_file
  - edit_file
---

# Next.js App Router 开发指南

## 概述

Next.js 14 的 App Router 是基于 React Server Components 的全新路由系统。

## 目录结构

\`\`\`
app/
├── layout.tsx          # 根布局
├── page.tsx            # 首页
├── loading.tsx         # 加载状态
├── error.tsx           # 错误边界
├── not-found.tsx       # 404 页面
├── api/
│   └── route.ts        # API 路由
└── [dynamic]/
    └── page.tsx        # 动态路由
\`\`\`

## 路由约定

### 页面文件

- `page.tsx` - 路由的 UI
- `layout.tsx` - 共享布局
- `loading.tsx` - 加载 UI
- `error.tsx` - 错误 UI
- `not-found.tsx` - 404 UI

### 代码示例

\`\`\`tsx
// app/blog/[slug]/page.tsx
export default async function BlogPost({
  params,
}: {
  params: { slug: string };
}) {
  const post = await getPost(params.slug);
  return <article>{post.content}</article>;
}
\`\`\`

## 数据获取

### Server Components

\`\`\`tsx
// 直接在组件中 async/await
async function ProductList() {
  const products = await fetch('https://api.example.com/products');
  return <ul>{/* render products */}</ul>;
}
\`\`\`

### Client Components

\`\`\`tsx
'use client';

import { useState, useEffect } from 'react';

export function Counter() {
  const [count, setCount] = useState(0);
  return <button onClick={() => setCount(c => c + 1)}>{count}</button>;
}
\`\`\`

## 最佳实践

1. 优先使用 Server Components
2. 只在需要交互时使用 'use client'
3. 使用 loading.tsx 提供加载状态
4. 使用 error.tsx 处理错误边界

配置技能

方式一:指定技能目录

typescript
import { createDeepAgent } from "deepagents";
import { MemorySaver } from "@langchain/langgraph";

const checkpointer = new MemorySaver();

const agent = createDeepAgent({
  skills: ["/skills/"],  // 技能目录路径
  checkpointer,
  systemPrompt: "你是一个编码助手,可以使用各种框架技能。"
});

方式二:指定具体技能文件

typescript
const agent = createDeepAgent({
  skills: [
    "/skills/react/SKILL.md",
    "/skills/vue/SKILL.md",
    "/skills/typescript/SKILL.md",
  ],
  checkpointer,
});

传入技能文件

调用时需要提供技能文件的实际内容:

typescript
import { createFileData } from "deepagents";

const reactSkill = `---
name: react-basics
description: React 基础知识
---

# React 基础

...技能内容...
`;

const result = await agent.invoke({
  messages: [{ role: "user", content: "帮我创建一个 React 组件" }],
  files: {
    "/skills/react/SKILL.md": createFileData(reactSkill),
  },
}, { configurable: { thread_id: "thread-1" } });

技能加载时机

SkillsMiddleware 会在以下时机决定是否加载技能:

自动加载

当代理发送消息时,中间件会:

  1. 分析当前对话上下文
  2. 检查技能目录中的技能描述
  3. 判断哪些技能与当前任务相关
  4. 自动加载相关技能到上下文

手动触发

代理也可以主动请求加载技能:

typescript
// 在系统提示词中指导代理
systemPrompt: `...

## 技能使用

你有以下技能可用:
- react-basics: React 基础知识
- nextjs-app-router: Next.js App Router 指南
- typescript-tips: TypeScript 高级技巧

当需要特定框架知识时,相关技能会自动加载到上下文中。
`

技能加载时机:自动加载与手动触发两种路径

技能 vs 记忆

特性技能 (Skills)记忆 (Memory)
来源开发者预定义代理从交互中学习
内容操作指南、模板、知识用户偏好、事实、历史
可修改❌ 通常只读✅ 代理可读写
作用域全局共享特定用户/线程
加载时机按需加载(渐进式披露)对话开始时
存储文件系统StoreBackend (持久化)

使用场景对比

用户问:"我喜欢什么风格的代码?"
→ 记忆(用户偏好)

用户问:"Next.js 的 App Router 怎么用?"
→ 技能(框架知识)

用户问:"上次我们讨论的项目架构是什么?"
→ 记忆(历史交互)

用户问:"React 组件的最佳实践是什么?"
→ 技能(通用知识)

技能与记忆的六维度对比

技能与子代理

继承规则

子代理类型技能继承
通用子代理 (general-purpose)✅ 自动继承主代理技能
自定义子代理❌ 不继承,需显式配置

为子代理配置技能

typescript
const codeReviewer = {
  name: "code-reviewer",
  description: "代码审查专家",
  systemPrompt: "你是一位资深代码审查专家。",
  tools: [readFileTool],
  skills: ["/skills/code-review/", "/skills/security/"],  // 子代理专属技能
};

const agent = createDeepAgent({
  skills: ["/skills/general/"],  // 主代理技能
  subagents: [codeReviewer],
});

技能隔离

技能状态在主代理和子代理之间完全隔离:

  • 主代理的技能对子代理不可见
  • 子代理的技能对主代理不可见
  • 每个代理有独立的 SkillsMiddleware 实例

子代理技能继承规则与隔离边界

创建自定义技能

步骤 1:规划技能结构

/skills/
├── react/
│   ├── SKILL.md           # React 基础
│   └── hooks/
│       └── SKILL.md       # React Hooks 专题
├── vue/
│   └── SKILL.md           # Vue 基础
├── typescript/
│   ├── SKILL.md           # TypeScript 基础
│   └── advanced/
│       └── SKILL.md       # TypeScript 高级
└── testing/
    └── SKILL.md           # 测试技能

步骤 2:编写技能文件

markdown
---
name: react-hooks
description: React Hooks 完全指南,包含 useState、useEffect、useContext 等所有内置 Hook 的使用方法和最佳实践
compatibility:
  - React 16.8+
allowed-tools:
  - read_file
  - write_file
  - edit_file
---

# React Hooks 完全指南

## useState

### 基础用法

\`\`\`tsx
const [count, setCount] = useState(0);
\`\`\`

### 函数式更新

\`\`\`tsx
setCount(prev => prev + 1);
\`\`\`

## useEffect

### 基础用法

\`\`\`tsx
useEffect(() => {
  // 副作用逻辑
  return () => {
    // 清理函数
  };
}, [dependencies]);
\`\`\`

### 常见模式

#### 只在挂载时执行

\`\`\`tsx
useEffect(() => {
  // 只执行一次
}, []);
\`\`\`

#### 监听特定依赖

\`\`\`tsx
useEffect(() => {
  // 当 userId 变化时执行
}, [userId]);
\`\`\`

## useContext

### 创建 Context

\`\`\`tsx
const ThemeContext = createContext<Theme>('light');
\`\`\`

### 使用 Context

\`\`\`tsx
const theme = useContext(ThemeContext);
\`\`\`

## 自定义 Hook

### 模板

\`\`\`tsx
function useCustomHook(params) {
  const [state, setState] = useState(initialValue);
  
  useEffect(() => {
    // 副作用逻辑
  }, [params]);
  
  return { state, ...actions };
}
\`\`\`

### 示例:useLocalStorage

\`\`\`tsx
function useLocalStorage<T>(key: string, initialValue: T) {
  const [value, setValue] = useState<T>(() => {
    const stored = localStorage.getItem(key);
    return stored ? JSON.parse(stored) : initialValue;
  });
  
  useEffect(() => {
    localStorage.setItem(key, JSON.stringify(value));
  }, [key, value]);
  
  return [value, setValue] as const;
}
\`\`\`

## 最佳实践

1. **依赖数组要完整**:包含所有在 effect 中使用的响应式值
2. **避免无限循环**:注意对象和数组的引用稳定性
3. **合理拆分 Hook**:一个 Hook 只做一件事
4. **使用 ESLint 插件**:eslint-plugin-react-hooks

步骤 3:组织技能目录

typescript
const agent = createDeepAgent({
  skills: ["/skills/"],  // 包含所有技能的根目录
  checkpointer,
  systemPrompt: `你是一个全栈开发助手。

## 可用技能

你有以下技能库可用(会按需自动加载):

### 前端框架
- react-basics: React 基础知识
- react-hooks: React Hooks 完全指南
- vue-basics: Vue 3 基础知识
- nextjs-app-router: Next.js App Router 指南

### 后端开发
- nodejs-basics: Node.js 基础
- express-guide: Express.js 指南
- prisma-orm: Prisma ORM 使用

### 测试
- testing-basics: 测试基础
- vitest-guide: Vitest 使用指南

当用户询问相关问题时,对应的技能会自动加载到上下文中。
`
});

实践示例

多框架编码助手

typescript
import { createDeepAgent } from "deepagents";
import { MemorySaver } from "@langchain/langgraph";
import { createFileData } from "deepagents";

const checkpointer = new MemorySaver();

const skills = {
  "/skills/react/SKILL.md": createFileData(`---
name: react-basics
description: React 组件开发基础
---
# React 基础
...
`),
  "/skills/vue/SKILL.md": createFileData(`---
name: vue-basics
description: Vue 3 组件开发基础
---
# Vue 3 基础
...
`),
  "/skills/typescript/SKILL.md": createFileData(`---
name: typescript-tips
description: TypeScript 高级技巧
---
# TypeScript 技巧
...
`),
};

const codingAssistant = createDeepAgent({
  skills: ["/skills/"],
  checkpointer,
  systemPrompt: `你是一个全栈开发助手,精通多种前端框架。

## 技能库

你有以下技能可用:
- react-basics: React 组件开发
- vue-basics: Vue 3 组件开发
- typescript-tips: TypeScript 高级技巧

当用户询问特定框架问题时,相关技能会自动加载。

## 工作方式

1. 理解用户需求
2. 判断需要哪种框架知识
3. 使用对应技能提供解答
4. 给出可运行的代码示例
`
});

async function main() {
  const result = await codingAssistant.invoke({
    messages: [{ role: "user", content: "帮我用 React 写一个计数器组件" }],
    files: skills,
  }, { configurable: { thread_id: "thread-1" } });
  
  console.log(result.messages[result.messages.length - 1].content);
}

main();

最佳实践

1. 技能粒度适中

✅ 好:一个技能聚焦一个主题
/skills/react-hooks/SKILL.md
/skills/react-context/SKILL.md
/skills/react-performance/SKILL.md

❌ 差:一个技能包含太多内容
/skills/react-everything/SKILL.md  // 太大,加载时占用过多上下文

2. 描述要精准

markdown
---
name: nextjs-app-router
description: Next.js 14 App Router 开发指南,包含路由、布局、数据获取、Server Components 等最佳实践
---

// ✅ 好:描述清晰,包含关键词,便于 LLM 判断是否加载
markdown
---
name: nextjs
description: Next.js 相关
---

// ❌ 差:描述太模糊,LLM 难以判断

3. 代码示例要完整

markdown
## 代码示例

### 完整的组件实现

\`\`\`tsx
// ✅ 好:完整、可运行的代码
import { useState } from 'react';

interface CounterProps {
  initialValue?: number;
}

export function Counter({ initialValue = 0 }: CounterProps) {
  const [count, setCount] = useState(initialValue);
  
  return (
    <div>
      <span>{count}</span>
      <button onClick={() => setCount(c => c + 1)}>+</button>
    </div>
  );
}
\`\`\`

// ❌ 差:片段代码,缺少上下文
\`\`\`tsx
const [count, setCount] = useState(0);
\`\`\`

4. 版本兼容性标注

markdown
---
name: react-server-components
compatibility:
  - React 18+
  - Next.js 13+
---

// 明确标注版本要求,避免用于不兼容的环境

创建自定义技能的四条最佳实践

小结

本文介绍了 DeepAgents 技能系统的核心概念和使用方法:

概念说明
渐进式披露按需加载,避免上下文爆炸
SKILL.md技能文件格式
Frontmatter技能元数据
SkillsMiddleware自动判断和加载技能

SKILL.md 结构

  • Frontmatter:name、description、compatibility、allowed-tools
  • 正文:知识、指南、代码模板

与记忆的区别

  • 技能 = 开发者预定义的通用知识
  • 记忆 = 代理学习的用户特定信息

最佳实践

  • ✅ 技能粒度适中
  • ✅ 描述精准、包含关键词
  • ✅ 代码示例完整可运行
  • ✅ 标注版本兼容性

技能系统核心概念总览

下一步

恭喜你完成了高级功能篇的学习!在接下来的教程中,我们将探索:

  • 安全执行篇:沙盒系统
  • 流式处理篇:实时输出
  • CLI 工具篇:终端编码助手
  • 项目实战篇:完整项目开发

实践任务

  1. 为你常用的框架创建一个 SKILL.md 文件
  2. 测试技能的自动加载:让代理根据问题自动选择技能
  3. 比较使用技能和不使用技能时的回答质量

参考资源

读文档、看源码、写代码,理解 AI Agent 本质 🤖