LangChain 实现 Skill 框架

Skill 基本概念

Skills 的概念由 Anthropic 提出,本质上是一种更高层次的模块化能力封装,用于扩展智能体的功能边界。每一个 Skill 都封装了指令、元数据以及可选的资源(如脚本、模板等),智能体在执行任务时,会根据上下文相关性自动选择并调用合适的 Skill。

Skill 🆚 Tools:

  • Skill:加载专业知识
  • Tools:执行动作,调用API

Skills的核心功能:

  1. 专业工作流:特定领域的多步骤操作流程
  2. 工具集成:使用特定文件格式或 API 的指导说明
  3. 领域专长:企业特有知识、数据架构、业务规则
  4. 资源包:处理复杂和重复任务所需的脚本、参考文档和相关资源

Skills的组成部分:

  • SKILL.md
    • 头部元数据 (YAML 格式):包含 name(名称)和 description(描述)字段。
    • 主体内容(Markdown 格式):关于如何使用该技能的说明和指引。只有在技能被触发后才会加载。
  • 脚本 (scripts/):可执行代码(Python/Bash 等),适用于需要确保可靠性或经常重复编写的任务。
  • 参考资料 (references/):文档和参考材料,按需加载到上下文中,用于指导 Claude 的工作流程和思考方式。
  • 资源文件 (assets/):在最终输出内容中可能需要使用到的文件

Skills的核心特征:

特征 说明
Prompt 驱动 Skill 的本质是一段专业领域的 prompt,而非代码逻辑
按需加载 Agent 根据用户问题判断需要哪个 Skill,再动态加载
自动发现 SkillMiddleware 自动把所有 Skill 的简短描述注入 system prompt
团队解耦 不同团队可以独立开发和维护各自的 Skill(只需新建 .md 文件)
数据注入 需要案件数据的 Skill 在加载时自动注入相关数据

通过 LangChain 中间件实现 Skill 渐进式加载框架

整体框架

  1. 扫描Skills目录下所有的Skill的name和description,封装到系统提示词;
  2. Agent读取所有Skill的name和description,选定skill执行load_skill工具,获取skill的详细内容。

实现方法

  1. 在Skills目录下创建一个新的Skill,目录名称为skill的名称,必须包含Skill.md,可以包含 scriptsreferences等目录。
text 复制代码
---
name: food-calorie
description: 精准计算各类食物卡路里,支持按食材、重量、烹饪方式拆分核算总热量
metadata:
  author: nobody
  version: 2.0.0
---
  1. 根据Skill文件格式确定数据类型,部分skill可能包含脚本文件:
python 复制代码
class Skill(TypedDict):
    """文件夹型技能:支持 scripts / references / assets"""

    name: str  # 技能名 = 文件夹名
    description: str  # 简短描述(来自 SKILL.md 第一行)
    content: str  # SKILL.md 完整内容
    path: Path  # 技能根目录
    scripts: list[Path]  # scripts/ 下所有文件
    references: list[Path]  # references/ 下所有文件
    assets: list[Path]  # assets/ 下所有文件
  1. 从skills目录中读取所有skill的基础信息:
python 复制代码
def load_skills_from_dir(skills_root_dir: Path) -> list[Skill]:
    skills = []
    for skill_dir in sorted(skills_root_dir.iterdir()):
        if not skill_dir.is_dir():
            continue

        skill_md = skill_dir / "SKILL.md"
        if not skill_md.exists():
            continue

        text = skill_md.read_text(encoding="utf-8")

        # ======================
        # 修复点:从 YAML 头读取 description
        # ======================
        lines = text.splitlines()
        description = "No description"

        # 解析 --- 包裹的 YAML 头部
        if len(lines) > 2 and lines[0].strip() == "---":
            try:
                # 找到下一个 ---
                end_idx = lines[1:].index("---") + 1
                header_lines = lines[1:end_idx]

                # 读取 description 字段
                for line in header_lines:
                    if line.strip().startswith("description:"):
                        description = line.split(":", 1)[1].strip()
                        break
            except ValueError:
                pass

        skills.append(
            Skill(
                name=skill_dir.name,
                description=description,
                content=text,
                path=skill_dir,
                scripts=(
                    list((skill_dir / "scripts").glob("*"))
                    if (skill_dir / "scripts").exists()
                    else []
                ),
                references=(
                    list((skill_dir / "references").rglob("*"))
                    if (skill_dir / "references").exists()
                    else []
                ),
                assets=(
                    list((skill_dir / "assets").glob("*"))
                    if (skill_dir / "assets").exists()
                    else []
                ),
            )
        )
    return skills
    
SKILLS = load_skills_from_dir(Path(__file__).parent)
  1. 定义工具,供Agent加载指定的skill
python 复制代码
@tool
def load_skill(skill_name: str) -> str:
    """Load the full content of a skill into the agent's context."""
    for skill in SKILLS:
        if skill["name"] == skill_name:
            return f"Loaded skill: {skill_name}\n\n{skill['content']}"

    available = ", ".join(s["name"] for s in SKILLS)
    return f"Skill '{skill_name}' not found. Available skills: {available}"
  1. 通过 AgentMiddleware 中间件实现 Skill 渐进式信息披露:
python 复制代码
class SkillMiddleware(AgentMiddleware):
    """自动把所有【文件夹技能】注入系统提示"""

    tools = [load_skill, list_skill_files, read_skill_file]

    def __init__(self):
        skills_list = []
        for skill in SKILLS:
            skills_list.append(f"- **{skill['name']}**: {skill['description']}")
        self.skills_prompt = "\n".join(skills_list)

    def wrap_model_call(
        self,
        request: ModelRequest,
        handler: Callable[[ModelRequest], ModelResponse],
    ) -> ModelResponse:
        skills_addendum = (
            f"\n\n## Available Skills\n\n{self.skills_prompt}\n\n"
            "Use load_skill to get full instructions.\n"
            "Use list_skill_files to list files in a skill.\n"
            "Use read_skill_file to read any file in a skill."
        )

        new_content = list(request.system_message.content_blocks) + [
            {"type": "text", "text": skills_addendum}
        ]
        new_system_message = SystemMessage(content=new_content)
        modified_request = request.override(system_message=new_system_message)
        return handler(modified_request)
  1. 测试:
python 复制代码
def test_skill_middleware():
    llm = ChatOpenAI(model="qwen-flash", temperature=0.3)
    agent = create_agent(
        model=llm,
        middleware=[SkillMiddleware()],
        checkpointer=InMemorySaver(),
        system_prompt="你是一位运动与营养学的专家",
    )
    config = {"configurable": {"thread_id": "1"}}

    result = agent.invoke(
        input={
            "messages": [
                {
                    "role": "user",
                    "content": "我今天吃了10碗米饭,而且还用青椒肉丝的汤汁浇在米饭上,摄入的卡路里是多少?",
                },
            ]
        },
        config=config,
    )

    # Print the conversation
    for message in result["messages"]:
        if hasattr(message, 'pretty_print'):
            message.pretty_print()
        else:
            print(f"{message.type}: {message.content}")

可以看到 Agent 加载了 food-calori skill:

yaml 复制代码
================================== Ai Message ==================================
Tool Calls:
  load_skill (call_c790c732842245a98dc85c)
 Call ID: call_c790c732842245a98dc85c
  Args:
    skill_name: food-calorie
================================= Tool Message =================================
Name: load_skill

Loaded skill: food-calorie

---
name: food-calorie
description: 精准计算各类食物卡路里,支持按食材、重量、烹饪方式拆分核算总热量
metadata:
  author: DevTeam
  version: 2.0.0
---

# 食物卡路里计算专家指南
## 角色定位
你是专业营养学计算助手,擅长拆解混合食材、区分原生食材/加工烹饪方式、按国标营养热量标准,精准核算单种或多种食物总卡路里。
......
......

================================= Ai Message ==================================
Tool Calls:
  read_skill_file (call_76edf6d96c894868b889aa)
 Call ID: call_76edf6d96c894868b889aa
  Args:
    skill_name: food-calorie
    file_path: references/standard_calories.md
================================= Tool Message =================================
Name: read_skill_file

参考资料:

  1. LangChain 官方 Skills 文档
  2. Skills 框架教学文档
相关推荐
一只废狗狗狗狗狗狗狗狗狗1 小时前
机器学习与深度学习理论入门概述
人工智能·深度学习·机器学习
算力百科小智1 小时前
NVIDIA A100 GPU:基于 Ampere 架构的选型方法论
人工智能·架构·智星云·gpu算力租用·a100显卡
秉寒-CHO1 小时前
从 PyCharm 到 Cursor:我的 LLM 项目 AI 编程工具链全解析
ide·人工智能·pycharm
永霖光电_UVLED1 小时前
新唐科技宣布402nm波长、4.5W输出功率的紫光激光大规模生产
人工智能·科技
算力百科小星1 小时前
2026 GPU算力平台租用详解(阿里云、腾讯云与智星云)
人工智能·智星云·gpu算力租用
南讯股份Nascent1 小时前
2026年零售行业CRM系统选型指南
人工智能·零售
bruce1281 小时前
编码智能体赋能后端全栈开发经验分享
人工智能·经验分享·agi
suke1 小时前
Claude Opus 4.7 来了:代码能力暴涨,还能“看见”更多细节,关键是没涨价
人工智能·ai编程·claude
GuangHeAI_ATing2 小时前
军工企业数据存储如何保障?横向实测三款航天级SSD的可靠性与性能(含湖南天硕G55系列技术拆解)
大数据·数据库·人工智能