在线咨询 400-826-1668
回到顶部
ARTICLE DETAIL

资讯详情

深耕国风建站与运营引流的一线实战洞察。

第二阶段(核心攻坚):死磕 LangGraph + ReAct 架构 + 工具调用,手撕一个带记忆的多工具 Agent

第二阶段(核心攻坚):死磕 LangGraph + ReAct 架构 + 工具调用,手撕一个带记忆的多工具 Agent 1. 为什么「带记忆的多工具 Agent」是面试硬通货大模型应用开发已经过了“会调 API 就能写简历”的阶段。现在面试官真正想考察的是你能否让模型在真实任务中自主决策、调用外部工具、并且记住上下文持续工作。这三点分别对应三个核心技术ReAct 架构Reasoning Acting 的循环让模型在“思考”和“行动”之间交替而不是一次性输出答案。LangGraph把 Agent 的决策流程建模成状态图比传统的 AgentExecutor 更灵活、更可控。工具调用Tool Calling让模型能查天气、搜网页、执行代码、读写数据库突破纯文本生成的边界。再加一层记忆——多轮对话里模型要记住用户之前说过什么、执行过什么这就是“带记忆的多工具 Agent”。这套组合拳在面试里几乎必考而且通常要求现场手撕代码。本文会用一段可以复制运行的 Python 代码完整实现一个带记忆的多工具 Agent。读完你就能直接拿去面试。核心概念在正式进入代码之前先建立三个关键概念的共识它们是理解后文所有示例的基础Agent智能体以大模型为决策中枢能够根据目标自主规划步骤、调用外部工具并持续迭代执行的应用形态。它不是简单的“问答机器人”而是一个会“干活”的执行体。ReAct 架构Reasoning Acting 的缩写让模型在“推理”与“行动”之间交替循环。模型先思考下一步再决定是调用工具还是输出最终答案直到任务完成。工具调用Tool Calling模型输出结构化指令、由程序实际执行外部工具并返回结果的能力。它让 Agent 从“只能生成文本”扩展为“能查数据、能计算、能操作外部系统”。这三个概念叠加起来再加上贯穿多轮对话的记忆就构成了本文要手撕的完整 Agent。2. ReAct 架构模型为什么要「边想边做」2.1 传统方式的问题如果只用一个简单的提示词让模型回答问题模型遇到“北京明天天气怎么样”这种需要实时数据的问题时只能凭训练数据编一个答案。这不是能力问题而是它没有获得外部信息的手段。2.2 ReAct 的核心循环ReAct 的核心是一个循环需要不需要接收用户问题模型思考需要工具吗输出工具调用指令执行工具拿到结果把结果喂回模型输出最终答案每一轮循环包含两个动作Reasoning推理模型分析当前状态决定下一步是调用工具还是直接回答。Acting行动模型生成一个结构化的工具调用由程序真正执行工具再把结果拼回上下文。这个循环会一直持续直到模型认为信息足够、输出最终答案为止。2.3 一个最小的 ReAct 提示词REACT_PROMPT你是一个智能助手可以使用以下工具 {tools} 请按照以下格式回答 Thought: 你的思考过程 Action: 要调用的工具名 Action Input: 工具的输入参数 Observation: 工具执行结果 ...Thought/Action/Action Input/Observation 可以重复多次 Thought: 我现在知道最终答案了 Final Answer: 给用户的最终回答 用户问题{question} 这个提示词就是 ReAct 的“灵魂”。它强制模型按照固定的节奏输出程序再解析Action和Action Input真正调用工具。现在很多框架已经把这个过程封装好了但面试官问你 ReAct 原理时你要能讲清楚这个循环。3. LangGraph 核心概念把 Agent 画成一张图3.1 为什么不用 LangChain 的 AgentExecutor早期 LangChain 的AgentExecutor是一个黑盒循环流程写死在框架里。想加记忆、加人工确认、加条件分支都很别扭。LangGraph 的思路完全不同把 Agent 的每个环节都拆成一个节点Node用边Edge连接起来形成一个显式的状态图。好处有三个流程可视化Agent 的每一步都看得见出问题好排查。高度可控可以随时在图里插入人工审批、重试、超时等节点。状态可持久化图的状态就是记忆的载体天然支持多轮对话。3.2 三个核心概念概念作用类比State在节点之间传递的数据结构流水线上的工件Node一个处理函数接收 State 并返回更新流水线上的工位Edge决定下一个节点是谁可以带条件流水线上的传送带一个最简单的图长这样fromtypingimportTypedDict,Annotatedfromlanggraph.graphimportStateGraph,START,ENDclassAgentState(TypedDict):messages:Annotated[list,messages]defnode_a(state:AgentState):# 处理逻辑返回要更新的字段return{messages:state[messages][A 处理完成]}graphStateGraph(AgentState)graph.add_node(a,node_a)graph.add_edge(START,a)graph.add_edge(a,END)appgraph.compile()3.3 条件边ReAct 循环的图表达ReAct 里“要不要继续调工具”是一个判断这在 LangGraph 里用条件边表达defshould_continue(state:AgentState)-str:last_messagestate[messages][-1]# 如果最后一条消息包含工具调用就回到工具节点ifgetattr(last_message,tool_calls,None):returntools# 否则结束returnEND graph.add_conditional_edges(agent,should_continue,{tools:tools,END:END})这就是 LangGraph 表达 ReAct 循环的完整方式agent节点负责推理tools节点负责执行工具条件边决定“继续做”还是“收工”。整个循环在图的状态里自动运行直到走出END。4. 实战手撕一个带记忆的多工具 Agent下面是一段完整可运行的代码。我们用langchain-openai作为模型层工具是两个常用场景查询天气和执行简单计算。整个 Agent 有完整的四轮对话记忆并且每轮都能调用不同工具。4.1 安装依赖pipinstalllanggraph langchain-openai langchain-core4.2 定义工具fromlangchain_core.toolsimporttooltooldefget_weather(city:str)-str:查询指定城市的天气。 参数: city: 城市名称例如北京 weather_data{北京:晴28°C空气质量优,上海:多云26°C,广州:雷阵雨32°C,深圳:晴30°C,}returnweather_data.get(city,f暂无{city}的天气数据)tooldefcalculate(expression:str)-str:执行一个数学表达式计算。 参数: expression: 数学表达式例如 123 * 45 67 try:resulteval(expression,{__builtins__:{}},{})returnf计算结果为{result}exceptExceptionase:returnf计算失败{e}说明示例中eval使用了受限的命名空间仅用于教学演示。生产环境请替换为ast解析或专用计算库避免任意代码执行风险。4.3 定义带记忆的 State记忆的本质是把历史消息保存在 State 里并且使用add_messages归并器来追加而不是覆盖fromtypingimportTypedDict,Annotatedfromlangchain_core.messagesimportBaseMessagefromlanggraph.graph.messageimportadd_messagesclassAgentState(TypedDict):# add_messages 保证了每轮消息会追加到列表末尾而不是覆盖messages:Annotated[list[BaseMessage],add_messages]4.4 绑定工具并构建图importosfromlangchain_openaiimportChatOpenAIfromlanggraph.graphimportStateGraph,START,ENDfromlanggraph.prebuiltimportToolNode# 初始化模型modelChatOpenAI(modelgpt-4o-mini,temperature0,api_keyos.getenv(OPENAI_API_KEY),)# 把工具列表绑定到模型tools[get_weather,calculate]model_with_toolsmodel.bind_tools(tools)defagent_node(state:AgentState):agent 节点模型根据当前消息决定调用工具还是直接回答。responsemodel_with_tools.invoke(state[messages])return{messages:[response]}# 构建图defbuild_agent():graphStateGraph(AgentState)graph.add_node(agent,agent_node)# ToolNode 会自动解析最后一条消息里的 tool_calls 并执行工具graph.add_node(tools,ToolNode(tools))defshould_continue(state:AgentState)-str:last_messagestate[messages][-1]ifgetattr(last_message,tool_calls,None):returntoolsreturnEND graph.add_edge(START,agent)graph.add_conditional_edges(agent,should_continue,{tools:tools,END:END})graph.add_edge(tools,agent)returngraph.compile()appbuild_agent()4.5 四轮对话验证记忆与工具切换fromlangchain_core.messagesimportHumanMessage,AIMessage# 记忆列表会贯穿所有轮次memory:list[BaseMessage][]# 第一轮查天气memory.append(HumanMessage(content北京今天天气怎么样))resultapp.invoke({messages:memory})memoryresult[messages]print(第一轮,memory[-1].content)# 第二轮计算题验证工具切换memory.append(HumanMessage(content请帮我算一下 1024 * 768 等于多少))resultapp.invoke({messages:memory})memoryresult[messages]print(第二轮,memory[-1].content)# 第三轮追问天气验证多轮记忆memory.append(HumanMessage(content那上海呢))resultapp.invoke({messages:memory})memoryresult[messages]print(第三轮,memory[-1].content)# 第四轮验证模型记得上下文memory.append(HumanMessage(content我刚才问了哪几个城市))resultapp.invoke({messages:memory})memoryresult[messages]print(第四轮,memory[-1].content)第三轮的“那上海呢”最能体现记忆的价值模型必须回顾前文才知道用户是在问天气而不是问别的。第四轮则直接考验模型对整段对话的回忆能力。5. 面试高频追问这样回答才加分5.1 “ReAct 和 Function Calling 是什么关系”参考答案Function Calling 是模型层的能力指模型可以输出结构化的工具调用请求ReAct 是应用层的循环架构它利用 Function Calling 来驱动“思考—行动—观察”的循环。两者不在一个层面Function Calling 是“手”ReAct 是“怎么用手”。5.2 “为什么用 LangGraph 而不是直接写 while 循环”参考答案while 循环也能实现 ReAct但 LangGraph 提供了三样东西一是状态管理图的 State 天然支持持久化、断点恢复和回溯二是可视化与可观测性每个节点的输入输出都能追踪三是可扩展性加人工审批、并行分支、超时重试都只需要改图结构不需要改动核心循环代码。5.3 “记忆存在哪怎么持久化”参考答案短期记忆存在 State 的messages列表里进程内就能跑持久化需要给graph.compile()传入checkpointer比如MemorySaver内存或SqliteSaver磁盘这样每次invoke都传入thread_id就能恢复对应会话的完整状态。跨会话的长期记忆还可以单独挂一个向量库把历史对话存成 embedding检索后拼进提示词。5.4 “工具调用失败了怎么办”参考答案分两层处理。第一层是模型层面在工具描述里写清楚异常返回格式模型拿到错误信息后会自己调整策略第二层是图层面用ToolNode的异常捕获或者自定义 try/except 包裹节点把工具异常转成一条普通消息返回给模型让它继续决策避免整个 Agent 崩溃。5.5 “怎么控制 Agent 不无限循环”参考答案三个手段一是设定max_iterations超过就强制结束并返回中间结果二是在should_continue里加判断连续 N 次调用同一个工具且输入没变化就终止三是给模型绑定max_tokens和时间预算。这些控制逻辑在图的条件边里实现非常自然。6. 总结这一阶段的目标不是“会用 LangGraph”而是能从零手写出一个带记忆的多工具 Agent并讲清楚每一层为什么这样设计。梳理一下核心链路ReAct 循环LangGraph 状态图Tool Calling记忆 State可运行的多工具 Agent面试时如果被要求手撕就按本文的节奏来先定义工具再定义带add_messages的 State然后构建agenttools两个节点最后用条件边串成循环。逻辑清晰、结构完整就是硬通货该有的样子。
返回列表