【大模型】从零构建 LLM Agent(一):用 LangGraph 跑通 ReAct Agent 从零构建 LLM Agent一用 LangGraph 跑通 ReAct Agent系列说明本系列围绕「从用到懂到造」展开记录基于自部署 LLM 构建 Agent 的实践。篇主题阶段1本文用create_react_agent跑通一个 ReAct Agent用2手写StateGraph拆解 ReAct 循环的状态机实现懂3脱离预置图自实现 agent loop造4引入长期记忆向量检索与 RAG造5多 Agent 协作与人机协同human-in-the-loop造6生产化可观测、容错与部署工程运行环境Python 3.11.15 · langgraph 1.2.9 · langchain-openai 1.3.5 · langchain-core 1.4.9模型Qwen3-235B-A22B公司内网自部署暴露 OpenAI 兼容接口/v1/chat/completions。适用场景本文不依赖 OpenAI / Anthropic 官方 API所有调用指向自部署的 Qwen3对同样使用自部署模型vLLM / Ollama 部署 Qwen、DeepSeek 等的场景可直接复用。1. Agent 与 Chatbot 的边界从工程视角二者的差别集中在控制流归属与是否具备外部交互能力维度ChatbotAgent控制流调用方驱动单轮请求-响应LLM 驱动内部循环工具调用无通过 function calling 协议调用外部函数状态对话历史短期消息链 可选长期记忆终止条件单次生成结束LLM 不再发起 tool_call据此可给出工程定义Agent 是以 LLM 为决策核心、通过 tool calling 协议与外部环境交互、由 LLM 自主控制循环终止的有状态系统。其最小实现可表述为一个循环while True: resp llm(messages, toolstools) if not resp.tool_calls: break messages execute(resp.tool_calls) # 得到 Observation messages resp return resp.contentLangGraph 的create_react_agent即对该循环的图封装。本文先用它跑通第 2 篇再拆开。2. 前置验证模型是否支持 tool callingtool calling 是 Agent 的硬依赖LLM 必须能按 OpenAI function calling 协议返回结构化的tool_calls框架据此分发到对应函数。Qwen3 走 OpenAI 兼容接口需实测确认。验证脚本fromopenaiimportOpenAI clientOpenAI(base_urlhttp://10.138.17.22:3000/v1,api_keysk-xxx)tools[{type:function,function:{name:get_weather,description:查询某城市实时天气,parameters:{type:object,properties:{city:{type:string}},required:[city]},}}]respclient.chat.completions.create(modelQwen3-235B-A22B,messages[{role:user,content:北京今天天气怎么样请用工具查询}],toolstools,)print(resp.choices[0].message.tool_calls)# - [ChatCompletionMessageToolCall(nameget_weather, arguments{city: 北京})]模型正确返回了结构化工具调用未编造天气内容。该协议链路可用后续基于langchain-openai的ChatOpenAI.bind_tools()可直接复用。备注Qwen3 默认开启 thinking 模式会在content中输出推理过程。在 tool calling 场景下其content为空、仅返回tool_calls不影响框架解析。3. 环境与依赖独立 conda 环境隔离依赖conda create-nagentpython3.11-yconda activate agent pipinstalllanggraph langchain-openai openai requests选型说明Python 3.11Python 3.9 已于 2025-10 EOLLangGraph 1.x 主流支持 3.10/3.11。langchain-openai因 Qwen 暴露 OpenAI 兼容接口ChatOpenAI可零适配接入无需额外 Provider。版本langgraph 1.2.9 / langchain-openai 1.3.5 / langchain-core 1.4.9。框架迭代快复现请对齐版本。已知问题另有 embedding 服务bge-m3/v1/embeddings当前网络不通本篇不涉及留待第 4 篇 RAG 部分处理。4. 实现三文件结构config.py 配置集中管理endpoint / api_key / model tools.py 工具集合tool 声明 agent.py 构建 agent、运行与 trace 输出4.1 工具定义工具是普通 Python 函数tool装饰器基于函数签名 docstring自动生成 JSON Schema作为 tool calling 协议中的function.parameters下发给 LLM。因此 docstring 既是文档也是契约质量直接影响模型选 tool 的准确率。fromlangchain_core.toolsimporttooltooldefget_weather(city:str)-str:查询指定城市的实时天气。fake_db{北京:晴25°C,上海:多云28°C,...}returnfake_db.get(city,f暂无{city}天气数据)本篇共定义四个工具get_weather、get_current_time、calculate、get_stock_price均为 mock 数据。其中calculate对表达式做了字符白名单校验并以禁用__builtins__的eval执行避免注入。4.2 构建与运行fromlangchain_openaiimportChatOpenAIfromlanggraph.prebuiltimportcreate_react_agent llmChatOpenAI(modelQwen3-235B-A22B,base_url...,api_key...,temperature0.7)agentcreate_react_agent(llm,[get_weather,get_current_time,calculate,get_stock_price],prompt你是一个个人助手优先调用工具获取信息不要编造。用中文回答。,)resultagent.invoke({messages:[{role:user,content:...}]})create_react_agent内部构建的状态图┌──────────┐ tool_calls 非空 ┌──────────┐ │ agent │ ────────────────▶│ tools │ │ (调 LLM) │◀──────────────── │ (执行函数) │ └──────────┘ ToolMessage 回填 └──────────┘ │ tool_calls 为空 ▼ END (输出 AIMessage)状态以消息列表为载体message-based stateagent 节点产出AIMessage含tool_callstools 节点执行后产出ToolMessage含结果二者均 append 到messages再回流 agent 节点直至tool_calls为空终止。第 2 篇会用StateGraph显式重建此结构。5. 行为验证与 trace 解读5.1 工具链式协作串行输入「现在几点了腾讯股票多少钱持有 100 股市值多少港元」[1] user: 现在几点了腾讯股票多少钱持有100股市值多少港元 [2] AIMessage tool_calls: get_current_time({}) [3] ToolMessage: 2026年07月15日 16:11:59 Wednesday [4] AIMessage tool_calls: get_stock_price({symbol: 腾讯}) [5] ToolMessage: 腾讯控股 当前 385.6 港元1.2% [6] AIMessage tool_calls: calculate({expression: 100 * 385.6}) [7] ToolMessage: 100 * 385.6 38560.0 [8] AIMessage: 市值约为 38,560 港元。第 [6] 步中calculate的入参385.6来源于第 [5] 步get_stock_price的返回。LLM 在第 [4]→[5] 取得股价前无法发起计算故 [2][4][6] 呈串行依赖。这条 trace 体现了 agent 对跨步骤数据依赖的隐式规划能力。5.2 无依赖调用并行输入「北京和上海今天哪个更暖和」[1] user: 北京和上海今天哪个更暖和 [2] AIMessage tool_calls: get_weather({city: 北京}) tool_calls: get_weather({city: 上海}) # 同一 AIMessage 内并行 [3] ToolMessage: 北京 晴25°C [4] ToolMessage: 上海 多云28°C [5] AIMessage: 上海更暖和。两个查询无数据依赖模型在单条AIMessage中发起多个tool_calls框架并行执行。对比 5.1可见串行/并行由任务的数据依赖决定由模型在生成 tool_calls 时隐式调度。5.3 工具边界判断输入「介绍一下你自己」时模型不发起任何tool_calls直接返回AIMessage。agent 循环在首轮即终止。这说明工具是否调用由模型基于 prompt 与 tool schema 自主判断而非强制。6. 小结ReAct 的工程实现是一个以消息列表为状态、以tool_calls是否为空为终止条件的循环tool基于签名与 docstring 生成 schema是工具契约的来源create_react_agent将上述循环封装为 agent↔tools 两节点的状态图agent 的自主性体现在工具选择、跨步数据依赖规划、串并行调度、终止判断均由 LLM 隐式完成。7. 下一阶段create_react_agent隐藏了状态结构与转移逻辑。下一篇改用langgraph.graph.StateGraph显式定义状态、节点与边重建等价的 ReAct 图拆解状态如何在节点间流转、循环如何被tool_calls条件边控制。对应代码见 series 仓库v1-first-agenttag。