LangChain与LangGraph实战:Agent Skills五层架构与12个案例

📅 发布时间:2026/8/30 4:04:49
LangChain与LangGraph实战:Agent Skills五层架构与12个案例 写 Agent 不能只靠一个 SystemPrompt 挂上几个工具。真正进入工程阶段后你会发现大部分问题都出在“能力怎么组织”哪些步骤应该由模型自由发挥哪些步骤必须固定执行失败之后向哪一层回退跨多轮会话时怎么把上一次的技能结果带回来。LangChain 生态在 2025 年的迭代中明显把重心从“提示词拼接”转向了“技能编排”也就是把可复用的能力封装成 Agent Skills再交给规划器、图结构或子 Agent 去调度。这篇内容不重复“三分钟搭一个 Agent”的教程而是用 12 个案例把 Agent Skills 架构中的规划层、技能层、工具层、记忆层和观测层完整拆开讲清楚每一层为什么存在、怎么实现、出错时从哪里查。1. 先分清工具、技能、Agent、AI Skills 到底有什么不同1.1 工具是“动词”技能是“完整流程”工具Tool在 LangChain 里通常指一个可以被模型直接调用的函数典型例子是天气查询、数据库查询、向量检索。工具的特点是原子性一次调用完成一个明确动作。技能Skill则是一个能力包。它由“一段技能描述、一段执行指令、一组工具、输入输出约束和校验规则”组成。技能不是某个函数的别名而是把多个工具和判断逻辑组合成一个可复用的执行单元。举个例子对比项工具技能粒度原子动作流程编排是否包含指令通常只有函数和描述包含多步骤说明和约束是否包含校验不强制建议包含前置和后置校验举例get_weather(city)出差天气技能查天气、查航班延误、生成出行建议复用方式单点复用整组复用下面是一个工具的最小写法from langchain_core.tools import tool tool def get_weather(city: str) - str: 查询某个城市当前的天气情况。 return f{city} 当前天气多云25 摄氏度同一个能力如果要做成技能就要在工具外面包一层“使用说明”和“步骤约束”。LangChain 本身没有强制规定技能文件必须是某个格式但在工程实践中YAML 是一种很直观的技能定义方式skill_id: business_trip_weather name: 出差天气助手 description: 当用户计划出差、查询目的城市天气、询问航班延误情况时使用。 tools: - get_weather - get_airport_delay instructions: | 1. 先调用 get_weather 查询目的地天气。 2. 如果用户提到航班或交通再调用 get_airport_delay。 3. 输出时按“天气 出行建议”的结构组织。 input_schema: city: string 出行日期: string这里要特别说明这段 YAML 本身不会被 LangChain 自动解析它属于团队内部的技能定义约定。真正接入 LangChain 时需要自己写一段加载逻辑把 YAML 中的工具列表和指令注入到 Agent 的 Prompt 和工具列表中。这种冗余设计是有价值的因为它把“技能资产”从代码里抽离出来可以在不修改 Python 代码的前提下调整 Agent 行为。1.2 Agent 是决策者技能是执行者很多人把“Agent Skills”和“Agent”混在一起导致架构讨论失去焦点。核心区别在于是否拥有决策循环。技能只是执行能力给定输入按照固定指令调用工具返回结果。Agent 是多轮决策循环它要根据用户问题判断该选哪个技能、调用后观察结果、判断是否还需要下一个技能直到任务完成或触发终止条件。用一句话概括技能是“怎么干”Agent 是“要不要干、先干哪个、干完怎么判断”。在系统设计上Agent 和技能的关系是上下层关系不是平级关系。同一个技能可以被不同 Agent 复用同一个 Agent 也可以持有多个技能。1.3 LangChain 与 LangGraph 在技能编排中的分工围绕“技能编排”这个目标LangChain 和 LangGraph 的定位很容易让人混淆。LangChain 这套框架提供的核心能力是工具、提示词、模型调用和 Agent 执行器的封装。传统上LangChain 的 Agent 执行器通过 ReAct 循环工作模型生成“思考 → 动作 → 工具结果 → 再思考”的序列直到模型认为任务完成。LangGraph 则是基于图结构的编排框架。它把 Agent 拆成节点Node和边Edge技能可以作为图中的一个节点节点之间可以配置条件路由、并行分支、循环和回退。对比项LangChainAgentExecutorLangGraph编排方式隐式循环显式图结构适合场景快速原型、简单工具链复杂分支、人工干预、可审计流程技能调用模型每次自行选工具可以固定技能节点也可以让模型路由可观测性中等高学习成本低中高两者不是替代关系。实际项目中LangGraph 可以用作整体流程骨架LangChain 的工具和模型封装继续承担单点能力。2. Agent Skills 的架构分层与任务规划能力2.1 五层架构从交互到底座一套可落地的 Agent Skills 架构至少包含五个层次层次职责常见实现交互层接收用户输入、返回最终结果Web 接口、命令行、消息服务规划编排层拆解任务、选择技能、判断终止LangGraph 图、AgentExecutor、提示词策略技能层按技能定义组合工具调用技能文件、技能注册表工具层连接外部系统或数据数据库工具、HTTP 工具、向量库工具底座层记忆、日志、监控、沙箱、模型网关Checkpointer、LangSmith、Redis、模型网关分层带来的直接好处是问题定位清晰。技能选错了先看规划层的 Prompt 和技能描述技能内部报错再查技能层和工具层上下文丢失多半要查记忆层。2.2 任务规划能力在 LangChain 里怎么实现“LangChain 中的任务规划能力是如何实现的”是常见问题。并非 LangChain 内部内置了一个特殊“规划器”而是通过三种方式实现第一种方式让模型在 ReAct 循环中动态规划。模型每轮生成一个动作工具结果返回后继续生成下一个动作。适合步骤较少、工具边界清晰的任务。第二种方式Plan-and-Execute。先将任务拆成计划再按计划逐步执行。LangGraph 里可以这样建模用户输入 │ ▼ 规划器LLM │ ├── 拆解为步骤 1 │ ▼ │ 技能/工具执行 │ ▼ │ 校验结果 ├── 拆解为步骤 2 │ ▼ │ 技能/工具执行 │ ▼ 汇总输出第三种方式把规划过程显式写入图结构。所有可能的分支都提前设计好规划器只负责选择走哪条边。这种方式可控性最高适合生产环境。这里要提醒一个误区不要把所有逻辑都交给模型即兴发挥。固定的、不允许出错的步骤应该用代码或图结构固定下来只有真正需要判断的场景才把控制权交给模型。2.3 技能注册表和输入契约技能定义需要纳入统一注册表避免出现“两个技能描述重叠模型不知道该选哪个”的问题。注册表最小结构如下{ skills: [ { skill_id: search_kb, name: 内部知识库检索, version: 1.0.0, enabled: true, tools: [query_knowledge_base] } ] }技能层的核心原则是输入输出都要有契约。输入契约决定了 Agent 需要向技能传递什么参数输出契约决定了上层如何消费执行结果。输入输出越随意技能的复用价值就越低。3. 环境准备先跑通一个最小可执行的技能调用3.1 依赖版本和运行环境不同项目的 LangChain 版本差异很大接口变化也快。下面的示例用于说明思路落地前务必确认当前环境的实际版本。依赖包用途建议langchain核心抽象使用与项目约定一致的版本langchain-openaiOpenAI 风格模型接入仅在使用该模型时安装langgraph图编排LangGraph 方式需要langchain-community社区集成按具体工具选择langchainhub示例提示词可选准备一个虚拟环境python -m venv .venv source .venv/bin/activate pip install langchain langchain-openai langgraph然后设置模型访问配置。生产环境建议通过环境变量注入不要把密钥写进代码或提交到仓库export OPENAI_API_KEYyour-key-here3.2 一个最小技能调用示例先看 LangChain 传统 AgentExecutor 的写法from langchain.agents import create_tool_calling_agent, AgentExecutor from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder from langchain_core.tools import tool from langchain_openai import ChatOpenAI tool def query_knowledge_base(query: str) - str: 查询内部知识库返回与 query 相关的文档片段。 # 实际项目中这里会调用向量库或搜索服务 return 登录超时时间可以通过配置文件 login_timeout 修改。 prompt ChatPromptTemplate.from_messages([ (system, 你是内部客服 Agent。请根据问题选择合适的工具。), (human, {input}), MessagesPlaceholder(variable_nameagent_scratchpad), ]) model ChatOpenAI(modelgpt-4o-mini, temperature0) agent create_tool_calling_agent(model, [query_knowledge_base], prompt) executor AgentExecutor(agentagent, tools[query_knowledge_base]) result executor.invoke({input: 帮我查一下登录超时怎么配置}) print(result[output])这段代码的思路是把工具注入 Agent让模型根据用户问题决定是否调用工具。当工具列表只有一两个时这个模式足够用。如果技能数量多、需要严格规定调用顺序建议改用 LangGraphfrom typing import TypedDict from langgraph.graph import StateGraph, START, END class State(TypedDict): question: str knowledge: str answer: str def retrieve_node(state: State): # 技能层检索知识库 return {knowledge: 相关文档内容} def answer_node(state: State): # 规划层生成回答 return {answer: 根据知识库生成的回答} graph StateGraph(State) graph.add_node(retrieve, retrieve_node) graph.add_node(answer, answer_node) graph.add_edge(START, retrieve) graph.add_edge(retrieve, answer) graph.add_edge(answer, END) app graph.compile()LangGraph 的价值在于把执行链路显式画出来。无论是新增技能、插入校验节点还是增加并行分支都只是在图上增加节点和边而不是在提示词里改说明文字。4. 12 个案例从技能封装到复杂编排下面的 12 个案例按难度递增排列。每个案例都包含场景、技能设计思路和落地的关键注意点。4.1 案例 1RAG 知识检索技能场景企业内部客服 Agent 需要回答制度、操作手册、FAQ 类问题。技能设计将向量库检索封装成单个技能而不是直接把检索工具暴露给 Agent。技能内部完成“问题改写 → 检索 → 截断 → 格式化”的流程。tool def search_kb(query: str, top_k: int 5) - str: 检索企业内部知识库返回文档片段列表。 docs vectorstore.similarity_search(query, ktop_k) formatted \n---\n.join([d.page_content[:800] for d in docs]) return formatted关键注意点检索结果不能原封不动返回。Agent 的上下文长度有限长文档会把大部分 token 消耗在冗余内容上。返回前必须做截断和去重。技能描述要标明触发场景避免用户问“我的账号被锁了”时Agent 仍然调用数据库查询技能而不是知识库技能。4.2 案例 2SQL 查询技能场景运营人员通过自然语言查询订单量、销售额等指标。技能设计不要把“任意 SQL 执行”直接交给 Agent否则存在数据安全和性能风险。应该把技能输入收窄为“指标 时间范围 维度”技能内部再生成受限 SQL。skill_id: business_metric_query name: 经营指标查询 description: 当用户询问订单量、销售额、用户数等经营指标时使用。 input_schema: metric: order_count | order_amount | active_users date_range: 最近7天 | 最近30天 | 本月 security: - 不允许执行 DELETE/UPDATE/DDL - 查询结果最大返回 100 行这个案例的核心是技能描述里能写清边界就不要让模型自由发挥。收窄输入不仅减少幻觉也降低注入风险。4.3 案例 3长文本分段总结技能场景用户上传一份 5 万字的产品需求文档要求生成摘要和行动项。技能设计长文本无法一次放入模型上下文技能内部需要分步骤处理。文档上传 │ ▼ 文本分段每段 5000 字带重叠 200 字 │ ▼ 逐段摘要可并行 │ ▼ 合并摘要 │ ▼ 提取行动项关键注意点分段时保留重叠区域避免章节边界信息被截断。逐段摘要的结果要保留来源章节编号否则合并阶段无法回溯原始内容。不要用一条“你是一个总结专家”的提示词处理整篇长文效果远不如分段加合并。4.4 案例 4计划拆解技能场景用户要求“帮我写一份市场调研报告覆盖竞品分析、用户画像和投放建议”。技能设计规划器先把任务拆成三个子任务再分别调用竞品检索、用户画像分析、建议生成三个技能。LangGraph 中可以这样建模def planner_node(state: State): # 调用 LLM 拆解任务 plan model.invoke(将以下请求拆解为 3-5 个可执行步骤: state[question]) return {plan: plan} def execute_plan_node(state: State): # 按计划调用具体技能 return {result: 逐项执行后的汇总结果}关键注意点计划拆解不是越细越好。步骤过多会导致多轮调用累积的错误概率上升。拆到“每个步骤能被一个技能独立完成”就足够了。4.5 案例 5并行信息收集技能场景调研报告需要同时收集多个数据源信息例如竞品官网、行业报告、新闻资讯。技能设计在 LangGraph 中使用并行节点让多个收集技能同时执行最后统一汇总。graph.add_node(collect_competitor, collect_competitor) graph.add_node(collect_report, collect_report) graph.add_node(collect_news, collect_news) graph.add_node(merge, merge_results) graph.add_edge(START, collect_competitor) graph.add_edge(START, collect_report) graph.add_edge(START, collect_news) graph.add_edge(collect_competitor, merge) graph.add_edge(collect_report, merge) graph.add_edge(collect_news, merge) graph.add_edge(merge, END)关键注意点并行节点不要直接写入同一个可变状态对象。如果多个节点同时向列表追加内容容易出现数据竞争或顺序不可控。建议每个节点返回独立字段汇总节点再统一合并。4.6 案例 6审核与改写技能场景Agent 生成的对外文案需要经过敏感词审核和格式规范检查。技能设计将“生成”和“审核”拆成两个独立节点。生成节点输出原始内容审核节点负责检查如果内容不合格则进入改写节点而不是在同一个 LLM 调用里让模型既生成又自审。def generation_node(state: State): return {raw_content: model.invoke(state[prompt])} def review_node(state: State): # 调用规则引擎或第二个 LLM 审查 issues check_sensitive_words(state[raw_content]) return {issues: issues} def rewrite_node(state: State): return {final_content: rewrite_with_issues(state[raw_content], state[issues])}关键注意点让同一个模型在同一轮生成中完成“生成自审”往往效果不好模型容易自我确认偏差。审核技能建议使用独立提示词或者直接接入规则引擎、敏感词库。4.7 案例 7技能回退与降级场景主搜索服务超时或返回空结果时Agent 必须切换到备用方案而不是直接报错。技能设计在技能内部实现回退链或在图结构里配置条件边。def search_primary(state: State): try: return {search_result: primary_search(state[query])} except Exception: return {search_result: None, search_failed: True} def search_fallback(state: State): if state.get(search_failed): return {search_result: fallback_search(state[query])} return state关键注意点回退策略要明确写在技能说明中。例如“当主搜索无结果时自动查询备用数据库并补充提示用户当前结果来自备用数据源”。不要让模型在失败后靠猜测继续执行。4.8 案例 8多 Agent 技能协作场景企业级助手需要同时处理财务、人力、技术三个领域问题。一个 Agent 很难维护所有领域的技能和权限。技能设计采用主管-子 Agent 模式。主管只负责路由把问题分发给对应领域子 Agent。每个子 Agent 只持有本领域技能。用户请求 │ ▼ 主管 Agent ├── 财务问题 → 财务子 Agent财务技能集 ├── 人力问题 → 人力子 Agent人事技能集 └── 技术问题 → 技术子 Agent技术技能集关键注意点子 Agent 返回的结果要统一格式。如果某个子 Agent 返回长文本另一个返回 JSON主管或前端就难以处理。建议所有子 Agent 都输出{answer, source, confidence}结构。4.9 案例 9会话记忆技能场景用户在多轮对话中不断补充条件例如“刚才说的那个方案把预算再加 20%”。技能设计单纯把历史消息全部塞进每次请求会快速占满上下文。更合理的是维护一份动态摘要和最近 N 条消息并在关键节点更新记忆。from langgraph.checkpoint.memory import InMemorySaver checkpointer InMemorySaver() graph graph.compile(checkpointercheckpointer) config {configurable: {thread_id: user-123}} result graph.invoke({question: 把预算增加 20%}, configconfig)关键注意点记忆不是“存得越多越好”。要区分会话内临时记忆和跨会话长期记忆。临时记忆保存最近上下文长期记忆只保存用户偏好、历史关键决策这类稳定信息。4.10 案例 10结构化输出技能场景Agent 查询订单详情后结果需要直接渲染到前端表格或对接业务系统。技能设计用结构化输出约束模型返回固定格式避免字段名漂移。from typing import TypedDict from langchain_core.pydantic_v1 import BaseModel, Field class OrderInfo(BaseModel): order_id: str Field(description订单号) amount: float Field(description订单金额) status: str Field(description订单状态) structured_llm model.with_structured_output(OrderInfo) result structured_llm.invoke(查询订单 A1001 的金额和状态)关键注意点结构化输出也需要校验。即使模型返回了 JSON也可能出现字段缺失、类型不符、枚举值超出预期的情况。运行前仍要写一遍简单的 schema 校验或正则检查。4.11 案例 11监控与观测技能场景Agent 上线生产后需要知道每个技能被调用了多少次、成功多少、失败多少、单次调用消耗多少 token。技能设计在技能入口和出口统一埋点输出 JSON Lines 格式日志。{timestamp: 2025-01-10T10:00:00Z, trace_id: abc123, skill_id: search_kb, action: start, params: {query: 登录超时}} {timestamp: 2025-01-10T10:00:05Z, trace_id: abc123, skill_id: search_kb, action: end, status: success, duration_ms: 5000, token_used: 1200}关键注意点日志里不要记录用户敏感信息例如手机号、身份证、完整密码。这类信息要么脱敏要么只记录哈希值。观测数据的价值在于复盘问题链路而不是完整复制用户数据。4.12 案例 12技能开关与灰度实验场景团队开发了新版本的 SQL 查询技能不能一次性全量替换旧技能否则出现异常会影响所有用户。技能设计给技能增加版本号和启用开关注册表可以根据用户或流量权重路由到不同版本。{ skill_id: business_metric_query, version: 2.0.0, enabled: true, routing_rules: { traffic_weight: 0.2, allow_user_ids: [tester123] } }关键注意点技能灰度必须配合回滚方案。当新版本技能错误率超过阈值时路由层要自动切回旧版本。不要等到用户投诉才手动改配置。5. 常见问题与排查链路5.1 技能调用混乱模型选错技能现象用户问“我的电脑连不上网”Agent 却调用 SQL 查询技能。原因技能描述边界与用户问题表达不匹配或者多个技能描述高度重叠。检查方式查看 Agent 规划阶段生成的中间消息确认模型选择技能的判断依据。处理建议在技能描述中增加“何时使用”和“何时不要使用”的明确说明。例如“当用户描述设备、网络、登录技术问题时使用不用于财务指标查询”。5.2 工具返回内容过长上下文溢出现象Agent 调用检索工具后工具返回 10 万字后续生成直接超过模型上下文限制。原因技能没有对工具返回结果做截断。处理建议在技能内部限制查询条数和单条长度。返回格式化内容时优先返回与问题最相关的摘要。问题现象常见原因检查方式处理建议技能反复调用模型无法确认结果正确查看多轮工具结果是否一致增加终止条件或让技能返回明确结论模型不调用工具技能描述不触发检查 Prompt 中技能名称是否可见调整描述或提高工具名称命中率API 报错版本接口变化查看 stack trace 和依赖版本锁定版本查询迁移文档结果格式不稳定缺少 schema 约束打印原始输出使用结构化输出并添加校验5.3 排查链路从入口到出口生产环境排查 Agent 问题建议按以下顺序走查看入口请求确认用户原始输入和路由参数。查看规划环节确认模型选择了哪个技能、为什么选择。查看技能调用参数确认传给工具的参数是否符合预期。查看工具返回确认返回内容是否为空、报错或截断。查看最终输出确认内容是否被后续节点改写过。把这条链路用 trace_id 串起来是排查技能问题的前提。没有链路追踪任何异常都只能靠猜。6. 更接近生产环境的最佳实践6.1 技能定义检查清单每次新增一个技能强制检查以下内容技能 ID、名称、版本号是否齐全。描述是否包含触发条件和排除场景。输入输出是否有明确 schema。是否设置了超时、重试、回退策略。是否在入口和出口记录了结构化日志。是否更新了技能注册表。是否经过灰度验证。6.2 学习环境与生产环境的差异学习环境中单靠一个 Prompt 和两三个工具就能实现很多功能。生产环境的要求比这严格得多。维度学习环境生产环境模型调用直接调用模型网关、限流、监控技能配置写死外置配置、注册表、灰度日志打印输出JSON Lines、trace_id、脱敏记忆每次从零开始Checkpointer、长期存储故障处理报错重试回退链、停机止损、自动降级安全不关注权限隔离、内容审核、数据边界版本控制无技能版本号、回滚方案6.3 落地时按什么顺序推进不要一上来规划 12 个技能。合理的推进顺序是先做 1 个检索技能和 1 个格式化输出技能跑通最小链路。再用 LangGraph 把“检索 → 生成 → 审核”三个节点固定下来。加入记忆让多轮对话能延续上下文。加入日志和 trace_id建立基础观测能力。最后做技能灰度、回退和权限控制。很多 Agent 项目失败不是模型能力不够而是把太多能力塞进了同一层。技能层存在的意义就是让 Agent 的每一个动作都可以被描述、被复用、被审计、被回滚。理解了这个前提再看 LangChain、LangGraph 以及各类技能框架思路会清晰很多。