前言

LangChain Deep Agents 是一个功能强大的 AI Agent 框架,其技能系统(Skills System)是该框架的核心特性之一。本文将从源码层面深入分析 Deep Agents 技能系统的设计理念、架构实现及工作原理,帮助开发者全面理解这一模块化能力扩展机制。

一、技能系统概述

1.1 什么是技能(Skill)

在 Deep Agents 框架中,技能是一种模块化、自包含的能力扩展包,用于为 AI Agent 提供专业化的知识、工作流程和工具集成。技能的设计理念类似于"入职指南"——它们能够将通用型 Agent 转化为具备特定领域专业知识和程序化能力的专业型 Agent。

1.2 技能系统的核心价值

技能系统为 Deep Agents 提供了以下核心能力:

  • 专业化工作流程:为特定领域提供多步骤的标准化操作流程
  • 工具集成:提供与特定文件格式或 API 交互的指导说明
  • 领域专业知识:封装公司特定知识、数据库模式、业务逻辑等
  • 资源捆绑:包含脚本、参考文档和资产文件,支持复杂和重复性任务

二、技能系统架构设计

2.1 整体架构

Deep Agents 技能系统采用分层架构设计,主要包含以下组件:

┌─────────────────────────────────────────────────────────────┐
│                      CLI 层 (deepagents-cli)                │
│  ┌─────────────┐  ┌─────────────┐  ┌─────────────────────┐  │
│  │   commands  │  │    load     │  │   built_in_skills   │  │
│  │   (CRUD)    │  │  (加载器)   │  │    (内置技能)       │  │
│  └─────────────┘  └─────────────┘  └─────────────────────┘  │
├─────────────────────────────────────────────────────────────┤
│                      SDK 层 (deepagents)                    │
│  ┌─────────────────────────────────────────────────────────┐│
│  │              SkillsMiddleware (技能中间件)              ││
│  │  ┌─────────────┐  ┌─────────────┐  ┌─────────────────┐ ││
│  │  │ 技能发现    │  │ 元数据解析  │  │ 系统提示注入    │ ││
│  │  └─────────────┘  └─────────────┘  └─────────────────┘ ││
│  └─────────────────────────────────────────────────────────┘│
├─────────────────────────────────────────────────────────────┤
│                     Backend 层 (存储后端)                   │
│  ┌─────────────┐  ┌─────────────┐  ┌─────────────────────┐  │
│  │ Filesystem  │  │    State    │  │       Store         │  │
│  │   Backend   │  │   Backend   │  │      Backend        │  │
│  └─────────────┘  └─────────────┘  └─────────────────────┘  │
└─────────────────────────────────────────────────────────────┘

2.2 技能目录结构

技能系统支持多级目录优先级机制,从低到高依次为:

优先级 目录路径 作用域 说明
0 <package>/built_in_skills/ 内置 随 CLI 包分发的内置技能
1 ~/.deepagents/<agent>/skills/ 用户级 用户自定义技能(deepagents 别名)
2 ~/.agents/skills/ 用户级 跨工具共享的用户技能
3 .deepagents/skills/ 项目级 项目特定技能(deepagents 别名)
4 .agents/skills/ 项目级 跨工具共享的项目技能

当多个目录包含同名技能时,高优先级目录中的技能将覆盖低优先级目录中的同名技能。

2.3 技能文件结构

每个技能是一个独立的目录,包含必需的 SKILL.md 文件和可选的资源文件:

skill-name/
├── SKILL.md              # 必需:技能定义文件
├── scripts/              # 可选:可执行脚本
│   └── helper.py
├── references/           # 可选:参考文档
│   └── api_docs.md
└── assets/               # 可选:资产文件
    └── template.json

三、核心实现分析

3.1 技能元数据定义(SkillMetadata)

技能元数据遵循 Agent Skills 规范(https://agentskills.io/specification),定义在 libs/deepagents/deepagents/middleware/skills.py 中:

class SkillMetadata(TypedDict):
    """技能元数据,遵循 Agent Skills 规范"""

    path: str
    """SKILL.md 文件的路径"""

    name: str
    """技能标识符
    约束条件:
    - 1-64 个字符
    - 仅允许 Unicode 小写字母数字和连字符
    - 不能以连字符开头或结尾
    - 不能包含连续的连字符
    - 必须与包含 SKILL.md 的父目录名称匹配
    """

    description: str
    """技能功能描述(1-1024 个字符)"""

    license: str | None
    """许可证名称或引用"""

    compatibility: str | None
    """环境要求(1-500 个字符)"""

    metadata: dict[str, str]
    """任意键值对,用于存储额外属性"""

    allowed_tools: list[str]
    """技能推荐使用的工具名称列表"""

3.2 SKILL.md 文件解析

技能定义文件采用 YAML frontmatter + Markdown 正文的格式。解析逻辑实现在 _parse_skill_metadata 函数中:

def _parse_skill_metadata(
    content: str,
    skill_path: str,
    directory_name: str,
) -> SkillMetadata | None:
    """解析 SKILL.md 文件的 YAML frontmatter"""

    # 安全检查:限制文件大小(10MB)
    if len(content) > MAX_SKILL_FILE_SIZE:
        logger.warning("跳过 %s: 内容过大 (%d bytes)", skill_path, len(content))
        return None

    # 匹配 YAML frontmatter(--- 分隔符之间的内容)
    frontmatter_pattern = r"^---\s*\n(.*?)\n---\s*\n"
    match = re.match(frontmatter_pattern, content, re.DOTALL)

    if not match:
        logger.warning("跳过 %s: 未找到有效的 YAML frontmatter", skill_path)
        return None

    # 使用 yaml.safe_load 安全解析
    frontmatter_data = yaml.safe_load(match.group(1))

    # 验证必需字段
    name = str(frontmatter_data.get("name", "")).strip()
    description = str(frontmatter_data.get("description", "")).strip()

    if not name or not description:
        logger.warning("跳过 %s: 缺少必需的 'name' 或 'description'", skill_path)
        return None

    # 验证名称格式
    is_valid, error = _validate_skill_name(str(name), directory_name)
    if not is_valid:
        logger.warning("技能 '%s' 不符合 Agent Skills 规范: %s", name, error)

    return SkillMetadata(
        name=str(name),
        description=description_str,
        path=skill_path,
        metadata=_validate_metadata(frontmatter_data.get("metadata", {}), skill_path),
        license=str(frontmatter_data.get("license", "")).strip() or None,
        compatibility=compatibility_str,
        allowed_tools=allowed_tools,
    )

3.3 技能中间件(SkillsMiddleware)

SkillsMiddleware 是技能系统的核心组件,负责在 Agent 执行前加载技能并将其注入系统提示。该类继承自 AgentMiddleware

class SkillsMiddleware(AgentMiddleware[SkillsState, ContextT, ResponseT]):
    """技能加载和系统提示注入中间件"""

    state_schema = SkillsState

    def __init__(self, *, backend: BACKEND_TYPES, sources: list[str]) -> None:
        """初始化技能中间件

        Args:
            backend: 后端实例或工厂函数
            sources: 技能源路径列表(如 ['/skills/user/', '/skills/project/'])
        """
        self._backend = backend
        self.sources = sources
        self.system_prompt_template = SKILLS_SYSTEM_PROMPT

    def before_agent(
        self,
        state: SkillsState,
        runtime: Runtime,
        config: RunnableConfig
    ) -> SkillsStateUpdate | None:
        """Agent 执行前加载技能元数据(同步版本)"""

        # 如果 skills_metadata 已存在于状态中,跳过加载
        if "skills_metadata" in state:
            return None

        backend = self._get_backend(state, runtime, config)
        all_skills: dict[str, SkillMetadata] = {}

        # 按顺序从每个源加载技能
        # 后加载的源会覆盖先加载的同名技能(后者优先)
        for source_path in self.sources:
            source_skills = _list_skills(backend, source_path)
            for skill in source_skills:
                all_skills[skill["name"]] = skill

        return SkillsStateUpdate(skills_metadata=list(all_skills.values()))

    def modify_request(
        self,
        request: ModelRequest[ContextT]
    ) -> ModelRequest[ContextT]:
        """将技能文档注入模型请求的系统消息"""

        skills_metadata = request.state.get("skills_metadata", [])
        skills_locations = self._format_skills_locations()
        skills_list = self._format_skills_list(skills_metadata)

        skills_section = self.system_prompt_template.format(
            skills_locations=skills_locations,
            skills_list=skills_list,
        )

        new_system_message = append_to_system_message(
            request.system_message,
            skills_section
        )

        return request.override(system_message=new_system_message)

3.4 渐进式披露(Progressive Disclosure)详解

渐进式披露是 Deep Agents 技能系统的核心设计理念,其目的是在保持 Agent 能力的同时,有效管理有限的上下文窗口资源。本节将结合源码深入分析这一机制的完整实现过程。

3.4.1 设计理念

上下文窗口是一种"公共资源",需要在系统提示、对话历史、技能元数据和用户请求之间合理分配。渐进式披露的核心思想是:只在需要时才加载详细信息,避免一次性将所有技能内容塞入上下文。

3.4.2 三层披露架构

渐进式披露采用三层架构,每层有不同的加载时机和大小限制:

层级 内容 加载时机 大小限制 Token 消耗
第一层 元数据(name + description) Agent 启动时,始终在上下文中 description ≤ 1024 字符 ~100 词/技能
第二层 SKILL.md 正文 Agent 主动读取时 建议 < 500 行,硬限制 10MB < 5000 词
第三层 捆绑资源(scripts/references/assets) Agent 按需读取或执行 无限制 按需消耗
3.4.3 第一层:元数据注入(始终在上下文中)

触发时机:Agent 执行前,由 before_agent 钩子自动触发

源码实现

# SkillsMiddleware.before_agent 方法
def before_agent(self, state: SkillsState, runtime: Runtime, config: RunnableConfig) -> SkillsStateUpdate | None:
    """Agent 执行前加载技能元数据"""

    # 如果已加载过,跳过(支持会话持久化)
    if "skills_metadata" in state:
        return None

    backend = self._get_backend(state, runtime, config)
    all_skills: dict[str, SkillMetadata] = {}

    # 按优先级顺序加载各源的技能
    for source_path in self.sources:
        source_skills = _list_skills(backend, source_path)
        for skill in source_skills:
            all_skills[skill["name"]] = skill  # 后加载的覆盖先加载的

    return SkillsStateUpdate(skills_metadata=list(all_skills.values()))

关键点

  • _list_skills 函数只解析 YAML frontmatter,不读取 Markdown 正文
  • 元数据存储在 SkillsState.skills_metadata 中,标记为 PrivateStateAttr(不传播给父 Agent)
  • 支持会话持久化:如果 skills_metadata 已存在,跳过重复加载

元数据解析过程

def _parse_skill_metadata(content: str, skill_path: str, directory_name: str) -> SkillMetadata | None:
    """只解析 YAML frontmatter,不读取正文"""

    # 正则匹配 frontmatter(--- 分隔符之间的内容)
    frontmatter_pattern = r"^---\s*\n(.*?)\n---\s*\n"
    match = re.match(frontmatter_pattern, content, re.DOTALL)

    if not match:
        return None

    # 只解析 frontmatter 部分
    frontmatter_str = match.group(1)
    frontmatter_data = yaml.safe_load(frontmatter_str)

    # 提取 name 和 description(正文被忽略)
    name = str(frontmatter_data.get("name", "")).strip()
    description = str(frontmatter_data.get("description", "")).strip()

    # description 限制为 1024 字符
    if len(description) > MAX_SKILL_DESCRIPTION_LENGTH:
        description = description[:MAX_SKILL_DESCRIPTION_LENGTH]

    return SkillMetadata(
        name=name,
        description=description,
        path=skill_path,  # 保存路径,供第二层使用
        # ...
    )

系统提示注入

# SkillsMiddleware.modify_request 方法
def modify_request(self, request: ModelRequest[ContextT]) -> ModelRequest[ContextT]:
    """将技能元数据注入系统提示"""

    skills_metadata = request.state.get("skills_metadata", [])
    skills_list = self._format_skills_list(skills_metadata)

    # 格式化技能列表(只包含元数据,不包含正文)
    skills_section = self.system_prompt_template.format(
        skills_locations=self._format_skills_locations(),
        skills_list=skills_list,
    )

    return request.override(
        system_message=append_to_system_message(request.system_message, skills_section)
    )

def _format_skills_list(self, skills: list[SkillMetadata]) -> str:
    """格式化技能列表,包含路径提示"""
    lines = []
    for skill in skills:
        # 显示名称和描述
        lines.append(f"- **{skill['name']}**: {skill['description']}")
        # 关键:提供路径,引导 Agent 进行第二层披露
        lines.append(f"  -> Read `{skill['path']}` for full instructions")
    return "\n".join(lines)

注入到系统提示的内容示例

## Skills System

You have access to a skills library that provides specialized capabilities.

**User Skills**: `/skills/user/`
**Project Skills**: `/skills/project/` (higher priority)

**Available Skills:**

- **query-writing**: For writing and executing SQL queries - from simple
  single-table queries to complex multi-table JOINs and aggregations
  -> Read `/skills/project/query-writing/SKILL.md` for full instructions

- **schema-exploration**: For discovering and understanding database structure,
  tables, columns, and relationships
  -> Read `/skills/project/schema-exploration/SKILL.md` for full instructions

**How to Use Skills (Progressive Disclosure):**

Skills follow a **progressive disclosure** pattern - you see their name and
description above, but only read full instructions when needed:

1. **Recognize when a skill applies**: Check if the user's task matches a skill's description
2. **Read the skill's full instructions**: Use the path shown in the skill list above
3. **Follow the skill's instructions**: SKILL.md contains step-by-step workflows
4. **Access supporting files**: Skills may include helper scripts, configs, or reference docs
3.4.4 第二层:SKILL.md 正文读取(Agent 主动触发)

触发时机:Agent 识别到任务匹配某技能后,主动调用 read_file 工具

实现机制:第二层披露不是由 SkillsMiddleware 自动完成的,而是依赖于:

  1. 系统提示中的引导指令
  2. FilesystemMiddleware 提供的 read_file 工具
  3. Agent 的自主决策能力

read_file 工具实现(FilesystemMiddleware):

def _create_read_file_tool(self) -> BaseTool:
    """创建 read_file 工具"""

    def sync_read_file(
        file_path: Annotated[str, "Absolute path to the file to read."],
        runtime: ToolRuntime[None, FilesystemState],
        offset: int = 0,   # 支持分页
        limit: int = 100,  # 默认读取 100 行
    ) -> str:
        resolved_backend = self._get_backend(runtime)

        # 调用后端读取文件
        result = resolved_backend.read(file_path, offset=offset, limit=limit)

        # 大文件截断保护
        if token_limit and len(result) >= NUM_CHARS_PER_TOKEN * token_limit:
            truncation_msg = READ_FILE_TRUNCATION_MSG.format(file_path=file_path)
            max_content_length = NUM_CHARS_PER_TOKEN * token_limit - len(truncation_msg)
            result = result[:max_content_length] + truncation_msg

        return result

    return StructuredTool.from_function(
        name="read_file",
        description=READ_FILE_TOOL_DESCRIPTION,
        func=sync_read_file,
    )

Agent 决策过程示例

用户请求: "帮我查询数据库中销售额最高的前5个客户"

Agent 思考过程:
1. 检查可用技能列表
2. 发现 "query-writing" 技能匹配 "SQL queries" 关键词
3. 决定读取技能详细指令

Agent 工具调用:
read_file(file_path="/skills/project/query-writing/SKILL.md")

返回内容(SKILL.md 正文):
# Query Writing Skill

## When to Use This Skill
Use this skill when you need to answer a question by writing and executing a SQL query.

## Workflow for Simple Queries
1. **Identify the table** - Which table has the data?
2. **Get the schema** - Use `sql_db_schema` to see columns
3. **Write the query** - SELECT relevant columns with WHERE/LIMIT/ORDER BY
...
3.4.5 第三层:捆绑资源按需加载

触发时机:Agent 在执行技能指令过程中,根据需要读取或执行资源文件

资源类型与加载方式

资源类型 目录 加载方式 是否消耗上下文
脚本 scripts/ execute 工具执行 否(仅输出结果)
参考文档 references/ read_file 工具读取
资产文件 assets/ 直接使用路径

脚本执行示例(不消耗上下文):

# Agent 可以直接执行脚本,无需读取内容
execute(command="python /skills/user/pdf-editor/scripts/rotate_pdf.py input.pdf 90")

参考文档按需读取示例

技能目录结构:
bigquery-skill/
├── SKILL.md (概览和导航)
└── references/
    ├── finance.md (财务指标)
    ├── sales.md (销售数据)
    └── product.md (产品数据)

用户请求: "查询本季度销售额"

Agent 行为:
1. 读取 SKILL.md(第二层)
2. 根据 SKILL.md 指引,只读取 references/sales.md(第三层)
3. 不读取 finance.md 和 product.md(节省上下文)
3.4.6 渐进式披露的设计模式

模式一:高层指南 + 引用文件

# PDF Processing

## Quick start
Extract text with pdfplumber: [简短示例]

## Advanced features
- **Form filling**: See [FORMS.md](references/FORMS.md) for complete guide
- **API reference**: See [REFERENCE.md](references/REFERENCE.md) for all methods

模式二:按领域组织

cloud-deploy/
├── SKILL.md (工作流 + 提供商选择指南)
└── references/
    ├── aws.md (AWS 部署模式)
    ├── gcp.md (GCP 部署模式)
    └── azure.md (Azure 部署模式)

当用户选择 AWS 时,Agent 只读取 aws.md

模式三:条件性详情

# DOCX Processing

## Creating documents
Use docx-js for new documents. See [DOCX-JS.md](references/DOCX-JS.md).

## Editing documents
For simple edits, modify the XML directly.

**For tracked changes**: See [REDLINING.md](references/REDLINING.md)
**For OOXML details**: See [OOXML.md](references/OOXML.md)
3.4.7 上下文消耗对比

假设一个技能包含以下内容:

组件 大小 传统方式 渐进式披露
元数据 100 词 100 词 100 词(始终加载)
SKILL.md 正文 2000 词 2000 词 0-2000 词(按需)
references/api.md 5000 词 5000 词 0-5000 词(按需)
references/examples.md 3000 词 3000 词 0-3000 词(按需)
总计 10100 词 10100 词 100-10100 词

在典型场景下,Agent 可能只需要加载元数据 + 部分正文,上下文消耗可降低 70-90%。

3.4.8 实现要点总结
  1. 元数据是触发器description 字段是 Agent 决定是否使用技能的唯一依据,必须包含清晰的触发条件
  2. 路径是桥梁path 字段连接第一层和第二层,引导 Agent 进行深入读取
  3. 正文是指令:SKILL.md 正文只在技能触发后加载,应聚焦于操作指令而非重复描述
  4. 资源是扩展:捆绑资源提供无限扩展能力,脚本可执行而不消耗上下文

3.5 技能发现与加载

技能发现通过 _list_skills 函数实现,该函数扫描后端存储中的技能目录:

def _list_skills(backend: BackendProtocol, source_path: str) -> list[SkillMetadata]:
    """从后端源列出所有技能

    预期结构:
    source_path/
    └── skill-name/
        ├── SKILL.md   # 必需
        └── helper.py  # 可选
    """
    skills: list[SkillMetadata] = []
    items = backend.ls_info(source_path)

    # 查找所有技能目录(包含 SKILL.md 的目录)
    skill_dirs = [item["path"] for item in items if item.get("is_dir")]

    if not skill_dirs:
        return []

    # 构建 SKILL.md 路径列表
    skill_md_paths = []
    for skill_dir_path in skill_dirs:
        skill_dir = PurePosixPath(skill_dir_path)
        skill_md_path = str(skill_dir / "SKILL.md")
        skill_md_paths.append((skill_dir_path, skill_md_path))

    # 批量下载 SKILL.md 文件
    paths_to_download = [p[1] for p in skill_md_paths]
    responses = backend.download_files(paths_to_download)

    # 解析每个下载的 SKILL.md
    for (skill_dir_path, skill_md_path), response in zip(skill_md_paths, responses):
        if response.error or response.content is None:
            continue

        content = response.content.decode("utf-8")
        directory_name = PurePosixPath(skill_dir_path).name

        skill_metadata = _parse_skill_metadata(
            content=content,
            skill_path=skill_md_path,
            directory_name=directory_name,
        )
        if skill_metadata:
            skills.append(skill_metadata)

    return skills

四、渐进式披露完整流程图

为了更直观地理解渐进式披露的工作流程,下面通过一个完整的时序图展示整个过程:

┌─────────────────────────────────────────────────────────────────────────────────┐
│                           渐进式披露完整流程                                      │
├─────────────────────────────────────────────────────────────────────────────────┤
│                                                                                 │
│  ┌─────────┐     ┌──────────────────┐     ┌─────────┐     ┌─────────────────┐  │
│  │  用户   │     │ SkillsMiddleware │     │  Agent  │     │ FilesystemMiddleware│
│  └────┬────┘     └────────┬─────────┘     └────┬────┘     └────────┬────────┘  │
│       │                   │                    │                   │           │
│       │                   │                    │                   │           │
│  ═════╪═══════════════════╪════════════════════╪═══════════════════╪═══════════│
│  第一层│: 元数据加载(Agent 启动时)            │                   │           │
│  ═════╪═══════════════════╪════════════════════╪═══════════════════╪═══════════│
│       │                   │                    │                   │           │
│       │    before_agent() │                    │                   │           │
│       │    ─────────────► │                    │                   │           │
│       │                   │ _list_skills()     │                   │           │
│       │                   │ (只解析frontmatter)│                   │           │
│       │                   │ ──────────────────►│                   │           │
│       │                   │                    │                   │           │
│       │                   │ modify_request()   │                   │           │
│       │                   │ (注入元数据到系统提示)                  │           │
│       │                   │ ──────────────────►│                   │           │
│       │                   │                    │                   │           │
│       │                   │    系统提示包含:    │                   │           │
│       │                   │    - 技能名称       │                   │           │
│       │                   │    - 技能描述       │                   │           │
│       │                   │    - SKILL.md路径   │                   │           │
│       │                   │                    │                   │           │
│  ═════╪═══════════════════╪════════════════════╪═══════════════════╪═══════════│
│  第二层│: SKILL.md 正文读取(Agent 主动触发)   │                   │           │
│  ═════╪═══════════════════╪════════════════════╪═══════════════════╪═══════════│
│       │                   │                    │                   │           │
│       │  "查询销售额最高的客户"                 │                   │           │
│       │ ─────────────────────────────────────► │                   │           │
│       │                   │                    │                   │           │
│       │                   │    Agent 识别任务   │                   │           │
│       │                   │    匹配 query-writing│                  │           │
│       │                   │                    │                   │           │
│       │                   │                    │ read_file()       │           │
│       │                   │                    │ ─────────────────►│           │
│       │                   │                    │                   │           │
│       │                   │                    │ 返回 SKILL.md 正文│           │
│       │                   │                    │ ◄─────────────────│           │
│       │                   │                    │                   │           │
│       │                   │    Agent 获得完整   │                   │           │
│       │                   │    工作流指令       │                   │           │
│       │                   │                    │                   │           │
│  ═════╪═══════════════════╪════════════════════╪═══════════════════╪═══════════│
│  第三层│: 捆绑资源按需加载(执行过程中)        │                   │           │
│  ═════╪═══════════════════╪════════════════════╪═══════════════════╪═══════════│
│       │                   │                    │                   │           │
│       │                   │    Agent 需要参考   │                   │           │
│       │                   │    数据库 schema    │                   │           │
│       │                   │                    │                   │           │
│       │                   │                    │ read_file()       │           │
│       │                   │                    │ (references/      │           │
│       │                   │                    │  schema.md)       │           │
│       │                   │                    │ ─────────────────►│           │
│       │                   │                    │                   │           │
│       │                   │    或执行脚本       │                   │           │
│       │                   │                    │ execute()         │           │
│       │                   │                    │ (scripts/         │           │
│       │                   │                    │  helper.py)       │           │
│       │                   │                    │ ─────────────────►│           │
│       │                   │                    │                   │           │
│       │  返回查询结果      │                    │                   │           │
│       │ ◄───────────────────────────────────── │                   │           │
│       │                   │                    │                   │           │
└─────────────────────────────────────────────────────────────────────────────────┘

五、CLI 层实现

4.1 技能管理命令

CLI 层提供了完整的技能 CRUD 操作,实现在 libs/cli/deepagents_cli/skills/commands.py 中:

# 技能列表
def _list(agent: str, *, project: bool = False) -> None:
    """列出所有可用技能"""

# 创建技能
def _create(skill_name: str, agent: str, project: bool = False) -> None:
    """创建新技能,生成模板 SKILL.md 文件"""

# 查看技能详情
def _info(skill_name: str, *, agent: str = "agent", project: bool = False) -> None:
    """显示特定技能的详细信息"""

# 删除技能
def _delete(skill_name: str, *, agent: str = "agent",
            project: bool = False, force: bool = False) -> None:
    """删除技能目录"""

4.2 技能名称验证

技能名称必须遵循 Agent Skills 规范:

def _validate_name(name: str) -> tuple[bool, str]:
    """验证技能名称

    要求:
    - 最多 64 个字符
    - 仅允许 Unicode 小写字母数字和连字符
    - 不能以连字符开头或结尾
    - 不能包含连续连字符
    - 不能包含路径遍历序列
    """
    if not name or not name.strip():
        return False, "不能为空"

    if len(name) > MAX_SKILL_NAME_LENGTH:
        return False, "不能超过 64 个字符"

    if ".." in name or "/" in name or "\\" in name:
        return False, "不能包含路径组件"

    if name.startswith("-") or name.endswith("-") or "--" in name:
        return False, "必须是小写字母数字,仅允许单个连字符"

    for c in name:
        if c == "-":
            continue
        if (c.isalpha() and c.islower()) or c.isdigit():
            continue
        return False, "必须是小写字母数字,仅允许单个连字符"

    return True, ""

4.3 扩展技能元数据

CLI 层扩展了基础 SkillMetadata,添加了来源追踪:

class ExtendedSkillMetadata(SkillMetadata):
    """扩展的技能元数据,用于 CLI 显示

    Attributes:
        source: 技能来源,可选值为 'built-in'、'user' 或 'project'
    """
    source: str

五、后端存储抽象

5.1 BackendProtocol

技能系统通过 BackendProtocol 抽象后端存储,支持多种存储实现:

class BackendProtocol(abc.ABC):
    """可插拔存储后端协议"""

    def ls_info(self, path: str) -> list[FileInfo]:
        """列出目录中的文件和元数据"""
        raise NotImplementedError

    def download_files(self, paths: list[str]) -> list[FileDownloadResponse]:
        """批量下载文件"""
        raise NotImplementedError

    # ... 其他文件操作方法

5.2 FilesystemBackend

FilesystemBackend 提供本地文件系统访问:

class FilesystemBackend(BackendProtocol):
    """直接从文件系统读写文件的后端"""

    def __init__(
        self,
        root_dir: str | Path | None = None,
        virtual_mode: bool | None = None,
        max_file_size_mb: int = 10,
    ) -> None:
        self.cwd = Path(root_dir).resolve() if root_dir else Path.cwd()
        self.virtual_mode = virtual_mode
        self.max_file_size_bytes = max_file_size_mb * 1024 * 1024

5.3 StateBackend 与 StoreBackend

除了文件系统后端,技能系统还支持:

  • StateBackend:基于 LangGraph 状态的临时/内存存储
  • StoreBackend:基于 LangGraph Store 的持久化存储

这种设计使技能系统能够在不同的运行环境中灵活部署。

六、与 Agent 的集成

6.1 create_deep_agent 集成

技能系统通过 create_deep_agent 函数与 Agent 集成:

def create_deep_agent(
    model: str | BaseChatModel | None = None,
    tools: Sequence[BaseTool | Callable | dict[str, Any]] | None = None,
    *,
    skills: list[str] | None = None,  # 技能源路径列表
    # ... 其他参数
) -> CompiledStateGraph:
    """创建 Deep Agent"""

    # 构建中间件栈
    deepagent_middleware: list[AgentMiddleware] = [
        TodoListMiddleware(),
    ]

    # 如果指定了技能,添加 SkillsMiddleware
    if skills is not None:
        deepagent_middleware.append(
            SkillsMiddleware(backend=backend, sources=skills)
        )

    # ... 其他中间件

    return create_agent(
        model,
        middleware=deepagent_middleware,
        # ...
    )

6.2 子代理技能支持

技能系统也支持为子代理(SubAgent)配置独立的技能集:

# 处理用户提供的子代理
for spec in subagents or []:
    # ...
    subagent_skills = spec.get("skills")
    if subagent_skills:
        subagent_middleware.append(
            SkillsMiddleware(backend=backend, sources=subagent_skills)
        )

七、内置技能示例

7.1 skill-creator 技能

Deep Agents CLI 内置了 skill-creator 技能,用于指导用户创建新技能:

---
name: skill-creator
description: "Guide for creating effective skills that extend agent capabilities..."
license: MIT
compatibility: designed for deepagents-cli
---

该技能提供了完整的技能创建指南,包括:

  • 技能结构说明
  • 渐进式披露设计原则
  • 创建流程步骤
  • 验证脚本使用方法

7.2 示例技能:query-writing

以下是 text-to-sql-agent 示例中的查询编写技能:

---
name: query-writing
description: For writing and executing SQL queries - from simple single-table
             queries to complex multi-table JOINs and aggregations
---

# Query Writing Skill

## When to Use This Skill
Use this skill when you need to answer a question by writing and executing a SQL query.

## Workflow for Simple Queries
1. **Identify the table** - Which table has the data?
2. **Get the schema** - Use `sql_db_schema` to see columns
3. **Write the query** - SELECT relevant columns with WHERE/LIMIT/ORDER BY
4. **Execute** - Run with `sql_db_query`
5. **Format answer** - Present results clearly

八、安全性考虑

8.1 文件大小限制

技能系统对 SKILL.md 文件实施大小限制,防止 DoS 攻击:

# 安全限制:SKILL.md 文件最大 10MB
MAX_SKILL_FILE_SIZE = 10 * 1024 * 1024

8.2 路径验证

CLI 层实施严格的路径验证,防止路径遍历攻击:

def _validate_skill_path(skill_dir: Path, base_dir: Path) -> tuple[bool, str]:
    """验证技能目录路径在基础目录内"""
    try:
        resolved_skill = skill_dir.resolve()
        resolved_base = base_dir.resolve()

        if not resolved_skill.is_relative_to(resolved_base):
            return False, f"技能目录必须在 {base_dir} 内"
    except (OSError, RuntimeError) as e:
        return False, f"无效路径: {e}"
    return True, ""

8.3 YAML 安全解析

使用 yaml.safe_load 而非 yaml.load,防止任意代码执行:

frontmatter_data = yaml.safe_load(frontmatter_str)

九、最佳实践

9.1 技能设计原则

  1. 简洁至上:上下文窗口是公共资源,只添加 Agent 真正需要的信息
  2. 适当的自由度:根据任务的脆弱性和可变性匹配具体程度
  3. 渐进式披露:将详细内容拆分到引用文件中,按需加载

9.2 SKILL.md 编写建议

  • description 字段:包含触发条件和使用场景,这是 Agent 决定是否使用技能的主要依据
  • 正文内容:仅包含触发后需要的指令,避免重复 description 中的信息
  • 引用文件:将详细参考资料放在 references/ 目录,保持 SKILL.md 精简

9.3 目录组织

my-skill/
├── SKILL.md              # 核心指令(<500 行)
├── scripts/              # 确定性脚本
│   └── process.py
├── references/           # 按需加载的参考文档
│   ├── api.md
│   └── examples.md
└── assets/               # 输出资产
    └── template.json

十、总结

LangChain Deep Agents 的技能系统是一个设计精良的模块化能力扩展机制。其核心特点包括:

  1. 分层架构:CLI 层、SDK 层、Backend 层职责清晰
  2. 多级优先级:支持内置、用户级、项目级技能的灵活覆盖
  3. 渐进式披露:有效管理上下文窗口,按需加载详细内容
  4. 后端抽象:支持文件系统、状态存储、持久化存储等多种后端
  5. 安全设计:文件大小限制、路径验证、安全 YAML 解析

通过技能系统,开发者可以将领域专业知识封装为可复用的模块,显著提升 AI Agent 在特定任务上的表现。这种设计理念值得在其他 Agent 框架中借鉴和推广。


Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐