大数跨境

LangChain+Skills 中间件实战(全网独一份),不再依赖任何客户端

LangChain+Skills 中间件实战(全网独一份),不再依赖任何客户端 慧测
2026-01-15
1

·  ·  ·

图片

但问智能出品

图片
  • 免费技术交流

    huice666/danwen668

  • 原创课程大纲

    https://huicewang.com/testing

  • B 站系列视频

    https://space.bilibili.com/1037858964

目前版本是1.4,2.0正式开源。记得收藏哦

📋 目录

1. 概述

2. 核心概念

3. 架构设计

4. 核心组件详解

5. 工作流程

6. 使用案例

7. 最佳实践

8. 常见问题

概述

什么是 Skills Middleware?

我们在代码 skills.py 实现了 Anthropic 的 Agent Skills 模式,这是一个用于加载和管理 AI Agent 技能的中间件系统。它采用 渐进式披露(Progressive Disclosure)设计模式,让 AI Agent 能够:

 📚 从多个来源加载技能

 🔍 按需查看技能详情

 🎯 智能匹配任务与技能

 🔄 动态更新技能库

核心特性

核心概念

1. Skill(技能)

技能是一个包含特定领域知识和工作流程的目录结构:

/skills/user/web-research/
   
   
   
├── SKILL.md          # 必需:技能定义文件
└── helper.py         # 可选:辅助脚本

SKILL.md 文件格式

---name: web-researchdescription: Structured approach to conducting thorough web researchlicense: MITcompatibility: Requires web-search toolmetadata:  author: John Doe  version: 1.0.0allowed-tools: web-search web-fetch---
# Web Research Skill
## When to Use- User asks you to research a topic- Need to gather information from multiple sources
## Workflow1. Identify key search terms2. Use web-search tool to find sources3. Analyze and synthesize information4. Present findings in structured format
## Examples...

2. Source(来源)

来源是技能目录的路径,用于组织和分层管理技能:

sources =[
"/skills/base/",# 基础技能(优先级最低)
"/skills/user/",# 用户技能
"/skills/project/",# 项目技能
"/skills/team/",# 团队技能(优先级最高)
]

优先级规则:后加载的来源会覆盖先加载的同名技能(Last One Wins)

3. Backend(后端)

后端是存储技能文件的抽象层,支持多种实现:

4. Progressive Disclosure(渐进式披露)

这是一种 UX 设计模式,应用在 AI Agent 上:

传统方式:一次性加载所有技能的完整内容 → Token 浪费

渐进式披露

1. 首先展示技能列表(名称 + 描述)

2. Agent 判断是否需要某个技能

3. 按需读取完整的 SKILL.md 文件

示例对比

❌ 传统方式(浪费 ~5000 tokens)
   
   
   
System Prompt:
  - web-research 技能完整内容(2000 tokens)
  - data-analysis 技能完整内容(1500 tokens)
  - code-review 技能完整内容(1500 tokens)

✅ 渐进式披露(仅 ~200 tokens)
System Prompt:
  - web-research: Structured approach to web research
    -> Read /skills/user/web-research/SKILL.md for details
  - data-analysis: Analyze datasets and generate insights
    -> Read /skills/user/data-analysis/SKILL.md for details

架构设计

数据流

1. Agent 启动
   
   
   
   ↓
2. before_agent() 被调用
   ↓
3. 遍历所有 sources
   ↓
4. Backend.ls_info() 列出技能目录
   ↓
5. Backend.download_files() 批量下载 SKILL.md
   ↓
6. _parse_skill_metadata() 解析 YAML frontmatter
   ↓
7. 存入 state.skills_metadata
   ↓
8. wrap_model_call() 注入到 system prompt
   ↓
9. Agent 看到技能列表,按需读取详情


核心组件详解

1. SkillMetadata(技能元数据)

class SkillMetadata(TypedDict):    name: str                    # 技能标识符    description: str             # 技能描述    path: str                    # SKILL.md 文件路径    license: str | None          # 许可证    compatibility: str | None    # 兼容性要求    metadata: dict[strstr]     # 自定义元数据    allowed_tools: list[str]     # 预批准的工具列表

字段详解

名称验证规则

# ✅ 有效名称"web-research""data-analysis-v2""code-review"
# ❌ 无效名称"Web-Research"      # 不能有大写"web_research"      # 不能用下划线"web--research"     # 不能有连续连字符"-web-research"     # 不能以连字符开头"web-research-"     # 不能以连字符结尾

2. SkillsMiddleware(技能中间件)

这是核心类,实现了 AgentMiddleware 接口。

初始化参数

def __init__(    self,    *,    backend: BACKEND_TYPES,      # 后端实例或工厂函数    sources: list[str]           # 技能来源路径列表) -> None:

backend 参数详解

# 方式 1: 直接传入后端实例(用于 FilesystemBackend)from just_ask.backends.filesystem import FilesystemBackend
backend = FilesystemBackend(root_dir="/path/to/skills")middleware = SkillsMiddleware(backend=backend, sources=[...])
# 方式 2: 使用工厂函数(用于 StateBackend)from just_ask.backends.state import StateBackend
middleware = SkillsMiddleware(    backend=lambda rt: StateBackend(rt),  # rt 是 ToolRuntime    sources=[...])

为什么 StateBackend 需要工厂函数?

 StateBackend 需要访问运行时上下文(Runtime)

 工厂函数延迟实例化,在运行时才创建 Backend

核心方法

before_agent() - 加载技能元数据
def before_agent(    self,    state: SkillsState,    runtime: Runtime,    config: RunnableConfig) -> SkillsStateUpdate | None:

执行时机:每次 Agent 交互前

作用

1. 检查 state.skills_metadata 是否已存在

2. 如果不存在,从所有 sources 加载技能

3. 返回 SkillsStateUpdate 更新状态

优化点

 如果 skills_metadata 已存在,直接返回 None(避免重复加载)

 使用字典去重,后加载的技能覆盖先加载的同名技能

wrap_model_call() - 注入技能到提示词
def wrap_model_call(    self,    request: ModelRequest,    handler: Callable[[ModelRequest], ModelResponse]) -> ModelResponse:

执行时机:每次调用模型前

作用

1. 从 request.state 获取 skills_metadata

2. 格式化技能列表

3. 注入到 system_prompt

4. 调用原始 handler

注入格式示例

## Skills System
   
   
   

You have access to a skills library...

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

**Available Skills:**

- **web-research**: Structured approach to conducting thorough web research
  -> Read `/skills/user/web-research/SKILL.md` for full instructions
- **data-analysis**: Analyze datasets and generate insights
  -> Read `/skills/user/data-analysis/SKILL.md` for full instructions

3. 辅助函数

_validate_skill_name() - 验证技能名称

def _validate_skill_name(name: str, directory_name: str) -> tuple[boolstr]:

验证规则

1. 不能为空

2. 最大 64 字符

3. 只能包含小写字母、数字和单个连字符

4. 不能以连字符开头或结尾

5. 必须与目录名匹配

返回值(is_valid, error_message)

_parse_skill_metadata() - 解析技能元数据

def _parse_skill_metadata(    content: str,    skill_path: str,    directory_name: str) -> SkillMetadata | None:

解析流程

1. 检查文件大小(最大 10MB,防止 DoS 攻击)

2. 使用正则提取 YAML frontmatter(--- 分隔符之间)

3. 使用 yaml.safe_load() 解析 YAML

4. 验证必需字段(name, description)

5. 验证名称格式(警告但不阻止加载)

6. 截断过长的描述(最大 1024 字符)

7. 解析 allowed-tools(空格分隔)

8. 返回 SkillMetadata 对象

错误处理

 文件过大 → 跳过并记录警告

 无 frontmatter → 跳过

 YAML 解析失败 → 跳过

 缺少必需字段 → 跳过

 名称不符合规范 → 警告但继续加载(向后兼容)

_list_skills() - 列出技能(同步)

def _list_skills(backend: BackendProtocol, source_path: str) -> list[SkillMetadata]:

执行流程

1. 调用 backend.ls_info(source_path) 列出所有子目录

2. 过滤出目录(is_dir=True

3. 为每个目录构造 SKILL.md 路径

4. 批量下载所有 SKILL.md 文件(backend.download_files()

5. 解析每个文件的元数据

6. 返回成功解析的技能列表

性能优化

 使用批量下载而非逐个下载

 跳过下载失败的文件(可能不是技能目录)

_alist_skills() - 列出技能(异步)

与 _list_skills() 相同,但使用异步 API:

 backend.als_info()

 backend.adownload_files()

工作流程

完整执行流程

文本流程图

┌─────────────────────────────────────────────────────────┐
   
   
   
│ 1. Agent 初始化                                          │
│    agent = create_agent(                                │
│        model=model,                                     │
│        middleware=[skills_middleware]                   │
│    )                                                    │
└─────────────────────────────────────────────────────────┘
                          ↓
┌─────────────────────────────────────────────────────────┐
│ 2. 用户发起请求                                          │
│    agent.invoke({"messages": [...]}, config)            │
└─────────────────────────────────────────────────────────┘
                          ↓
┌─────────────────────────────────────────────────────────┐
│ 3. before_agent() 钩子触发                               │
│    - 检查 state.skills_metadata 是否存在                 │
│    - 如果不存在,加载技能:                               │
│      ├── 遍历 sources: ["/skills/base/", ...]          │
│      ├── 调用 _list_skills(backend, source)             │
│      │   ├── backend.ls_info() 列出目录                 │
│      │   ├── backend.download_files() 批量下载          │
│      │   └── _parse_skill_metadata() 解析               │
│      └── 合并所有技能(后加载覆盖先加载)                 │
│    - 返回 SkillsStateUpdate                             │
└─────────────────────────────────────────────────────────┘
                          ↓
┌─────────────────────────────────────────────────────────┐
│ 4. 状态更新                                              │
│    state.skills_metadata = [                            │
│        {"name": "web-research", ...},                   │
│        {"name": "data-analysis", ...}                   │
│    ]                                                    │
└─────────────────────────────────────────────────────────┘
                          ↓
┌─────────────────────────────────────────────────────────┐
│ 5. wrap_model_call() 钩子触发                            │
│    - 从 request.state 获取 skills_metadata              │
│    - 格式化技能列表:                                     │
│      "- **web-research**: Description                   │
│         -> Read /path/to/SKILL.md for details"          │
│    - 注入到 system_prompt                               │
│    - 调用原始 handler(modified_request)                 │
└─────────────────────────────────────────────────────────┘
                          ↓
┌─────────────────────────────────────────────────────────┐
│ 6. 模型看到技能列表                                       │
│    System Prompt:                                       │
│    """                                                  │
│    ## Skills System                                     │
│    Available Skills:                                    │
│    - web-research: ...                                  │
│      -> Read /skills/user/web-research/SKILL.md         │
│    """                                                  │
└─────────────────────────────────────────────────────────┘
                          ↓
┌─────────────────────────────────────────────────────────┐
│ 7. Agent 判断是否需要技能                                 │
│    用户: "帮我研究量子计算的最新进展"                       │
│    Agent 思考: 这需要 web-research 技能                  │
└─────────────────────────────────────────────────────────┘
                          ↓
┌─────────────────────────────────────────────────────────┐
│ 8. Agent 读取技能详情                                     │
│    使用 view 工具读取:                                    │
│    /skills/user/web-research/SKILL.md                   │
│                                                         │
│    获得完整的工作流程和最佳实践                            │
└─────────────────────────────────────────────────────────┘
                          ↓
┌─────────────────────────────────────────────────────────┐
│ 9. Agent 执行技能                                        │
│    按照 SKILL.md 中的步骤:                              │
│    1. 识别关键搜索词                                      │
│    2. 使用 web-search 工具                               │
│    3. 分析和综合信息                                      │
│    4. 以结构化格式呈现                                    │
└─────────────────────────────────────────────────────────┘

技能覆盖机制

当多个 sources 包含同名技能时,后加载的覆盖先加载的

sources = [    "/skills/base/",      # 包含 web-research v1.0    "/skills/user/",      # 包含 web-research v2.0(用户自定义)]
# 加载过程:all_skills = {}# 第一轮:加载 /skills/base/all_skills["web-research"] = SkillMetadata(name="web-research", version="1.0", ...)
# 第二轮:加载 /skills/user/all_skills["web-research"] = SkillMetadata(name="web-research", version="2.0", ...)# ↑ 覆盖了 v1.0
# 最终结果:使用 v2.0

应用场景

 基础技能库 + 用户自定义技能

 通用技能 + 项目特定技能

 默认配置 + 团队定制

使用案例

案例 1:基础使用 - 文件系统后端

from langchain.agents import create_agentfrom langchain.chat_models import init_chat_modelfrom just_ask.backends.filesystem import FilesystemBackendfrom just_ask.middleware.skills import SkillsMiddleware
# 1. 初始化模型model = init_chat_model("deepseek:deepseek-chat")
# 2. 创建文件系统后端backend = FilesystemBackend(root_dir="/path/to/skills")
# 3. 创建技能中间件skills_middleware = SkillsMiddleware(    backend=backend,    sources=[        "/skills/base/",      # 基础技能        "/skills/user/",      # 用户技能    ])
# 4. 创建 Agentagent = create_agent(    model=model,    tools=[],    middleware=[skills_middleware])
# 5. 使用 Agentresponse = agent.invoke({    "messages": [{"role""user""content""帮我研究 AI Agent 的最新进展"}]})
print(response["messages"][-1].content)

目录结构

/path/to/skills/
   
   
   
├── skills/
│   ├── base/
│   │   ├── web-research/
│   │   │   ├── SKILL.md
│   │   │   └── search_helper.py
│   │   └── data-analysis/
│   │       └── SKILL.md
│   └── user/
│       └── custom-research/
│           └── SKILL.md

案例 2:内存后端 - 临时技能

from langchain.agents import create_agentfrom langchain.chat_models import init_chat_modelfrom just_ask.backends.state import StateBackendfrom just_ask.middleware.skills import SkillsMiddleware
model = init_chat_model("deepseek:deepseek-chat")
# 使用工厂函数创建 StateBackendskills_middleware = SkillsMiddleware(    backend=lambda rt: StateBackend(rt),  # 延迟实例化    sources=["/skills/session/"])
agent = create_agent(    model=model,    tools=[],    middleware=[skills_middleware])
# 在运行时动态添加技能# (需要通过 StateBackend 的 API 添加文件)

适用场景

 会话级临时技能

 测试环境

 不需要持久化的场景

案例 3:多层技能系统

from langchain.agents import create_agentfrom langchain.chat_models import init_chat_modelfrom just_ask.backends.filesystem import FilesystemBackendfrom just_ask.middleware.skills import SkillsMiddleware
model = init_chat_model("deepseek:deepseek-chat")backend = FilesystemBackend(root_dir="/company/skills")
# 四层技能系统skills_middleware = SkillsMiddleware(    backend=backend,    sources=[        "/skills/global/",      # 全局基础技能(优先级 1)        "/skills/department/",  # 部门技能(优先级 2)        "/skills/team/",        # 团队技能(优先级 3)        "/skills/personal/",    # 个人技能(优先级 4,最高)    ])
agent = create_agent(    model=model,    tools=[],    middleware=[skills_middleware])

优势

 全局技能可被所有人使用

 部门技能针对特定业务

 团队技能适应团队工作流

 个人技能满足个性化需求

 高优先级技能可覆盖低优先级同名技能

案例 4:带工具的技能

创建一个包含辅助脚本的技能:

目录结构

/skills/user/code-review/
   
   
   
├── SKILL.md
├── check_style.py
└── analyze_complexity.py

SKILL.md

---name: code-reviewdescription: Comprehensive code review workflowallowed-tools: view launch-processmetadata:  author: DevTeam  version: 2.0.0---
# Code Review Skill
## When to Use- User asks to review code- Need to check code quality
## Workflow
1. **Read the code**   ```   Use view tool to read the target file   ```
2. **Run style checker**   ```bash   python /skills/user/code-review/check_style.py <file_path>   ```
3. **Analyze complexity**   ```bash   python /skills/user/code-review/analyze_complexity.py <file_path>   ```
4. **Provide feedback**   - List issues found   - Suggest improvements   - Highlight good practices
## Example
User: "Review my Python script at src/main.py"
Agent:1. Read src/main.py2. Run style checker: `python /skills/.../check_style.py src/main.py`3. Run complexity analyzer4. Provide comprehensive feedback

使用示例

from langchain.agents import create_agentfrom langchain.chat_models import init_chat_modelfrom just_ask.backends.filesystem import FilesystemBackendfrom just_ask.middleware.skills import SkillsMiddleware
model = init_chat_model("deepseek:deepseek-chat")backend = FilesystemBackend(root_dir="/path/to/skills")
skills_middleware = SkillsMiddleware(    backend=backend,    sources=["/skills/user/"])
agent = create_agent(    model=model,    tools=[],  # Agent 会使用内置的 view 和 launch-process 工具    middleware=[skills_middleware])
# Agent 会自动发现 code-review 技能并使用辅助脚本response = agent.invoke({    "messages": [{"role""user""content""Review my code at src/main.py"}]})

案例 5:动态技能更新

from langchain.agents import create_agentfrom langchain.chat_models import init_chat_modelfrom just_ask.backends.filesystem import FilesystemBackendfrom just_ask.middleware.skills import SkillsMiddlewareimport os
model = init_chat_model("deepseek:deepseek-chat")backend = FilesystemBackend(root_dir="/path/to/skills")
skills_middleware = SkillsMiddleware(    backend=backend,    sources=["/skills/dynamic/"])
agent = create_agent(    model=model,    tools=[],    middleware=[skills_middleware])
# 第一次调用 - 加载现有技能response1 = agent.invoke({    "messages": [{"role""user""content""What skills do you have?"}]})
# 动态添加新技能os.makedirs("/path/to/skills/skills/dynamic/new-skill", exist_ok=True)with open("/path/to/skills/skills/dynamic/new-skill/SKILL.md""w"as f:    f.write("""---name: new-skilldescription: A newly added skill---
# New SkillThis skill was added dynamically!""")
# 第二次调用 - 需要新的会话才能看到新技能# (因为 before_agent 会检查 skills_metadata 是否已存在)# 解决方案:使用新的 thread_id 或清空状态

注意:技能在 before_agent() 中加载,如果 state.skills_metadata 已存在,不会重新加载。要刷新技能列表,需要:

1. 使用新的会话(新 thread_id)

2. 清空状态

3. 重启 Agent

案例 6:自定义系统提示模板

from just_ask.middleware.skills import SkillsMiddleware
# 创建自定义中间件类class CustomSkillsMiddleware(SkillsMiddleware):    def __init__(self, **kwargs):        super().__init__(**kwargs)        # 自定义系统提示模板        self.system_prompt_template = """## 🎯 专业技能库
你拥有以下专业技能:
{skills_list}
**使用指南**:1. 识别任务类型2. 选择合适的技能3. 阅读技能文档(路径已在上方列出)4. 严格按照技能工作流执行
**技能来源**:{skills_locations}"""
# 使用自定义中间件skills_middleware = CustomSkillsMiddleware(    backend=backend,    sources=["/skills/user/"])

最佳实践

1. 技能设计原则

✅ 好的技能设计

---name: api-integrationdescription: Step-by-step guide for integrating third-party APIs---
# API Integration Skill
## When to Use- Integrating a new API- Troubleshooting API issues
## Prerequisites- API documentation URL- Authentication credentials
## Workflow
### Step 1: Read API DocumentationUse web-fetch to retrieve API docs
### Step 2: Test Authentication```pythonimport requestsresponse = requests.get(api_url, headers={"Authorization": f"Bearer {token}"})```
### Step 3: Implement Core Functionality...
## Common Pitfalls- Forgetting to handle rate limits- Not validating responses
## Examples...

优点

●清晰的使用场景

●明确的前置条件

●分步骤的工作流

●包含常见的陷阱

❌ 不好的技能设计

---name: do-stuffdescription: Does various things---
# Do Stuff
Just do whatever the user asks.

问题

 描述模糊

 没有明确的工作流

 缺少示例

 不符合命名规范(太宽泛)

2. 技能组织策略

按领域组织

/skills/
   
   
   
├── base/
│   ├── web-research/
│   ├── data-analysis/
│   └── code-review/
├── domain/
│   ├── finance/
│   │   ├── stock-analysis/
│   │   └── risk-assessment/
│   └── healthcare/
│       ├── medical-research/
│       └── patient-data-analysis/
└── project/
    └── project-specific-skill/

按团队组织

/skills/
   
   
   
├── global/          # 全公司共享
├── engineering/     # 工程团队
├── marketing/       # 市场团队
└── sales/          # 销售团队

3. 性能优化

减少技能数量

# ❌ 过多技能(100+ 个)sources = ["/skills/all/"]  # 包含 100+ 个技能
# ✅ 精选技能(10-20 个)sources = [    "/skills/core/",      # 10 个核心技能    "/skills/project/"    # 5 个项目特定技能]

原因

 技能列表会注入到每次模型调用的 system prompt

 过多技能会消耗大量 tokens

 Agent 难以选择合适的技能

使用批量下载

_list_skills() 已经使用了批量下载:

# ✅ 批量下载(高效)paths = [skill1_path, skill2_path, skill3_path]responses = backend.download_files(paths)  # 一次调用
# ❌ 逐个下载(低效)for path in paths:    response = backend.download_file(path)  # 多次调用

4. 安全考虑

文件大小限制

MAX_SKILL_FILE_SIZE = 10 * 1024 * 1024  # 10MB
if len(content) > MAX_SKILL_FILE_SIZE:    logger.warning("Skipping %s: content too large", skill_path)    return None

防止:DoS 攻击(恶意上传超大文件)

路径安全

使用 PurePosixPath 确保路径安全:

from pathlib import PurePosixPath
# ✅ 安全的路径操作skill_dir = PurePosixPath(skill_dir_path)skill_md_path = str(skill_dir / "SKILL.md")
# ❌ 不安全的字符串拼接skill_md_path = skill_dir_path + "/SKILL.md"  # 可能有路径遍历风险

YAML 安全解析

# ✅ 使用 safe_load(安全)frontmatter_data = yaml.safe_load(frontmatter_str)
# ❌ 使用 load(不安全,可执行任意代码)

5. 错误处理

优雅降级

# 技能加载失败不应导致 Agent 崩溃try:    skill_metadata = _parse_skill_metadata(content, skill_path, directory_name)    if skill_metadata:        skills.append(skill_metadata)except Exception as e:    logger.warning("Failed to parse skill %s: %s", skill_path, e)    # 继续处理其他技能

详细日志

logger.warning("Skipping %s: no valid YAML frontmatter found", skill_path)logger.warning("Invalid YAML in %s%s", skill_path, e)logger.warning("Skipping %s: missing required 'name' or 'description'", skill_path)

好处

 帮助调试技能配置问题

 不影响其他技能的加载

常见问题

Q1: 技能没有被加载?

可能原因

1. 目录结构不正确

    /skills/user/SKILL.md          # 缺少技能目录    /skills/user/my-skill/SKILL.md  # 正确

2. SKILL.md 缺少 frontmatter

   ❌ # My Skill      This is my skill.
   ✅ ---      name: my-skill      description: My skill description      ---      # My Skill

3. 名称不匹配

   目录名: /skills/user/web-research/   YAML name: web_research  ❌ 不匹配
   应该是:   YAML name: web-research  ✅

4. Backend 路径配置错误

# 如果使用 FilesystemBackendbackend = FilesystemBackend(root_dir="/path/to/skills")
# sources 应该相对于 root_dirsources = ["/skills/user/"]  # 实际路径: /path/to/skills/skills/user/

调试方法

import logginglogging.basicConfig(level=logging.WARNING) # 运行 Agent,查看日志输出agent.invoke({"messages": [...]})

Q2: 技能列表为空?

检查清单

1. Backend 是否正确初始化

# 检查 backend 是否能列出文件items = backend.ls_info("/skills/user/")print(items)  # 应该看到技能目录

2. Sources 路径是否正确

# 确保路径存在sources = ["/skills/user/"]  # 检查这个路径在 backend 中是否存在

3. 是否有权限访问文件

# 检查文件权限ls -la /path/to/skills/skills/user/

Q3: 技能被覆盖了?

这是预期行为!后加载的 source 会覆盖先加载的同名技能。

示例

sources = [    "/skills/base/",      # 包含 web-research v1.0    "/skills/user/",      # 包含 web-research v2.0]# 结果:使用 v2.0(user 覆盖 base)

解决方案

 重命名技能以避免冲突

 调整 sources 顺序

 使用不同的技能名称

Q4: 如何刷新技能列表?

问题:添加新技能后,Agent 看不到。

原因before_agent() 只在 skills_metadata 不存在时加载技能。

解决方案

1. 使用新的会话

# 每次使用新的 thread_idconfig1 = {"configurable": {"thread_id""session_1"}}config2 = {"configurable": {"thread_id""session_2"}}  # 新会话会重新加载

2. 清空状态(如果使用 StateGraph)

# 手动清空 skills_metadatastate["skills_metadata"] = None

3. 重启 Agent

# 重新创建 Agent 实例agent = create_agent(...)

Q5: 如何限制 Agent 只使用特定技能?

方法 1:使用 allowed_tools(实验性)

--- name: restricted-skill description: A skill with tool restrictions allowed-tools: web-search view--- 

注意:这是实验性功能,可能不被所有 Agent 实现支持。

方法 2:在 system prompt 中明确指定

agent = create_agent(    model=model,    tools=[],    middleware=[skills_middleware],    system_prompt="""    You should ONLY use the following skills:    - web-research    - data-analysis
    Do NOT use any other skills.    """)

方法 3:过滤技能列表

class FilteredSkillsMiddleware(SkillsMiddleware):    def __init__(selfallowed_skills: list[str], **kwargs):        super().__init__(**kwargs)        self.allowed_skills = allowed_skills
    def before_agent(self, state, runtime, config):        result = super().before_agent(state, runtime, config)        if result:            # 过滤技能            result["skills_metadata"] = [                skill for skill in result["skills_metadata"]                if skill["name"in self.allowed_skills            ]        return result
# 使用middleware = FilteredSkillsMiddleware(    allowed_skills=["web-research""data-analysis"],    backend=backend,    sources=["/skills/user/"])

Q6: 技能文件太大怎么办?

问题:SKILL.md 文件超过 10MB 限制。

解决方案

1. 拆分技能

❌ /skills/user/mega-skill/SKILL.md (15MB)
   
   
   
 
✅ /skills/user/skill-part-1/SKILL.md (5MB)
   /skills/user/skill-part-2/SKILL.md (5MB)
   /skills/user/skill-part-3/SKILL.md (5MB)

2. 使用外部文件

   ---   name: my-skill   description: My skill with external docs   ---
   # My Skill
   For detailed documentation, see:   - [Part 1](./docs/part1.md)   - [Part 2](./docs/part2.md)
   Agent can read these files using the view tool.


3. 修改限制(不推荐)

# 在 skills.py 中修改
   
   
   
MAX_SKILL_FILE_SIZE = 50 * 1024 * 1024  # 50MB

Q7: 如何调试技能加载?

启用详细日志

import logging
# 设置日志级别logging.basicConfig(    level=logging.DEBUG,    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
# 只启用 skills 模块的日志logger = logging.getLogger('just_ask.middleware.skills')logger.setLevel(logging.DEBUG)

手动测试技能加载

from just_ask.backends.filesystem import FilesystemBackendfrom just_ask.middleware.skills import _list_skills
backend = FilesystemBackend(root_dir="/path/to/skills")skills = _list_skills(backend, "/skills/user/")
print(f"Loaded {len(skills)} skills:")for skill in skills:    print(f"  - {skill['name']}{skill['description']}")    print(f"    Path: {skill['path']}")

Q8: 如何在技能中使用环境变量?

在 SKILL.md 中引用

---name: api-callerdescription: Call external APIs---
# API Caller Skill
## Setup
Ensure the following environment variables are set:- `API_KEY`: Your API key- `API_URL`: API endpoint URL
## Usage
```pythonimport osapi_key = os.getenv("API_KEY")api_url = os.getenv("API_URL")

Q9: 技能可以调用其他技能吗?

可以! 

在 SKILL.md 中引用其他技能:

---name: advanced-researchdescription: Advanced research combining multiple skills---
# Advanced Research Skill
## Workflow
1. **Use web-research skill**   Read `/skills/user/web-research/SKILL.md` and follow its workflow   to gather initial information.
2. **Use data-analysis skill**   Read `/skills/user/data-analysis/SKILL.md` and analyze the   gathered data.
3. **Synthesize results**   Combine insights from both skills.

Agent 会

1. 读取 advanced-research 技能

2. 发现需要 web-research 技能

3. 读取 web-research 技能

4. 执行 web-research 工作流

5. 继续执行 advanced-research 的后续步骤

高级主题

1. 自定义 Backend 实现

如果需要从自定义存储加载技能(如数据库、S3),可以实现 BackendProtocol

from just_ask.backends.protocol import BackendProtocol, DownloadResponse
class DatabaseBackend(BackendProtocol):    def __init__(self, db_connection):        self.db = db_connection
    def ls_info(self, path: str) -> list[dict]:        """列出目录内容"""        # 从数据库查询        results = self.db.query(            "SELECT name, is_directory FROM files WHERE parent_path = ?",            (path,)        )        return [            {"path"f"{path}/{row['name']}""is_dir": row['is_directory']}            for row in results        ]
    def download_files(self, paths: list[str]) -> list[DownloadResponse]:        """批量下载文件"""        responses = []        for path in paths:            content = self.db.query(                "SELECT content FROM files WHERE path = ?",                (path,)            )            if content:                responses.append(DownloadResponse(                    content=content[0]['content'].encode('utf-8'),                    error=None                ))            else:                responses.append(DownloadResponse(                    content=None,                    error="File not found"                ))        return responses
    # 实现其他必需方法...

2. 动态技能生成

使用 LLM 动态生成技能:

from langchain.chat_models import init_chat_model
def generate_skill(task_description: str) -> str:    """使用 LLM 生成技能定义"""    model = init_chat_model("gpt-4")
    prompt = f"""    Generate a SKILL.md file for the following task:    {task_description}
    Follow the Agent Skills specification format.    """
    response = model.invoke(prompt)    return response.content
# 生成技能skill_content = generate_skill("Analyze stock market trends")
# 保存到文件系统with open("/skills/user/stock-analysis/SKILL.md""w"as f:    f.write(skill_content)

3. 技能推荐系统

根据用户任务推荐合适的技能:

from langchain.chat_models import init_chat_model
class SkillRecommender:    def __init__(self, skills_metadata: list[SkillMetadata]):        self.skills = skills_metadata        self.model = init_chat_model("gpt-4")
    def recommend(self, user_task: str) -> list[str]:        """推荐技能"""        skills_list = "\n".join([            f"- {s['name']}{s['description']}"            for s in self.skills        ])
        prompt = f"""        User task: {user_task}
        Available skills:        {skills_list}
        Which skills would be most helpful? Return skill names only.        """
        response = self.model.invoke(prompt)        # 解析响应,提取技能名称        return response.content.strip().split("\n")

总结

核心要点

1. 渐进式披露:先展示元数据,按需加载详情,节省 tokens

2. 多源分层:支持从多个来源加载技能,实现优先级覆盖

3. 后端抽象:不依赖文件系统,可移植到不同存储

4. 规范兼容:遵循 Agent Skills 规范,与社区标准一致

5. 安全可靠:文件大小限制、安全的 YAML 解析、优雅的错误处理

适用场景

 ✅ 需要复杂工作流的 AI Agent

 ✅ 多团队协作的技能管理

 ✅ 需要版本控制和分层管理的技能库

 ✅ 需要动态更新技能的系统

不适用场景

 ❌ 简单的单次对话(过度设计)

 ❌ 技能数量极少(<3 个)

 ❌ 不需要结构化工作流的场景

下一步

1. 创建第一个技能:从简单的 web-research 技能开始

2. 测试渐进式披露:观察 Agent 如何按需加载技能

3. 构建技能库:逐步积累常用技能

4. 优化工作流:根据实际使用情况调整技能内容

参考资源

 Agent Skills 规范

 LangChain 中间件文档

 LangGraph 文档

版本: 1.4.0

最后更新: 2026-01-13

作者: 但问智能团队

免费进群技术交流
图片
huice666
danwen668

【声明】内容源于网络
0
0
慧测
专注人工智能前沿技术落地企业实战应用
内容 404
粉丝 0
慧测 专注人工智能前沿技术落地企业实战应用
总阅读104
粉丝0
内容404