游乐游手机版
首页/AI教程/文章详情

Langchain智能体调用技巧与实战指南

时间:2026-05-28 08:26
AgentSkills是一种可复用、可版本管理的“技能包”,通过文件夹打包操作流程与资源,将AI使用范式从“提示词工程”升级为“流程工程”。在LangChain中,技能元信息注入系统提示词,详细内容按需动态加载,从而节省Token、提升输出稳定性与灵活性。

Agent Skills 是个啥?

你可以把 Agent Skills 理解为“给 AI 看的可执行入职手册”。它的核心思路很简单:用一个包含 SKILL.md 文件的文件夹,把一套完整的操作流程、脚本、模板和参考资料打包起来,形成一个可复用、可版本管理、并且能按需加载的“技能包”。

Langchain 调用 Agent Skills

一个标准的 Skill 文件夹结构通常是这样的:

a-specific-skill/
├── SKILL.md # 核心文件(必需):定义技能的元数据、执行流程与最佳实践。
├── scripts/ # 可选:存放可执行的 Python、Shell 脚本,用于执行确定性强的计算或操作。
├── references/ # 可选:存放需要按需引用的参考资料,如 API 文档、数据模板、知识库文章。
└── assets/ # 可选:存放任务输出所需的静态资源,如 PPT 模板、图片素材等。

Skills 为啥突然火起来了?

回想一下之前用 Claude、GPT 或 Gemini 处理复杂重复任务时的场景,是不是总绕不开这几个痛点?

  1. 提示词又臭又长:每次都要从头写一遍超长指令,频繁复制粘贴不说,还动不动就超出上下文限制。
  2. 输出质量像开盲盒:今天严谨专业,明天敷衍了事,风格和质量全凭运气,得靠反复修改提示词来“校准”。
  3. 像带了个失忆的实习生:同样的任务,比如写周报、做竞品分析、改代码风格,每次都得重新教一遍,无法形成可沉淀的经验。

Skills 的出现,本质上是一次思路的升级:从“提示词工程”转向了“流程工程”。它让普通用户也能将个人习惯、团队方法乃至企业 SOP(标准作业程序)沉淀下来,变成可复用、可分享、甚至可交易的标准化能力资产。这彻底改变了大模型的使用范式。

背后的行业趋势也很明显:

  • 过去:大家追求训练一个“全能型”大模型,希望它什么都会,结果往往是样样都懂一点,样样都不精。
  • 现在:思路转变为“基础大模型 + 按需加载专项 Skills”。让模型变得专注、高效且专业,需要什么技能就加载什么,随用随配。

这种模式还带来了一系列实实在在的好处:

  • 更省 Token:不用再把所有知识都塞进 Prompt 里。只需在系统提示词中注入技能的名称和简要描述(单条技能仅占用约 100 Token),具体细节等需要时再动态加载。

  • 更专业:每个 Skill 都针对特定领域场景进行打磨,能力更聚焦,输出的结果自然也更可控、更可靠。

  • 易维护:想更新或优化某个能力?直接修改对应的技能文件就行,完全不需要重新训练模型。

  • 高灵活:支持动态组合与按需加载,可以根据手头的任务自由搭配不同的技能集,构建专属的工作流。

把 Skills 塞进 LangChain

在 LangChain 生态中,集成 Agent Skills 主要有两种主流方式:

  • 使用 Deep Agents:如果你用的是 langchain-deepagents 这个框架,事情就简单了。在创建 Agent 时,直接通过 skills=["/path/to/skills/"] 参数指定技能目录,框架会自动扫描目录结构,识别并加载每个子目录下的 SKILL.md 文件。
  • 使用原生 LangChain Agent:如果你用的是 ReAct、Function Calling 这类原生的工具型 Agent,情况就稍微复杂一点。因为 LangChain 本身并没有内置“技能目录扫描”的功能,需要我们自己动手实现:
    • 遍历指定的技能文件夹;
    • 解析每个技能目录下的 SKILL.md 等描述文件;
    • 将技能逻辑封装成标准的 Tool(工具),然后通过自定义的 load_skill 等方法加载,供 Agent 调用。

下面我们重点聊聊第二种方式的实现细节。第一种方式在 Deep Agents 的官方文档里有很清晰的案例,这里就不赘述了。

技能准备

假设你已经准备好了两个技能文件夹,一个用于 SQL 优化,一个用于前端页面设计。

skills/
├── sql-optimization/
│   └── SKILL.md
├── frontend-design/
│   └── SKILL.md
└── ...想加啥技能就新建个目录

每个 SKILL.md 文件通常包含两部分:文件头部的 frontmatter(用于定义技能名称、描述等元数据)和正文部分的技能详细说明。例如:

---
name: sales_analytics
description: 用于销售数据分析的技能,包含数据库 schema 和常见查询示例。
---
# sales_analytics
## Overview
...

扫描并解析所有 SKILL.md

第一步,我们需要一个函数来扫描技能目录,把所有技能加载到内存中。在同级目录下创建一个 load_skills.py 文件,写入以下代码:

from pathlib import Path
from typing import List, TypedDict
import yaml

class SkillDict(TypedDict):
    """A skill that can be progressively disclosed to the agent."""
    name: str
    description: str
    content: str

def load_skills_from_dir(skills_dir: str) -> List[SkillDict]:
    """扫描目录,解析每个 SKILL.md,返回技能列表"""
    skills = []
    base_dir = Path(__file__).parent
    skills_path = base_dir / skills_dir

    for skill_dir in skills_path.iterdir():
        if not skill_dir.is_dir():
            continue
        skill_file = skill_dir / "SKILL.md"
        if not skill_file.exists():
            continue

        content = skill_file.read_text(encoding="utf-8")
        # 解析 frontmatter(假定格式正确)
        # 简单起见用 yaml 解析,也可以用 python-frontmatter 库
        parts = content.split("---", maxsplit=2)
        if len(parts) >= 3:
            frontmatter_str = parts[1].strip()
            body = parts[2].strip()
            meta = yaml.safe_load(frontmatter_str)
            name = meta.get("name", skill_dir.name)
            description = meta.get("description", "")
        else:
            # 没有 frontmatter,就用目录名和整个内容
            name = skill_dir.name
            description = f"Skill for {name}"
            body = content

        skills.append({
            "name": name,
            "description": description,
            "content": body,  # 或者包含整个文件内容
            "dir": str(skill_dir),
        })
    return skills

# 全局技能列表
SKILLS = load_skills_from_dir("skills")

写一个 load_skill 工具

接下来,创建 skills_tools.py 文件,用于定义核心的 load_skill 工具。这个工具的作用很明确:根据传入的技能名称,找到并返回对应 SKILL.md 文件的完整内容,从而让 Agent 获得执行该技能所需的全套指令、策略和操作规范。

@tool
def load_skill(skill_name: str) -> str:
    """Load a complete skill into the agent's context.
    Use this tool when you need detailed information about handling a specific
    type of request. It provides complete instructions, strategy rules, and
    operational guidance within the skill's domain.

    Args:
        skill_name: The name of the skill to load
            (e.g., "expense_reporting", "tra vel_booking")
    """
    for skill in SKILLS:
        if skill["name"] == skill_name:
            return f"Loaded skill: {skill_name}nn{skill['content']}"
    a vailable = ", ".join(s["name"] for s in SKILLS)
    return f"Skill '{skill_name}' not found. A vailable skills: {a vailable}"

把技能描述注入 System Prompt

这里有个关键技巧:我们不需要在系统提示词里塞进所有技能的完整内容,那样太占 Token 了。正确的做法是,只注入技能的“元信息”——也就是名称和简短描述。完整的技能指令,则通过上面定义的 load_skill 工具在需要时动态加载。

我们可以通过实现一个自定义的 SkillMiddleware 中间件来自动完成这个“元信息注入”和上下文管理的流程。

from langchain.agents.middleware import AgentMiddleware, ModelRequest, ModelResponse

class SkillMiddleware(AgentMiddleware):
    tools = [load_skill, view_skill_file, run_skill_script]

    def __init__(self, skills_list: List[SkillDict]):
        lines = []
        # 遍历技能列表,生成技能元信息
        # 每个技能元信息包含技能名称和描述
        for s in skills_list:
            lines.append(f"- {s['name']}: {s['description']}")
        self.skills_prompt = "n".join(lines)

    def wrap_model_call(
        self,
        request: ModelRequest,
        handler: Callable[[ModelRequest], ModelResponse],
    ) -> ModelResponse:
        # 组合技能元信息与系统提示词
        skills_addendum = (
            f"nn## A vailable Skillsnn{self.skills_prompt}nn"
            "Use the load_skill tool when you need detailed information "
            "about handling a specific type of request."
        )
        # 获取当前系统提示词内容
        current_content = getattr(request.system_message, "content", "") or ""
        # 合并当前系统提示词与技能元信息
        new_system_message = SystemMessage(content=current_content + skills_addendum)
        # 重写请求,包含新的系统提示词
        new_request = request.override(system_message=new_system_message)
        # 调用模型处理新的请求
        return handler(new_request)

创建带有技能的 Agent

万事俱备,现在可以创建一个集成了技能的 Agent 了。

def main():
    model = ChatOpenAI(model="minimax/minimax-m2.5", temperature=0.7, streaming=True)
    agent = create_agent(
        model,
        system_prompt="你是一个助手,可以根据用户问题加载不同技能来完成任务。",
        middleware=[SkillMiddleware(SKILLS)],
    )
    thread_id = str(uuid.uuid4())
    result = agent.invoke(
        {
            "messages": [
                HumanMessage(
                    content="帮我写一个最近一个月下单金额超过 1000 的客户 SQL",
                    # noqa: E501
                ),
            ]
        },
        config={"configurable": {"thread_id": thread_id}},
    )
    for message in result["messages"]:
        if hasattr(message, "pretty_print"):
            message.pretty_print()
        else:
            print(f"{message.type}: {message.content}")

if __name__ == "__main__":
    main()

这样一来,当 Agent 遇到需要特定技能详细内容的任务时,它就会自动调用 load_skill("sql-optimization"),把完整的 SKILL.md 内容(以及你放在技能目录里的其他资源)读入上下文。

运行日志

运行上面的代码,你会看到类似下面的交互过程:

================================ Human Message =================================
帮我写一个最近一个月下单金额超过 1000 的客户 SQL
================================== Ai Message ==================================
Tool Calls:
load_skill (call_3a8973b46576482ca734cdfd)
  Call ID: call_3a8973b46576482ca734cdfd
  Args:
    skill_name: sql-optimization
================================= Tool Message =================================
Name: load_skill
Loaded skill: sql-optimization

# SQL Performance Optimization Assistant
Expert SQL performance optimization for ${selection} (or entire project if no selection). Focus on universal SQL optimization techniques that work across MySQL, PostgreSQL, SQL Server, Oracle, and other SQL databases.
...
================================== Ai Message ==================================
# 查询最近一个月下单金额超过 1000 的客户

根据常见的电商数据库结构,以下是几种数据库的写法:

## 通用 SQL(MySQL/PostgreSQL/SQL Server 通用)
SELECT
    c.id AS customer_id,
    c.name AS customer_name,
    c.email,
    SUM(o.total_amount) AS total_order_amount,
    COUNT(o.id) AS order_count
FROM customers c
INNER JOIN orders o ON c.id = o.customer_id
WHERE o.created_at >= DATE_SUB(CURRENT_DATE, INTERVAL 1 MONTH)
-- PostgreSQL 使用: WHERE o.created_at >= CURRENT_DATE - INTERVAL '1 MONTH'
-- SQL Server 使用: WHERE o.created_at >= DATEADD(MONTH, -1, GETDATE())
    AND o.status != 'cancelled' -- 排除已取消订单
GROUP BY c.id, c.name, c.email
HA VING SUM(o.total_amount) > 1000
ORDER BY total_order_amount DESC;

## 建议的索引
-- 订单表索引
CREATE INDEX idx_orders_created_at ON orders(created_at);
CREATE INDEX idx_orders_customer_id ON orders(customer_id);
CREATE INDEX idx_orders_customer_date ON orders(customer_id, created_at, total_amount);

## 说明
| 要素 | 说明 |
|------|------|
| **时间范围** | 最近 30 天(`DATE_SUB(CURRENT_DATE, INTERVAL 1 MONTH)`) |
| **过滤条件** | 排除已取消订单(`status != 'cancelled'`) |
| **聚合** | 按客户汇总下单金额和订单数 |
| **筛选** | `HA VING SUM(o.total_amount) > 1000` 过滤金额超过 1000 的客户 |

如果你能提供具体的表结构或数据库类型,我可以给你更精确的查询!

至此,一个最小可用的 Skills 调用单元就完成了。

读取技能相关参考文件与执行脚本调用

当然,一个完整的技能体系可能不止一个 SKILL.md 文件。如果还想支持读取技能目录下的参考文档,或者执行内置的脚本,只需要再额外扩展两个工具即可:

  • view_skill_file:查看指定技能目录下的任意参考文件内容。
  • run_skill_script:执行技能目录下的 Python 或 Shell 脚本。

最后,把这两个新工具也集成到之前的 SkillMiddleware 中间件里,Agent 就能自动调用它们了。

import subprocess
from pathlib import Path
from typing import Optional
from langchain.tools import tool
from load_skills import SKILLS

SKILLS_DIR = Path("./skills")

def validate_path(base_dir: Path, target_path: str) -> Path:
    """Validate path safety to prevent path tra versal attacks.
    Ensures the target path is within the base_dir.
    """
    abs_base = base_dir.resolve()
    abs_target = (abs_base / target_path).resolve()
    if not str(abs_target).startswith(str(abs_base)):
        raise ValueError(f"Invalid path access: {target_path} is outside the allowed directory")
    return abs_target

@tool
def view_skill_file(skill_name: str, file_name: str) -> str:
    """View a reference file within a skill's directory (e.g., docs, data files, configs).

    Args:
        skill_name: Name of the skill.
        file_name: Name of the file to view within the skill's directory.
    """
    try:
        skill_dir = SKILLS_DIR / skill_name
        safe_path = validate_path(skill_dir, file_name)
        if not safe_path.exists():
            return f"Error: File '{file_name}' not found in skill '{skill_name}'."
        if safe_path.stat().st_size > 1 * 1024 * 1024:
            return (f"Error: File too large ({safe_path.name}), use a more specific path.")
        return safe_path.read_text(encoding="utf-8")
    except ValueError as e:
        return f"Security error: {str(e)}"
    except Exception as e:
        return f"Error reading file: {str(e)}"

@tool
def run_skill_script(skill_name: str, script_name: str, arguments: Optional[str] = "") -> str:
    """Execute a script within a skill's directory (supports .py and .sh).

    Args:
        skill_name: Name of the skill.
        script_name: Name of the script file to execute.
        arguments: Optional string of arguments to pass to the script.
    """
    try:
        skill_dir = SKILLS_DIR / skill_name
        safe_script_path = validate_path(skill_dir, script_name)
        if not safe_script_path.exists():
            return f"Error: Script '{script_name}' not found."

        command = []
        if safe_script_path.suffix == ".py":
            command = ["python", str(safe_script_path)]
        elif safe_script_path.suffix == ".sh":
            command = ["bash", str(safe_script_path)]
        else:
            return f"Error: Unsupported script type '{safe_script_path.suffix}'. Supported: .py, .sh"

        if arguments:
            import shlex
            command.extend(shlex.split(arguments))

        result = subprocess.run(
            command,
            capture_output=True,
            text=True,
            timeout=60,
            cwd=str(skill_dir),
        )
        if result.returncode == 0:
            return f"Script executed successfully:n{result.stdout}"
        else:
            return f"Script failed (exit code {result.returncode}):n{result.stderr}"
    except subprocess.TimeoutExpired:
        return "Error: Script execution timed out (60s limit)."
    except ValueError as e:
        return f"Security error: {str(e)}"
    except Exception as e:
        return f"Error executing script: {str(e)}"
来源:https://juejin.cn/post/7623296000480526377
上一篇AI视频生成器DreamSeedance2.0海外爆火 实测教程带你体验 下一篇Buddies AI智能伙伴深度体验与功能解析
本站内容用于信息整理与展示,如有侵权或内容问题请及时联系处理。

相关推荐

补充同频道和同主题内容,方便继续浏览更多相关内容。

同类最新

继续查看同栏目最近更新的文章。

更多
GPT Workspace通过GPT-5强化Google Workspace,文档表格邮件创作效率与智能化提升
AI教程 · 2026-05-29

GPT Workspace通过GPT-5强化Google Workspace,文档表格邮件创作效率与智能化提升

GPT Workspace 产品介绍:GPT-5 如何增强 Google Workspace 工作效率 如果你每天都在使用 Google Workspace 进行文档撰写、表格处理、邮件沟通和演示制作,一定深有体会:大量重复性的办公任务耗费了宝贵的时间。现在,GPT Workspace 将 GPT-

AI助手提升年终总结与周报效率的精准营销策略
AI教程 · 2026-05-29

AI助手提升年终总结与周报效率的精准营销策略

适合需求:在信息爆炸的时代,企业所承受的竞争压力几乎覆盖了所有维度,其中营销领域尤为令人困扰。无论是撰写年终总结还是生成周报,精准的营销策略已成为不可或缺的需求——没有谁愿意在庞杂的数据中迷失方向。当我们复盘营销活动时,总会思考:过去哪些数字营销策略真正发挥了效果?哪些内容营销策略有待改进?然而实际

Afri Studio 非洲创意工作室
AI教程 · 2026-05-29

Afri Studio 非洲创意工作室

Afri Studio是什么先来聊聊Afri Studio——它是Afri AI团队推出的一款AI媒体创作工作室,目标很明确:把原本高高在上的智能技术拉下神坛,让普通用户也能轻松生成高质量的文本、图像、音频等内容。换句话说,这是一个面向内容创作者、博主、营销人员、艺术家的“AI工具箱”,帮你高效搞定

Geniea专注Midjourney提示词优化提升创意生成效率
AI教程 · 2026-05-29

Geniea专注Midjourney提示词优化提升创意生成效率

Geniea产品详解:Midjourney提示优化工具Geniea是一款专注于Midjourney提示词优化的智能平台,致力于帮助创作者快速生成高质量且富有创意的提示方案。无论您需要电影镜头、食品摄影还是汽车广告等场景的提示词,只需输入简单指令,系统便会自动输出优化后的提示文本,大幅提升创作效率。提

幼儿园大班毕业典礼方案PPT AI轻松制作精彩回顾
AI教程 · 2026-05-29

幼儿园大班毕业典礼方案PPT AI轻松制作精彩回顾

使用情景 每年毕业季来临之际,幼儿园大班毕业典礼的筹备工作,总是牵动着众多老师、家长和孩子们的心弦。这不仅仅是一场简单的活动,更是孩子们人生中首个重要的成长仪式,标志着他们告别幼儿时光、迈向新阶段的里程碑。对于家长而言,这也是一次充满感怀的“毕业”,意味着一段陪伴旅程的暂时落幕。 如何让这场典礼既温