第九章 Agent Skills与Lang框架开发与应用


文档摘要

第九章 Agent Skills与Lang框架开发与应用 前言 在前八章中,我们已经学习了 LangChain 和 LangGraph 的核心概念、组件以及如何构建具有记忆和工具调用能力的智能体。然而,一个真正强大的智能体离不开丰富且高效的“技能”(Skills)。 本章将深入探讨如何为智能体扩展技能,包括使用内置工具包、自定义开发专用技能,以及如何对 LangChain/LangGraph 框架本身进行扩展,以满足更加复杂和定制化的需求。 那我们开始吧 在开始正式了解Skills 我们需要了解一下Agent工作模式 9.

第九章 Agent Skills与Lang框架开发与应用

前言

在前八章中,我们已经学习了 LangChain 和 LangGraph 的核心概念、组件以及如何构建具有记忆和工具调用能力的智能体。然而,一个真正强大的智能体离不开丰富且高效的“技能”(Skills)。

本章将深入探讨如何为智能体扩展技能,包括使用内置工具包、自定义开发专用技能,以及如何对 LangChain/LangGraph 框架本身进行扩展,以满足更加复杂和定制化的需求。

那我们开始吧~

在开始正式了解Skills 我们需要了解一下Agent工作模式

9.1 Agent工作模式:Workflow Agent 与 Deep Agent

在构建以大语言模型(LLM)为核心的智能应用时,开发过程中面临的问题往往是:我们应该让系统按照预设的剧本一丝不苟地执行,还是赋予它根据现场情况临场发挥、自主决策的能力?

这两种思路,对应了两种截然不同的Agent工作模式:Workflow Agent(工作流智能体)Deep Agent(深度智能体)。理解这两种模式的区别与联系,是设计和开发健壮、高效Agent系统的基石。

9.1.1 Workflow Agent:确定性工作流模式解析

Workflow Agent 是一种将复杂任务拆解为一系列预定义步骤,并严格按照这些步骤进行执行的智能体系统。在这个模式下,任务的执行路径是预先设计好的,就像工厂里的流水线,每个节点负责特定的处理(如调用LLM、使用工具、执行代码),数据沿着固定的流程从一个节点流向下一个节点。

通 俗理解的话,就像工厂的生产线,从原料加工到成品出厂,每个环节的操作、顺序、标准都是固定的,工人(Agent)只需按流程完成各自环节的工作,无需思考“下一步该做什么”,容错率低、结果可预期。

因此,Workflow Agent的核心在于其确定性可预测性,在企业的实际案例中,大多数的智能体往往是工作流模式,因为不稳定性和不可预测性对项目来说往往是灾难性的。

Workflow Agent最适合解决那些流程固定、逻辑明确、对准确性要求高的任务。在这些场景中,宁可牺牲一些灵活性,也要确保结果的稳定和规范。

例如:

  • 学生作业自动批改(固定流程:接收作业→提取答案→对比标准答案→打分→生成批改报告)

  • 固定格式文档生成(固定流程:实验报告模板填充:读取实验数据→填写指定模块→生成完整报告→导出文件)

  • 狼人杀游戏项目(固定流程:狼人杀人→发言→投票)

在Workflow中,Agent的“技能”被封装为一个个可复用的节点(Node)子图(Subgraph)

子图本身可以是一个完整的小型工作流,内部包含了更细粒度的步骤和逻辑。这种组织方式体现了模块化层次化的设计思想。

例如,在一个“智能客服”Workflow中,可以有一个“查询订单状态”的技能节点。

这个节点内部,实际上是一个包含了“获取用户ID -> 调用订单API -> 解析返回数据 -> 格式化输出”等步骤的小型子图。

通过这种方式,开发者可以将复杂的业务逻辑封装成高内聚、低耦合的技能模块,在主工作流中像搭积木一样调用它们,极大地提升了代码的复用性和可维护性。

9.1.2 Deep Agent:动态推理与技能编排模式

与Workflow Agent的“固定流程”不同,Deep Agent的核心是“动态推理”——它不预设固定的执行步骤,而是根据任务需求、实时状态,自主判断“需要调用哪些技能”“按什么顺序调用”,甚至能在执行过程中调整策略,应对不确定性场景。

通俗理解:Deep Agent就像一个“自主思考的助手”,接到任务后,会先分析任务目标,思考“我需要做什么、第一步该做什么、用什么技能、做完这一步下一步该怎么做”,遇到问题(如技能调用失败、输入不符合预期)会自主调整,无需人类提前预设所有流程。

例如最近特别火的 OpenClaw项目正是这一模式的绝佳实践范例,它展示了Deep Agent如何从概念走向现实应用,实际上OpenClaw中并没有内置各种工作流,而是通过大语言模型自主思考自主完成。

9-1

Deep Agent是一个具备感知、规划、记忆和工具使用能力的闭环系统。它通常基于类似ReAct(Reason+Act)的推理循环工作:思考(Thought)-> 行动(Action)-> 观察(Observation),循环往复,直到完成任务。

OpenClaw正是这一理念的产物,它本质上是一个AI智能体执行网关 。它本身不具备思考能力,但通过桥接大语言模型的“推理能力”和操作系统的“执行能力”,让AI从“会说”进化为“会做” 。其核心能力体现在:

  1. 动态任务分解:面对一个高层目标(如“帮我研究一下长新冠的最新疗法”),通过OpenClaw接入的Deep Agent能够自主将其分解为多个子任务,例如“搜索医学文献数据库”、“筛选近一年的临床试验”、“总结关键发现”等。
  2. 复杂推理与规划:它不仅能分解任务,还能理解子任务之间的依赖关系,并规划出最优的执行顺序。例如,它知道必须先搜索文献,才能对文献内容进行总结。
  3. 自我反思与优化:Deep Agent可以评估自己行动的结果。如果某个工具调用失败或发现新信息,它可以及时调整计划,甚至推翻之前的结论重新探索,表现出类似人类的反思能力。
  4. 自发性与主动性:OpenClaw的“心跳”机制让AI不再是“被动等待命令的工具”,而是变成了“主动待命的员工”。它会定期自动醒来,检查邮箱、日历等,在后台默默完成任务。

9.1.3 模式对比与选型决策指南

Workflow Agent和Deep Agent没有“优劣之分”,只有“适配与否”。实际开发中,需根据任务特点、需求场景,选择合适的Agent模式;若任务复杂,也可将两种模式结合使用(如核心流程用Workflow,不确定环节用Deep Agent)。

随时大语言模型的能力越来越强,Deep Agent模型展现出比较高的潜力,但这并不意味着Workflow Agent就变得一无是处,流程固定、逻辑明确、对准确性要求高的任务往往还是更加适配Workflow Agent。

为了方便大家快速区分和选择,我们从三个核心维度,对两种模式进行对比:

对比维度 Workflow Agent(确定性工作流) Deep Agent(动态推理)
灵活性 低:流程固定,无法应对异常和需求变化,只能按预设步骤执行 高:可自主推理、动态调整技能和流程,能应对不确定场景
可控性 高:执行过程透明,步骤和结果可预期,出现问题易排查 低:推理过程复杂,执行路径不固定,出现问题难定位原因
开发复杂度 低:只需梳理固定流程,绑定技能和步骤,无需设计推理逻辑,适合新手开发 高:需设计推理机制、技能协同逻辑、状态管理等,对开发能力要求高

总结:Workflow Agent适合“标准化、简单化、固定化”任务,是基础开发的首选;Deep Agent适合“复杂、不确定、需自主决策”的任务,是高阶开发的核心。实际应用中,可根据任务的核心需求,灵活选择或结合两种模式,提升Agent的执行效率和适配性。

9.2 Agent Skills概述与设计理念

9.2.1 从ReAct到Agent Skills的演进

9.2.1.1传统ReAct模式的局限性

回顾我们在9.1节中介绍的ReAct模式——"思考(Thought)→ 行动(Action)→ 观察(Observation)"的循环,它为Deep Agent提供了基础的决策框架。然而,当我们将ReAct模式应用于真实世界的复杂场景时,几个深刻的局限性逐渐浮现:

  1. 工具数量激增导致决策质量下降

    想象一下,你给一位实习生(Agent)分配任务,同时给了他一本包含500个工具使用说明的手册,并要求他每做一步决策前都必须翻阅完整本手册。结果可想而知——他会陷入信息过载,难以快速找到当前任务真正需要的那个工具。这正是ReAct模式面临的核心困境。研究表明,当提供给Agent的工具超过20-30个时,模型在每一步都需要从海量工具描述中筛选出正确选项,决策的准确率会显著下降。工具越多,选择越难,错误率越高。

  2. Token消耗与延迟问题

    每个工具的完整描述(包括名称、功能说明、参数格式、使用示例)平均需要数百个token。如果Agent需要同时知晓100个工具,仅工具描述本身就可能消耗数万token,这还未开始执行实际任务。 更大的问题在于成本——以Anthropic的API为例,每次需要动态注入新的工具定义时,都会导致缓存失效,需要支付完整的token费用外加缓存写入成本,相比缓存读取可能高出数倍。

  3. 上下文窗口的瓶颈

    即便最新的模型拥有百万token级别的上下文窗口,但将所有工具描述都塞入上下文的做法仍是粗暴且低效的。

9.2.1.2 Agent Skills的提出背景与解决思路

2025年10月,Anthropic正式发布了Agent Skills功能,两个月后将其作为开放标准推向市场。OpenAI、GitHub、VS Code、Cursor等主流平台迅速跟进,标志着AI Agent设计思维的一次重要转向。

Agent Skills的核心思路可以用一句话概括:"给AI看的可执行入职手册"

不再让模型面对“零散的、大量的工具”,而是把“功能相关的一组工具”封装成一个“技能(Skill)”,模型只需要先选择“技能”,再在技能内部选择具体工具——相当于把100个零散工具,分成10个“工具包”(技能),你要钉钉子,先选“木工技能包”,再在包里选锤子,效率和准确性都会提升。

这种思路巧妙地绕过了上述三重挑战:

  • 不把所有工具描述一次性塞进上下文,而是采用"渐进式披露"策略,只在需要时才加载完整内容
  • 让Agent先知道"我有什么能力"(元数据),再决定"我需要用哪个能力"(完整指令)
  • 将"工具连接"(MCP协议)与"流程封装"(Skills)分离,前者解决如何调用外部系统,后者解决如何专业地完成任务

9-2

9.2.2 Agent Skills的核心特征

9.2.2.1 Skill的构成与规范

一个标准的Agent Skill,本质上就是一个带SKILL.md文件的文件夹。这种极简的设计使其既易于理解,又具备强大的扩展能力。

典型的技能目录结构

my-skill/ # 技能根目录(名称与skill的name字段一致) ├── SKILL.md # 必需:元数据 + 执行指令 ├── scripts/ # 可选:可执行代码脚本 │ ├── execute_sql.py │ └── data_processor.py ├── references/ # 可选:参考资料文档 │ ├── api_docs.md │ └── best_practices.pdf └── assets/ # 可选:模板、资源文件 ├── report_template.docx └── config.json

Skill的元数据(名称、描述、版本等)

SKILL.md文件必须以YAML格式的前置元数据(frontmatter)开头,至少包含namedescription两个字段

name: mysql-employees-nl2sql-analysis description: 基于MySQL employees数据库,将中文业务问题转换为受控SQL查询并输出分析报告 license: Apache-2.0 compatibility: Python 3.8+, requires mysql-connector-python metadata: author: data-team version: "2.1.0" category: database-analysis

description的写作要点:需要清晰描述"技能是做什么的"以及"什么时候应该使用它"。建议包含关键词,方便Agent在运行时通过语义匹配自动发现合适的技能

除了核心的SKILL.md,技能目录还可以包含三类辅助资源:

  • scripts/:存放可重用的脚本代码。这些脚本可以被Agent直接调用执行,避免每次动态编写代码,节省token并提高可靠性。脚本应有清晰的接口设计,并通过SKILL.md说明使用方法。
  • references/:存放参考资料文档,如API文档、数据字典、规范手册等。Agent可以在需要时通过相对路径引用这些文档,避免一次性加载所有内容。
  • assets/:存放模板、配置文件、静态资源等。例如报告生成的模板文件、品牌设计规范中的Logo资源等。

这种模块化的组织方式使得技能既可以简单(仅SKILL.md),也可以随需求增长而自然演进,同时保持结构清晰。

9.2.2.2 双层工具架构设计

Agent Skills最具创新性的设计之一是其双层工具架构,这种架构从根本上解决了工具规模化的挑战。

1.门面工具(Loader Tools)的作用与设计

门面工具是Agent在初始状态下就能看到的"技能入口"。每个技能在加载元数据阶段,只向Agent暴露一个极简的门面工具—通常只包含技能名称和一句话描述。

例如,一个PDF处理技能的门面工具可能是:

tool: use_skill_pdf_processing description: 当用户需要处理PDF文件(合并、拆分、提取文本、填写表单)时使用

这个门面工具极其轻量,仅占用数十个token,但告诉了Agent最关键的信息:什么情况下应该激活这个技能。

2.内容工具(Content Tools)的按需暴露

只有当Agent基于用户问题判断需要某个技能,并调用对应的门面工具后,系统才会真正加载该技能的完整SKILL.md。这时,Agent才能"看到"技能内部包含的详细指令、脚本调用方法、参考资料引用等全部内容。

这种设计类似于图书馆的目录卡+书架取书模式:你不需要把整座图书馆的书都搬到自己桌上,只需要先查阅目录卡找到感兴趣的书,再去书架取阅。

3.架构优势:减少初始认知负担、支持无限扩展

双层架构带来了两个关键优势:

  1. 大幅减少初始认知负担:无论企业部署了多少个技能(几十个、几百个甚至上千个),Agent启动时只需加载所有技能的元数据(门面工具),总token消耗可控。这就像一位医生不需要同时记住所有病症的治疗手册,只需要知道遇到什么症状该翻哪本书。
  2. 支持技能库的无限扩展:由于技能的具体内容是按需加载的,技能库的规模增长不会线性增加初始上下文的压力。这为企业和社区构建庞大的技能生态铺平了道路——截至2026年初,GitHub上的技能仓库已累积超过2万颗星,出现了大量由社群和企业共享的技能模块。

9.2.2.3 状态驱动的动态过滤机制

除了双层架构,Agent Skills还引入了一种精巧的状态驱动动态过滤机制,进一步优化工具的可见性和选择准确性。

运行时状态的作用(如skills_loaded变量)

Agent在执行过程中会维护一个运行时状态变量,例如skills_loaded,用于记录当前会话中已经激活了哪些技能。一旦某个技能被激活并加载了完整内容,其内部的所有工具就会"加入"Agent的可用工具集。

更重要的是,一些实现允许在技能元数据中声明allowed-tools字段,预批准该技能可以调用的工具列表。这相当于为每个技能划定了一个"权限边界",确保技能在运行时只能使用经过批准的工具体系。

中间件如何基于状态过滤工具列表

在Agent的推理循环中,可以插入一个工具过滤中间件。这个中间件的职责是:基于当前的skills_loaded状态,动态调整暴露给模型的工具列表。

例如,当Agent尚未激活任何技能时,工具列表中只包含基础工具(如bash文件读写)和所有技能的门面工具。一旦激活了"数据库分析技能",该技能内部的execute_sqlvisualize_data等工具就会加入列表,而其他未激活技能的内部工具仍保持隐藏。

这种过滤机制确保了模型在每个决策点上面对的选项都是当前上下文中最相关、最精简的集合,显著提升了选择的准确性。

状态更新的触发方式(通过Tool返回值)

状态更新通常由工具的返回值触发。例如,当Agent调用某个技能的门面工具时,该工具的返回值中会包含一个特殊标志,指示系统需要加载对应技能的完整内容并更新skills_loaded状态。

这种设计使得状态的变更与Agent的自然行动完全融合——Agent不需要特殊的"加载技能"指令,它只需要像使用普通工具一样调用门面工具,系统会在背后完成所有状态管理工作。

9.2.3 Skills的工作机制

理解了Skills的核心特征后,我们来看它的完整工作流程。Skills的运行遵循一个清晰的三段式流程:匹配 → 读取 → 执行

三段式执行流程详解

  1. 匹配:用户问题与门面工具的匹配

    当用户向Agent提出一个问题时,Agent首先基于已有的门面工具(所有技能的元数据)进行初步判断。

    例如,用户问:"分析一下公司里谁的话语权最高?"Agent会在思考循环中遍历所有门面工具的描述,发现mysql-employees-nl2sql-analysis技能的描述"基于MySQL employees数据库,将中文业务问题转换为受控SQL查询并输出分析报告"与当前需求高度匹配。

    这种匹配可以是显式的(用户直接提到技能名称),也可以是隐式的(模型基于语义自动判断)。

  2. 读取:加载完整技能指令与工具集

    一旦Agent决定使用某个技能,它会调用对应的门面工具。此时,系统接管控制流,执行以下操作:

    • 读取该技能目录下的完整SKILL.md文件
    • 将其内容注入Agent的上下文
    • 更新运行时状态(如skills_loaded),将该技能内部的所有工具(如execute_sql.py)加入可用工具列表
    • 将控制权返回给Agent,继续推理循环

    从Agent的视角看,它只是调用了一个工具,然后突然"知道"了更多信息和更多的可用工具——这正是Skills的精妙之处。

  3. 执行:调用技能内工具完成任务

    现在,Agent拥有了完整的技能指导。它会按照SKILL.md中定义的步骤逐步执行:

    • 理解用户问题,生成符合规则的SQL
    • 调用execute_sql.py脚本执行查询
    • 获取查询结果
    • 基于结果编写分析报告
    • 如果需要更多信息,可能会生成新的SQL再次查询,形成循环

    整个过程中,Agent严格遵循技能指令中的约束(如只生成单条SELECT、使用特定表等),确保输出符合专业规范。

9-3

那我们现在举一个例子:

假设我们有一个专门用于查询LangGraph框架文档的技能:

name: langgraph-docs-query description: 查询LangGraph框架的官方文档,解答关于Graph构建、状态管理、检查点机制等问题

用户问题:"LangGraph里怎么给Agent添加记忆功能?"

执行流程

  1. 匹配阶段:Agent识别到问题涉及"LangGraph"和"记忆功能",与langgraph-docs-query技能描述匹配。
  2. 读取阶段:系统加载该技能的完整SKILL.md,其中包含:
    • LangGraph文档的目录结构和关键概念索引
    • 如何使用grepfind命令在本地文档中搜索
    • 当搜索结果不明确时,如何查看相关示例代码
    • 回答格式要求(必须引用文档位置)
  3. 执行阶段
    • Agent首先在文档中搜索"memory"关键词,找到关于MemorySaver检查点的章节
    • 阅读相关文档后,发现记忆功能需要配置checkpointer
    • 进一步搜索"checkpointer配置示例",找到完整的代码片段
    • 整合信息,向用户回答:"在LangGraph中添加记忆功能,需要使用MemorySaver检查点。配置方式如下:from langgraph.checkpoint import MemorySaver; memory = MemorySaver(); graph = Graph(..., checkpointer=memory)

整个过程无需人为干预,Agent在技能指导下自主完成了从信息检索到答案生成的全部步骤。

技能激活后的持久化与多轮对话保持

技能激活后,其影响会在整个会话期间保持。这意味着:

  • 一旦加载了langgraph-docs-query技能,后续所有与LangGraph相关的问题都可以直接使用已加载的文档知识和工具,无需重复激活。
  • Agent会记住当前激活的技能集,在多轮对话中持续应用这些技能的专业约束。
  • 如果对话转向其他领域,Agent可以保持其他技能待命状态,只在需要时激活新技能,形成"技能栈"。

这种设计使得Agent能够在同一会话中无缝切换多个专业领域,同时保持每个领域的专业规范——就像一个全科医生可以根据病人情况调用不同专科的知识。

9.2.4 Skills与相关概念的对比

为了更好地理解Agent Skills的独特价值,我们需要将它与其他相关概念进行对比。

Skills vs Tools

维度 Tools(工具) Skills(技能)
本质 原子操作接口 流程知识封装
粒度 单一功能点(如"搜索网页"、"执行SQL") 完整任务流程(包含多个工具调用和决策逻辑)
内容 API定义、参数说明 元数据+指令文档+脚本资源
加载时机 通常一次性全部加载 渐进式加载,按需激活
关注点 "能做什么操作" "怎么专业地完成任务"

类比:工具是乐高积木的单个模块,技能是用这些积木搭建好的模型图纸。工具告诉你"我可以旋转",技能告诉你"按照这个图纸,你可以搭出一辆能动的汽车"。

Skills vs Memory

维度 Memory(记忆) Skills(技能)
本质 历史信息的存储与检索 专业能力的封装与激活
内容 对话历史、用户偏好、事实知识 操作规程、领域约束、工具脚本
时间性 随时间动态积累和更新 相对静态,版本化演进
访问方式 基于相关性检索 基于任务匹配激活
作用 保持上下文连续性和个性化 提供专业工作流和能力

关系:记忆告诉Agent"这个用户喜欢什么",技能告诉Agent"这类任务应该怎么做"。一个好的Agent既需要记住用户偏好(记忆),也需要掌握完成任务的专业方法(技能)。

Skills vs Workflow

维度 Workflow(工作流) Skills(技能)
本质 预定义的执行路径 可激活的能力封装
灵活性 低,严格遵循预设流程 高,Agent可自主决定执行方式
控制点 开发者通过代码编排 Agent通过技能指令引导
适用范围 流程固定的重复任务 需要专业知识的开放任务
修改方式 修改代码或流程图 更新SKILL.md文档

关系:工作流像是火车轨道,规定了必须走的路线;技能像是给司机的一本《驾驶手册》,告诉他如何根据路况灵活驾驶到达目的地。在一些复杂系统中,两者可以结合——宏观流程使用工作流保证稳定,局部决策点使用技能提供智能。

下表总结了四个概念的核心差异

概念 粒度 加载时机 信息规模 适用场景
工具(Tools) 原子功能 通常一次性加载 小(KB级) 基础操作能力
记忆(Memory) 信息片段 持续动态检索 可变 上下文保持、个性化
工作流(Workflow) 完整流程 启动时确定 中等 固定路径的重复任务
技能(Skills) 能力模块 按需渐进加载 可扩展(MB级+) 专业知识封装、复杂任务

9.2.3 本章小节

本节我们深入探讨了Agent Skills的演进历程、核心特征和工作机制。从ReAct模式的局限性出发,我们理解了为什么需要一种新的能力封装范式;通过剖析Skills的构成规范、双层架构和动态过滤机制,我们看到了它是如何巧妙解决工具规模化挑战的;通过三段式执行流程和真实案例,我们掌握了Skills的实际运作方式;最后通过与相关概念的对比,我们明确了Skills在整个Agent生态系统中的独特定位。

Skills的出现,标志着AI Agent设计思维的一次重要转向:从"给模型更多的工具"转向"给模型更好的流程知识"。它让通用Agent能够通过按需加载专业知识,在各个垂直领域展现出专家级的执行能力,同时保持核心模型的简洁和高效。正如Anthropic所期望的,Skills正在成为企业AI基础设施的关键组成部分,决定着明天AI助理的工作效率

9.3 Deep Agent框架核心与技能封装实践

前面都是理论学习,非常的枯燥无聊,能坚持到这里非常不容易,给自己鼓掌

从这节开始,内容会更加的偏实践一些。Go Go Go~

欢迎来到智能体开发的进阶世界。在之前的课程中,我们已经学习了如何使用LangGraph构建基础的状态图和简单的智能体。但随着任务复杂度的提升(例如需要多工具协作、长时间运行、动态切换技能),简单的智能体循环(ReAct)就显得力不从心了。

为了解决这个问题,LangChain团队推出了Deep Agent框架。它不是一个全新的框架,而是构建在LangGraph之上的一个“智能体装备”(Agent Harness),旨在通过模块化的设计和内置的最佳实践,让开发者能够轻松构建处理长周期、复杂任务的“深度”智能体。

9.3.1 Deep Agent框架概述

Deep Agent的设计灵感来源于Claude Code和Manus等前沿应用,其核心思想是将复杂任务分解,并通过一个“主智能体”(Orchestrator)进行规划、委派和执行。它并非要取代LangGraph,而是将LangGraph的最佳实践(如状态管理、图结构)封装起来,提供一套开箱即用的解决方案。

deepagents是一个基于LangChain核心代理构建模块的独立库。它使用LangGraph运行时来实现持久执行、流式传输、人机交互等功能。

deepagents提供了2个版本的应用,分别是 Deep Agents SDK 和 Deep Agents CLI 其中, DeepAgents CLI 是基于 SDK 做的命令行工具

安装方法:

pip install deepagents-cli

安装完成后运行

deepagents

就能进入到 DeepAgents CLI

9-4

DeepAgents CLI 不是我们本章的学习重点,本节的重点是学习掌握 Deep Agents SDK 的方法,然后和langchain、langgraph框架进行深度融合~

那么,让我们先来理解它的四大核心设计思想,这也是整个框架的基石:

  • 技能即子图:这是Deep Agent最核心的概念。在Deep Agent中,一个“技能”(Skill)不再是简单的一个函数(工具),而是被封装为一个独立的LangGraph子图。这意味着一个技能内部可以包含自己的逻辑、状态和多个工具调用。技能之间通过全局状态进行通信,实现了高内聚、低耦合的模块化设计。
  • 状态驱动:Deep Agent重度依赖LangGraph的持久化状态(State)来管理一切:对话历史、中间结果、待办事项列表(Todo List)、虚拟文件系统中的文件内容,以及技能的激活标记等。这种状态驱动的方式使得智能体能够拥有多轮对话的记忆,并根据当前状态动态调整行为。
  • 动态工具暴露:当一个智能体拥有几十甚至上百个工具时,将全部工具的描述都塞给大语言模型(LLM),会严重浪费Token并降低推理效率。Deep Agent通过状态管理器追踪当前“激活”的技能,并只向LLM暴露这些已激活技能的内部工具,实现了“按需供给”,大大提升了模型的响应速度和准确性。
  • 可插拔架构:Deep Agent的设计极具弹性。通过技能注册表,你可以从本地文件夹、远程Git仓库甚至是内置库中动态加载技能。框架还会智能地处理版本冲突和依赖关系,让你可以像搭积木一样组合不同的能力。
  • 检查点与恢复:集成LangGraph的检查点机制,Deep Agent支持在对话的任何节点保存状态。这意味着你可以随时中断一个长时间运行的任务(例如等待用户审批),然后在未来某个时刻无缝恢复执行,这对于需要人工介入(Human-in-the-Loop)的场景至关重要

9.3.2 Deep Agent框架核心组件

框架的核心组件就像“工具箱的各个零件”,各自负责特定功能,协同工作才能实现框架的完整能力。下面结合“功能+学生易懂的类比”,详细说明每个组件的作用,无需死记硬背,理解功能即可:

  • 状态管理器(State Manager):核心功能是“存储和管理所有关键信息”,类比为“笔记本”——记录对话上下文(你和AI说的话)、当前激活了哪些技能(比如正在用“数据处理”技能)、每个技能的中间结果(比如数据处理得到的表格),供所有技能调用和参考。
  • 技能注册表(Skill Registry):核心功能是“管理所有技能”,类比为“技能清单”——记录所有可用技能的基本信息(名称、版本、存放路径)、如何加载技能、技能之间的依赖关系,还能处理版本冲突(比如两个“文本翻译”技能,一个是v1.0,一个是v2.0,注册表会按规则选择优先使用的版本)。
  • 动态工具过滤器(Dynamic Tool Filter):核心功能是“筛选工具”,类比为“工具筛选器”——根据当前状态(激活的技能),从所有工具中筛选出“有用的”,只暴露给AI,避免工具过多导致AI推理变慢。
  • 检查点机制(Checkpoint):核心功能是“持久化状态”,类比为“存档功能”——定期保存当前的对话状态和技能运行进度,中断后可恢复,同时优化长对话的历史记录,避免内存溢出。

关键总结:这四个组件缺一不可,状态管理器是“数据中心”,技能注册表是“技能管家”,动态工具过滤器是“效率优化器”,检查点是“安全保障”。

9.3.3 Deep Agent调用工具

在理解了Deep Agent的核心设计思想后,我们首先来看最基础的能力——工具调用。工具(Tools)是智能体与世界交互的“手”和“脚”,它们可以是函数、API接口或任何可执行的操作。在Deep Agents SDK中,调用工具的方式非常简洁,但背后却运用了我们之前讨论的动态工具过滤器状态管理器来优化性能。

让我们从一个最简单的例子开始:创建一个能够查询天气的智能体。

前置操作

# 安装必要的库(如果还未安装) # pip install -qU deepagents langchain-openai

9.3.3.1 单个工具调用

代码实现

from deepagents import create_deep_agent from langchain_openai import ChatOpenAI import os import json from dotenv import load_dotenv load_dotenv() # 1. 创建 LLM llm = ChatOpenAI( api_key=os.getenv("API_KEY"), base_url="https://api.deepseek.com", model="deepseek-chat", temperature=0.3 ) # 2. 定义工具函数 def get_weather(city: str) -> str: """ 获取指定城市的天气信息 Args: city: 城市名称,例如"北京"、"上海" Returns: 返回该城市的天气描述 """ # 这里简化处理,实际应用中可调用真实的天气API weather_data = { "北京": "晴朗,气温15-25℃", "上海": "多云,气温18-28℃", "广州": "阵雨,气温22-30℃", "深圳": "阴天,气温23-29℃" } return weather_data.get(city, f"暂无{city}的天气数据") def print_agent_response(response): """ 美化打印智能体的响应数据,突出关键信息,提升可读性 """ print("="*80) print(" 智能体完整交互过程(格式化输出)") print("="*80) # 遍历所有消息 for idx, msg in enumerate(response["messages"]): print(f"\n[{idx+1}] 消息类型: {msg.__class__.__name__}") # 1. 打印核心内容 print(f" 内容: {msg.content}") # 2. 打印工具调用信息(如果有) if hasattr(msg, 'tool_calls') and msg.tool_calls: print(f" ️ 工具调用:") for tool_call in msg.tool_calls: print(f" - 工具名称: {tool_call.get('name', 'N/A')}") print(f" - 调用参数: {json.dumps(tool_call.get('args', {}), ensure_ascii=False, indent=6)}") print(f" - 调用ID: {tool_call.get('id', 'N/A')}") # 3. 打印工具返回信息(如果有) if hasattr(msg, 'name') and msg.name: print(f" 工具返回: {msg.name}") if hasattr(msg, 'tool_call_id'): print(f" - 关联调用ID: {msg.tool_call_id}") # 4. 打印元数据(关键信息,简化显示) if msg.response_metadata: print(f" 元数据:") print(f" - 模型名称: {msg.response_metadata.get('model_name', 'N/A')}") print(f" - 完成原因: {msg.response_metadata.get('finish_reason', 'N/A')}") if 'token_usage' in msg.response_metadata: token_usage = msg.response_metadata['token_usage'] print(f" - 令牌使用: 输入{token_usage.get('prompt_tokens', 0)} | 输出{token_usage.get('completion_tokens', 0)} | 总计{token_usage.get('total_tokens', 0)}") # 提取并高亮最终回答 final_answer = next((msg.content for msg in reversed(response["messages"]) if isinstance(msg, type(response["messages"][-1])) and not msg.tool_calls), "无最终回答") print("\n" + "="*80) print(" 最终回答总结:") print("="*80) print(final_answer) print("="*80) # 3. 创建智能体 agent = create_deep_agent( model=llm, tools=[get_weather], # 将工具传入 system_prompt="你是一个有用的助手,可以使用工具查询天气信息。" ) # 4. 运行智能体 response = agent.invoke( {"messages": [{"role": "user", "content": "北京今天天气怎么样?"}]} ) print_agent_response(response)

运行结果

================================================================================ 智能体完整交互过程(格式化输出) ================================================================================ [1] 消息类型: HumanMessage 内容: 北京今天天气怎么样? [2] 消息类型: AIMessage 内容: 我来帮您查询北京今天的天气信息。 ️ 工具调用: - 工具名称: get_weather - 调用参数: { "city": "北京" } - 调用ID: call_00_fRaaBIBB66BGfUjgWQoFHHKo 元数据: - 模型名称: deepseek-chat - 完成原因: tool_calls - 令牌使用: 输入5830 | 输出52 | 总计5882 [3] 消息类型: ToolMessage 内容: 晴朗,气温15-25℃ 工具返回: get_weather - 关联调用ID: call_00_fRaaBIBB66BGfUjgWQoFHHKo [4] 消息类型: AIMessage 内容: 北京今天天气晴朗,气温在15-25℃之间,是一个比较舒适的日子。 元数据: - 模型名称: deepseek-chat - 完成原因: stop - 令牌使用: 输入5906 | 输出18 | 总计5924 ================================================================================ 最终回答总结: ================================================================================ 北京今天天气晴朗,气温在15-25℃之间,是一个比较舒适的日子。 ================================================================================

9.3.3.2 多个工具协同工作

实际应用中,我们往往需要多个工具协同工作。下面是一个支持天气查询和计算器的智能体示例:

# 1. 安装必要的库(如果还未安装) # pip install -qU deepagents langchain-openai from deepagents import create_deep_agent from langchain_openai import ChatOpenAI import os from dotenv import load_dotenv load_dotenv() # 1. 创建 LLM llm = ChatOpenAI( api_key=os.getenv("API_KEY"), base_url="https://api.deepseek.com", model="deepseek-chat", temperature=0.3 ) # 工具1:天气查询 def get_weather(city: str) -> str: """获取指定城市的天气信息""" weather_data = { "北京": "晴朗,15-25℃", "上海": "多云,18-28℃", } return weather_data.get(city, f"暂无{city}的天气数据") # 工具2:简单计算器 def calculator(expression: str) -> str: """ 计算数学表达式 Args: expression: 数学表达式,例如"2 + 3 * 4" Returns: 计算结果 """ try: # 注意:实际应用中应使用更安全的方式 result = eval(expression) return f"计算结果: {result}" except Exception as e: return f"计算错误: {str(e)}" # 工具3:获取当前时间 def get_current_time(format: str = "%Y-%m-%d %H:%M:%S") -> str: """获取当前系统时间""" from datetime import datetime return datetime.now().strftime(format) # 创建拥有多个工具的智能体 agent = create_deep_agent( model=llm, tools=[get_weather, calculator, get_current_time], system_prompt="""你是一个多功能助手,可以: 1. 查询城市天气 2. 进行数学计算 3. 获取当前时间 请根据用户问题选择合适的工具。""" ) # 测试不同的调用 test_queries = [ "北京天气怎么样?", "计算 15 * 7 + 23 等于多少?", "现在几点了?" ] for query in test_queries: print(f"\n用户: {query}") response = agent.invoke( {"messages": [{"role": "user", "content": query}]} ) print(f"助手: {response['messages'][-1].content}")

运行结果

用户: 北京天气怎么样? 助手: 北京今天的天气是晴朗,气温在15-25℃之间。天气不错,温度适宜。 用户: 计算 15 * 7 + 23 等于多少? 助手: 15 * 7 + 23 = 128 用户: 现在几点了? 助手: 现在是2026年3月13日 11:37:39。

9.3.3.3 动态工具过滤

你可能会问:如果我有几十个工具,每次调用都把所有工具描述传给 LLM,不会浪费 Token 吗?这正是动态工具筛选发挥作用的地方。

Deep Agents SDK 的设计理念支持「按需向 LLM 暴露工具」—— 你可以将工具按业务场景分组(如 “生活服务工具”“计算服务工具”),并根据用户当前的需求,只将匹配场景的工具列表传给 LLM。在简单场景中,你可以直接传入所有工具;但在复杂场景下,你可通过意图识别先判断用户需要哪些工具,再动态筛选出对应工具列表传入,以此减少不必要的 Token 消耗。

我们来看一个例子

# 严格遵循官方文档的依赖和接口 # pip install -qU deepagents==0.4.10 langchain-openai python-dotenv langchain-core from deepagents import create_deep_agent from langchain_openai import ChatOpenAI from langchain_core.prompts import PromptTemplate from langchain_core.output_parsers import StrOutputParser import os from datetime import datetime from dotenv import load_dotenv # 加载环境变量(确保 .env 文件中有 API_KEY) load_dotenv() # 1. 初始化 LLM(完全对齐官方文档的用法) llm = ChatOpenAI( api_key=os.getenv("API_KEY"), base_url="https://api.deepseek.com", model="deepseek-chat", temperature=0.1 # 意图识别降低随机性,结果更稳定 ) # 2. 定义官方规范的工具函数(单一职责+清晰文档+错误处理) def get_weather(city: str) -> str: """ 获取指定城市的天气信息(仅支持北京、上海) Args: city: 城市名称(中文全称,如"北京") Returns: 格式化的天气信息字符串 """ if not city or not isinstance(city, str): return "❌ 请输入有效的中文城市名称(如:北京)" weather_map = {"北京": "晴朗,15-25℃", "上海": "多云,18-28℃"} return weather_map.get(city.strip(), f"⚠️ 暂无{city}的天气数据") def calculator(expression: str) -> str: """ 计算简单的数学表达式(支持 +-*/ 运算) Args: expression: 数学表达式字符串(如"2 + 3 * 4") Returns: 计算结果或错误提示 """ if not expression or not isinstance(expression, str): return "❌ 请输入有效的数学表达式(如:15*7+23)" try: # 安全执行计算(官方推荐的基础安全做法) result = eval(expression, {"__builtins__": None}, {}) return f"✅ 计算结果:{result}" except Exception as e: return f"❌ 计算错误:{str(e)}" def get_current_time(format_str: str = "%Y-%m-%d %H:%M:%S") -> str: """ 获取当前系统时间 Args: format_str: 时间格式化字符串(默认:%Y-%m-%d %H:%M:%S) Returns: 格式化的当前时间字符串 """ try: return f"✅ 当前时间:{datetime.now().strftime(format_str)}" except ValueError: return f"❌ 时间格式错误,请使用合法的格式化字符串" # 3. 官方标准:意图识别(动态过滤工具的核心) # 功能:让LLM分析用户问题,判断需要用到哪些工具类别 def identify_tool_category(user_query: str) -> str: """ 官方规范的意图识别:返回工具类别(生活服务/计算服务/无) """ # 官方推荐的 Prompt 写法(清晰、标准化,便于LLM理解) prompt = PromptTemplate( template=""" 你是工具分类助手,仅需根据用户问题判断工具类别,输出规则: 1. 天气、时间相关 → 生活服务 2. 数学计算相关 → 计算服务 3. 其他问题 → 无 无需解释,仅返回:生活服务/计算服务/无 用户问题:{query} """, input_variables=["query"] ) # 官方标准的 LLM 调用链(Prompt + LLM + 输出解析) chain = prompt | llm | StrOutputParser() category = chain.invoke({"query": user_query}).strip() return category # 4. 官方核心:动态过滤工具(无需Skill类,纯官方接口) def get_filtered_tools(category: str) -> list: """ 根据意图类别筛选工具列表(官方推荐的动态过滤方式) """ tool_mapping = { "生活服务": [get_weather, get_current_time], "计算服务": [calculator], "无": [] } return tool_mapping.get(category, []) # 5. 封装官方标准的智能体调用流程 def run_deep_agent(user_query: str) -> str: """ 官方规范的调用流程:识别意图→过滤工具→创建智能体→返回结果 """ # 步骤1:识别用户意图,确定工具类别 category = identify_tool_category(user_query) # 步骤2:动态过滤工具(核心:只传需要的工具,节省Token) filtered_tools = get_filtered_tools(category) # 步骤3:根据是否有匹配工具,调整智能体配置(核心修改点) if not filtered_tools: # 无匹配工具时:创建无工具的智能体,让LLM原生回答 agent = create_deep_agent( model=llm, tools=[], # 不传任何工具,触发原生调用 system_prompt="你是一个专业助手,友好、准确地回答用户的所有问题。" ) else: # 有匹配工具时:仅使用过滤后的工具回答 agent = create_deep_agent( model=llm, tools=filtered_tools, system_prompt="你是一个专业助手,仅使用提供的工具回答用户问题,回答简洁准确。" ) # 步骤4:调用智能体(官方标准的输入格式) response = agent.invoke({ "messages": [{"role": "user", "content": user_query}] }) # 步骤5:提取并返回结果(官方返回格式) return response["messages"][-1].content # 6. 测试(官方标准的使用方式) if __name__ == "__main__": test_queries = [ "北京今天的天气怎么样?", # 生活服务工具 "计算 25*8-120 等于多少?", # 计算服务工具 "现在几点了?", # 生活服务工具 "介绍一下你自己", # 无匹配工具 → 原生调用LLM回答 "人工智能的发展前景如何?" # 无匹配工具 → 原生调用LLM回答 ] for query in test_queries: print(f"\n用户:{query}") print(f"助手:{run_deep_agent(query)}")

运行结果

用户:北京今天的天气怎么样? 助手:北京今天的天气是晴朗,气温在15-25℃之间。 用户:计算 25*8-120 等于多少? 助手:25 × 8 - 120 = 80 用户:现在几点了? 助手:现在是 2026年3月13日 14:49:07。 用户:介绍一下你自己 助手:我是 Deep Agent,一个专业的 AI 助手,专注于帮助用户高效完成任务。 我的核心特点: - **简洁直接**:我会直接回答问题或执行任务,不添加不必要的开场白或解释 - **专业客观**:优先考虑准确性,必要时会礼貌地纠正用户的错误观点 - **工具驱动**:使用各种工具(文件系统操作、任务管理等)来实际解决问题 - **结果导向**:持续工作直到任务完成,而不是中途停下来解释我会做什么 我能做什么: - 文件操作:读取、编辑、搜索文件和目录 - 任务管理:将复杂任务分解为可执行的步骤 - 子代理协调:将独立任务分配给专门的子代理并行处理 - 代码分析:理解现有代码库并按照现有模式进行修改 工作方式: 1. 先理解:阅读相关文件,了解现有模式和需求 2. 再行动:快速准确地实施解决方案 3. 后验证:检查工作是否符合要求,必要时迭代改进

9.3.3.4 工具调用的最佳实践

  1. 写清晰的函数名和文档字符串:LLM通过它们理解工具用途
  2. 使用类型提示:参数类型帮助LLM正确填充参数
  3. 保持工具单一职责:每个工具只做一件事,做好一件事
  4. 提供友好的错误处理:工具内部应捕获异常,返回易读的错误信息

9.3.4 Deep Agent调用skills

在前面的教程中我们了解到技能(Skill)是Deep Agent的核心单元,是封装了特定业务逻辑、工具集和状态管理的LangGraph子图,区别于“单一功能”的工具,技能可以完成更复杂的连贯任务(例如“数据采集→数据清洗→结果可视化”的完整流程)。调用技能是Deep Agent实现复杂任务处理的核心操作,其本质是让主智能体(Orchestrator)加载技能子图、激活技能,并通过全局状态实现技能与主智能体、技能与技能之间的通信。

实战案例:DeepAgents + 自定义技能

下面是一个完整的实战案例,展示了如何使用DeepAgents框架结合DeepSeek API创建自定义技能,用于处理报销整理任务:

  1. 创建自定义技能

首先,我们需要创建一个技能目录结构,其中包含SKILL.md文件来定义技能的行为:

技能目录结构:

tmp/skills/reimbursement/ ├── SKILL.md # 技能定义文件 └── ... # 其他相关文件

SKILL.md 内容:

--- name: reimbursement description: 当用户需要整理消费记录、打车费、餐饮费、购物费用为报销清单或费用清单时调用。适用于用户提供散乱的消费文字,需要提取日期、项目、金额并汇总的场景。 --- # 报销整理技能 ## 必须执行的步骤 收到用户提供的消费记录后,你必须**按顺序**完成以下操作: ### 步骤1:提取信息 从用户输入中提取每笔消费的: - 日期(如:周一、2024-01-15) - 消费项目(如:打车、吃饭、办公用品) - 金额(数字部分) ### 步骤2:数据整理 - 将所有金额转换为数字类型 - 按日期排序(从早到晚) - 计算总金额 ### 步骤3:输出表格 使用 Markdown 格式输出: | 日期 | 消费项目 | 金额(元) | |------|----------|-------------| | 周一 | 打车去机场 | 45.00 | | 周三 | 请客户吃饭 | 380.00 | | 周五 | 买办公用品 | 128.50 | **总计:553.50 元** ## 重要提醒 - 金额必须保留两位小数 - 如果日期信息不完整,按原文输出并标记 - 不要遗漏任何一条消费记录
  1. 使用Python代码调用技能

创建好技能定义后,我们可以使用以下Python代码来调用这个技能:

#!/usr/bin/env python3 """ DeepAgents + DeepSeek 自定义 Skills 完整示例 支持 Windows / macOS / Linux 跨平台 """ import asyncio import os from pathlib import Path from langchain_openai import ChatOpenAI from deepagents import create_deep_agent from deepagents.backends import FilesystemBackend # ============================================================ # 配置区域 - 请根据你的实际情况修改 # ============================================================ # DeepSeek API 配置 # 官方 API MODEL_NAME = "deepseek-chat" # 或 deepseek-reasoner API_BASE = "https://api.deepseek.com" API_KEY = os.getenv("API_KEY") # 从环境变量获取API密钥 current_root = Path(__file__).parent.resolve() # 3. skills 目录 skills_dir = current_root / "skills" # 4. 初始化 Backend(文件系统模式) backend = FilesystemBackend( root_dir=str(current_root), virtual_mode=True # 使用虚拟模式加载技能 ) # 5. 初始化 DeepSeek 模型 deepseek_model = ChatOpenAI( api_key=API_KEY, base_url=API_BASE, model="deepseek-chat", temperature=0.3 ) # 6. 创建 DeepAgent(新版 API 正确方式) agent = create_deep_agent( name="ReimbursementAgent", model=deepseek_model, backend=backend, skills=["reimbursement"], debug=True ) def print_teaching_demo(result: dict): print("\n" + "="*90) print(" 教学演示:Agent 执行全流程解析") print("="*90) # 1. 打印完整消息历史 print("\n" + "-"*90) print(" 1. 完整消息交互链") print("-"*90) messages = result.get("messages", []) for i, msg in enumerate(messages, 1): msg_type = msg.__class__.__name__ # 角色判断 if msg_type == "HumanMessage": role = " 用户" box_color = "" elif msg_type == "AIMessage": role = " AI 模型" box_color = "" elif msg_type == "ToolMessage": role = " 工具" box_color = "" else: role = "❓ 未知" box_color = "⬜" print(f"\n{box_color} 【第 {i} 轮】 {role} ({msg_type}) {box_color}") # 打印内容 if msg.content: print(f"\n 内容:") for line in msg.content.split('\n'): print(f" {line}") # 打印 AI 的工具调用 if msg_type == "AIMessage" and hasattr(msg, 'tool_calls') and msg.tool_calls: print(f"\n ️ 模型思考:需要调用工具") for tc in msg.tool_calls: print(f" → 调用工具:{tc['name']}") print(f" → 输入参数:{tc['args']}") # 2. 打印最终结果 print("\n" + "="*90) print("✅ 2. 最终结果") print("="*90) print(f"\n{result['messages'][-1].content}") print("\n" + "="*90) async def main(): user_input = """ 帮我整理这些消费: 1. 3月25号 打车 35元 2. 3月27号 午餐 68.5元 3. 3月28号 买文具 120元 """ print("===== 整理结果 =====") # 这里用 .ainvoke(),输入格式是 LangGraph 标准的 messages 列表 result = await agent.ainvoke({ "messages": [("user", user_input)] }) # 输出最后一条消息(AI 的回复) print_teaching_demo(result) if __name__ == "__main__": asyncio.run(main())

案例解析

这个实战案例展示了Deep Agent调用技能的完整流程:

  1. 技能配置:通过FilesystemBackend加载本地技能目录
  2. 技能激活:在创建Deep Agent时指定要使用的技能(skills=["reimbursement"]
  3. 技能执行:当用户提出报销整理需求时,Agent自动调用reimbursement技能
  4. 结果展示:通过print_teaching_demo函数详细展示了Agent的完整执行流程

9.4 实战项目

9.4.1 从零实现Agent Skills框架

  • 任务目标:基于LangChain中间件实现动态技能加载框架
  • 核心步骤
    1. 定义BaseSkill抽象类
    2. 实现SkillRegistry
    3. 编写SkillMiddleware
    4. 创建skills技能
    5. 构建测试智能体验证动态加载
  • 验收指标:技能按需激活、工具过滤正确、Token消耗对比

9.5 本章小结

  • 核心知识点回顾
  • 技能机制的价值总结
  • 常见陷阱与避坑指南
  • 后续学习展望(多智能体、自我反思等)

发布者: 作者: 转发
评论区 (0)
U