从零构建AI智能体:基于LangChain的ReAct模式实战指南

发布时间:2026/8/4 7:12:30
从零构建AI智能体:基于LangChain的ReAct模式实战指南 在实际 AI 项目开发中我们常常遇到这样的困境大语言模型LLM本身能力强大能说会道但让它独立完成一个复杂的、多步骤的任务时却常常表现得像个“健忘的专家”——它可能忘记上一步的指令无法调用外部工具或者在需要决策时陷入循环。这正是智能体工程Agent Engineering要解决的核心问题。它不是一个单一的工具或库而是一套工程化的设计模式与架构思想旨在将 LLM 从一个“聊天大脑”升级为一个能够自主感知、规划、决策和执行的“智能体”。本文面向希望将 LLM 能力真正融入业务流程的开发者、架构师和技术决策者。我们将从零开始构建一个能够理解复杂任务、拆解步骤、调用工具并持续学习的智能体系统。整个过程不依赖任何单一商业平台而是聚焦于可复现的开源框架和设计模式。你将理解智能体的核心组件如规划器、工具集、记忆模块掌握如何用代码实现一个具备基本能力的智能体并学会在生产环境中部署、监控和优化它。最终你将获得一套可应用于客服自动化、数据分析、代码生成等场景的实战能力。1. 智能体工程的核心从“聊天”到“行动”的范式转变在深入代码之前必须厘清智能体Agent与普通 LLM 应用的本质区别。普通应用是“一问一答”的静态模式用户输入模型输出交互结束。而智能体是“目标驱动”的动态系统它拥有持续的目标、内部状态和与外界交互的能力。1.1 智能体的基本架构ReAct 模式与思考-行动-观察循环当前最主流的智能体架构思想源于 ReActReasoning Acting模式。其核心是一个循环智能体接收任务进行内部“思考”Reasoning决定下一步“行动”Acting通常是调用一个工具然后“观察”Observation行动的结果并基于此进行下一轮思考直到任务完成或无法继续。这个循环由几个关键组件支撑规划器Planner通常是 LLM 本身负责理解任务并将其分解为一系列可执行的子步骤。例如任务“帮我分析上个月的销售数据并生成报告”规划器可能将其分解为1. 连接数据库2. 查询上月销售数据3. 计算关键指标4. 调用图表生成工具5. 汇总成报告文档。工具集Tools智能体可以调用的外部函数或 API。这是智能体突破 LLM 知识截止日期和无法执行操作限制的关键。工具可以是搜索引擎、计算器、数据库查询、代码执行环境、文件操作等。记忆模块Memory分为短期记忆会话历史和长期记忆向量数据库存储的知识。它让智能体记住之前的对话、工具调用结果和学到的知识避免重复工作和上下文遗忘。执行引擎Agent Core协调以上所有组件管理 ReAct 循环的执行流程处理异常并决定何时终止。1.2 主流智能体框架选型LangChain vs. LlamaIndex vs. 自研对于初学者和快速原型开发使用成熟框架是最高效的选择。下表对比了两个最流行的开源框架特性LangChainLlamaIndex核心定位构建由LLM驱动的应用程序的全功能框架链Chains、智能体Agents是其重要组成部分。专注于数据索引与检索的数据框架其智能体能力建立在强大的数据连接之上。智能体支持原生、强大提供多种智能体类型如 ReAct, OpenAI Functions, Plan-and-Execute。工具集成极其丰富。提供智能体接口但其设计更倾向于让智能体基于检索到的上下文数据进行推理和操作。上手难度中等偏高概念多抽象层次高灵活性极强。中等如果你核心需求是让LLM与你的数据对话它更直接。适用场景需要复杂工作流、多工具协调、自定义逻辑的通用型智能体应用。以企业私有数据问答、文档分析为核心并需要在此基础上执行简单动作的场景。代码风格声明式与编程式结合通过组合链、智能体、工具等组件来构建应用。更偏向于围绕“索引”和“查询引擎”构建智能体作为上层抽象。对于本文的实战我们将选择LangChain因为它提供了最标准、最完整的智能体抽象其设计思想也最具普适性。理解了 LangChain 的智能体再迁移到其他框架或自研都会非常容易。2. 环境准备与项目初始化构建智能体的工作台我们将构建一个“数据分析智能体”它能够理解用户对数据的需求自动编写并执行 Python 代码如 pandas 操作进行分析最后总结结果。这个场景涵盖了规划理解需求、拆解步骤、工具使用代码执行和记忆保留数据上下文。2.1 基础环境与依赖安装首先确保你的开发环境已安装 Python推荐 3.9 以上版本。我们将创建一个干净的虚拟环境并安装核心依赖。# 创建并进入项目目录 mkdir ai_agent_project cd ai_agent_project # 创建虚拟环境以 conda 为例也可使用 venv conda create -n agent_env python3.10 -y conda activate agent_env # 安装 LangChain 及其相关依赖。我们使用 OpenAI 的模型作为智能体的“大脑”。 # 注意你需要准备一个有效的 OPENAI_API_KEY。 pip install langchain langchain-openai langchain-experimental # 安装代码执行工具所需的依赖 pip install pandas numpy matplotlib # 安装 Jupyter 内核工具用于安全执行生成的代码 pip install ipykernel关键解释langchain: 核心框架。langchain-openai: OpenAI 模型的官方 LangChain 集成。langchain-experimental: 包含一些尚在实验阶段但非常有用的组件如高级智能体类型。pandas, numpy, matplotlib: 我们的智能体将用来进行数据分析的库。ipykernel: 提供一个相对隔离的代码执行环境比直接使用exec()更安全。2.2 项目结构设计一个清晰的目录结构有助于管理智能体的各个模块。建议如下ai_agent_project/ ├── agents/ # 智能体核心定义 │ ├── __init__.py │ └── data_analyst_agent.py ├── tools/ # 自定义工具集 │ ├── __init__.py │ └── code_executor.py ├── memory/ # 记忆模块如自定义记忆后端 │ └── __init__.py ├── config/ # 配置文件 │ └── settings.py ├── data/ # 示例数据文件 │ └── sample_sales.csv ├── logs/ # 运行日志 ├── main.py # 应用主入口 └── requirements.txt现在在config/settings.py中配置你的 API 密钥和环境变量# config/settings.py import os from dotenv import load_dotenv load_dotenv() # 从 .env 文件加载环境变量 OPENAI_API_KEY os.getenv(OPENAI_API_KEY) # 你可以在这里添加其他配置如模型名称、温度等 MODEL_NAME gpt-4o-mini # 或 gpt-3.5-turbo创建一个.env文件在项目根目录注意不要提交到版本控制OPENAI_API_KEYyour_openai_api_key_here3. 构建核心组件工具、记忆与智能体实例智能体的强大与否很大程度上取决于其“工具包”的丰富程度和设计合理性。3.1 创建自定义代码执行工具LangChain 提供了BaseTool抽象类。我们将创建一个安全的代码执行工具它允许智能体运行 Python 代码来处理数据。# tools/code_executor.py import ast import sys import traceback from io import StringIO from typing import Type, Optional from langchain.tools import BaseTool from pydantic import BaseModel, Field class CodeExecutionInput(BaseModel): 代码执行工具的输入模型。 code: str Field(description需要被执行的 Python 代码字符串。) class SafeCodeExecutorTool(BaseTool): name python_code_executor description 用于执行 Python 代码以进行数据分析、计算或绘图。 输入必须是有效的 Python 代码字符串。 代码应尽可能自包含避免依赖未定义的变量。 工具会返回代码的标准输出、错误信息或最后一个表达式的值。 args_schema: Type[BaseModel] CodeExecutionInput return_direct: bool False # 执行后继续循环不直接返回给用户 def _run(self, code: str) - str: 执行代码并捕获输出。 # 1. 安全检查尝试解析代码语法 try: ast.parse(code) except SyntaxError as e: return f语法错误: {e} # 2. 准备一个干净的执行环境 local_vars { pd: __import__(pandas), np: __import__(numpy), plt: __import__(matplotlib.pyplot), __builtins__: __builtins__, } global_vars {} # 3. 重定向标准输出和错误 old_stdout sys.stdout old_stderr sys.stderr sys.stdout captured_output StringIO() sys.stderr captured_error StringIO() result None try: # 4. 执行代码 exec(code, global_vars, local_vars) # 尝试获取最后一个表达式的值非赋值语句 tree ast.parse(code) if tree.body and isinstance(tree.body[-1], ast.Expr): last_expr ast.unparse(tree.body[-1]) result eval(last_expr, global_vars, local_vars) except Exception as e: # 5. 捕获运行时异常 captured_error.write(traceback.format_exc()) finally: # 6. 恢复标准输出/错误 sys.stdout old_stdout sys.stderr old_stderr output captured_output.getvalue() error captured_error.getvalue() # 7. 构造返回信息 if error: return f执行出错:\n{error} else: response f代码执行成功。\n标准输出:\n{output} if result is not None: response f\n最后一个表达式的结果: {repr(result)} return response async def _arun(self, code: str) - str: 异步版本本例中暂不实现。 raise NotImplementedError(此工具不支持异步执行。)关键解释与安全考量输入验证使用args_schema定义强类型输入确保智能体传入的是代码字符串。语法检查使用ast.parse提前检查代码语法避免明显的恶意代码结构。沙箱环境我们创建了一个local_vars字典作为执行命名空间只预先导入了pandas、numpy和matplotlib.pyplot。这在一定程度上限制了可访问的模块。注意这并非完全安全的沙箱生产环境需要考虑更严格的隔离方案如 Docker 容器、专用沙箱服务。输出捕获重定向sys.stdout和sys.stderr来捕获打印信息和错误。结果提取尝试解析代码的最后一个表达式并求值将结果返回给智能体这有助于链式计算。错误处理完整捕获并返回异常堆栈帮助智能体理解哪里出错了。3.2 配置智能体使用 LangChain 的 ReAct 模式现在我们将工具、LLM 和记忆组合起来创建一个 ReAct 智能体。# agents/data_analyst_agent.py import os import sys sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) from langchain.agents import AgentExecutor, create_react_agent from langchain_openai import ChatOpenAI from langchain.prompts import PromptTemplate from langchain.memory import ConversationBufferMemory from tools.code_executor import SafeCodeExecutorTool from config.settings import OPENAI_API_KEY, MODEL_NAME def create_data_analyst_agent(): 创建并返回一个数据分析智能体执行器。 # 1. 初始化 LLM llm ChatOpenAI( openai_api_keyOPENAI_API_KEY, model_nameMODEL_NAME, temperature0, # 对于执行任务低温度保证稳定性 streamingFalse, ) # 2. 准备工具列表 tools [SafeCodeExecutorTool()] # 3. 创建 ReAct 提示模板 # LangChain 有内置的 ReAct 模板但我们可以微调以更适合数据分析场景 prompt_template 你是一个专业的数据分析助手。你可以使用工具来执行 Python 代码以分析数据、计算统计量或生成图表。 请严格遵循以下格式 问题用户提出的问题 思考你需要分析问题并决定是否需要使用工具。如果需要请解释原因和计划。 行动需要调用的工具名称必须是以下之一[{tool_names}] 行动输入工具的输入必须是一个格式正确的 JSON 字符串例如 {{code: import pandas as pd; df pd.read_csv(data.csv)}} 观察工具返回的结果 ... (这个 思考/行动/行动输入/观察 循环可以重复多次) 思考我现在知道了最终答案 最终答案对用户问题的清晰、完整的回答包含数据、结论或图表描述。 开始 之前的对话历史 {history} 问题{input} 思考{agent_scratchpad} prompt PromptTemplate.from_template(prompt_template) # 4. 初始化记忆 memory ConversationBufferMemory(memory_keyhistory, return_messagesTrue) # 5. 创建 ReAct 智能体 agent create_react_agent(llm, tools, prompt) # 6. 创建智能体执行器它负责运行循环 agent_executor AgentExecutor( agentagent, toolstools, memorymemory, verboseTrue, # 打印详细的思考过程便于调试 handle_parsing_errorsTrue, # 优雅处理解析错误 max_iterations10, # 防止无限循环 early_stopping_methodgenerate, # 当智能体输出最终答案时停止 ) return agent_executor if __name__ __main__: # 快速测试 agent create_data_analyst_agent() test_query 我有一个CSV文件在 data/sample_sales.csv请用pandas加载它并告诉我总销售额是多少。 result agent.invoke({input: test_query}) print(\n 智能体最终回答 ) print(result[output])关键参数说明temperature0对于执行具体任务的智能体将“创造性”降至最低保证输出和工具调用的稳定性。verboseTrue在开发阶段至关重要它会打印出智能体完整的“思考”、“行动”、“观察”过程是调试智能体逻辑的主要依据。handle_parsing_errorsTrue当智能体输出的动作格式不符合预期时尝试让 LLM 重新生成而不是直接崩溃。max_iterations10安全阀强制限制 ReAct 循环的最大次数防止因逻辑错误导致无限循环和 API 费用失控。prompt_template我们自定义的提示词明确了输出格式并加入了{history}和{agent_scratchpad}两个关键变量前者由记忆模块填充后者由执行引擎在每一步填充。4. 运行验证与结果分析观察智能体如何工作准备一个简单的数据文件data/sample_sales.csvdate,product,region,quantity,unit_price 2024-01-01,Product_A,North,10,100 2024-01-02,Product_B,South,5,150 2024-01-03,Product_A,East,8,100 2024-01-04,Product_C,North,12,80 2024-01-05,Product_B,West,6,150运行python agents/data_analyst_agent.py。由于设置了verboseTrue你将在控制台看到类似以下的详细输出 进入新的 AgentExecutor 链... 思考用户想让我分析一个CSV文件。我需要先读取这个文件。我可以使用 python_code_executor 工具来执行读取CSV的Python代码。 行动python_code_executor 行动输入{code: import pandas as pd; df pd.read_csv(data/sample_sales.csv); print(df.head()); print(\\n数据形状, df.shape)} 观察代码执行成功。 标准输出: date product region quantity unit_price 0 2024-01-01 Product_A North 10 100 1 2024-01-02 Product_B South 5 150 2 2024-01-03 Product_A East 8 100 3 2024-01-04 Product_C North 12 80 4 2024-01-05 Product_B West 6 150 数据形状 (5, 5) 最后一个表达式的结果: None 思考数据已成功加载。用户要求计算总销售额。我需要先计算每行的销售额quantity * unit_price然后求和。 行动python_code_executor 行动输入{code: import pandas as pd; df pd.read_csv(data/sample_sales.csv); df[sales] df[quantity] * df[unit_price]; total_sales df[sales].sum(); print(f总销售额为: {total_sales})} 观察代码执行成功。 标准输出: 总销售额为: 4460 最后一个表达式的结果: None 思考我已经计算出了总销售额并打印了出来。现在可以给出最终答案了。 最终答案已成功加载 data/sample_sales.csv 文件。数据包含5行5列。计算得到的总销售额为 **4460**。结果分析规划与分解智能体正确理解了任务并将其分解为两个子步骤1. 加载并查看数据2. 计算总销售额。工具调用在每一步“思考”后它都正确地选择了python_code_executor工具并传入了格式正确的 JSON 输入。状态保持在第二步的代码中它直接使用了df变量这依赖于上一步代码在同一执行环境中运行的结果。这得益于我们的工具设计local_vars在单次调用内是持续的。更复杂的场景可能需要智能体显式地将数据保存到变量或上下文中。循环终止在得到计算结果后智能体判断任务完成输出“最终答案”并结束了循环。5. 常见问题排查当智能体“失灵”时怎么办智能体开发中90%的问题集中在提示词、工具定义和循环逻辑上。以下是典型问题及排查路径。5.1 问题智能体陷入无限循环或重复调用同一工具现象控制台不断打印相似的“思考-行动-观察”循环无法输出最终答案直到达到max_iterations限制。可能原因与排查工具描述不清检查工具的description字段是否清晰、无歧义地说明了工具的用途、输入格式和输出。模糊的描述会导致 LLM 误用工具。观察结果不具信息量工具返回的结果observation可能太简单如只返回“成功”或太复杂如大段错误堆栈导致 LLM 无法基于此做出有效决策。确保工具返回的信息能明确指导下一步行动。提示词缺少终止条件在提示词模板中必须明确告知智能体“何时”以及“如何”给出最终答案。强化“最终答案”部分的描述。LLM 温度过高将temperature设为 0 或一个很低的值如 0.1以增加决策的确定性。解决方案优化工具描述description “用于执行Python代码。输入必须是包含有效Python代码的JSON对象键为‘code’。工具将返回执行后的打印输出或错误信息。”让工具返回结构化信息例如“成功。输出[...]。计算结果是xxx。”在提示词中增加约束“如果你已经获得了足够的信息来回答问题请直接输出‘最终答案’不要再调用工具。”5.2 问题智能体无法正确解析工具输入格式现象日志中出现“Could not parse LLM output: ...”或智能体输出的行动输入不是有效的 JSON。可能原因与排查提示词格式要求不严格检查提示词模板中关于行动输入的说明。必须明确要求是JSON字符串并给出精确示例。LLM 能力不足过于复杂或嵌套的工具输入格式可能让某些模型困惑。尽量简化输入结构。未启用错误处理确保AgentExecutor初始化时设置了handle_parsing_errorsTrue。这会让框架在解析失败时尝试让 LLM 重新生成输出。解决方案在提示词中使用更明确的示例“行动输入{{code: print(hello)}}”。使用OpenAI Functions或Structured Tools等更现代的智能体类型它们利用模型的原生函数调用能力格式错误率大大降低。5.3 问题工具执行失败但智能体无法从错误中恢复现象工具返回了错误信息如“执行出错: NameError: name pd is not defined”但智能体在下一轮思考中要么无视错误要么做出了错误的决策。可能原因与排查错误信息不友好工具返回的原始异常堆栈对 LLM 来说可能难以理解。需要在工具层面对错误进行提炼和转译。缺乏错误处理指导提示词中没有教导智能体如何处理工具错误。它可能不知道“遇到 NameError 意味着需要导入模块”。解决方案在工具的_run方法中将捕获的异常转换为更自然的语言return f“代码执行失败错误类型是‘{type(e).__name__}’。具体信息{str(e)}。请检查变量名是否正确或是否缺少必要的导入语句。”在提示词的思考部分加入引导“如果观察结果显示工具执行出错请分析错误原因修正你的代码然后再次尝试。”5.4 智能体开发调试清单当智能体行为不符合预期时请按此顺序检查日志是否开启了verboseTrue仔细阅读每一步的“思考”、“行动”、“观察”。提示词复制完整的提示词包含被填充的变量到 OpenAI Playground看模型是否能按格式响应。工具单独测试你的工具输入智能体试图调用的参数看是否能返回预期结果。记忆检查memory中存储的历史消息看是否有干扰信息或信息丢失。LLM 调用检查 API 密钥、网络、模型名称是否正确以及是否触发了速率限制。6. 进阶优化与生产实践从原型到可用的系统一个能在学习环境跑通的智能体距离生产可用还有很大差距。以下是关键的优化方向。6.1 增强工具能力与安全性基础的代码执行工具风险极高。生产环境必须强化严格沙箱使用Docker容器或在安全的云函数中执行代码限制 CPU、内存、网络和文件系统访问。超时控制为工具执行设置超时如 30 秒防止恶意或低效代码长期占用资源。代码审查在执行前用规则引擎或另一个轻量级 LLM 对代码进行安全检查过滤危险操作如os.system,__import__(‘os’)。专用工具与其提供一个万能的代码执行器不如为常见操作创建专用工具如query_database_tool,generate_chart_tool,send_email_tool。专用工具更安全、更易控制。6.2 优化记忆与上下文管理ConversationBufferMemory会无限制地增长很快会耗尽 LLM 的上下文窗口。摘要式记忆使用ConversationSummaryMemory或ConversationSummaryBufferMemory定期将长对话压缩成摘要。向量记忆将重要的对话片段或工具执行结果存入向量数据库如Chroma,Weaviate。当需要相关信息时通过检索增强生成RAG的方式动态注入上下文。分层记忆区分短期会话记忆和长期知识记忆。将学到的通用知识如“用户偏好图表类型为折线图”存入长期记忆供后续会话使用。6.3 引入规划与验证机制复杂任务需要更高级的规划。Plan-and-Execute 架构使用一个“规划者”LLM 先制定详细的步骤计划再由一个“执行者”LLM 或智能体按步骤调用工具。这比单一的 ReAct 循环更适合长流程任务。子智能体多智能体创建多个各司其职的智能体如“数据获取智能体”、“分析智能体”、“报告生成智能体”由一个“主管智能体”进行协调。这符合单一职责原则也便于调试。输出验证在智能体给出最终答案前引入一个“验证”步骤。可以是规则校验如数字是否在合理范围也可以是让另一个 LLM 进行逻辑审查。6.4 监控、评估与持续改进没有监控的智能体上线是危险的。链路追踪记录每个用户会话的完整轨迹包括所有的思考、行动、观察。这对于复现问题和优化提示词至关重要。关键指标定义并监控成功率、任务完成步数、工具调用错误率、用户满意度等指标。A/B测试对提示词、工具集或模型进行 A/B 测试用数据驱动优化。人工审核与强化学习对于关键任务初期引入人工审核环节。将人工纠正的案例作为新的训练数据微调 LLM 或优化提示词实现智能体的持续进化。智能体工程是将 LLM 从“玩具”变为“生产力工具”的关键桥梁。它要求开发者不仅懂 prompt 技巧更要具备系统设计、安全编程和运维思维。从构建一个简单的 ReAct 智能体开始逐步为其添加更强大的工具、更可靠的记忆和更严谨的管控流程你就能打造出真正解决实际业务问题的 AI 应用。下一步你可以尝试集成更多类型的工具如网络搜索、API 调用或者探索更复杂的多智能体协作架构例如让一个智能体专门负责代码生成另一个负责代码审查和安全检查。

相关新闻