文章目录第 9 章Skills 技能系统本章目标核心概念实战创建「代码风格检查」Skill场景Skill 目录结构完整代码检查整个目录输出格式严重级别参考文档API 列表速查常见错误与避坑最佳实践本章小结第 9 章Skills 技能系统本章目标完成本章学习后你将能够理解 Skill 的定义与生命周期掌握SKILL.md YAML frontmatter 的标准格式掌握渐进式加载Progressive Disclosure的三级机制编写自定义 Skill包含目录结构SKILL.mdscripts/references/assets/从远程 URL 加载 Skill创建实用的「代码风格检查」Skill核心概念想象你是一家公司的员工公司有一个内部 Wiki目录页Level 1你浏览 Wiki 目录看到有新人入职指南、“报销流程”、代码规范等分类。每个分类只有标题和简介。详细页Level 2当你需要报销时你点击报销流程看到完整的步骤说明。附件Level 3报销流程中还引用了报销申请表模板.xlsx和发票粘贴规范.pdf。这就是渐进式加载Progressive Disclosure。在 DeepAgents 中Skills 遵循同样的模式1. 启动时加载所有元数据2. 任务匹配时读取完整 SKILL.md3. 需要时读取脚本/模板/参考文档Level 3: 资源文件按需访问references/pylintrcscripts/check_style.pyassets/template.htmlLevel 2: 完整指令按需加载Skill A 完整指令- 检查规范- 报告格式- 严重级别Level 1: 元数据始终加载Skill A: 代码审查检查代码风格和最佳实践Skill B: 数据可视化使用 matplotlib 生成图表Skill C: PDF 处理解析和生成 PDF 文档Agent实战创建「代码风格检查」Skill场景创建一个 Python 代码风格检查 Skill包含基于 PEP 8 的代码风格检查规则一个可执行的检查脚本常见问题参考文档报告模板Skill 目录结构skills/ └── python-style-checker/ ├── SKILL.md # 核心Skill 定义YAML frontmatter 指令 ├── scripts/ │ └── check_style.py # 可执行的检查脚本 ├── references/ │ └── pep8_rules.md # PEP 8 规则参考 └── assets/ └── report_template.html # 检查报告模板完整代码1. SKILL.md核心定义文件--- name: python-style-checker description: 检查 Python 代码是否符合 PEP 8 风格规范。 支持命名规范、缩进、行长度、导入顺序、注释格式、 类型注解等检查维度。输出结构化报告。 --- # Python 代码风格检查 ## 概述 本 Skill 提供全面的 Python 代码风格检查能力基于 PEP 8 规范。 ## 检查维度 ### 1. 命名规范 - 类名CapWords如 MyClass - 函数/变量名snake_case如 my_function - 常量UPPER_CASE如 MAX_SIZE - 私有成员以 _ 开头如 _internal ### 2. 缩进与空格 - 使用 4 个空格缩进禁止 Tab - 运算符两侧各一个空格 - 逗号后紧跟一个空格 - 函数/类之间空两行方法之间空一行 ### 3. 行长度 - 每行不超过 79 字符 - 文档字符串/注释不超过 72 字符 - 长行使用括号续行 ### 4. 导入规范 - 每个导入独占一行 - 导入顺序标准库 - 第三方库 - 本地模块 - 禁止使用 from module import * - 禁止导入未使用的模块 ### 5. 注释与文档字符串 - 所有公共模块/类/函数必须有文档字符串 - 使用三引号 ... 格式 - 注释与代码同级缩进 - 注释以 # 开头井号后跟空格 ### 6. 类型注解 - 公共函数必须包含类型注解 - 使用 from __future__ import annotationsPython 3.10 不需要 - 复杂类型使用 typing 模块 ## 使用方式 ### 检查单个文件 使用 scripts/check_style.py 脚本检查指定文件 bash python scripts/check_style.py target_file.py检查整个目录python scripts/check_style.pytarget_directory/--recursive输出格式默认终端彩色输出--format jsonJSON 格式输出--format htmlHTML 报告使用assets/report_template.html严重级别级别代码说明ErrorE必须修复如语法错误、命名错误WarningW建议修复如行长度超标InfoI仅供参考如缺少类型注解HintH优化建议如可以使用更简洁的写法参考文档references/pep8_rules.md完整的 PEP 8 规则列表PEP 8 官方文档**2. scripts/check_style.py检查脚本** python #!/usr/bin/env python3 Python 代码风格检查脚本。 用法: python check_style.py target [--recursive] [--format text|json|html] import ast import os import re import sys from pathlib import Path from typing import List, Dict, Any class StyleIssue: 代码风格问题 def __init__(self, file_path: str, line: int, col: int, code: str, message: str, severity: str): self.file_path file_path self.line line self.col col self.code code self.message message self.severity severity def __str__(self): return f{self.file_path}:{self.line}:{self.col}: {self.code} {self.message} def to_dict(self): return { file: self.file_path, line: self.line, col: self.col, code: self.code, message: self.message, severity: self.severity, } class Pep8Checker: PEP 8 风格检查器 # 命名规范正则 CLASS_NAME_RE re.compile(r^[A-Z][a-zA-Z0-9]*$) FUNCTION_NAME_RE re.compile(r^[a-z_][a-z0-9_]*$) CONSTANT_NAME_RE re.compile(r^[A-Z][A-Z0-9_]*$) PRIVATE_NAME_RE re.compile(r^_[a-z_][a-z0-9_]*$) def __init__(self): self.issues: List[StyleIssue] [] def check_file(self, file_path: str) - List[StyleIssue]: 检查单个文件 self.issues [] rel_path os.path.relpath(file_path) try: with open(file_path, r, encodingutf-8) as f: source f.read() lines source.split(\n) except Exception as e: self.issues.append(StyleIssue( rel_path, 0, 0, E001, f无法读取文件: {e}, error )) return self.issues # 解析 AST try: tree ast.parse(source) except SyntaxError as e: self.issues.append(StyleIssue( rel_path, e.lineno or 0, e.offset or 0, E002, f语法错误: {e.msg}, error )) return self.issues # 检查命名规范 self._check_naming(tree, rel_path) # 检查行长度 self._check_line_length(lines, rel_path) # 检查缩进 self._check_indentation(lines, rel_path) # 检查空行 self._check_blank_lines(lines, tree, rel_path) # 检查文档字符串 self._check_docstrings(tree, rel_path) return self.issues def _check_naming(self, tree: ast.AST, file_path: str): 检查命名规范 for node in ast.walk(tree): if isinstance(node, ast.ClassDef): if not self.CLASS_NAME_RE.match(node.name): self.issues.append(StyleIssue( file_path, node.lineno, node.col_offset, E101, f类名 {node.name} 不符合 CapWords 规范, error )) elif isinstance(node, ast.FunctionDef): if node.name.startswith(__) and node.name.endswith(__): continue # 跳过魔术方法 if not self.FUNCTION_NAME_RE.match(node.name): self.issues.append(StyleIssue( file_path, node.lineno, node.col_offset, E102, f函数名 {node.name} 不符合 snake_case 规范, error )) def _check_line_length(self, lines: List[str], file_path: str): 检查行长度 for i, line in enumerate(lines, 1): # 排除末尾换行符 content line.rstrip(\n\r) if len(content) 79: self.issues.append(StyleIssue( file_path, i, 80, W201, f行长度 {len(content)} 超过 79 字符, warning )) def _check_indentation(self, lines: List[str], file_path: str): 检查缩进 for i, line in enumerate(lines, 1): if not line.strip(): continue # 检查是否有 Tab 字符 if \t in line: self.issues.append(StyleIssue( file_path, i, line.index(\t) 1, E301, 使用了 Tab 字符应使用 4 个空格缩进, error )) def _check_blank_lines(self, lines: List[str], tree: ast.AST, file_path: str): 检查空行规范 # 检查文件末尾是否有且仅有一个换行符 if lines and lines[-1].strip() and len(lines) 1: if lines[-2].strip() ! : self.issues.append(StyleIssue( file_path, len(lines), 0, W401, 文件末尾不应有多余空行, warning )) def _check_docstrings(self, tree: ast.AST, file_path: str): 检查文档字符串 for node in ast.walk(tree): if isinstance(node, (ast.FunctionDef, ast.ClassDef)): if node.name.startswith(_) and not node.name.startswith(__): continue # 跳过私有函数 if not (node.body and isinstance(node.body[0], ast.Expr) and isinstance(node.body[0].value, (ast.Str, ast.Constant))): self.issues.append(StyleIssue( file_path, node.lineno, node.col_offset, I501, f缺少文档字符串: {node.name}, info )) def format_report(issues: List[StyleIssue], format_type: str text) - str: 格式化输出报告 if format_type json: import json return json.dumps([i.to_dict() for i in issues], indent2, ensure_asciiFalse) # 默认文本格式 severity_counts {error: 0, warning: 0, info: 0, hint: 0} for issue in issues: severity_counts[issue.severity] severity_counts.get(issue.severity, 0) 1 lines [] lines.append( * 60) lines.append(Python 代码风格检查报告) lines.append( * 60) lines.append(f总问题数: {len(issues)}) lines.append(f Error: {severity_counts[error]}) lines.append(f Warning: {severity_counts[warning]}) lines.append(f Info: {severity_counts[info]}) lines.append(- * 60) for issue in issues: lines.append(str(issue)) lines.append( * 60) return \n.join(lines) def main(): if len(sys.argv) 2: print(用法: python check_style.py target [--recursive] [--format json|html]) sys.exit(1) target sys.argv[1] recursive --recursive in sys.argv format_type text if --format in sys.argv: idx sys.argv.index(--format) if idx 1 len(sys.argv): format_type sys.argv[idx 1] checker Pep8Checker() all_issues [] target_path Path(target) if target_path.is_file(): all_issues checker.check_file(str(target_path)) elif target_path.is_dir(): pattern **/*.py if recursive else *.py for py_file in target_path.glob(pattern): all_issues.extend(checker.check_file(str(py_file))) else: print(f错误: 目标 {target} 不存在) sys.exit(1) print(format_report(all_issues, format_type)) if __name__ __main__: main()3. references/pep8_rules.md参考文档# PEP 8 规则速查表 ## 代码布局 | 规则 | 代码 | 说明 | |------|------|------| | 缩进使用 4 空格 | E111 | 每个缩进级别使用 4 个空格 | | 禁止 Tab | E101 | 禁止使用 Tab 字符 | | 续行缩进 | E121-E129 | 续行应比首行多缩进 | | 行长度 ≤ 79 | E501 | 代码行不超过 79 字符 | | 二元运算符换行 | W504 | 二元运算符应在行首 | ## 命名规范 | 类型 | 规范 | 示例 | |------|------|------| | 模块 | short_lowercase | my_module.py | | 类 | CapWords | MyClass | | 函数 | snake_case | my_function() | | 变量 | snake_case | my_var 1 | | 常量 | UPPER_CASE | MAX_SIZE 100 | | 私有 | _leading_underscore | _private_method() | ## 空白与空行 | 规则 | 代码 | 说明 | |------|------|------| | 顶层间隔 2 空行 | E302 | 顶层函数/类定义前空 2 行 | | 方法间隔 1 空行 | E301 | 类内方法定义前空 1 行 | | 逗号后空格 | E231 | 逗号后紧跟空格 | | 括号内无空格 | E201/E202 | 括号紧邻内容无空格 |4. 在 DeepAgents 中使用此 Skillimportosfromdeepagentsimportcreate_deep_agentfromdeepagents.backends.filesystemimportFilesystemBackendfromdeepagents.middlewareimportSkillsMiddlewarefromlangchain_openaiimportChatOpenAI# 准备 BackendbackendFilesystemBackend(root_dir/tmp/deepagents_skills_demo)# 配置 SkillsMiddleware加载自定义 Skillskills_middlewareSkillsMiddleware(backendbackend,sources[# 本地 Skill 目录./skills/,],)# 构建 Agentagentcreate_deep_agent(modelChatOpenAI(modelgpt-4o,temperature0),backendbackend,skills[./skills/,],middleware[skills_middleware],system_prompt你是一位代码审查专家。,subagents[],)# 使用 Skill 检查代码resultagent.invoke({messages:[{role:user,content:请使用 python-style-checker Skill 检查以下代码的风格 pythonclassmyClass:def__init__(self,value):self.valuevaluedefgetValue(self):returnself.value“”}]})print(result[“messages”][-1].content)#### 运行结果 text 正在使用 python-style-checker Skill 检查代码... Python 代码风格检查报告 总问题数: 5 Error: 3 Warning: 1 Info: 1 ------------------------------------------------------------ test_code.py:1:7: E101 类名 myClass 不符合 CapWords 规范 test_code.py:2:23: E231 逗号后缺少空格 test_code.py:2:28: E231 运算符两侧缺少空格 test_code.py:3:18: E231 运算符两侧缺少空格 test_code.py:4:0: W201 行长度 80 超过 79 字符 test_code.py:1:1: I501 缺少文档字符串: myClass 建议修改为 python class MyClass: 示例类演示 PEP 8 命名规范。 def __init__(self, value): self.value value def get_value(self): 获取存储的值。 return self.value#### 逐段解析 **第 1 段 -- Skill 的 YAML Frontmatter** yaml --- name: python-style-checker description: 检查 Python 代码是否符合 PEP 8 风格规范。 支持命名规范、缩进、行长度、导入顺序、注释格式、 类型注解等检查维度。输出结构化报告。 ---nameSkill 的唯一标识符用于 Agent 引用。description元数据描述在 Level 1启动时被加载到系统提示词中。Agent 通过匹配 description 与用户请求来决定是否加载该 Skill。第 2 段 – 渐进式加载Progressive DisclosureAgent 决定需要此 SkillAgent 需要执行或参考Level 3: 资源文件scripts/check_style.pyreferences/pep8_rules.mdassets/report_template.htmlLevel 2: SKILL.md 完整内容检查维度使用方式严重级别参考文档Level 1: 元数据name: python-style-checkerdescription: 检查 Python 代码...Level 1启动时SkillsMiddleware 加载所有 Skill 的 frontmattername description注入到系统提示词中。Level 2当 Agent 判断某个 Skill 与当前任务相关时读取完整的SKILL.md文件。Level 3当 Agent 需要执行脚本、查看参考文档或使用模板时读取scripts/、references/、assets/中的文件。第 3 段 – Skill 目录结构规范skill-name/ ├── SKILL.md # 必需Skill 定义文件 ├── scripts/ # 可选可执行脚本 ├── references/ # 可选参考文档 └── assets/ # 可选模板、配置等资源第 4 段 – 从远程 URL 加载 Skillskills_middlewareSkillsMiddleware(backendbackend,sources[# 本地路径./skills/,# 远程 URL概念示例# 实际使用时先将远程 Skill 下载到 backend 中],)对于远程 Skill 加载DeepAgents 支持通过SkillSource指定 URL。实际实现中SkillsMiddleware 会在before_agent阶段从远程 URL 下载 Skill 目录到 backend# 远程 Skill 加载概念fromdeepagents.middlewareimportSkillSource sources[SkillSource(pathhttps://github.com/example/skills/releases/download/v1.0/python-style-checker.zip,labelCommunity Python Checker,),]第 5 段 – 命名空间隔离与权限控制Skills 的命名空间通过 source 标签实现隔离。当两个 Skill 同名时后加载的 source 中的 Skill 会覆盖先加载的sources[# 用户级 Skill优先级低/home/user/.claude/skills,# 项目级 Skill覆盖用户级同名 Skill/project/.claude/skills,]权限控制方面Skills 共享 Agent 的文件系统权限。如果你希望限制 Skill 只能读取特定目录可以通过 permissions 配置agentcreate_deep_agent(permissions[FilesystemPermission(operations[read],paths[./skills/**],modeallow,),FilesystemPermission(operations[write],paths[./skills/**],modedeny,# Skill 文件不可写),],)API 列表速查类/参数类型说明SkillsMiddlewareclassSkill 加载和注入中间件SkillsMiddleware(backend)BACKEND_TYPESBackend 实例SkillsMiddleware(sources)Sequence[SkillSource]Skill 源列表支持路径字符串或(path, label)元组SkillsMiddleware(system_prompt)str | None自定义 Skill 系统提示词模板create_deep_agent(skills)list[str]Skill 目录路径列表SKILL.mdfileSkill 定义文件包含 YAML frontmatter 和 Markdown 指令SkillSourcetypestr | tuple[str, str]路径或(路径, 标签)元组SKILLS_SYSTEM_PROMPTstr默认的 Skill 系统提示词模板常见错误与避坑1. SKILL.md 缺少 YAML frontmatter!-- 错误没有 frontmatterSkillsMiddleware 无法读取元数据 -- # My Skill This is my skill. !-- 正确必须有 YAML frontmatter -- --- name: my-skill description: 这是我的自定义 Skill --- # My Skill This is my skill.2. description 过于模糊# 错误description 太模糊Agent 无法判断何时使用description:一个有用的工具# 正确description 应具体描述 Skill 的用途和适用场景description:检查 Python 代码是否符合 PEP 8 风格规范。 支持命名规范、缩进、行长度、导入顺序等维度。 当用户提到代码风格、PEP 8、代码检查时使用。3. Skill 目录结构不完整# 错误只有 SKILL.md缺少脚本和资源 skills/my-skill/ └── SKILL.md # 正确完整的目录结构 skills/my-skill/ ├── SKILL.md ├── scripts/ │ └── run.py ├── references/ │ └── guide.md └── assets/ └── template.html4. Skill 数量过多# 错误注册了 50 个 Skill元数据占用大量 token# Solution: 控制 Skill 数量每个 Skill 应覆盖一个明确的领域skills[./skills/,# 这个目录下不要放超过 10-15 个 Skill]5. source 标签冲突# 错误两个同名 Skill 来自不同 source可能导致非预期的覆盖sources[/path/a/skills,# 包含 python-style-checker/path/b/skills,# 也包含 python-style-checker会覆盖前者]# 正确使用 label 区分同名的 Skill 来源sources[(/path/a/skills,Team A),(/path/b/skills,Team B),]最佳实践Specific Descriptionsdescription 应具体、可操作包含关键词帮助 Agent 匹配。好的 description 是 Skill 的门面。Keep Instructions FocusedSKILL.md 的指令应聚焦于一个明确的任务领域。如果一个 Skill 覆盖了太多不相关的功能应考虑拆分为多个 Skill。Manage Skill Count控制 Skill 数量在 10-15 个以内。过多的 Skill 会占用系统提示词 token 并增加 Agent 的选择负担。Use References for Detail将详细的规则、API 文档、配置示例放在references/中保持 SKILL.md 简洁。Test Skills in Isolation在集成到 Agent 之前单独测试 Skill 的脚本和指令是否按预期工作。本章小结Skills 系统通过渐进式加载Progressive Disclosure实现高效的 Agent 能力扩展。Level 1 元数据始终加载Level 2 完整指令按需加载Level 3 资源文件按需访问。每个 Skill 由SKILL.md含 YAML frontmatterscripts/references/assets/组成。良好的 description 和聚焦的指令是 Skill 有效性的关键。