代理即循环结构:LangChain的create_agent与LangGraph的Fit功能解析
20行代码实现的工具调用代理、循环会遇到的四种生成问题,以及何时从LangChain的create_agent切换到LangGraph的状态图。
工具的使用备受关注,但决定何时调用工具、如何处理输出以及何时停止的,其实是围绕模型构建的循环。人们将这种循环称为“编排”,不过这个说法让它听起来比实际更重要。最简单的理解方式是先整体观察整个流程,然后再看看为何它还不足以用于实际应用。
约二十行代码即可实现一个完整智能体
以工具调用流程为例:模型请求使用某个工具,你的代码执行该工具,随后输出结果返回给模型,只需在这一切周围加上一个while循环即可。首先需要初始化对话内容:
messages = [system_prompt, user_question]
接着是循环本身:
while True:
reply = model(messages, tools=TOOLS) if not reply.tool_calls:
return reply.text # the model answered; done for call in reply.tool_calls:
result = TOOLS[call.name](**call.args) # your code runs it
messages.append(tool_result(call.id, result))
那是一个真正的智能体,而非卡通形象。模型会选择要调用的工具。你的代码执行该工具并将结果追加进去。这一过程会不断重复,直到模型返回文本而非工具调用请求。
如果觉得它的规模如此之小有些令人失望,那正是预期的效果。框架所提供的任何功能,在这个循环进入生产环境后都只需添加一次即可。
逐步演示单次运行过程
假设有一个值班分诊智能体被问及自12月版本发布以来,为何夜间与运输商的同步会因ERR_4412错误而失败。它在没有框架辅助的情况下,仅通过一次循环就三步找到了答案。
第一轮。模型尚未给出回答,它发出请求search_runbooks("ERR_4412"),循环随即执行该请求。返回三个结果,其中最上面的一条指出该错误意味着运输商的认证令牌已过期。这条信息会被追加到消息中。
第2轮。读完相关内容后,模型请求调用get_release_notes("december")。返回了一份文档,其中说明令牌刷新间隔从24小时缩短到了12小时。内容再次被追加到记录中。
第3轮。模型同时持有这两份结果,以纯文本形式回复且未再发起任何工具请求。由于夜间运行的同步任务时长超过12小时,令牌会在任务进行到一半时失效。由于没有执行请求,循环就此结束。
模型选择了相应的工具并依次执行它们。消息列表增加了两条记录,之后再无其他变化。
运行一周后会出现什么问题
在实际使用环境中通常会按此顺序出现四个问题,这些问题均与模型本身无关,而是与其运行环境有关。
重启问题。在第二轮时,系统因故障变得异常缓慢,八分钟后容器被重新启动。由于messages仅存储在内存中,这些数据会消失,程序必须从问题开始处重新运行。解决方法是每轮结束后保存对话内容,之后再从该保存点继续。
危险工具。另一个夜晚,在第四轮时,模型得出的修复方案是rollback_release("dec-2025")。让模型在凌晨3点独自执行这样的操作是没人愿意看到的。此时必须停止循环,询问人类操作员,等待——可能要等到早上——然后再从暂停处继续。单纯的while循环是无法等待到早上的。
错误的转向。第5步选择了不佳的搜索查询,而第6步到第16步则通过稍作修改重复该搜索,每次都会产生费用。仅仅盯着16条信息的列表是无法发现问题出在哪里的。有效的办法是设定步骤上限,并记录下每一步的操作过程以便回放。
该往哪走。身份验证错误需要一套处理步骤,数据错误则需要另一套步骤;当两种工具得出不同结果时,应交由人工处理。模型虽能较好地选择合适的工具,但不应在每次运行时都即兴改变控制流程。各分支路径应被明确记录下来,以确保每次执行时的行为一致。
再看看那四个要素。没有一个人会指责模型聪明或笨拙,它们都源于周围的代码——即代码会持续运行什么、何时停止、记录了什么以及由谁来选择执行路径。编排正是那个位于外层的部分,正因如此,哪怕是一个包含二十行代码的循环也可能完全正确,但仍需要进一步优化。
将步骤转化为框图
再次绘制相同的执行流程,让每个步骤都以一个框表示,并用箭头连接:分类、搜索运行手册、获取发布说明、提出修复方案、获得批准,然后执行或回复。当步骤以框的形式呈现时,就会出现三种新的能力。
- 每个框在结束时都会将状态保存到持久存储中,这样重启后流程会从上一个完成的框继续,而非从头开始。
- 任何框都可以暂停整个执行流程,并在需要时等待人工处理,同时状态会被安全保存。
分支结构存在于“分类”节点中,包括认证路径、数据路径以及升级路径。你画了那三根箭头。模型仍然会在路径内的工具中选择,但路径本身由你决定。
模型本身没有变化。循环结构现在有了可见的形态,可以保存也可以中断。
LangChain 是什么
LangChain 是一个 Python 库,还有对应的 JavaScript 版本,它为大型语言模型应用提供了现成的组件,这样就不必每次都重新编写相同的代码结构。有三种类型的组件尤为重要。
模型封装器。跨不同服务提供商的统一接口。ChatAnthropic、ChatOpenAI和ChatGoogleGenerativeAI都提供了相同的.invoke(messages)方法,因此更换服务提供商只需修改一行代码,无需重写请求代码。
集成功能。为模型周边组件提供的适配器:向量存储、文档加载器、检索器、嵌入模型以及数百种外部工具。这正是节省大量工作量的关键,因为否则每个组件都需要由你自己编写和维护。
打包好的智能体循环。自2025年10月1.0版本发布以来,入口函数为create_agent,导入方式如下:
from langchain.agents import create_agent
使用方法如下:
agent = create_agent(
model="anthropic:claude-sonnet-4-6",
tools=[search_runbooks, get_release_notes],
system_prompt="You are the on-call triage assistant.",
)
result = agent.invoke({"messages": [("user", "Why is ERR_4412 failing?")]})
实际上,早先提到的循环是预先写好的。它也支持中间件:即在模型调用或工具调用前后触发的小型钩子函数。现有的中间件功能包括在工具执行前暂停等待人工确认、清除个人数据以及压缩长篇对话内容。
create_agent没有提供对循环内部逻辑的控制:无法查看各步骤之间的状态,也没有长达数天的暂停功能,而且进程崩溃会导致整个运行过程终止。
LangGraph是什么
create_agent的背后是LangGraph,这是一个由同一开发者开发的独立库。LangChain提供的是已经完成的循环结构,而LangGraph则提供构建自定义循环的组件,其结构类似于上图所示的图形。你需要定义三样东西。
状态。一个在处理流程中传递的键值对象。用于分类时,它会存储迄今为止的所有消息、错误类型、建议的修复方案以及该方案是否已被批准。
节点。接收状态并返回更新结果的普通 Python 函数。classify、search_runbooks、propose_fix 和 ask_human 都只是普通的函数而已。
边。执行顺序的体现。普通边为固定的箭头,而条件边则是读取状态并选择下一个节点的函数,由此实现了权限与数据处理的区分。
导入内容:
from langgraph.graph import StateGraph
由这些数据点构成的图表:
g = StateGraph(TriageState)
g.add_node("classify", classify)
g.add_node("search", search_runbooks)
g.add_node("propose", propose_fix)
g.add_node("approve", ask_human)
g.add_edge("classify", "search")
g.add_conditional_edges("search", route_by_error_type)
g.add_edge("propose", "approve")app = g.compile(checkpointer=PostgresSaver(...))
接近结尾的一行内容至关重要:它是一个检查点指针,即用于保存状态的位置,在图被编译时会被传入。借助它,可以在每个节点之后写入状态。重启、等待某人时的interrupt()操作,以及回退到某个节点以重新向前执行流程,这些功能都源自该参数而非手动编写的代码。你所需要做的就是绘制框和箭头。
两者的区别就在于此。LangChain提供了一个你无法窥见的循环结构;而LangGraph则要求你自行绘制这个循环,作为交换,你可以查看它、保存它、暂停它并重新执行它。上述四种情况正是无法再看不到内部结构时不可接受的时刻。
一个栈,两扇门
create_agent 是基于 LangGraph 图构建的,因此选择 LangChain 还是 LangGraph 并非框架层面的决策,而只是决定使用哪扇门的选项,实际上两者是相辅相成的。一个由 create_agent 创建的智能体本身就是一个图,这意味着它可以作为单个节点被嵌入到更大的 StateGraph 中。较大的图负责处理分类、审批和持久化功能,而内部的智能体则负责模型需要做出决策的部分。
当今天就需要一个可用的智能体,且其内部结构无关紧要时,可使用 create_agent。很多智能体根本不需要更复杂的结构。只有当需要在运行过程中查看状态、等待人工干预、从崩溃中恢复或回退时,才需要使用 StateGraph,这些正是之前提到的那四个问题。
Anthropic和OpenAI提供的供应商SDK位于该技术栈之外,而非其中。它们运行相同的循环逻辑,且仅与某一模型系列相关联;如果使用同一供应商,集成时的阻力最小。其代价则是缺乏可移植性。
给团队的建议
先写出那二十行循环代码,即便之后会丢弃。这只需一小时时间,之后就能清楚地了解各个框架在为你做什么,这也是调试它们的唯一方法。
一旦出现上述四个问题中的任何一个,应立即切换到LangGraph。能够调用三个工具并给出答案的聊天机器人无需图结构支持;而需要持续运行、等待审批或可重复执行的智能体则必须依赖图结构,从头构建持久化执行机制本身就是一个独立的项目。
团队往往在第一天就选用图形化工具来处理那些简单循环就能解决的问题,然后却花费四分之一的时间去学习框架,而本应将这些时间用于解决问题本身。