本教程展示如何使用 progressive disclosure,一种让智能体按需加载信息而不是预先加载全部信息的上下文管理技术,来实现 skills,也就是基于提示词的专用指令。智能体通过工具调用加载技能,而不是动态更改系统提示词。它只发现并加载每个任务所需的技能。 使用场景: 假设你要构建一个智能体,帮助大型企业跨不同业务条线编写 SQL 查询。你的组织可能为每个业务条线使用独立的数据存储,也可能使用包含数千张表的单体数据库。无论哪种方式,预先加载所有 schema 都会压垮上下文窗口。渐进式披露通过只在需要时加载相关 schema 来解决这个问题。这种架构还支持不同产品负责人和利益相关方独立贡献并维护各自业务条线的技能。 你将构建的内容: 一个带有两个技能的 SQL 查询助手,分别是销售分析和库存管理。智能体在系统提示词中看到轻量级技能描述,然后仅在与用户查询相关时,通过工具调用加载完整数据库 schema 和业务逻辑。
如需查看包含查询执行、错误修正和验证的完整 SQL agent 示例,请参阅 SQL Agent 教程。本教程重点介绍可应用于任何领域的渐进式披露模式。
渐进式披露由 Anthropic 推广,用于构建可扩展的智能体技能系统。该方法使用三层架构(metadata → core content → detailed resources),让智能体只在需要时加载信息。有关这项技术的更多信息,请参阅 Equipping agents for the real world with Agent Skills

工作原理

当用户请求 SQL 查询时,流程如下: 为什么使用渐进式披露:
  • 减少上下文使用量:只加载任务所需的 2-3 个技能,而不是加载所有可用技能
  • 支持团队自主协作:不同团队可以独立开发专用技能,类似于其他多智能体架构
  • 高效扩展:可以添加数十个或数百个技能,而不会让上下文过载
  • 简化对话历史:使用单个智能体和一个对话线程
什么是技能: Claude Code 推广的技能主要基于提示词,是面向特定业务任务的自包含专用指令单元。在 Claude Code 中,技能以文件系统中的目录和文件形式暴露,并通过文件操作发现。技能通过提示词指导行为,可以提供工具使用信息,也可以包含供编码智能体执行的示例代码。
带有渐进式披露的技能可以看作一种 RAG (Retrieval-Augmented Generation),其中每个技能都是一个检索单元。不过,它不一定由 embeddings 或关键词搜索支持,也可以由用于浏览内容的工具支持,例如文件操作,或本教程中的直接查找。
权衡:
  • 延迟:按需加载技能需要额外的工具调用,这会增加首次需要某个技能的请求延迟
  • 工作流控制:基础实现依赖提示词引导技能使用。如果没有自定义逻辑,无法强制执行“始终先尝试技能 A,再尝试技能 B”这类硬约束
实现你自己的技能系统在构建自己的技能实现时,也就是本教程采用的方式,核心概念是渐进式披露,按需加载信息。除此之外,你可以灵活选择实现方式:
  • 存储:数据库、S3、内存数据结构,或任何后端
  • 发现:直接查找(本教程)、面向大型技能集合的 RAG、文件系统扫描,或 API 调用
  • 加载逻辑:自定义延迟特征,并添加搜索技能内容或对相关性排序的逻辑
  • 副作用:定义技能加载时发生什么,例如暴露与该技能关联的工具,第 8 节会介绍
这种灵活性让你可以围绕性能、存储和工作流控制的具体需求进行优化。

设置

安装

本教程需要 langchain 包:
npm install langchain
有关更多详细信息,请参阅安装指南

LangSmith

设置 LangSmith,以检查智能体内部发生的情况。然后设置以下环境变量:
export LANGSMITH_TRACING="true"
export LANGSMITH_API_KEY="..."

选择 LLM

从 LangChain 的集成套件中选择一个聊天模型:
👉 Read the OpenAI chat model integration docs
npm install @langchain/openai
import { initChatModel } from "langchain";

process.env.OPENAI_API_KEY = "your-api-key";

const model = await initChatModel("gpt-5.4");

1. 定义技能

首先,定义技能结构。每个技能都有名称、简短描述(显示在系统提示词中),以及完整内容(按需加载):
import { z } from "zod";

// 可以渐进式披露给智能体的技能
const SkillSchema = z.object({
  name: z.string(),  // 技能的唯一标识符
  description: z.string(),  // 显示在系统提示词中的 1-2 句描述
  content: z.string(),  // 包含详细指令的完整技能内容
});

type Skill = z.infer<typeof SkillSchema>;
现在为 SQL 查询助手定义示例技能。这些技能被设计为描述轻量(预先显示给智能体),但内容详细(仅在需要时加载):
import { context } from "langchain";

const SKILLS: Skill[] = [
  {
    name: "sales_analytics",
    description:
      "用于销售数据分析的数据库 schema 和业务逻辑,包括 customers、orders 和 revenue。",
    content: context`
    # 销售分析 Schema

    ## 表

    ### customers
    - customer_id (PRIMARY KEY)
    - name
    - email
    - signup_date
    - status (active/inactive)
    - customer_tier (bronze/silver/gold/platinum)

    ### orders
    - order_id (PRIMARY KEY)
    - customer_id (FOREIGN KEY -> customers)
    - order_date
    - status (pending/completed/cancelled/refunded)
    - total_amount
    - sales_region (north/south/east/west)

    ### order_items
    - item_id (PRIMARY KEY)
    - order_id (FOREIGN KEY -> orders)
    - product_id
    - quantity
    - unit_price
    - discount_percent

    ## 业务逻辑

    **活跃客户**:
    status = 'active' AND signup_date <= CURRENT_DATE - INTERVAL '90 days'

    **收入计算**:
    仅统计 status = 'completed' 的订单。
    使用 orders 表中的 total_amount,它已经包含折扣计算。

    **客户生命周期价值 (CLV)**:
    某个客户所有已完成订单金额的总和。

    **高价值订单**:
    total_amount > 1000 的订单

    ## 示例查询

    -- 获取上一季度收入最高的 10 个客户
    SELECT
        c.customer_id,
        c.name,
        c.customer_tier,
        SUM(o.total_amount) as total_revenue
    FROM customers c
    JOIN orders o ON c.customer_id = o.customer_id
    WHERE o.status = 'completed'
    AND o.order_date >= CURRENT_DATE - INTERVAL '3 months'
    GROUP BY c.customer_id, c.name, c.customer_tier
    ORDER BY total_revenue DESC
    LIMIT 10;`,
  },
  {
    name: "inventory_management",
    description:
      "用于库存跟踪的数据库 schema 和业务逻辑,包括 products、warehouses 和 stock levels。",
    content: context`
    # 库存管理 Schema

    ## 表

    ### products
    - product_id (PRIMARY KEY)
    - product_name
    - sku
    - category
    - unit_cost
    - reorder_point (重新订购前的最低库存水平)
    - discontinued (boolean)

    ### warehouses
    - warehouse_id (PRIMARY KEY)
    - warehouse_name
    - location
    - capacity

    ### inventory
    - inventory_id (PRIMARY KEY)
    - product_id (FOREIGN KEY -> products)
    - warehouse_id (FOREIGN KEY -> warehouses)
    - quantity_on_hand
    - last_updated

    ### stock_movements
    - movement_id (PRIMARY KEY)
    - product_id (FOREIGN KEY -> products)
    - warehouse_id (FOREIGN KEY -> warehouses)
    - movement_type (inbound/outbound/transfer/adjustment)
    - quantity (inbound 为正数,outbound 为负数)
    - movement_date
    - reference_number

    ## 业务逻辑

    **可用库存**:
    inventory 表中 quantity_on_hand > 0 的 quantity_on_hand

    **需要重新订购的产品**:
    所有仓库中的 total quantity_on_hand 小于或等于
    产品 reorder_point 的产品

    **仅活跃产品**:
    除非专门分析已停用商品,否则排除 discontinued = true 的产品

    **库存估值**:
    每个产品的 quantity_on_hand * unit_cost

    ## 示例查询

    -- 查找所有仓库中低于重新订购点的产品
    SELECT
        p.product_id,
        p.product_name,
        p.reorder_point,
        SUM(i.quantity_on_hand) as total_stock,
        p.unit_cost,
        (p.reorder_point - SUM(i.quantity_on_hand)) as units_to_reorder
    FROM products p
    JOIN inventory i ON p.product_id = i.product_id
    WHERE p.discontinued = false
    GROUP BY p.product_id, p.product_name, p.reorder_point, p.unit_cost
    HAVING SUM(i.quantity_on_hand) <= p.reorder_point
    ORDER BY units_to_reorder DESC;`,
  },
];

2. 创建技能加载工具

创建一个按需加载完整技能内容的工具:
import { tool } from "langchain";
import { z } from "zod";

const loadSkill = tool(  
  async ({ skillName }) => {
    // 查找并返回请求的技能
    const skill = SKILLS.find((s) => s.name === skillName);
    if (skill) {
      return `已加载技能:${skillName}\n\n${skill.content}`;
    }

    // 未找到技能
    const available = SKILLS.map((s) => s.name).join(", ");
    return `未找到技能 '${skillName}'。可用技能:${available}`;
  },
  {
    name: "load_skill",
    description: `将技能的完整内容加载到智能体上下文中。

当你需要有关如何处理特定类型请求的详细信息时使用此工具。
它会提供该技能领域的完整指令、策略和指南。`,
    schema: z.object({
      skillName: z.string().describe("要加载的技能名称"),
    }),
  }
);
load_skill 工具以字符串形式返回完整技能内容,该字符串会作为 ToolMessage 成为对话的一部分。有关创建和使用工具的更多详细信息,请参阅工具指南

3. 构建技能中间件

创建自定义中间件,将技能描述注入系统提示词。该中间件让技能可被发现,而不需要预先加载完整内容。
本指南演示如何创建自定义中间件。有关中间件概念和模式的完整指南,请参阅自定义中间件文档
import { createMiddleware } from "langchain";

// 根据 SKILLS 列表构建技能提示词
const skillsPrompt = SKILLS.map(
  (skill) => `- **${skill.name}**: ${skill.description}`
).join("\n");

const skillMiddleware = createMiddleware({
  name: "skillMiddleware",
  tools: [loadSkill],
  wrapModelCall: async (request, handler) => {
    // 构建技能补充内容
    const skillsAddendum =
      `\n\n## 可用技能\n\n${skillsPrompt}\n\n` +
      "当你需要有关如何处理特定类型请求的详细信息时," +
      "使用 load_skill 工具。";

    // 追加到系统提示词
    const newSystemPrompt = request.systemPrompt + skillsAddendum;

    return handler({
      ...request,
      systemPrompt: newSystemPrompt,
    });
  },
});
中间件会将技能描述追加到系统提示词,让智能体知道可用技能,而不加载完整内容。load_skill 工具注册为类变量,因此智能体可以使用它。
生产环境注意事项:为简化示例,本教程在 __init__ 中加载技能列表。在生产系统中,你可能希望改为在 before_agent hook 中加载技能,以便定期刷新并反映最新变更,例如新增技能或修改现有技能时。有关详细信息,请参阅 before_agent hook 文档

4. 创建支持技能的智能体

现在创建带有技能中间件和用于状态持久化的 checkpointer 的智能体:
import { createAgent } from "langchain";
import { MemorySaver } from "@langchain/langgraph";

// 创建支持技能的智能体
const agent = createAgent({
  model,
  systemPrompt:
    "你是一个 SQL 查询助手,帮助用户" +
    "针对业务数据库编写查询。",
  middleware: [skillMiddleware],
  checkpointer: new MemorySaver(),
});
智能体现在可以在系统提示词中访问技能描述,并在需要时调用 load_skill 来检索完整技能内容。checkpointer 会跨轮次维护对话历史。

5. 测试渐进式披露

使用一个需要特定技能知识的问题来测试智能体:
// 此对话线程的配置
const threadId = crypto.randomUUID();
const config = { configurable: { thread_id: threadId } };

// 请求一个 SQL 查询
const result = await agent.invoke(  
  {
    messages: [
      {
        role: "user",
        content:
          "编写一个 SQL 查询,找出上个月" +
          "下过超过 $1000 订单的所有客户",
      },
    ],
  },
  config
);

// 打印对话
for (const message of result.messages) {
  console.log(`${message._getType()}: ${message.content}`);
}
预期输出:
================================ Human Message =================================

编写一个 SQL 查询,找出上个月下过超过 $1000 订单的所有客户
================================== Ai Message ==================================
Tool Calls:
  load_skill (call_abc123)
 Call ID: call_abc123
  Args:
    skill_name: sales_analytics
================================= Tool Message =================================
Name: load_skill

已加载技能:sales_analytics

# 销售分析 Schema

## 表

### customers
- customer_id (PRIMARY KEY)
- name
- email
- signup_date
- status (active/inactive)
- customer_tier (bronze/silver/gold/platinum)

### orders
- order_id (PRIMARY KEY)
- customer_id (FOREIGN KEY -> customers)
- order_date
- status (pending/completed/cancelled/refunded)
- total_amount
- sales_region (north/south/east/west)

[... schema 剩余部分 ...]

## 业务逻辑

**高价值订单**:`total_amount > 1000` 的订单
**收入计算**:仅统计 `status = 'completed'` 的订单

================================== Ai Message ==================================

下面是一个 SQL 查询,用于找出上个月下过超过 $1000 订单的所有客户:

\`\`\`sql
SELECT DISTINCT
    c.customer_id,
    c.name,
    c.email,
    c.customer_tier
FROM customers c
JOIN orders o ON c.customer_id = o.customer_id
WHERE o.total_amount > 1000
  AND o.status = 'completed'
  AND o.order_date >= CURRENT_DATE - INTERVAL '1 month'
ORDER BY c.customer_id;
\`\`\`

此查询:
- 将 customers 与其 orders 连接起来
- 使用 total_amount 字段过滤高价值订单(>$1000)
- 仅包含已完成订单,符合业务逻辑
- 限制为上个月的订单
- 返回不同客户,以避免客户有多个符合条件的订单时出现重复
智能体在系统提示词中看到轻量级技能描述,识别出该问题需要销售数据库知识,调用 load_skill("sales_analytics") 获取完整 schema 和业务逻辑,然后使用这些信息编写符合数据库约定的正确查询。

6. 进阶:使用自定义状态添加约束

你可以添加约束,强制某些工具只能在特定技能加载后可用。这需要在自定义智能体状态中跟踪已加载的技能。

定义自定义状态

首先,扩展智能体状态以跟踪已加载技能:
import { StateSchema } from "@langchain/langgraph";
import { z } from "zod";

const CustomState = new StateSchema({
  skillsLoaded: z.array(z.string()).optional(),  // 跟踪已加载的技能
});

更新 load_skill 以修改状态

修改 load_skill 工具,使其在加载技能时更新状态:
import { tool, ToolMessage, type ToolRuntime } from "langchain";
import { Command } from "@langchain/langgraph";
import { z } from "zod";

const loadSkill = tool(  
  async ({ skillName }, runtime: ToolRuntime<typeof CustomState.State>) => {
    // 查找并返回请求的技能
    const skill = SKILLS.find((s) => s.name === skillName);

    if (skill) {
      const skillContent = `已加载技能:${skillName}\n\n${skill.content}`;

      // 更新状态以跟踪已加载技能
      return new Command({
        update: {
          messages: [  
            new ToolMessage({
              content: skillContent,
              tool_call_id: runtime.toolCallId,
            }),
          ],
          skillsLoaded: [skillName],
        },
      });
    }

    // 未找到技能
    const available = SKILLS.map((s) => s.name).join(", ");
    return new Command({
      update: {
        messages: [
          new ToolMessage({
            content: `未找到技能 '${skillName}'。可用技能:${available}`,
            tool_call_id: runtime.toolCallId,
          }),
        ],
      },
    });
  },
  {
    name: "load_skill",
    description: `将技能的完整内容加载到智能体上下文中。`,
    schema: z.object({
      skillName: z.string().describe("要加载的技能名称"),
    }),
  }
);

创建受约束的工具

创建一个仅在加载特定技能后才能使用的工具:
const writeSqlQuery = tool(  
  async ({ query, vertical }, runtime: ToolRuntime<typeof CustomState.State>) => {
    // 检查是否已经加载所需技能
    const skillsLoaded = runtime.state.skillsLoaded ?? [];

    if (!skillsLoaded.includes(vertical)) {
      return (  
        `错误:你必须先加载 '${vertical}' 技能,` +
        `才能在编写查询前了解数据库 schema。` +
        `使用 load_skill('${vertical}') 加载 schema。`
      );
    }

    // 验证并格式化查询
    return (
      `${vertical} 的 SQL 查询:\n\n` +
      `\`\`\`sql\n${query}\n\`\`\`\n\n` +
      `✓ 已根据 ${vertical} schema 验证查询\n` +
      `可以对数据库执行。`
    );
  },
  {
    name: "write_sql_query",
    description: `为特定业务条线编写并验证 SQL 查询。

此工具帮助格式化和验证 SQL 查询。你必须先加载相应技能,
以了解数据库 schema。`,
    schema: z.object({
      query: z.string().describe("要编写的 SQL 查询"),
      vertical: z.string().describe("业务条线(sales_analytics 或 inventory_management)"),
    }),
  }
);

更新中间件和智能体

更新中间件以使用自定义状态 schema:
const skillMiddleware = createMiddleware({
  name: "skillMiddleware",
  stateSchema: CustomState,
  tools: [loadSkill, writeSqlQuery],
  // ... 中间件实现的其余部分保持不变
});
使用注册了受约束工具的中间件创建智能体:
const agent = createAgent({
  model,
  systemPrompt:
    "你是一个 SQL 查询助手,帮助用户" +
    "针对业务数据库编写查询。",
  middleware: [skillMiddleware],
  checkpointer: new MemorySaver(),
});
现在,如果智能体在加载所需技能前尝试使用 write_sql_query,它会收到错误消息,提示它先加载相应技能,例如 sales_analyticsinventory_management。这可确保智能体在尝试验证查询前具备必要的 schema 知识。

完整示例

下面是一个完整、可运行的实现,组合了本教程中的所有部分:
import {
  tool,
  createAgent,
  createMiddleware,
  ToolMessage,
  context,
  type ToolRuntime,
} from "langchain";
import { MemorySaver, Command } from "@langchain/langgraph";
import { ChatOpenAI } from "@langchain/openai";
import { z } from "zod";

// 可以渐进式披露给智能体的技能
const SkillSchema = z.object({
  name: z.string(), // 技能的唯一标识符
  description: z.string(), // 显示在系统提示词中的 1-2 句描述
  content: z.string(), // 包含详细指令的完整技能内容
});

type Skill = z.infer<typeof SkillSchema>;

const SKILLS: Skill[] = [
  {
    name: "sales_analytics",
    description:
      "用于销售数据分析的数据库 schema 和业务逻辑,包括 customers、orders 和 revenue。",
    content: context`
    # 销售分析 Schema

    ## 表

    ### customers
    - customer_id (PRIMARY KEY)
    - name
    - email
    - signup_date
    - status (active/inactive)
    - customer_tier (bronze/silver/gold/platinum)

    ### orders
    - order_id (PRIMARY KEY)
    - customer_id (FOREIGN KEY -> customers)
    - order_date
    - status (pending/completed/cancelled/refunded)
    - total_amount
    - sales_region (north/south/east/west)

    ### order_items
    - item_id (PRIMARY KEY)
    - order_id (FOREIGN KEY -> orders)
    - product_id
    - quantity
    - unit_price
    - discount_percent

    ## 业务逻辑

    **活跃客户**:status = 'active' AND signup_date <= CURRENT_DATE - INTERVAL '90 days'

    **收入计算**:
    仅统计 status = 'completed' 的订单。使用 orders 表中的 total_amount,
    它已经包含折扣计算。

    **客户生命周期价值 (CLV)**:
    某个客户所有已完成订单金额的总和。

    **高价值订单**:
    total_amount > 1000 的订单

    ## 示例查询
    -- 获取上一季度收入最高的 10 个客户
    SELECT
        c.customer_id,
        c.name,
        c.customer_tier,
        SUM(o.total_amount) as total_revenue
    FROM customers c
    JOIN orders o ON c.customer_id = o.customer_id
    WHERE o.status = 'completed'
    AND o.order_date >= CURRENT_DATE - INTERVAL '3 months'
    GROUP BY c.customer_id, c.name, c.customer_tier
    ORDER BY total_revenue DESC
    LIMIT 10;`,
  },
  {
    name: "inventory_management",
    description:
      "用于库存跟踪的数据库 schema 和业务逻辑,包括 products、warehouses 和 stock levels。",
    content: context`
    # 库存管理 Schema

    ## 表

    ### products
    - product_id (PRIMARY KEY)
    - product_name
    - sku
    - category
    - unit_cost
    - reorder_point (重新订购前的最低库存水平)
    - discontinued (boolean)

    ### warehouses
    - warehouse_id (PRIMARY KEY)
    - warehouse_name
    - location
    - capacity

    ### inventory
    - inventory_id (PRIMARY KEY)
    - product_id (FOREIGN KEY -> products)
    - warehouse_id (FOREIGN KEY -> warehouses)
    - quantity_on_hand
    - last_updated

    ### stock_movements
    - movement_id (PRIMARY KEY)
    - product_id (FOREIGN KEY -> products)
    - warehouse_id (FOREIGN KEY -> warehouses)
    - movement_type (inbound/outbound/transfer/adjustment)
    - quantity (inbound 为正数,outbound 为负数)
    - movement_date
    - reference_number

    ## 业务逻辑

    **可用库存**:
    inventory 表中 quantity_on_hand > 0 的 quantity_on_hand

    **需要重新订购的产品**:
    所有仓库中的 total quantity_on_hand 小于或等于
    产品 reorder_point 的产品

    **仅活跃产品**:
    除非专门分析已停用商品,否则排除 discontinued = true 的产品

    **库存估值**:
    每个产品的 quantity_on_hand * unit_cost

    ## 示例查询

    -- 查找所有仓库中低于重新订购点的产品
    SELECT
        p.product_id,
        p.product_name,
        p.reorder_point,
        SUM(i.quantity_on_hand) as total_stock,
        p.unit_cost,
        (p.reorder_point - SUM(i.quantity_on_hand)) as units_to_reorder
    FROM products p
    JOIN inventory i ON p.product_id = i.product_id
    WHERE p.discontinued = false
    GROUP BY p.product_id, p.product_name, p.reorder_point, p.unit_cost
    HAVING SUM(i.quantity_on_hand) <= p.reorder_point
    ORDER BY units_to_reorder DESC;`,
  },
];

// const loadSkill = tool(
//   async ({ skillName }) => {
//     // 查找并返回请求的技能
//     const skill = SKILLS.find((s) => s.name === skillName);
//     if (skill) {
//       return `已加载技能:${skillName}\n\n${skill.content}`;
//     }

//     // 未找到技能
//     const available = SKILLS.map((s) => s.name).join(", ");
//     return `未找到技能 '${skillName}'。可用技能:${available}`;
//   },
//   {
//     name: "load_skill",
//     description: `将技能的完整内容加载到智能体上下文中。

// 当你需要有关如何处理特定类型请求的详细信息时使用此工具。
// 它会提供该技能领域的完整指令、策略和指南。`,
//     schema: z.object({
//       skillName: z.string().describe("要加载的技能名称"),
//     }),
//   }
// );

// 根据 SKILLS 列表构建技能提示词
const skillsPrompt = SKILLS.map(
  (skill) => `- **${skill.name}**: ${skill.description}`
).join("\n");

const skillMiddleware = createMiddleware({
  name: "skillMiddleware",
  tools: [loadSkill],
  wrapModelCall: async (request, handler) => {
    // 构建技能补充内容
    const skillsAddendum =
      `\n\n## 可用技能\n\n${skillsPrompt}\n\n` +
      "当你需要有关如何处理特定类型请求的详细信息时," +
      "使用 load_skill 工具。";

    // 追加到系统提示词
    const newSystemPrompt = request.systemPrompt + skillsAddendum;

    return handler({
      ...request,
      systemPrompt: newSystemPrompt,
    });
  },
});

const model = new ChatOpenAI({
  model: "gpt-5.4-mini",
  temperature: 0,
});

// 创建支持技能的智能体
const agent = createAgent({
  model,
  systemPrompt:
    "你是一个 SQL 查询助手,帮助用户" +
    "针对业务数据库编写查询。",
  middleware: [skillMiddleware],
  checkpointer: new MemorySaver(),
});

// 此对话线程的配置
const threadId = crypto.randomUUID();
const config = { configurable: { thread_id: threadId } };

// 请求一个 SQL 查询
const result = await agent.invoke(
  {
    messages: [
      {
        role: "user",
        content:
          "编写一个 SQL 查询,找出上个月" +
          "下过超过 $1000 订单的所有客户",
      },
    ],
  },
  config
);

// 打印对话
for (const message of result.messages) {
  console.log(`${message.type}: ${message.content}`);
}
这个完整示例包含:
  • 包含完整数据库 schema 的技能定义
  • 用于按需加载的 load_skill 工具
  • 将技能描述注入系统提示词的 SkillMiddleware
  • 使用中间件和 checkpointer 创建智能体
  • 展示智能体如何加载技能并编写 SQL 查询的示例用法
要运行此示例,你需要:
  1. 安装所需包:pip install langchain langchain-openai langgraph
  2. 设置 API key,例如 export OPENAI_API_KEY=...
  3. 将模型初始化替换为你首选的 LLM provider

实现变体

本教程将技能实现为通过工具调用加载的内存 Python 字典。不过,还有多种方式可以使用技能实现渐进式披露:存储后端:
  • 内存(本教程):将技能定义为 Python 数据结构,访问速度快,没有 I/O 开销
  • 文件系统(Claude Code 方法):技能是包含文件的目录,通过 read_file 等文件操作发现
  • 远程存储:技能位于 S3、数据库、Notion 或 API 中,按需获取
技能发现(智能体如何知道有哪些技能):
  • 系统提示词列表:在系统提示词中列出技能描述(本教程使用)
  • 基于文件:通过扫描目录发现技能(Claude Code 方法)
  • 基于注册表:查询技能注册表服务或 API 以获取可用技能
  • 动态查找:通过工具调用列出可用技能
渐进式披露策略(如何加载技能内容):
  • 单次加载:在一次工具调用中加载整个技能内容(本教程使用)
  • 分页加载:对于大型技能,将技能内容分成多个页面或块加载
  • 基于搜索:在特定技能内容中搜索相关章节,例如对技能文件使用 grep/read 操作
  • 分层加载:先加载技能概览,再深入特定小节
大小注意事项(未校准的经验模型,请针对你的系统优化):
  • 小型技能(< 1K tokens / 约 750 词):可以直接包含在系统提示词中,并使用 prompt caching 缓存,以节省成本并加快响应
  • 中型技能(1-10K tokens / 约 750-7.5K 词):适合按需加载,以避免上下文开销(本教程)
  • 大型技能(> 10K tokens / 约 7.5K 词,或 > 上下文窗口的 5-10%):应使用分页、基于搜索的加载或分层探索等渐进式披露技术,以避免消耗过多上下文
选择取决于你的需求:内存方式最快,但技能更新需要重新部署;基于文件或远程存储的方式则支持无需改代码的动态技能管理。

渐进式披露和上下文工程

渐进式披露本质上是一种 context engineering 技术。你在管理哪些信息可供智能体使用,以及何时可用。本教程重点介绍加载数据库 schema,但相同原则也适用于其他类型的上下文。

与 few-shot prompting 结合

对于 SQL 查询场景,你可以扩展渐进式披露,动态加载与用户查询匹配的 few-shot examples示例方法:
  1. 用户提问:“找出 6 个月未下单的客户”
  2. 智能体加载 sales_analytics schema,如本教程所示
  3. 智能体还会加载 2-3 个相关示例查询,通过语义搜索或基于标签的查找:
    • 查找不活跃客户的查询
    • 使用基于日期过滤的查询
    • 连接 customers 和 orders 表的查询
  4. 智能体同时使用 schema 知识和示例模式编写查询
渐进式披露(按需加载 schema)与动态 few-shot prompting(加载相关示例)的组合,形成了一种强大的上下文工程模式。它可以扩展到大型知识库,同时提供高质量、基于事实的输出。

后续步骤