LangChain Deep Agents Skills系统架构深度解析
本文深入分析了LangChain Deep Agents框架的技能系统(Skills System)。技能系统采用分层架构设计,包含CLI层、SDK层和Backend层,支持多级目录优先级机制。每个技能是一个独立目录,包含SKILL.md定义文件和可选资源。文章详细解析了技能元数据定义、SKILL.md文件解析逻辑以及核心组件SkillsMiddleware的实现,该系统通过模块化技能扩展AI A
前言
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 自动完成的,而是依赖于:
- 系统提示中的引导指令
- FilesystemMiddleware 提供的
read_file工具 - 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 实现要点总结
- 元数据是触发器:
description字段是 Agent 决定是否使用技能的唯一依据,必须包含清晰的触发条件 - 路径是桥梁:
path字段连接第一层和第二层,引导 Agent 进行深入读取 - 正文是指令:SKILL.md 正文只在技能触发后加载,应聚焦于操作指令而非重复描述
- 资源是扩展:捆绑资源提供无限扩展能力,脚本可执行而不消耗上下文
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 技能设计原则
- 简洁至上:上下文窗口是公共资源,只添加 Agent 真正需要的信息
- 适当的自由度:根据任务的脆弱性和可变性匹配具体程度
- 渐进式披露:将详细内容拆分到引用文件中,按需加载
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 的技能系统是一个设计精良的模块化能力扩展机制。其核心特点包括:
- 分层架构:CLI 层、SDK 层、Backend 层职责清晰
- 多级优先级:支持内置、用户级、项目级技能的灵活覆盖
- 渐进式披露:有效管理上下文窗口,按需加载详细内容
- 后端抽象:支持文件系统、状态存储、持久化存储等多种后端
- 安全设计:文件大小限制、路径验证、安全 YAML 解析
通过技能系统,开发者可以将领域专业知识封装为可复用的模块,显著提升 AI Agent 在特定任务上的表现。这种设计理念值得在其他 Agent 框架中借鉴和推广。
更多推荐



所有评论(0)