第 15 章

Skills(技能)

用可复用的能力模块扩展 Deep Agent 的专业知识

从"通用助手"到"领域专家"的关键升级

版本要求

版本要求:Skills 需要 deepagents>=1.7.0。请确保你的项目依赖满足此版本。

为什么需要它

你的 Agent 能搜文档、调 API,但每个新任务都要从头写提示词?

没有 Skills 的痛点

  • 每换一个场景,提示词和指令全得重写
  • 复杂的领域知识只能塞进系统提示,token 爆炸
  • 团队里每个人的 Agent 各自为战,无法复用经验
  • 辅助函数散落在代码各处,维护成本高
  • 想让 Agent 学会新技能?改代码、改配置、全靠手工

Skills 的解法

  • 把领域知识封装成独立的 Skill 文件夹,即插即用
  • Agent 按需读取,不用时不占 token(渐进式披露)
  • 团队共享 Skill 目录,一次编写,处处复用
  • 辅助代码打包在 Skill 里,模块化管理
  • 新增技能只需加一个文件夹,零代码侵入
问题核心:Agent 的"聪明"不在于模型参数,而在于你给它装了多少专业知识。Skills 就是给 Agent 装技能包的标准化方式——像给手机装 App 一样简单(当然这只是比喻——实际上 Skills 是包含 SKILL.md 指令文件的文件夹,Agent 在需要时根据描述匹配并读取完整内容)。

Skills 简介

可复用的 Agent 能力模块,提供专业化的工作流和领域知识

说白了,Skill 就是一个文件夹,里面至少有一个 SKILL.md 文件告诉 Agent "你什么时候该用我、怎么用"。它还可以包含脚本、文档、模板等附加资源。

核心组成

  • SKILL.md —— 必须,包含元数据和指令
  • 附加脚本 —— 可选,如 index.ts 辅助函数
  • 参考文档 —— 可选,如知识库、API 文档
  • 模板资源 —— 可选,如报告模板、配置文件

典型目录结构

skills/
├── langgraph-docs/
│   └── SKILL.md
└── arxiv_search/
    ├── SKILL.md
    └── arxiv_search.ts
重要约束:所有附加资源(脚本、文档、模板)都必须在 SKILL.md 中被引用,并说明其内容和使用方式,否则 Agent 不知道何时使用它们。另外 SKILL.md 文件不得超过 10 MB,description 字段不超过 1024 字符。

工作原理

渐进式披露——Agent 只在需要时才读取 Skill 的完整内容

当你创建 Deep Agent 时,传入 Skills 目录列表。Agent 启动时只读取元数据(frontmatter),等收到用户提示后,才判断是否需要深入查看。

1. 注册

启动时读取每个 SKILL.md 的 frontmatter

2. 匹配

用户提问时,检查哪个 Skill 的描述匹配

3. 读取

匹配后读取完整的 SKILL.md 和支持文件

4. 执行

按 Skill 指令操作,调用脚本或辅助函数

// Agent 系统提示中注入的 Skills 信息(简化)
// Agent 启动时只看到每个 Skill 的 name 和 description
// 只有匹配用户问题后才会读取完整的 SKILL.md

// 用户问:"LangGraph 的 checkpointer 怎么用?"
// 1. Agent 扫描所有 Skill 的 description,匹配到 langgraph-docs
// 2. Agent 读取 /skills/langgraph-docs/SKILL.md 的完整内容
// 3. Agent 按指令调用 fetch_url 获取文档,组织回答
图:Skills 渐进式披露流程
渐进式披露的好处:如果你有 20 个 Skill,Agent 不需要每次都加载全部内容。它只看描述(几行字),匹配后才深入。这大幅节省 token 开销。

SKILL.md 结构

frontmatter 元数据 + Markdown 指令体,就是全部

---
name: langgraph-docs
description: 用于处理与 LangGraph 相关的请求,
  以获取相关文档来提供准确的指导。
module: index.ts
---

# langgraph-docs

## Overview
解释如何访问 LangGraph 文档来回答问题和指导实现。

## Instructions
1. 使用 fetch_url 工具读取文档索引
2. 选择 2-4 个最相关的文档 URL
3. 获取选中的文档内容
4. 基于文档内容回答用户问题
frontmatter 字段必填说明
name是Skill 的唯一标识符
description是Agent 用它判断是否匹配(≤1024 字符)
module否可导入的辅助模块路径(相对于 Skill 目录)
license否许可证信息
allowed-tools否限制该 Skill 可使用的工具
metadata否额外的元数据(作者、版本等)
写好 description 是关键:Agent 仅根据 description 决定是否使用某个 Skill。描述太模糊 = 匹配不到,描述太窄 = 漏掉相关场景。建议写清楚"适用于什么类型的请求"。

完整示例

一个实际的文档查询 Skill,包含辅助代码模块

下面是一个真实可用的 Skill,教会 Agent 如何获取 LangGraph 文档并回答问题:

---
name: langgraph-docs
description: 用于与 LangGraph 相关的请求,获取相关文档。
module: index.ts
---

# langgraph-docs

## Instructions

### 1. 获取文档索引
使用 fetch_url 工具读取:https://docs.langchain.com/llms.txt

### 2. 选择相关文档
根据问题,从索引中选择 2-4 个最相关的 URL。
优先选择:how-to 指南 > 核心概念 > 教程 > API 参考

### 3. 获取文档内容
使用 fetch_url 读取选中的 URL。

### 4. 回答问题
先给出直接答案,附上必要的上下文和关键步骤。
避免大段引用,改用自己的话概括并附上链接。

### 5. 提供参考链接
使用辅助函数将 .md 链接转换为实际页面 URL:

```typescript
const { resolveLlmsUrl } = await import("@/skills/langgraph-docs");
const pageUrls = ["https://docs.langchain.com/oss/langgraph/concepts.md"]
  .map(resolveLlmsUrl);
```
辅助模块也要放在 Skill 文件夹里:上面 module: index.ts 指向的文件提供 resolveLlmsUrl 函数,让 Agent 在执行时可以导入使用。

辅助代码模块

Skill 的 module 字段指向可被 Interpreter 导入的 TypeScript 文件

辅助模块是 Skill 的"代码部分"。Agent 通过 await import("@/skills/<name>") 导入它,直接调用里面的函数。

// skills/langgraph-docs/index.ts
export function resolveLlmsUrl(url: string) {
  return url.endsWith(".md") ? url.slice(0, -3) : url;
}
// Agent 在 Interpreter 中使用
const { resolveLlmsUrl } = await import("@/skills/langgraph-docs");

const urls = [
  "https://docs.langchain.com/oss/langgraph/concepts.md",
  "https://docs.langchain.com/oss/langgraph/tutorials.md",
];
const pageUrls = [...new Set(urls.map(resolveLlmsUrl))];
// → ["/oss/langgraph/concepts", "/oss/langgraph/tutorials"]

适合做模块的场景

  • 数据标准化(格式转换、清洗)
  • 确定性计算(评分、分组、聚合)
  • URL 处理、字符串解析
  • 验证逻辑(格式检查、规则校验)

不适合做模块的场景

  • 需要调用外部 API(用工具代替)
  • 需要操作系统文件系统(用沙箱)
  • 需要人类判断的逻辑(用中断)
  • 纯文本指令(写在 SKILL.md 里)

基本用法

创建 Agent 时传入 skills 目录列表,就这么简单

三种 Backend 都支持 Skills,区别在于 Skill 文件的来源:

import { createDeepAgent, StateBackend } from "deepagents";
import { createCodeInterpreterMiddleware } from "@langchain/quickjs";
// 注意:部分版本导出的是 createQuickJSMiddleware(工具名 js_eval),
// 若 import 报错请换用 createQuickJSMiddleware(见第 17 章详解)。
import { MemorySaver } from "@langchain/langgraph";

const backend = new StateBackend();
const checkpointer = new MemorySaver();

const agent = await createDeepAgent({
  model: "google-genai:gemini-3.1-pro-preview",
  backend,
  checkpointer,              // 必需!Skills 需要 checkpointer 持久化状态
  skills: ["/skills/"],      // Skill 源路径,相对于 backend 根目录
  middleware: [
    // skillsBackend 必须与 Skills 加载时使用相同的 backend
    createCodeInterpreterMiddleware({ skillsBackend: backend }),
  ],
});

StateBackend

通过 invoke({ files: {...} }) 传入 Skill 文件内容。

内存中传递

StoreBackend

通过 store.put() 预存 Skill 文件到 Store。

持久化存储

FilesystemBackend

Skills 从磁盘加载,路径相对于 rootDir。

本地文件系统

路径必须用正斜杠:skills 参数中的路径使用 POSIX 风格(正斜杠),且相对于 backend 的根目录。如果省略此参数,则不加载任何 Skills。

StateBackend 示例

从远程 URL 加载 Skill 文件并通过 invoke 传入

import { createDeepAgent, StateBackend, type FileData } from "deepagents";
import { createCodeInterpreterMiddleware } from "@langchain/quickjs";
import { MemorySaver } from "@langchain/langgraph";

const backend = new StateBackend();
const checkpointer = new MemorySaver();

function createFileData(content: string): FileData {
  const now = new Date().toISOString();
  return {
    content: content.split("\n"),
    created_at: now,
    modified_at: now,
  };
}

// 从远程加载 Skill 文件
const skillsFiles: Record<string, FileData> = {};
const skillUrl =
  "https://raw.githubusercontent.com/langchain-ai/deepagentsjs/main/examples/skills/langgraph-docs/SKILL.md";
const response = await fetch(skillUrl);
const skillContent = await response.text();
skillsFiles["/skills/langgraph-docs/SKILL.md"] = createFileData(skillContent);

const agent = await createDeepAgent({
  model: "google-genai:gemini-3.1-pro-preview",
  backend,
  checkpointer,
  skills: ["/skills/"],
  middleware: [createCodeInterpreterMiddleware({ skillsBackend: backend })],
});

// 调用时传入 Skill 文件
const result = await agent.invoke({
  messages: [{ role: "user", content: "what is langgraph?" }],
  files: skillsFiles,
}, { configurable: { thread_id: `thread-${Date.now()}` } });

StoreBackend 示例

使用 store.put() 预存 Skill 文件,Agent 从 Store 读取

import { createDeepAgent, StoreBackend, type FileData } from "deepagents";
import { createCodeInterpreterMiddleware } from "@langchain/quickjs";
import { InMemoryStore, MemorySaver } from "@langchain/langgraph";

const checkpointer = new MemorySaver();
const store = new InMemoryStore();
const backend = new StoreBackend({
  namespace: () => ["filesystem"],
});

function createFileData(content: string): FileData {
  const now = new Date().toISOString();
  return {
    content: content.split("\n"),
    created_at: now,
    modified_at: now,
  };
}

// 从远程加载 Skill 并存入 Store
const skillUrl =
  "https://raw.githubusercontent.com/langchain-ai/deepagentsjs/main/examples/skills/langgraph-docs/SKILL.md";
const response = await fetch(skillUrl);
const skillContent = await response.text();
const fileData = createFileData(skillContent);

// 关键:用 store.put() 预存 Skill 文件
await store.put(["filesystem"], "/skills/langgraph-docs/SKILL.md", fileData);

const agent = await createDeepAgent({
  model: "google-genai:gemini-3.1-pro-preview",
  backend,
  store,                    // 传入 store
  checkpointer,
  skills: ["/skills/"],
  middleware: [createCodeInterpreterMiddleware({ skillsBackend: backend })],
});

const config = {
  recursionLimit: 50,
  configurable: { thread_id: `thread-${Date.now()}` },
};
const result = await agent.invoke(
  { messages: [{ role: "user", content: "what is langraph?" }] },
  config,
);
与 StateBackend 的区别:StoreBackend 通过 store.put() 预存文件到持久化存储中,不需要在每次 invoke() 时传入 files 参数。适合生产环境和多 Agent 共享场景。

文件系统示例

Skill 文件放在本地磁盘,Agent 自动扫描加载

import { createDeepAgent, FilesystemBackend } from "deepagents";
import { createCodeInterpreterMiddleware } from "@langchain/quickjs";
import { MemorySaver } from "@langchain/langgraph";

const backend = new FilesystemBackend({ rootDir: process.cwd() });

const agent = await createDeepAgent({
  model: "google-genai:gemini-3.1-pro-preview",
  backend,
  skills: ["./examples/skills/"],   // 相对于 rootDir
  interruptOn: {
    read_file: true,
    write_file: true,
    delete_file: true,
  },
  checkpointer,
  middleware: [createCodeInterpreterMiddleware({ skillsBackend: backend })],
});

const result = await agent.invoke({
  messages: [{ role: "user", content: "what is langgraph?" }],
}, { configurable: { thread_id: `thread-${Date.now()}` } });
最简方案:FilesystemBackend 是本地开发的首选。把 Skill 文件夹放在项目目录下,Agent 直接从磁盘读取,无需手动传文件。

解释器技能

让 Agent 可以导入并调用 Skill 里的辅助函数

普通 Skill 提供指令和上下文。Interpreter Skill 还提供可导入的函数,让 Agent 在代码执行环境中直接调用。

何时使用 Interpreter Skill

  • 可复用 —— 跨提示、Agent、项目复用
  • 确定性 —— 每次需要相同的行为
  • 过于细节 —— 不适合放在模型上下文中
  • 工作流组件 —— 评分、标准化、验证、聚合

四步启用流程

  1. 在 SKILL.md 的 frontmatter 中添加 module 字段
  2. 创建 Agent 时通过 skills 参数传入
  3. 使用相同的 backend 配置 Interpreter 中间件
  4. Agent 通过 await import("@/skills/<name>") 导入
skills/
└── order-helpers/
    ├── SKILL.md        # frontmatter 中添加 module: index.ts
    └── index.ts        # 导出 groupByStatus 等函数
// Agent 在 Interpreter 中导入使用
const { groupByStatus } = await import("@/skills/order-helpers");
const grouped = groupByStatus(orders);
grouped;

解释器技能实战

一个完整的 order-helpers Skill,从定义到配置到使用

---
name: order-helpers
description: Helper functions for normalizing and grouping order records.
module: index.ts
---

# order-helpers
Use this skill when order records need deterministic cleanup or aggregation.

```typescript
const { groupByStatus } = await import("@/skills/order-helpers");
groupByStatus(...);
```
// skills/order-helpers/index.ts
interface Order {
  id: string;
  status: string;
}

export function groupByStatus(orders: Order[]) {
  return orders.reduce((acc, order) => {
    acc[order.status] = acc[order.status] ?? [];
    acc[order.status].push(order);
    return acc;
  }, {} as Record<string, Order[]>);
}
// 配置 Agent
import { createDeepAgent, StateBackend } from "deepagents";
import { createCodeInterpreterMiddleware } from "@langchain/quickjs";

const backend = new StateBackend();

const agent = createDeepAgent({
  model: "openai:gpt-5.4",
  backend,
  skills: ["/skills/"],
  middleware: [createCodeInterpreterMiddleware({ skillsBackend: backend })],
});
关键配置:必须使用与 Skills 加载时相同的 backend 来配置 Interpreter 中间件,这样 Interpreter 才能找到 Skill 模块文件。

源优先级

多个 Skill 源有同名 Skill 时,后列出的源优先级更高

这使你可以对来自不同来源的 Skills 进行分层叠加——用项目级覆盖用户级。

// 如果两个源都包含名为 "web-search" 的 Skill,
// 来自 "/skills/project/" 的那个会胜出(最后加载)。
const agent = await createDeepAgent({
  skills: ["/skills/user/", "/skills/project/"],
  // ...
});

CLI 风格的分层路径

[
  "~/.deepagents/{agent}/skills/",     // 用户级(最低优先级)
  "~/.agents/skills/",                  // 全局用户级
  "<project>/.deepagents/skills/",    // 项目级
  "<project>/.agents/skills/",        // 项目级(最高优先级)
]

覆盖规则

  • 后列出的源覆盖先列出的同名 Skill
  • 可用于定制化:全局 Skill + 项目特定覆盖
  • SDK 不会自动扫描 CLI 目录,需手动传入
注意:SDK 仅加载你在 skills 中传入的源。它不会自动扫描 ~/.deepagents/ 或 ~/.agents/ 等 CLI 目录。如果你想在 SDK 中使用 CLI 风格的分层,需要显式传入所有路径。

子 Agent 技能

主 Agent 和子 Agent 的 Skills 完全隔离,互不可见

通用子 Agent

自动继承主 Agent 的 Skills。无需额外配置。

自动继承

自定义子 Agent

不继承主 Agent 的 Skills。需要在定义中添加 skills 参数。

需手动配置

const researchSubagent = {
  name: "researcher",
  description: "Research assistant with specialized skills",
  systemPrompt: "You are a researcher.",
  tools: [webSearch],
  skills: ["/skills/research/", "/skills/web-search/"],
  // ↑ 子 Agent 专属的 Skills,主 Agent 的 Skills 不可见
};

const agent = await createDeepAgent({
  model: "google-genai:gemini-3.1-pro-preview",
  skills: ["/skills/main/"],  // 主 Agent 和通用子 Agent 获得这些
  subagents: [researchSubagent],  // 研究者只获得自己的
});
隔离设计:Skill 状态在主 Agent 和子 Agent 之间完全隔离。主 Agent 看不到子 Agent 的 Skills,反之亦然。这避免了命名冲突和状态污染。

三者对比

Skills、Memory(AGENTS.md)和 Tools 各有分工,选对工具很重要

SkillsMemoryTools
目的 按需发现的专业能力 始终加载的持久上下文 执行具体操作的函数
加载方式 仅相关时读取 始终注入系统提示 始终注册为可用工具
格式 目录 + SKILL.md AGENTS.md 文件 TypeScript/JS 函数
token 开销 低(按需加载) 高(始终加载) 低(只有描述)
适用场景 特定任务的大段指令 项目约定、偏好设置 原子化的操作动作

选择 Skills 的场景

  • 上下文量很大,不想始终加载
  • 需要打包成更大的操作并提供额外上下文
  • Agent 可以访问文件系统

选择 Tools 的场景

  • 操作是原子化的(查天气、发邮件)
  • Agent 无法访问文件系统
  • 逻辑简单,不需要额外上下文
图:Skills vs Memory vs Tools 选择决策

沙箱执行脚本

Skill 脚本需要沙箱 backend 才能执行,否则只能读取

Skills 可以包含 Python 等脚本文件。Agent 可以从任何 backend 读取它们,但要执行这些脚本,需要访问 shell——只有沙箱 backend 才能提供。

// 使用 CompositeBackend 将 Skills 路由到 StoreBackend
// 同时用沙箱作为默认 backend
import { CompositeBackend, createDeepAgent, StoreBackend } from "deepagents";
import { InMemoryStore } from "@langchain/langgraph";
import { DaytonaSandbox } from "@langchain/daytona";

const store = new InMemoryStore();
const sandbox = await DaytonaSandbox.create({ language: "python" });

const backend = new CompositeBackend(sandbox, {
  "/skills/": new StoreBackend({
    store,
    namespace: () => ["skills", "builtin"],
  }),
});

const agent = await createDeepAgent({
  model: "google-genai:gemini-3.1-pro-preview",
  backend,
  skills: ["/skills/"],
  store,
  middleware: [createSkillSandboxSyncMiddleware(backend)],
});
关键中间件:使用 createSkillSandboxSyncMiddleware 在每次 Agent 运行前,将 Store 中的 Skill 文件同步到沙箱文件系统。否则沙箱里找不到脚本文件。

不同提供商的 model 字符串

以上示例使用 Google 的模型。以下是其他 6 个提供商的 model 字符串变体,只需替换 model 参数:

点击展开:OpenAI
model: "openai:gpt-5.4"
点击展开:Anthropic
model: "anthropic:claude-sonnet-4-6"
点击展开:OpenRouter
model: "openrouter:anthropic/claude-sonnet-4-6"
点击展开:Fireworks
model: "fireworks:accounts/fireworks/models/qwen3p5-397b-a17b"
点击展开:Baseten
model: "baseten:zai-org/GLM-5"
点击展开:Ollama(本地模型)
model: "ollama:devstral-2"
格式规则:model 字符串格式为 <provider>:<model-name>。不同提供商只需替换 model 参数,其余代码(沙箱创建、CompositeBackend 配置、中间件)完全相同。

同步中间件

在 Agent 启动前将 Skill 文件从 Store 复制到沙箱

function createSkillSandboxSyncMiddleware(backend: CompositeBackend) {
  return createMiddleware({
    name: "SkillSandboxSyncMiddleware",
    beforeAgent: async (state, runtime) => {
      const store = (runtime as any).store;
      if (!store) throw new Error("Store is required for skill sync.");

      const encoder = new TextEncoder();
      const files: Array<[string, Uint8Array]> = [];

      // 从共享命名空间读取所有 Skill 文件
      for (const item of await store.search(["skills", "builtin"])) {
        const key = String(item.key);
        const data = item.value as FileData;
        files.push([
          `/skills${key}`,
          encoder.encode(data.content.join("\n")),
        ]);
      }

      // 批量上传到沙箱
      if (files.length > 0) await backend.uploadFiles(files);
      return state;
    },
  });
}
执行流程:beforeAgent 钩子在每次 Agent 调用前运行。它从共享命名空间读取 Skill 文件,然后上传到沙箱。同步完成后,Agent 就可以用 execute 工具执行脚本了。

Sandbox 清理模式

务必使用 try/finally 确保 sandbox 资源被正确释放,即使 Agent 执行出错:

async function main() {
  const store = new InMemoryStore();
  await seedSkillStore(store);

  const sandbox = await DaytonaSandbox.create({
    language: "python",
    timeout: 300,
  });

  const backend = new CompositeBackend(sandbox, {
    "/skills/": new StoreBackend({
      store,
      namespace: () => ["skills", "builtin"],
    }),
  });

  try {
    const agent = await createDeepAgent({
      model: "google-genai:gemini-3.1-pro-preview",
      backend,
      skills: ["/skills/"],
      store,
      middleware: [createSkillSandboxSyncMiddleware(backend)],
    });
    // ... 使用 agent ...
  } finally {
    // 无论成功还是失败,都要关闭沙箱释放资源
    await sandbox.close();
  }
}

main().catch((err) => {
  console.error(err);
  process.exitCode = 1;
});
资源泄漏风险:如果不使用 try/finally 包裹,当 Agent 执行过程中抛出异常时,sandbox.close() 不会被调用,导致沙箱资源泄漏。始终在 finally 块中清理沙箱。

预加载技能

部署时将 Skill 文件从磁盘加载到共享 Store 命名空间

import { readFile, readdir } from "node:fs/promises";
import { join, posix, relative, resolve } from "node:path";

async function walkFiles(dir: string): Promise<string[]> {
  const entries = await readdir(dir, { withFileTypes: true });
  const files: string[] = [];
  for (const entry of entries) {
    const fullPath = join(dir, entry.name);
    if (entry.isDirectory()) {
      files.push(...(await walkFiles(fullPath)));
    } else if (entry.isFile()) {
      files.push(fullPath);
    }
  }
  return files.sort((a, b) => a.localeCompare(b));
}

async function seedSkillStore(store: InMemoryStore) {
  const skillsDir = resolve(__dirname, "skills");
  const filePaths = await walkFiles(skillsDir);

  for (const filePath of filePaths) {
    const rel = relative(skillsDir, filePath);
    const key = `/${posix.normalize(rel.split("\\").join("/"))}`;
    const content = await readFile(filePath, "utf8");
    await store.put(["skills", "builtin"], key, createFileData(content));
  }
}

// 部署时调用一次
await seedSkillStore(store);
路径注意:StoreBackend 的键是相对于路由 backend 根目录的路径。CompositeBackend 在委派之前会剥离路由前缀(/skills/),所以 store 的键应该是类似 /<skillname>/SKILL.md 的格式。

键验证函数

在将 Skill 文件路径写入 Store 时,务必验证路径的安全性,防止路径遍历和 glob 注入:

function normalizeSkillsStoreKey(key: string): string {
  const k = String(key);
  // 拒绝包含 ".." 的路径(防止路径遍历)和 glob 通配符
  if (k.includes("..") || /[*?]/.test(k)) {
    throw new Error(`Invalid key: ${key}`);
  }
  // 确保以 "/" 开头
  return k.startsWith("/") ? k : `/${k}`;
}
安全第一:normalizeSkillsStoreKey 拒绝包含 ..(路径遍历)和 * ?(glob 模式)的键。所有写入 Store 的路径都应该先通过此函数验证。

技能与记忆

两者服务不同目的:按需能力 vs 始终在线的上下文

Skills —— 按需加载

  • 加载方式:Agent 判定相关时才读取
  • 格式:命名目录中的 SKILL.md
  • 分层:用户级 → 项目级(后者优先)
  • 典型内容:特定任务的指令和脚本
  • 大小:可以很大(最大 10MB)

Memory —— 始终在线

  • 加载方式:启动时注入系统提示
  • 格式:AGENTS.md 文件
  • 分层:用户级 → 项目级(合并)
  • 典型内容:项目约定、偏好设置
  • 大小:应保持精简
选型原则:如果上下文始终相关(项目规范、团队约定),用 Memory。如果上下文特定于任务且可能很大(文档查询、代码生成模板),用 Skills。

Agent 所见

配置 Skills 后,Agent 的系统提示会注入 Skills System 部分

Agent 看到的 Skills 信息包括:每个 Skill 的名称、描述和路径。它遵循三步流程来使用 Skill:

1. Match

检查是否有 Skill 描述与当前任务匹配

2. Read

匹配后读取完整 SKILL.md 和支持文件

3. Execute

遵循指令,调用脚本和辅助函数

// Agent 系统提示中的 Skills System 部分(简化)
// ═══════════════════════════════
// Available Skills:
//   - langgraph-docs (/skills/langgraph-docs/SKILL.md)
//     "用于与 LangGraph 相关的请求..."
//   - arxiv-search (/skills/arxiv_search/SKILL.md)
//     "用于搜索 arXiv 预印本论文库..."
// ═══════════════════════════════

// 用户提问后,Agent 自行判断是否需要读取某个 Skill
写好 description:Agent 仅根据描述字段决定是否使用 Skill。描述清晰具体 = 匹配更精准。建议用"用于[什么类型的请求]"的句式。

最佳实践

写好 Skill 的五个关键原则

1. 精准描述

description 写清楚适用场景。太宽泛会误匹配,太窄会漏掉相关请求。用"用于处理 [X] 类型的请求"的格式。

2. 模块化设计

一个 Skill 只做一件事。不要把"文档查询"和"代码生成"塞进同一个 Skill。拆开更容易复用和维护。

3. 引用所有资源

SKILL.md 中必须引用并说明每个附加文件的用途。Agent 不会自动发现未提及的文件。

4. 善用分层覆盖

用 skills 数组的顺序实现"全局 → 项目 → 个人"的覆盖链。后列出的源优先级更高。

5. 控制 Skill 大小

SKILL.md 不超过 10 MB,description 不超过 1024 字符。文件过大会被跳过,描述过长会被截断。

黄金法则:把 Skill 想象成写给未来 Agent 的"操作手册"。它需要在没有你指导的情况下,仅凭手册内容正确执行任务。

本章回顾

到这里,你已经掌握了 Deep Agent Skills 的完整知识

你学到了什么

  • Skill 是包含 SKILL.md 的文件夹,封装可复用的能力
  • Agent 使用渐进式披露:先读描述,匹配后才深入
  • Interpreter Skill 提供可导入的辅助函数
  • 三种 Backend 都支持 Skills,路径用正斜杠
  • 多个源的优先级:后列出的覆盖先列出的
  • 主 Agent 和子 Agent 的 Skills 完全隔离
  • 沙箱执行需要 同步中间件 把文件传入沙箱

关键 API 速查

API用途
skills: ["/skills/"]传入 Skill 源路径
module: index.ts声明可导入模块
import("@/skills/<name>")Agent 导入模块
middleware: [...]同步 Skill 到沙箱
核心结论:Skills 让 Agent 从"什么都会一点"变成"特定领域很专业",而且不浪费 token。它是 Deep Agent 生态里最灵活的能力扩展机制——像给手机装 App 一样,按需加载,即插即用。

延伸阅读

官方资源

相关章节

  • 子 Agent —— 自定义子 Agent 的 Skills 配置
  • Backends —— 三种 Backend 的 Skills 加载方式
  • 沙箱 —— 在沙箱中执行 Skill 脚本
  • 生产部署 —— Skills 的生产环境最佳实践
  • 使用自定义中间件同步 Skills 和 Memories —— 双向同步 Skills 和 Memories 的完整生产级示例(Going to Production)

调试工具