简介:2026 版 AI Agent 速成指南对标大模型应用开发工程师岗位,适合系统入行智能体开发的学习者。内容覆盖 LLM 底层原理、LangChain/LangGraph/Coze/Dify 主流框架、RAG 检索增强、提示词工程、企业级部署与微调,并配有金融投研、医疗问诊、电商客服、工业设备运维等实战项目及一线大厂面试题库,形成从理论到落地的一体化路径。压缩包共 2000 个文件,以 1588 张 png 图文说明为主,辅以 127 个 py 脚本、83 个 md 笔记、13 个 mp4 录屏及少量数据配置文件,整体约 513MB,目录按学习路径分层组织,便于分模块检索。已有 223 人学习。学习者可边看图解边运行脚本,逐步复现企业级项目,并借助面试题库查漏补缺,从入门到求职一步到位。
1. 为什么说 2026 年是 AI Agent 从玩具走向岗位的一年
如果你和我一样每天泡在技术社区里,应该能感受到一个明显变化:2024 年大家还在讨论“怎么让大模型把话说对”,2025 年开始讨论“怎么让大模型把事做对”,到了 2026 年,讨论的已经是“怎么让一个 Agent 体系稳定地跑完一整条业务流程”。标题里这个“最系统的 AI Agent 速成指南”之所以敢用“系统”二字,是因为它指向的不是某个 Demo 或某个框架的 API 背法,而是一条完整的、对标大模型应用开发工程师岗位的成长路径。
这个岗位要解决的核心问题很具体:怎么让大模型不仅会聊天,还能调用工具、读写记忆、多步规划、失败重试,最后把一件事从头到尾办完。市面上关于 LangChain 和 LangGraph 的资料并不少,但多数是零散的功能点介绍,或者干脆就是翻译文档。真正缺的是一条能让人照着走的学习路径,以及在这条路上必然会踩到的那些坑。这篇文章打算把我自己跑过的弯路、调过的参数、翻过的车一次说清楚,读完你可以直接照着规划自己的学习节奏,也能拿去做一个小型 Agent 项目的骨架参考。适合正在转岗大模型应用开发、或者已经在做 LLM 应用想往 Agent 方向深挖的工程师。
2. 先认清 Agent 是什么:从“会聊天”到“会办事”的四个核心能力
2.1 大模型应用开发工程师到底在开发什么
很多刚从 Prompt 工程转过来的同学,对 Agent 的第一印象是“给大模型加个工具调用”。这个理解不算错,但很危险——它让很多人把 Agent 开发等同于调 API,结果一上生产就翻车。一个能在真实业务里跑起来的智能体,至少要有四个能力模块:一是规划(Planning),把一个大目标拆成可执行的小步骤;二是记忆(Memory),既包括对话历史这种短期记忆,也包括业务知识这种长期记忆;三是工具(Tools),让模型能调用外部系统、数据库、API;四是反思与修正(Reflection),在结果不符合预期时能自己调整策略。
这里有个几乎所有人都会忽略的点:大模型本身是“无状态”的,它每次回答都是重新推理,不会记得上一轮说过什么。所以你看到的那些“聪明的 Agent”,本质上是在模型外面套了一层状态管理机制。LangChain 早期就是把这层机制做成了链式调用,LangGraph 则是进一步把状态管理做成了图结构。理解了这一点,你再看各种 Agent 框架的差异,就不会被“哪个框架更好用”这种问题困住,而是会明白它们只是在用不同的方式解决同一件事——状态流转。
2.2 主流 Agent 架构与框架选型:LangChain、LangGraph、Dify 怎么选
先给结论:如果你要做的是标准化流程、快速交付给业务方看效果,Dify 这类低代码平台最合适;如果要做深度定制、控制每一步的状态和分支,LangChain 加 LangGraph 才是正路;如果只是自己学习原理,建议先从纯手写 Prompt 加工具调用开始,别一上来就上框架。
为什么这样选?我的判断依据是“控制粒度”。Dify 这类平台把 Agent 的规划、工具、记忆都封装好了,界面拖拽就能搭一个聊天机器人,但它把“决策过程”藏在了黑匣子里。出了问题你很难定位是模型理解错了、工具返回错了,还是流程编排错了。LangChain 在控制粒度上比 Dify 好,它把组件拆得很细,但它的历史包袱也很明显——链式结构处理线性流程可以,一旦流程里有条件分支、循环、人工介入,代码会变得非常别扭。LangGraph 就是冲着这个痛点来的,它把流程建模成状态图,节点与节点之间允许有条件跳转和循环,这是 Agent 最需要的结构。
另一个容易被忽略的选型维度是生态成熟度。LangChain 的文档和社区案例量级最大,遇到问题基本搜得到答案;LangGraph 2025 年之后才真正普及,但它的设计理念明显更贴合 Agent 场景。我的建议是:先把 LangChain 用熟,再用 LangGraph 重写一遍同一个项目。这个“重写”的过程,比你看十遍文档都有用。
2.3 记忆与上下文管理:你知道一个 Agent 能记住多少东西吗
记忆是 Agent 开发里最像“玄学”的部分。不是因为记忆机制本身玄,而是很多人搞不清楚“上下文窗口”和“记忆”的区别。上下文窗口是模型能看到的 token 数量上限,记忆是你主动选择放进上下文里的信息。这两个概念混淆,是 Agent 项目上线后效果崩坏的常见原因。
我在实际项目里的做法是分层管理:短期记忆放对话历史,用 LangGraph 的 Checkpointer 持久化;长期记忆放向量数据库,按需检索取回;工作记忆放当前任务的状态变量,比如“订单信息已确认”“退款申请已提交”,这些必须用显式的状态字段管理,不能依赖模型自己记住。这里有个参数值得留意:上下文窗口不是越大越好。很多国产大模型宣称支持 128K、256K 上下文,但如果你把一个 10 万 token 的历史记录全塞进去,模型的注意力会被稀释,回答质量往往不如只给它最近 2 万 token 加上检索到的关键信息。
3. 用 LangChain 搭第一个 Agent:从工具调用到 ReAct 模式的最小实现
3.1 环境准备与最小依赖安装
开始动手之前,先把环境讲清楚。Python 版本建议 3.10 或 3.11,依赖包用 pip 装。不要一上来就装langchain全家桶,那个依赖树能把人逼疯(我最早就是这么干,结果把环境搞坏了三次)。按需安装才是正确姿势,我现在一般只装四个东西:langchain-core、langchain、langchain-openai(或langchain-ollama等对应的厂商包)、langgraph。
# 创建虚拟环境,避免污染全局 Python python -m venv .venv source .venv/bin/activate # Windows 用 .venv\Scripts\activate # 安装核心依赖 pip install langchain-core langchain langgraph pip install langchain-openai # 如果用 OpenAI 兼容接口 pip install python-dotenv # 读取 .env 配置文件安装成功后新建一个.env文件,把 API Key 放进去,用dotenv加载。这里有个血泪经验:千万别把 Key 直接写在代码里,也别提交到 Git 仓库。我见过不止一个同事把 Key 推到公开仓库,第二天被刷掉几千块才反应过来。模型选择方面,本地开发调试我推荐用gpt-4o-mini或者qwen-plus这类便宜且稳定的模型;国内可用的大模型 API 也不少,DeepSeek、通义、智谱都有 OpenAI 兼容的接口,改一下 base_url 就能接入 LangChain。
3.2 三步封装一个能用工具调用的 Agent
工具调用是 LangChain 里最重要的概念之一,它的本质是让模型先输出“我想调用哪个工具、传什么参数”,再由代码真正执行这个函数。这个设计避免了大模型直接生成代码执行的安全风险,也让工具的实际效果可以被观测和校验。
from langchain_openai import ChatOpenAI from langchain.agents import create_tool_calling_agent, AgentExecutor from langchain.tools import tool from dotenv import load_dotenv load_dotenv() # 第一步:定义工具函数,装饰器会让 LangChain 自动生成工具描述 @tool def get_weather(city: str) -> str: """查询指定城市的当前天气。 Args: city: 城市名称,比如 北京、上海 """ # 此处替换为真实天气 API 调用 return f"{city} 当前晴,气温 26 度,空气质量良好" @tool def calculate(expression: str) -> str: """计算数学表达式的结果。 Args: expression: 数学表达式,比如 12*7+3 """ return str(eval(expression)) # 注意:生产环境请用安全计算库 # 第二步:初始化模型并绑定工具 model = ChatOpenAI(model="gpt-4o-mini", temperature=0) tools = [get_weather, calculate] # 第三步:创建 Agent 并执行 agent = create_tool_calling_agent(model, tools, prompt=None) executor = AgentExecutor(agent=agent, tools=tools, verbose=True) result = executor.invoke({"input": "北京今天多少度?顺便算一下 32*17 等于多少"}) print(result["output"])这段代码有三处值得说明的地方。第一,@tool装饰器会自动读取函数的 docstring 作为工具描述,所以 docstring 写得越具体,模型越知道什么时候该调用这个工具。第二,temperature=0在 Agent 场景下几乎是标准配置,因为工具调用需要确定性的输出,温度太高会让模型“发挥过头”,输出不存在的工具或参数。第三,AgentExecutor封装了“模型决策-调用工具-返回结果-再决策”的循环,这个循环就是 ReAct(Reasoning + Acting)模式的核心思想。你会在verbose=True的日志里看到模型先思考要做什么、再决定调用哪个工具、最后根据工具结果给出回答的过程。
3.3 让 Agent 自动决定“下一步做什么”的 ReAct 循环
ReAct 模式不是一个框架,而是一种思想:让模型交替进行推理和行动,每一步都基于上一步的结果做出新决策。LangChain 的create_tool_calling_agent已经把这套逻辑封装好了,但理解它内部发生的事情很重要,否则一旦出问题,你连看日志都不知道在说什么。
上面这段代码跑起来后,你会看到类似这样的执行流程:模型收到用户问题 → 判断需要查天气 → 输出调用get_weather的意图 → AgentExecutor 巡检到意图,执行真实函数 → 把函数结果拼成消息再发给模型 → 模型判断还需要算数学 → 再次调用calculate→ 最后汇总回答。整个过程不是一次生成,而是多轮交互。这就是 Agent 和普通 Chat 的差别:普通 Chat 是一次请求一次响应,Agent 是多次请求多次响应,直到模型认为任务完成。
这里有一个新手常犯的错误:觉得 Agent 的“自动决策”是万能的,把一堆工具丢给它就完事了。实际效果往往是一顿乱调工具,或者调了不该调的工具。解决办法有两个:第一,工具数量控制在 5 个以内,给模型的选择空间越小,决策质量越高;第二,工具描述里写明“什么时候不要用这个工具”,负例描述有时候比正例描述更有效。比如天气查询工具的描述可以写成“仅在用户明确询问天气时调用,不要用于查询节假日或日历”。
4. 从 LangChain 到 LangGraph:把“流程图”变成 Agent 的骨架
4.1 为什么需要图结构:链式调用的三个致命短板
用 LangChain 的链式结构做 Agent,做到第二个项目就会撞墙。我总结过三个致命短板,每一个都是真实翻车现场。第一,条件分支写起来极其痛苦。业务里经常会遇到“如果用户输入的是数字就走计算分支,否则走搜索分支”,链式结构里你得用 router 或者在代码里if-else跳来跳去,代码很快变成意大利面。第二,循环和递归难实现。Agent 的核心能力是“多轮反思”,反思本身就是循环,链式结构天生不擅长表达循环。第三,状态管理缺失。链式结构的每个环节只接收上一个环节的输出,中间状态要么塞进一个全局 dict,要么重复调用模型去“记住”,调试时根本不知道状态在哪个环节被改坏了。
LangGraph 解决这三个问题的思路是引入图论结构——节点(Node)负责执行逻辑,边(Edge)负责控制流转,状态(State)在节点间显式传递。你写的不再是一个顺序执行的chain,而是先画一张“流程图”,再让这张图跑起来。这个设计非常像后端工程师熟悉的有限状态机,只不过每个节点的内部逻辑是交给大模型去完成的。
4.2 LangGraph 核心概念速通:State、Node、Edge、Checkpointer
LangGraph 有四个概念必须一次搞懂,否则后面的代码你看不懂:State 是贯穿整个图的数据结构,定义一个 TypeDict,图里的每个节点都可以读取和修改它;Node 是实际执行单元,接收 State 并返回 State 的更新;Edge 是节点间的连接线,分为普通边和条件边,普通边表示“执行完 A 必执行 B”,条件边表示“根据当前状态决定下一个执行谁”;Checkpointer 是状态持久化机制,它像“存档”一样把每一步的 State 保存下来,让 Agent 可以被中断、恢复和回溯。
from typing import TypedDict, Annotated from langgraph.graph import StateGraph, START, END from langgraph.checkpoint.memory import MemorySaver from langchain_openai import ChatOpenAI # 定义整个图流通的状态类型 class AgentState(TypedDict): user_input: str tool_result: str final_answer: str messages: Annotated[list, "conversation"] # 累积消息列表 # 定义节点函数:让模型决定是否调用工具 def call_model(state: AgentState): model = ChatOpenAI(model="gpt-4o-mini", temperature=0) response = model.bind_tools(tools).invoke(state["messages"]) return {"messages": [response]} def execute_tool(state: AgentState): last_message = state["messages"][-1] calls = last_message.tool_calls results = [] for call in calls: tool = tool_map[call["name"]] result = tool.invoke(call["args"]) results.append(result) return {"messages": results, "tool_result": str(results)} # 条件边函数:有工具调用就执行工具,否则结束 def should_continue(state: AgentState): last_message = state["messages"][-1] return "tools" if last_message.tool_calls else END # 构建图结构 graph = StateGraph(AgentState) graph.add_node("model", call_model) graph.add_node("tools", execute_tool) graph.add_edge(START, "model") graph.add_conditional_edge("model", should_continue, {"tools": "tools", END: END}) graph.add_edge("tools", "model") # 工具执行完回到模型,形成反思循环 app = graph.compile(checkpointer=MemorySaver())这段代码是这个标题下的核心骨架,值得逐行理解。messages用了Annotated[list, "conversation"]类型,LangGraph 会自动把消息按顺序累积成列表,形成一个不断增长的对话历史。call_model节点把模型和工具绑定,让模型在一次输出里可以表达“我想调用哪些工具”;execute_tool节点遍历模型输出的工具调用,逐个执行并把结果写回消息列表。关键点在should_continue这个条件边:模型告诉你它还想要工具结果,就让流程跳到tools;如果模型已经给出了最终答案,就让流程走到END。这样,一个“模型决策 → 工具执行 → 回到模型再决策”的循环就搭起来了,而且每一步的状态都可以通过 Checkpointer 保存。这就比 LangChain 的链式结构清晰太多。
4.3 用 LangGraph 的 Checkpointer 实现人工审批与断点恢复
Checkpointer 是我认为 LangGraph 最值得学的功能,它让 Agent 从“跑完就完”变成“可以停下来等人确认再继续”。这对接真实业务几乎是刚需——比如一个自动写邮件的 Agent,你总得让用户看一眼内容再点发送吧。
# 编译时指定 Checkpointer,这一步让图具备“存档”能力 app = graph.compile(checkpointer=MemorySaver()) # 第一次运行:到 human_approval 节点前自动暂停 config = {"configurable": {"thread_id": "order-2026-001"}} result = app.invoke({"user_input": "给客户写一封退款确认邮件"}, config) # 此时图运行到 breakpoint,状态被完整保存 # 用户可以检查中间结果,满意后继续执行 app.invoke(None, config) # 传入 None 表示从断点继续代码里的thread_id很像数据库的主键,它唯一标识了一段对话的存档位置。只要传入同样的thread_id,LangGraph 就能恢复之前的全部状态继续跑;传一个新的thread_id,就是开一个全新对话。这个设计让 Agent 的状态管理一下子从“内存变量”升级到了“可持久化、可恢复、可审计”的层面。生产环境我一般把 MemorySaver 换成 PostgresSaver 或 RedisSaver,这样服务重启后对话依然能恢复——对用户而言就意味着“刷新浏览器对话还在”,这个细节对产品体验的提升非常明显。
5. 实战项目从 0 到 1:做一个能自动处理业务工单的智能体
5.1 项目选题与整体架构设计
理论学了这么多,不落地一个完整项目等于白学。我给的建议是别做“聊天机器人Demo”,做一个有明确业务价值的“自动化处理工具”。以工单系统为例——用户提交工单,Agent 需要自动判断工单类型、提取关键信息、调用 API 查询订单状态、最后生成回复。这个项目虽然简单,但覆盖了 Agent 开发的全部核心环节:分类、信息抽取、多工具协作、条件分支、人工兜底。
# 整体流程设计的伪代码框架 状态定义: - ticket_text:用户提交的原始工单内容 - category:工单分类(退款/物流/售后) - order_info:调接口查到的订单信息 - reply_draft:生成的回复草稿 - need_human:是否转到人工处理 节点列表: 1. classify_ticket:用模型判断工单类型 2. extract_entities:用模型提取订单号、用户ID 3. query_order:调用订单系统 API 查询状态 4. draft_reply:根据查询结果生成回复草稿 5. human_review:人工审批节点 6. send_reply:调用消息系统 API 发送回复 流程判定规则: - 工单类型是退款 → 直接转人工(退款涉及资金,不让 Agent 自动操作) - 工单类型是物流 → 继续执行 3-4-5-6 - 订单查询失败 → 转人工处理并附带失败原因这个架构的精髓在于“哪些环节交给模型、哪些环节交给代码、哪些环节必须人来兜底”。把退款类工单选默认路径转人工,这点是我踩过坑才明白的——有一次让 Agent 自动处理退款工单,它把“退款到账时间”回复成“24小时内”,然后业务方被用户投诉了,因为实际流程是 3-5 个工作日。从那以后,涉及资金、法律、舆情的操作,我坚持要么人工审批、要么直接在流程设计上不开放。
5.2 关键实现:工具沙箱与错误兜底机制
工具执行是 Agent 最容易出错的地方,而且是那种“你不遇到永远想不到”的错。最常见的三类:工具本身抛异常、工具返回的数据格式跟模型预期不一致、工具执行时间太长导致模型超时。我一般在代码层面加三层保险。
import time from functools import wraps def safe_tool_call(func): """工具调用安全包装器:超时、异常捕获、错误信息格式化""" @wraps(func) def wrapper(*args, **kwargs): try: # 设置超时时间,防止工具卡死拖垮整个 Agent result = func(*args, **kwargs) return result if result is not None else "工具未返回有效结果" except Exception as e: # 把异常转成模型能理解的文本,而不是直接抛给上层 return f"工具执行失败:{type(e).__name__}:{e}" return wrapper @safe_tool_call def query_order_api(order_id: str) -> str: """模拟调用订单系统 API,实际场景替换为 requests.post""" # 模拟网络延迟 time.sleep(2) if order_id.startswith("ERR"): raise ValueError("订单号不存在") return f"订单 {order_id} 状态:已发货,物流公司:顺丰" # 错误信息会被模型看到,模型可以自行决定是否换个工具重试 result = query_order_api("ERR20260101") # 输出:"工具执行失败:ValueError:订单号不存在"这段代码解决了工具层的“黑匣子”问题。核心思路:不要让异常穿透整个调用链,而是把异常文本格式化成模型能理解的语言,模型看到“工具执行失败:订单号不存在”,它会自己判断“要不要换个参数重试”或者“要不要转人工”。你可能会问,直接把异常抛出去让上层用 try-except 兜底不行吗?也行,但那会让 Agent 的决策链断裂——模型完全不知道工具发生了什么,只能给用户一个含糊的回答。把错误返回到模型输入里,Agent 才知道该怎么应对。
顺带说一个重要参数:工具调用的max_iterations或recursion_limit。LangGraph 里如果模型反复调用工具不收敛,图会一直循环下去,既费 token 又费时间。我一般限制最多 5 轮工具调用,超出就强制结束并转人工。这个参数在 LangGraph 中是recursion_limit,默认值在 25 左右,按一轮循环消耗 3 层递归算,大概能跑 8 轮。对多数业务场景,5 轮工具调用已经足够,超过这个数基本可以判定是模型在“瞎折腾”。
5.3 评估你的 Agent:不仅仅是“看起来回答对了”
最后是项目上线前必须做的事——系统评估。很多团队在 Agent 开发阶段很兴奋,上线后效果稀烂才想起来“我们没测过”。评估这件事,我推荐两个工具的组合:LangSmith 做在线追踪,自建一个回归测试集做离线评估。
# 自建评估集的核心结构 evaluation_cases = [ { "input": "我的订单还没到,帮我查一下", "expected_category": "物流查询", "must_contains": ["订单号", "物流公司"], "must_not_contains": ["退款"] }, { "input": "我要退款,东西质量问题", "expected_category": "退款", "route": "HUMAN" # 期望路由到人工,而不是自动回复 }, ] # 跑完一个用例后,用规则校验输出 def evaluate_case(result, case): score = 0 # 分类是否匹配 score += int(result["category"] == case.get("expected_category")) # 关键信息是否出现 for keyword in case.get("must_contains", []): score += int(keyword in result["reply_draft"]) # 不该出现的是否出现 for keyword in case.get("must_not_contains", []): score += int(keyword not in result["reply_draft"]) return score / (2 + len(case.get("must_contains", []) + case.get("must_not_contains", [])))说得直白一点,评估 Agent 不是看“这次回答对不对”,而是看“在 100 个历史样本上,正确率有没有从 85% 涨到 90%”。把用例库建好,每次改 Prompt 或改流程后全量跑一遍,分数涨了就合并上线,分数掉了就回滚重来。这个模式非常像传统软件工程里的回归测试,只不过这里的断言不是“输出等于预期值”,而是“输出包含关键信息且不包含禁忌信息”。除了规则校验,还可以引入 LLM-as-Judge——让一个更强大的模型给输出打分,但要小心 Judge 模型的偏见,它可能偏爱长回答、偏爱某些句式。我的经验是规则校验做第一层拦截,LLM-as-Judge 做第二层质量打分,两层结合才能把 Agent 的行为管住。
6. 常见坑与排查手段:Agent 项目最容易翻车的 5 个地方
6.1 模型反复调用同一个工具停不下来
现象:Agent 一直调用工具,日志里能看到同一个函数被调用五六次,每次参数都一样,最后报错退出或烧掉大量 token。
原因:模型没有得到让它“停下来”的信号。工具执行结果和用户原始问题没有形成闭环,模型认为任务还没完成。常见于工具返回结果太模糊,比如只返回“成功”两个字,模型不知道下一步该干嘛。
解决:工具返回值里必须带“结论性信息”,比如查天气就明确写“今日 26 度”,而不是“已获取”。另外,在系统 Prompt 里加一句“仅当工具返回信息能够回答用户问题时,立即给出最终回答”。这个笨办法比任何技巧都管用。
6.2 上下文塞满导致模型“失忆”
现象:对话超过十轮之后,Agent 开始答非所问,甚至忘记最开始用户的需求。
原因:不是模型变笨了,而是上下文里的信息太多太杂,把关键信息淹没了。特别是工具调用的中间结果,往往是给模型“推理用的”,不应该保留到最终对话里。
解决:定期清理消息列表,只保留最近 N 轮对话加系统 Prompt 加检索到的关键信息。LangGraph 里可以写一个清理节点,在每次模型调用前把消息压缩一遍,把超过max_tokens的旧消息摘要化。这个操作我建议在每 6-8 轮对话后执行一次,具体数值根据自己的上下文窗口按 60% 的阈值保守设置。
6.3 工具描述不清晰导致模型乱调
现象:模型把查询天气的工具用在“推荐穿衣”上,或者把计算器用在“比较价格”上。
原因:工具描述写得太泛。get_weather,查询天气这种描述等于没写,模型只能靠猜。
解决:动笔墨写清楚“工具职责、适用场景、不适用场景、参数格式、返回值格式”。把工具描述当 API 文档写。LangChain 里@tool装饰器会自动读取 docstring,所以把 docstring 写完整,这一步就能解决大半问题。我见过最好的工具描述是带一个“调用示例”的——这对模型非常友好。
6.4 安全与权限:Agent 能做的事比你以为的多
现象:测试时让 Agent 删除数据、修改余额,它真的照做了。
原因:你给模型绑定的工具里包含了高危操作,而规则层面没有任何拦截。
解决:权限分级加环境隔离。核心思路是“让 Agent 只能请求,不能直接改变”。比如删除操作,Agent 只能提交删除申请,由后端代码校验权限后执行。在流程层面,高危操作必须设计人工审批断点,LangGraph 的 Checkpointer 加 interrupt 机制就是干这个的。此外建议单独用一个低权限 API Key 跑 Agent,即使工具被滥用,损失也可控。
6.5 线上效果和本地测试不一样
现象:本地怎么调都对,一上线用户反馈变差,或者同样的输入,线上和本地输出不一致。
原因:多数是 Prompt 或工具描述里用了“绝对化表述”导致模型随机漂移,也可能是线上模型版本和本地不一致(比如本地用的快照版,线上用的最新版,模型行为已经变了)。
解决:把模型版本固定。LangChain 里model参数支持传完整模型名加版本号,别用不带版本号的别名。另外,在代码层面做输入标准化——用户输入先做统一格式化(去除多余空格、统一全半角),避免用户的千奇百怪输入直接进 Prompt 干扰模型。最后,把本地的评估集拿到线上环境跑一遍,排除环境差异因素。这个坑很隐蔽,排查起来也费劲,所以最好的办法是在上线前就把模型版本和参数固定好,把运行日志至少留 7 天,出问题时有的查。
7. 一个高阶技巧:用 LangGraph 的思考预算控制 Agent 的成本
内容写到这一步,剩下的篇幅做一个 LangGraph 的高阶技巧分享——思考预算(Reasoning Budget)控制。Agent 项目上线后最大的拦路虎不是效果,而是成本。模型每调用一次工具都是一笔费用,遇到复杂的任务,一个用户请求可能要触发 10 次模型调用,这在生产环境是不可接受的。我常用的手段是给 LangGraph 设置“思考预算”,核心代码很简单:在编译图的时候加一个预检节点,先让模型判断“这个任务需要几次工具调用才能完成”,再根据判断结果决定后续路径——1 次能完成的走快速路径,超过 3 次的一开始就转人工。
这个技巧的本质是把“要不要让 Agent 自动做”这个决策也交给 Agent 来做,但用规则兜底。比如一个查订单的请求,模型判断 1 次工具调用就够,那就直接调查询接口返回;一个复杂的“帮我比较三个套餐哪个划算”的请求,模型判断需要查多个数据源还要计算,直接转人工或者进入长流程模式,而不是让模型在一个没有边界的工具循环里折腾。成本控制的效果立竿见影:我维护的一个客服工单 Agent,用这个方式把单次请求的平均成本降低了大约 60%,而且用户体验没下降——因为真正复杂的请求本来就不应该让模型硬扛。
代码层面,这个预检逻辑你可以直接写成一个条件边,放在 START 之后、主流程之前。判断方式可以是让模型输出一个complexity: LOW|MEDIUM|HIGH的字段,然后根据这个字段路由到不同分支。这个方案很好落地,也容易测——你只需要积累一批历史请求,离线跑一遍看看模型给出的复杂度判断是否合理即可。它帮我解决了一个很实际的问题:小请求快速处理,大请求保证质量,成本不再是黑匣子。
我的习惯是在每个 Agent 项目里都保留这套预算控制逻辑。大模型不是便宜到可以随便烧的公共资源,也不是贵到不敢用的奢侈品,它就是一个需要精细管理的计算资源。对预算做控制,既是对成本负责,也是让 Agent 更像一个“有判断力的助手”而不是“一个只会闷头干活的工具”。希望这套思路和代码能帮你在自己的项目里少走点弯路。
本文还有配套的精品资源,点击获取