【LangGraph实战】《LangGraph实战》_28.[第2章 LangGraph框架概览] 手把手构建ReAct Agent:从定义状态到运行图

从“原理懵圈”到“代码封神”:一篇让你彻底告别Agent智障期的LangGraph ReAct实战全攻略,手把手教你把状态、工具、循环、编译一次跑通!
文字目录:
- ReAct Agent核心机制解密:为什么你的Agent总像没头苍蝇?
- State定义:别让Agent沦为“金鱼记忆”患者
- Tool集成:给Agent装上真手脚,而不是画饼充饥
- ReAct循环节点:思考与行动的编排艺术
- Graph编译与运行:从散落节点到严密工作流
- 运行调试:当Agent开始“抽风”,你怎么接招?
嗨,大家好呀,我是你的老朋友精通代码大仙。接下来我们一起学习 《LangChain核心技术与LLM项目实践》
学编程就像打怪升级,总会遇到卡关的时候。但最怕的不是卡关,是你连BOSS的机制都没搞明白,就提着木剑往上冲。你是不是也这样?刷了几篇Agent的科普文,看了几个“五分钟上手LangGraph”的视频,感觉自己行了,结果一打开IDE,面对State、Node、Edge、ToolNode、Conditional Edge这些概念,瞬间从“我悟了”变成“我废了”。别慌,今天这篇就是专门给你这把“木剑”附魔的。咱们不整虚的,就从零开始,手把手在LangGraph里搭建一个真正能跑通的ReAct Agent。看完这篇,你不仅能跑通代码,还能明白每一步为什么要这么干。
1. ReAct Agent核心机制解密:为什么你的Agent总像没头苍蝇?
很多人一上来就急着抄代码,结果抄完发现Agent要么不调用工具瞎编答案,要么调了工具之后直接输出原始JSON,完全不给用户一个像样的回复。这就是典型的“机制没搞懂,代码全白写”。
ReAct,说白了就是Reasoning + Acting。它不是LangGraph独有的概念,但LangGraph把它做成了一个显式的状态机。核心逻辑是一个“思考-行动-观察”的循环。LLM负责在脑子里盘算(Thought),决定要不要调用工具;如果要,就发出工具调用指令(Action);工具跑完把结果吐回来(Observation);LLM拿到结果再接着想,直到觉得可以回答用户了,才输出最终答案。
痛点分析
新手在这个环节有三个致命的思维误区。
误区一:以为有了Function Calling就等于有了ReAct。 错。Function Calling只是LLM具备“发出工具调用指令”的能力,但它不负责“循环”。没有循环,模型最多调一次工具,根本不会根据工具返回的结果进行二次推理。你让Agent查天气,它调了API拿到数据,直接就把原始JSON甩给用户,这就是缺少ReAct循环的表现。
误区二:把Thought和Observation混为一谈。 有些同学喜欢在prompt里写:“你先想一想,然后调用工具,然后把工具结果告诉我”。模型确实照做了,但它的“想一想”和“工具结果”全塞在一个回复里,流程黑盒,你根本分不清它现在处于哪个阶段。一旦出错,你都不知道是模型想错了,还是工具返回错了。
误区三:总觉得“一步直达”比“循环迭代”更高级。 很多新手写Agent时,潜意识里希望LLM一次就给出完美答案,好像循环多了就显得Agent很笨。实际上,真实世界的复杂问题必须拆解。ReAct的魅力就在于让LLM像人一样“走一步看一步”,而不是开局一张嘴,剩下全靠蒙。
举个错误的代码思路,你是不是也写过类似的东西?
# 错误示范:试图用一个prompt解决所有问题
def naive_agent(query):
llm = ChatOpenAI()
tools_description = "你可以使用搜索工具..."
prompt = f"{tools_description}\n用户问题:{query}\n请思考并直接给出答案。"
return llm.invoke(prompt).content
这段代码的问题在于,LLM根本不知道它该在什么时候停下来去调工具。它要么瞎编,要么把工具描述当知识库来用,输出一堆似是而非的东西。
解决方案/正确做法
在LangGraph里,ReAct循环不是藏在prompt里的“潜规则”,而是被画在图上的“显式拓扑”。你必须用节点(Node)和边(Edge)把“思考→行动→观察→再思考”这个环给搭出来。
这样做的好处是,流程完全白盒化。每一个Thought都是一次LLM调用节点,每一个Action都是ToolNode的执行,Observation就是ToolNode返回的ToolMessage。你可以精确地知道Agent现在卡在哪个环节,状态里存了什么。
正确的认知应该是:把LangGraph当成一个状态机框架,而不是一个prompt工程框架。prompt只是节点内部的一小部分,图的骨架才是灵魂。当你显式地定义了循环,Agent才能像下图这样“转起来”:
小结: 搞懂ReAct循环是你不被Agent带偏的第一道防火墙。别再把希望全寄托在prompt的“聪明才智”上,显式的图结构才是可控性的来源。
2. State定义:别让Agent沦为“金鱼记忆”患者
如果你把LangGraph的图比作一个工厂,那State就是工厂里的传送带。每个节点是工位,State负责把物料从一个工位运到下一个工位。ReAct Agent的State,最核心的职责就是保存对话历史,让LLM在每次思考时都能拿到完整的上下文,包括它自己之前的Thought、发出去的Action,以及收回来的Observation。
痛点分析
新手在定义State时,简直是“百花齐放”地踩坑。
坑位一:抱着原生dict不放。 很多人图省事,直接定义state = {"messages": [], "query": ""},然后在各个节点里靠Key硬编码去取。这样做在节点少的时候似乎能跑,但一旦节点多了,你根本记不住哪个节点往state里塞了什么字段,类型对不对,是不是已经被某个节点偷偷改掉了。调试的时候,state就像个黑盒,print出来一长串,你看着都头大。
坑位二:消息被“覆盖”而不是“追加”。 有些同学知道要用TypedDict,于是写了个:
# 错误示范:没有使用reducer,消息会被覆盖
class BadState(TypedDict):
messages: list
结果agent节点返回{"messages": [new_message]},直接把上一轮的对话历史给顶掉了。Agent瞬间失忆,问它“刚才我说了什么”,它一脸茫然。这根本不是金鱼记忆,这是被格式化硬盘了。
坑位三:字段命名和类型与LangChain生态脱节。 LangGraph和LangChain是一家人,它们约定俗成地把对话历史放在一个叫做messages的字段里,类型是Sequence[BaseMessage],并且配合一个叫做add_messages的reducer。如果你非要自己起个名叫chat_history,类型写成list[dict],那很多预置组件(比如ToolNode、检查点机制)就和你对不上了,后续集成会越来越痛苦。
看看这个典型的错误示范:
from typing import TypedDict
# 错误:类型太松散,没有reducer,字段也不规范
class MyState(TypedDict):
input: str
history: list # 用list而不是Sequence[BaseMessage]
result: str
解决方案/正确做法
在LangGraph里,定义State的黄金法则是:用TypedDict做骨架,用Annotated做Reducer,尽量对齐MessagesState的约定。
最推荐的做法是直接继承MessagesState,它内部已经帮你定义好了带add_messages reducer的messages字段。但如果你需要加一些自定义字段(比如循环计数器、用户ID),那就自己定义TypedDict:
from typing import Annotated, Sequence, TypedDict
from langchain_core.messages import BaseMessage
from langgraph.graph.message import add_messages
class AgentState(TypedDict):
# 核心:messages字段必须使用add_messages这个reducer
messages: Annotated[Sequence[BaseMessage], add_messages]
# 自定义字段,用于后续防死循环等场景
iteration_count: int
这里Annotated[Sequence[BaseMessage], add_messages]是灵魂。add_messages是一个reducer函数,它告诉LangGraph:当多个节点都返回messages字段时,不要覆盖,而是把它们追加合并成一个列表。这样agent节点返回的AIMessage和tools节点返回的ToolMessage就能自动拼接在一起,形成完整的ReAct轨迹。
这样做的好处是什么?首先,类型安全。你的IDE能自动提示state里有哪些字段,是什么类型。其次,状态流转清晰,每个节点只负责生产自己的那部分数据,剩下的交给reducer去合并。最后,与LangChain生态无缝兼容,ToolNode、Memory、Checkpointer都能直接识别messages字段。
小结: State定义得像样,Agent才不会在节点间“失忆”或者“胡言乱语”。记住,Annotated加add_messages就是你在LangGraph里的“记忆保险”。
3. Tool集成:给Agent装上真手脚,而不是画饼充饥
Agent光会思考没用,它得能干实事。Tool就是Agent的手脚,让它能查天气、搜资料、算数学、调数据库。但在LangGraph里,Tool不是随便写个Python函数就完事的,它必须让LLM看得懂、调得动、收得回。
痛点分析
新手在Tool环节犯的错,堪称“花样作死大赛”。
作死第一式:普通函数直接上岗。 你以为写了个def search(query): ...就能给LLM用了?太天真。LLM又不是你的同事,你叫它一声它不知道这个函数需要什么参数、返回什么格式。没有@tool装饰器,就没有JSON Schema,LLM的Function Calling机制根本不知道该往哪插参数。
作死第二式:Tool的描述写得像谜语。 来看这个经典反面教材:
from langchain_core.tools import tool
@tool
def foo(query: str) -> str:
"""这是一个搜索工具"""
...
LLM看到“这是一个搜索工具”,内心是崩溃的:搜什么?参数query是关键词还是URL?返回的是摘要还是全文?描述不清楚,LLM要么不敢调,要么瞎调。Tool的docstring就是LLM的“使用说明书”,你写得含糊,它就用得拉胯。
作死第三式:返回的数据格式对LLM极不友好。 比如这样:
@tool
def search(query: str):
result = api_call(query)
return result # 返回一个复杂的对象或裸JSON
LLM看到的是一个Python对象或者一团没格式化的JSON,它根本不知道怎么从中提取信息给人类用户。ToolNode内部不会帮你做“让LLM看得舒服”的格式化。
作死第四式:绑定节点时直接用裸函数。
# 错误:直接把函数当节点加进去
builder.add_node("tools", search_weather)
这样做绕过了ToolNode的管理,工具调用的并行处理、错误封装、消息格式转换全都没了。LangGraph的ToolNode是专门用来执行工具并生成标准ToolMessage的,你必须用它。
解决方案/正确做法
定义Tool的正确姿势,我总结为“三件套”:@tool装饰器写清楚、返回字符串要干净、ToolNode来托管。
第一步,写好你的Tool函数,docstring必须包含“这个工具是干嘛的”、“每个参数是什么意思”:
from langchain_core.tools import tool
@tool
def search_weather(city: str) -> str:
"""查询指定城市的实时天气情况。
Args:
city: 城市名称,例如'北京'、'Shanghai'。
Returns:
该城市当前天气的简洁文字描述。
"""
# 这里假装调用了天气API
return f"{city}当前气温26℃,晴,空气质量优。"
第二步,把Tool装进ToolNode。ToolNode会自动处理LLM发来的tool_calls,并行执行对应的工具函数,然后把结果包装成标准的ToolMessage塞回State里:
from langgraph.prebuilt import ToolNode
tools = [search_weather]
tool_node = ToolNode(tools)
# 在StateGraph中注册节点
builder.add_node("tools", tool_node)
第三步,也是最关键的一步,让你的LLM“知道”它有这些工具可用。通过bind_tools方法:
from langchain_openai import ChatOpenAI
llm = ChatOpenAI(model="gpt-4o-mini")
# 绑定!绑定!绑定!重要的事情说三遍
llm_with_tools = llm.bind_tools(tools)
只有bind之后,LLM在推理时才会输出带有tool_calls字段的AIMessage。LangGraph的agent节点拿到这个消息,才能通过条件边把它路由到tools节点去执行。
这样做的好处是,整个Tool调用链路标准化了。你不再需要手写正则去解析模型想调什么工具,也不用自己拼消息格式。ToolNode帮你把脏活累活全干了,你只需要关心业务逻辑。
小结: Tool定义得清爽,Agent才知道“有事可做,有路可走”。记住,你的docstring不是写给人看的,是写给LLM看的“需求文档”。
4. ReAct循环节点:思考与行动的编排艺术
好了,现在咱们有了State当记忆,有了Tool当手脚,接下来该把ReAct循环真正落地成LangGraph的节点和边了。这是整个Agent的“心脏起搏器”,跳得好不好,全看这一块的编排。
痛点分析
这一块的坑,深不见底。
深坑一:LLM没bind_tools,模型永远“懒得动手”。 有些同学定义了Tool,加进了ToolNode,结果在agent节点里直接调用裸LLM:
# 错误:LLM没有绑定工具,它根本不知道能调工具
def agent_node(state: AgentState):
llm = ChatOpenAI(model="gpt-4o-mini")
return {"messages": [llm.invoke(state["messages"])]}
这样模型永远只输出文本,永远不会产生tool_calls,你的ToolNode就成了摆设,Agent退化成了一个纯聊天机器人。
深坑二:条件边逻辑写反,Agent陷入“鬼打墙”。 来看一个经典死循环:
# 错误:永远返回tools,停不下来
def should_continue(state: AgentState):
last_msg = state["messages"][-1]
# 忘了检查tool_calls是否存在,或者逻辑写反了
return "tools"
# 然后这样连边
builder.add_conditional_edges("agent", should_continue, {"tools": "tools"})
结果Agent调完工具,回到LLM,LLM输出最终答案,但条件边一看,还是把它踢回tools节点。于是Agent就在那疯狂自转,直到你的API余额烧光。
深坑三:节点返回的数据结构不遵守State契约。 LangGraph要求节点返回一个dict,key对应State的字段名。有些同学直接返回一个字符串或者一个Message对象:
# 错误:返回的不是dict
def bad_agent_node(state: AgentState):
response = llm_with_tools.invoke(state["messages"])
return response # 这是一个AIMessage对象,不是dict
LangGraph拿到这个对象,发现它没法和State合并,直接报错或者行为异常。
深坑四:忘记把tools节点连回agent节点。 有些同学知道tools执行完要回到LLM继续思考,但忘了加这条边:
# 漏了这条关键的回头路
# builder.add_edge("tools", "agent")
于是Tool执行完,图就不知道往哪走了,直接卡住或者跑到奇怪的地方去。
解决方案/正确做法
正确的节点编排,要拆成“思考节点”和“行动节点”,再用一条“条件边”做路由。
思考节点(agent节点): 负责调用绑定了工具的LLM,产出Thought和潜在的Action请求。
def agent_node(state: AgentState):
"""Agent思考节点:调用LLM决定下一步"""
messages = state["messages"]
# 必须是绑定了tools的LLM
response = llm_with_tools.invoke(messages)
# 返回dict,key对齐State字段,value是list(因为add_messages要求追加)
return {"messages": [response]}
行动节点(tools节点): 前面已经用ToolNode定义好了,它会消费tool_calls,产出ToolMessage。
条件边(should_continue): 负责检查LLM的最后一句话,判断是要继续调工具,还是已经可以收官了。
from langgraph.graph import END
def should_continue(state: AgentState) -> str:
"""路由决策:Agent是继续干活,还是直接交卷?"""
last_message = state["messages"][-1]
# 关键判断:最后一条消息是不是AIMessage,且它有没有要求调用工具
if last_message.tool_calls:
return "tools" # 有工具要调,去tools节点
return END # 没了,结束
这里有几个细节必须注意。首先,last_message.tool_calls是LangChain的AIMessage对象上的标准属性,如果LLM决定调工具,这个列表里会有字典,包含name和arguments。其次,返回值"tools"和END必须与add_conditional_edges里的映射字典对上号。
然后,把图的流转关系画清楚:
对应的代码是:
from langgraph.graph import StateGraph, END
builder = StateGraph(AgentState)
# 注册节点
builder.add_node("agent", agent_node)
builder.add_node("tools", tool_node)
# 关键:tools执行完,必须带着Observation回到agent重新思考
builder.add_edge("tools", "agent")
# 条件边:agent之后,是继续调工具,还是结束?
builder.add_conditional_edges(
"agent",
should_continue,
{
"tools": "tools", # 如果should_continue返回"tools",就去tools节点
END: END # 如果返回END,就结束
}
)
这样做的好处是,职责分离到了极致。agent节点只负责“想”,tools节点只负责“干”,should_continue只负责“判”。三者互不干涉,通过State传递消息。你想改LLM模型?只改agent节点。你想加新工具?只改ToolNode的tools列表。这种解耦,就是LangGraph相比纯prompt方案的最大优势。
小结: 节点职责清晰,边逻辑靠谱,ReAct循环才能转起来而不是“鬼打墙”。记住,bind_tools是起点,条件边是闸门,回指边是生命线。
5. Graph编译与运行:从散落节点到严密工作流
零件都造好了,现在要把它们组装成一辆能跑的车。在LangGraph里,这个组装过程就是构建StateGraph,添加节点与边,设置入口,最后compile。编译通过的那一刻,你的Agent才算真正“活”过来。
痛点分析
到了组装环节,新手往往会暴露“工程能力不足”的短板。
短板一:入口点没设,图成了“无头苍蝇”。 有些同学辛辛苦苦把节点和边都加好了,一运行报错:Missing entry point。因为他忘了告诉LangGraph:“嘿,用户消息来了,先交给谁处理?”没有set_entry_point,图根本不知道从哪个节点开始跑。
短板二:条件边的映射字典写反。 add_conditional_edges接收三个参数:源节点、路由函数、映射字典。新手常常搞混映射字典的key和value。比如:
# 错误:映射写反了,或者key对不上should_continue的返回值
builder.add_conditional_edges(
"agent",
should_continue,
{"tools": END, END: "tools"} # 完全反了!
)
结果Agent该结束时去调工具,该调工具时直接结束,行为完全混乱。
短板三:输入格式错误,invoke直接崩。 LangGraph的图期望输入是一个符合State结构的字典。有些同学直接传字符串:
# 错误:直接传字符串,State需要的是dict
result = graph.invoke("上海天气怎么样?")
正确的方式应该是传入一个dict,且messages字段是Message对象的列表。
短板四:compile之前没检查,运行时才暴雷。 有些图的结构问题(比如节点不存在却被边引用了)在compile阶段就能发现,但新手常常builder.compile()和graph.invoke()写在一起,报错信息一出来,分不清是构建问题还是运行问题。
解决方案/正确做法
组装Graph要遵循“四步走”原则:加节点、设入口、连边、再编译。
第一步,创建StateGraph,把定义好的节点加进去:
from langgraph.graph import StateGraph
builder = StateGraph(AgentState)
builder.add_node("agent", agent_node)
builder.add_node("tools", tool_node)
第二步,设置入口点。用户的输入消息,应该先交给agent节点去理解和规划:
builder.set_entry_point("agent")
第三步,把边连好。包括普通的顺序边和条件边:
# tools干完活,必须回到agent复盘
builder.add_edge("tools", "agent")
# agent干完活,由should_continue决定下一步
builder.add_conditional_edges(
"agent",
should_continue,
{
"tools": "tools",
END: END
}
)
这里要注意,add_conditional_edges的第三个参数是一个“路由返回值→目标节点”的映射。你的should_continue函数返回什么字符串,这里就必须有对应的key。
第四步,编译:
react_graph = builder.compile()
compile()会把你的图结构编译成一个CompiledGraph对象。这个对象不仅支持同步的invoke,还支持异步的ainvoke、流式输出的stream,以及后面会讲到的断点调试能力。
编译完成后,运行它:
from langchain_core.messages import HumanMessage
final_state = react_graph.invoke(
{
"messages": [HumanMessage(content="上海天气怎么样,适合穿什么衣服?")]
}
)
# 取最后一条AI消息作为答案
print(final_state["messages"][-1].content)
当你看到Agent正确地调用了天气工具,拿到气温数据,然后结合穿衣建议给出最终回答时,那种成就感,真的比你单纯调一个聊天接口爽多了。因为你知道,这一整个决策链条,是你亲手用代码搭出来的,每一步都可控、可观测、可修改。
小结: Graph编译通过的那一刻,你才真正拥有了一个“活”的ReAct Agent。记住,入口、边映射、输入格式,这三样东西错了,编译器救不了你,只能靠自己细心。
6. 运行调试:当Agent开始“抽风”,你怎么接招
代码写完了,一跑就崩?或者Agent陷入了“我觉得我需要搜索→搜索完了→我觉得我还需要搜索”的死循环?别慌,Agent开发里,写代码只占三成,调试占七成。会写Agent是入门,会调Agent才是本事。
痛点分析
调试Agent的痛,和调试普通应用完全不是一个量级。因为Agent的行为带有“不确定性”,同样的输入,LLM可能这次想调工具,下次就直接回答了。
痛点一:黑盒运行,中间过程全靠猜。 很多新手只用一个graph.invoke(),然后直接拿最后一条消息。中间LLM想了什么、调了什么工具、工具返回了什么,完全看不到。一旦结果不对,你只能对着最终结果干瞪眼,根本不知道问题出在哪个节点。
# 错误:黑盒运行,放弃治疗
result = graph.invoke({"messages": [HumanMessage(content="...")]})
print(result["messages"][-1].content) # 错了?那再跑一次试试
痛点二:Tool异常直接炸穿整个Graph。 你的Agent去调一个外部API,结果网络超时了,或者API返回500。如果你的Tool函数里没有try-catch,异常会直接抛到LangGraph的执行引擎里,整个图直接挂掉,用户看到的就是一堆难看的报错堆栈。
@tool
def fragile_tool(query: str) -> str:
# 一旦api_call抛异常,整个Agent崩掉
return api_call(query)
痛点三:Token无限燃烧,钱包在哭泣。 ReAct循环如果没有终止条件,或者条件边写错了,Agent就会陷入无限循环。调一次工具,回来再想想,想想又调一次……你的API调用次数和Token消耗直线上升,等发现的时候,余额已经蒸发了一半。
痛点四:状态黑盒,不知道图跑到哪了。 特别是当图比较复杂的时候,你甚至不确定某个节点到底有没有被执行,或者它拿到的State是不是你预期的那样。
解决方案/正确做法
调试Agent,我送你四把利器:stream观测、异常兜底、循环熔断、状态审计。
第一把利器:用stream代替invoke,让过程白盒化。
LangGraph的stream方法可以让你看到图执行的每一个步骤,每个节点输出什么State变化,一目了然:
for event in react_graph.stream(
{"messages": [HumanMessage(content="北京明天天气如何?")]}
):
for node_name, node_state in event.items():
print(f"--- 节点 [{node_name}] 输出 ---")
# 通常我们关心最后一条消息的内容
last_msg = node_state["messages"][-1]
print(last_msg)
print()
运行之后,你会看到类似这样的输出顺序:agent节点先输出一个带tool_calls的AIMessage,然后tools节点输出一个ToolMessage,然后agent节点再输出一个最终的AIMessage。哪个环节掉了链子,一眼就能定位。
第二把利器:Tool内部做异常处理,把错误变成“人话”返回给LLM。
不要让Tool抛异常,而是把异常包装成字符串,让LLM有机会在下一轮Thought里处理或者告知用户:
@tool
def robust_search(query: str) -> str:
"""尝试搜索,失败时返回友好提示"""
try:
return api_call(query)
except Exception as e:
# 把错误信息作为Observation返回给LLM
return f"搜索工具调用失败,错误信息:{str(e)}。请尝试换一种问法。"
这样即使工具挂了,Agent也不会崩,它可能会告诉用户:“刚才搜索出了点问题,但我可以直接根据已有知识回答……”
第三把利器:在State里加计数器,实现循环熔断。
这是生产环境必备的安全阀。修改你的State和节点逻辑:
class AgentState(TypedDict):
messages: Annotated[Sequence[BaseMessage], add_messages]
iteration_count: int # 增加循环计数器
在agent节点里,每次调用都把计数器加一:
def agent_node(state: AgentState):
messages = state["messages"]
response = llm_with_tools.invoke(messages)
current_count = state.get("iteration_count", 0)
return {
"messages": [response],
"iteration_count": current_count + 1
}
在should_continue里加上熔断判断:
def should_continue(state: AgentState) -> str:
# 如果循环超过5轮,强制结束,防止死循环
if state.get("iteration_count", 0) > 5:
return END
last_message = state["messages"][-1]
if last_message.tool_calls:
return "tools"
return END
这个小小的改动,能让你在深夜跑测试的时候睡个安稳觉。Agent再抽风,最多转5圈就得给我停下。
第四把利器:善用状态打印做审计。
在关键节点里,你可以临时加上print,把当前的messages长度、最后一条消息的类型打印出来。虽然土,但极其有效。LangGraph的状态是纯粹的Python对象,没有黑魔法,print是最好用的调试手段之一。
def agent_node(state: AgentState):
print(f"[DEBUG] 当前消息数: {len(state['messages'])}")
# ... 后续逻辑
这四招结合起来,你的Agent就从“不可控的玄学玩具”变成了“可观测的工程组件”。
小结: 会写Agent是入门,会调Agent才是本事。调试能力决定了你的Agent是玩具还是生产工具。记住,stream是你的眼睛,熔断是你的保险,异常处理是你的底线。
写在最后
咱们这一路走下来,从ReAct循环的原理,到State的记忆设计,到Tool的手脚安装,再到节点的编排、图的编译,最后到运行的调试,算是把LangGraph里构建一个ReAct Agent的完整链路给盘透了。
我知道,看完这篇文章,你可能还是会遇到报错,还是会发现Agent偶尔不按套路出牌。但这很正常。LangGraph不是那种“ import 一下就能毁天灭地”的魔法库,它是一个让你“把不确定的LLM行为框定在确定的状态机里”的工程框架。它的价值不在于让你少写几行代码,而在于让你能清楚地知道:此刻Agent在想什么,下一步它会做什么,如果错了该去哪个节点修。
编程之路不易,但每一步成长都算数。你今天搞明白了State和Reducer的区别,明天就能少踩一个坑;你今天亲手编译通过了一个Graph,明天就能在此基础上搭出更复杂的智能工作流。保持好奇,持续动手,别怕报错,报错才是你真正在学习的证明。你已经在路上了,继续往前走,你也能成为把Agent玩得飞起的大仙。
关注私信备注:“资料代找获取”,全网计算机学习资料代找:例如:
《课程:AI 大模型工程师系统课程 (22 章完整版 持续更新)》
《课程:AI 大模型系统实战课第四期 (2026 年开课 持续更新)》
《课程:2026 年 AGI 大模型系统课 23 期》
《课程:2026 年 AGI 大模型系统课 21 期》
《课程:AI 大模型实战课 8 期 (2026 年 2 月最新完结版)》
《课程:AI 大模型系统实战课三期》
《课程:AI 大模型系统课程 (2026 年 2 月开课 持续更新)》
《课程:AI 大模型全阶课程 (2025 年 12 月开课 2026 年 6 月结课)》
《课程:AI 大模型工程师全阶课程 (2025 年 10 月开课 2026 年 4 月结课)》
《课程:2026 年最新大模型 Agent 开发系统课 (持续更新)》
《课程:LLM 多模态视觉大模型系统课》
《课程:大模型 AI 应用开发企业级项目实战课 (2026 年 1 月开课)》
《课程:大模型智能体线上速成班 V2.0》
《课程:Java+AI 大模型智能应用开发全阶课》
《课程:Python+AI 大模型实战视频教程》
《书籍:软件工程 3.0: 大模型驱动的研发新范式.pdf》
《课程:人工智能大模型系统课 (2026 年 1 月底完结版)》
《课程:AI 大模型零基础到商业实战全栈课第五期》
《课程:Vue3.5+Electron + 大模型跨平台 AI 桌面聊天应用实战 (2025)》
《课程:AI 大模型实战训练营 从入门到实战轻松上手》
《课程:2026 年 AI 大模型 RAG 与 Agent 智能体项目实战开发课》
《课程:大模型训练营配套补充资料》
更多推荐



所有评论(0)