从零构建AI Agent核心:ReAct循环、工具调用与可观测性实践

📅 2026/8/12 15:40:05
从零构建AI Agent核心:ReAct循环、工具调用与可观测性实践
1. 项目概述从概念到可运行的AI Agent核心骨架最近在AI应用开发圈里一个词的热度居高不下AI Agent。无论是技术论坛还是产品讨论似乎不提Agent就显得不够前沿。但当你真正想动手搭建一个属于自己的Agent时面对网上铺天盖地的框架LangChain、AutoGen、CrewAI等和复杂的概念规划、记忆、工具使用、多智能体协作很容易感到无从下手。我们需要的往往不是一个功能齐全但笨重的“全家桶”而是一个清晰、简洁、可运行的核心实现用来理解Agent到底是如何“思考”和“行动”的。今天我们就来动手实现一个最简化的AI Agent核心。这个Agent将具备几个关键能力它能根据目标进行“思考-行动-观察”的循环ReAct能调用外部工具如搜索、计算我们能观察它的内部决策过程可观测并且它的“记忆”可以保存下来持久化。这个项目不依赖任何重型框架我们将用Python从零开始一步步构建这个“麻雀虽小五脏俱全”的Agent骨架。通过这个过程你不仅能理解Agent的核心运作机制更能获得一个可以在此基础上无限扩展的坚实基础代码。2. 核心架构与设计思路拆解在开始写代码之前我们必须先想清楚这个简化Agent的骨架应该长什么样。一个典型的AI Agent其核心可以抽象为一个持续运行的循环。这个循环的每一次迭代都代表Agent对外部世界的一次“感知-思考-行动”过程。我们参考学术界和工业界广泛认可的ReActReasoning Acting范式它完美地诠释了这一过程。2.1 ReAct循环Agent的“大脑”工作流ReAct的核心思想是让大型语言模型LLM交替进行推理Reasoning和行动Acting。推理步骤让模型分析当前情况、制定计划或解释观察结果行动步骤则让模型执行一个具体的动作比如调用一个工具。这个循环会一直持续直到任务完成或达到终止条件。在我们的简化实现中这个循环将包含以下四个阶段任务解析与初始化接收用户输入的目标并初始化Agent的状态。思考ReasonLLM根据当前状态包括目标、历史观察、可用工具进行分析决定下一步该“想”什么或“做”什么。行动Act如果上一步决定要行动则解析出要调用的工具名称和参数并执行该工具。观察Observe获取工具执行的结果将其作为新的“观察”加入到Agent的状态历史中。这个循环会一直进行直到LLM在“思考”步骤中输出一个特殊的结束标记例如“Final Answer: ...”或者达到预设的最大循环次数。2.2 工具调用Agent的“手和脚”Agent不能只空想必须能影响外部世界或获取新信息。这就是工具调用的作用。一个工具本质上是一个函数它有一个名称、一段描述和具体的实现。例如search_web(query): 根据查询词搜索网络。calculator(expression): 计算一个数学表达式。get_weather(city): 获取某个城市的天气。LLM在“思考”后如果决定采取行动就需要以特定的格式如Action: tool_name[action_input]输出其决定。我们的系统需要能解析这个格式找到对应的工具函数传入参数并执行最后将结果返回给LLM作为下一轮循环的“观察”。2.3 可观测性给Agent装上“调试器”对于一个自动运行的智能体如果它出错了或者做出了匪夷所思的决策我们该如何排查这就需要可观测性。在我们的实现中可观测性意味着我们要完整记录Agent运行过程中的所有“思维痕迹”。这包括每一轮循环中发送给LLM的完整提示词Prompt。LLM返回的原始响应包含思考和行动指令。解析出的具体行动工具名和参数。工具执行的结果观察。Agent的完整对话历史。我们将把这些信息以结构化的方式如字典列表实时输出到控制台并可能保存到日志文件中。这样开发者就能像查看程序日志一样清晰地追溯Agent的每一步决策这对于调试和优化提示词至关重要。2.4 持久化让Agent拥有“记忆”一个没有记忆的Agent每次都是“全新”的这显然不符合智能体的定义。持久化就是为了解决这个问题。我们需要让Agent的状态在程序重启后得以保留。这里的状态主要指对话历史用户与Agent的多轮交互记录。Agent的内部状态可选例如长期目标、学到的知识片段等。最简单的持久化方式就是将对话历史以JSON或文本格式保存到本地文件或数据库中。每次启动Agent时先加载历史记录将其作为上下文的一部分输入给LLM这样Agent就能“记得”之前说过什么、做过什么实现连续的、有上下文的对话。2.5 技术选型为什么是这些组件为了实现上述设计我们需要做出具体的技术选择编程语言Python。这是AI领域的事实标准拥有最丰富的LLM接入库和工具生态。LLM接口OpenAI API (GPT系列)或开源模型本地接口如Ollama。为了简化我们首选OpenAI API因为它稳定、易用并且GPT模型在遵循指令和格式输出方面表现优异。我们会使用openai这个官方库。持久化存储本地JSON文件。对于这个简化项目文件存储是最轻量、最直接的方式。我们将把对话历史保存为一个.json文件。工具模拟由于我们搭建的是核心框架为了演示我们将模拟几个工具函数而不是真正去调用搜索引擎或天气API。这足以展示整个调用链路。这个架构的优点是高度模块化ReAct循环是控制器工具调用是执行器可观测性是监控器持久化是存储器。每一部分都可以独立替换和增强。3. 核心模块实现与代码解析接下来我们进入实战环节用代码将上述设计一一实现。请确保你已安装Python建议3.8和openai库pip install openai。你需要准备一个OpenAI API Key。3.1 环境准备与基础配置首先我们创建一个新的Python文件比如simple_agent.py并导入必要的库设置API密钥。import openai import json import os from datetime import datetime from typing import Dict, List, Any, Optional, Callable # 设置OpenAI API Key (请替换成你的真实Key或通过环境变量设置) openai.api_key os.getenv(“OPENAI_API_KEY”, “your-api-key-here”) # 定义一个简单的日志函数用于可观测性输出 def log_agent_step(step_type: str, data: Dict): “”“记录Agent的每一步操作。”“” timestamp datetime.now().strftime(“%Y-%m-%d %H:%M:%S”) print(f”\n[{timestamp}] {step_type.upper()}:“) # 美化打印字典 print(json.dumps(data, indent2, ensure_asciiFalse))注意在实际项目中绝对不要将API密钥硬编码在代码中。务必通过环境变量os.getenv(“OPENAI_API_KEY”)或安全的密钥管理服务来加载。3.2 工具系统设计与实现工具系统是Agent与外界交互的桥梁。我们设计一个Tool类来统一管理。class Tool: “”“工具类封装一个可被Agent调用的函数。”“” def __init__(self, name: str, description: str, func: Callable): “”“ 初始化工具。 :param name: 工具名称LLM将通过这个名字来调用它。 :param description: 工具描述用于告诉LLM这个工具是做什么的。 :param func: 工具对应的Python函数。 “”“ self.name name self.description description self.func func def execute(self, **kwargs) - str: “”“执行工具并返回字符串格式的结果。”“” try: result self.func(**kwargs) return str(result) except Exception as e: return f”Error executing tool ‘{self.name}’: {str(e)}“ # 定义几个示例工具函数 def search_web(query: str) - str: “”“模拟网络搜索。在实际应用中这里会调用Google Search API等。”“” # 这里我们模拟返回一些固定结果 mock_results { “python tutorial”: “Python is a popular programming language. Here are some tutorials...“, “weather in Beijing”: “The weather in Beijing is sunny, 25°C.“, “calculate 22”: “The result of 22 is 4.“, } return mock_results.get(query, f”No results found for ‘{query}’.“) def calculator(expression: str) - str: “”“计算数学表达式。警告直接使用eval有安全风险此处仅用于演示。”“” try: # 严重警告在生产环境中应对表达式进行严格的净化和校验避免代码注入。 # 这里仅为最简化演示使用eval。 result eval(expression, {“__builtins__”: None}, {}) return str(result) except Exception as e: return f”Calculation error: {str(e)}“ def get_weather(city: str) - str: “”“模拟获取天气。”“” weather_data { “Beijing”: “Sunny, 25°C, humidity 40%.”, “Shanghai”: “Cloudy, 22°C, humidity 65%.”, “New York”: “Rainy, 18°C, humidity 80%.”, } return weather_data.get(city, f”Weather data not available for {city}.“) # 创建工具注册表 TOOL_REGISTRY: Dict[str, Tool] { “search”: Tool(“search”, “Useful for searching the web for current information. Input should be a search query.”, search_web), “calculator”: Tool(“calculator”, “Useful for performing mathematical calculations. Input should be a valid arithmetic expression.”, calculator), “get_weather”: Tool(“get_weather”, “Useful for getting the current weather in a city. Input should be a city name.”, get_weather), }实操心得工具描述description非常关键。它需要清晰、简洁地告诉LLM两件事1. 这个工具是干什么的2. 输入应该是什么格式好的描述能极大提高LLM调用工具的准确性。另外工具函数的返回值最好是字符串以便LLM处理。3.3 ReAct循环引擎Agent的核心控制器这是整个Agent的“大脑”和“循环泵”。我们将实现一个ReactAgent类。class ReactAgent: “”“基于ReAct范式的简化AI Agent。”“” def __init__(self, model: str “gpt-3.5-turbo”, max_iterations: int 10): “”“ 初始化Agent。 :param model: 使用的LLM模型名称。 :param max_iterations: ReAct循环的最大迭代次数防止无限循环。 “”“ self.model model self.max_iterations max_iterations self.conversation_history: List[Dict[str, str]] [] # 存储对话历史 self.iteration_count 0 def _build_system_prompt(self) - str: “”“构建系统提示词定义Agent的角色、能力和格式要求。”“” tools_text “\n”.join([f”- {name}: {tool.description}” for name, tool in TOOL_REGISTRY.items()]) return f”””You are a helpful AI assistant that can use tools to solve problems. You have access to the following tools: {tools_text} You must always respond in the following format: Thought: (your reasoning about what to do next) Action: (the name of the tool to use, must be one of [{‘, ‘.join(TOOL_REGISTRY.keys())}]) Action Input: (the input to the tool, as a valid JSON string, e.g., {{“query”: “python tutorial”}}) Observation: (the result of the action) … (this Thought/Action/Action Input/Observation can repeat N times) When you have enough information to give a final answer to the user, you MUST output: Thought: I now have the final answer. Final Answer: (your final answer to the original user question) Important Rules: 1. The “Action” must be exactly one of the provided tool names. 2. The “Action Input” must be a valid JSON string that matches the tool’s expected input. 3. You can only take ONE action per response. 4. If a tool returns an error, analyze the error in your next Thought. “”” def _call_llm(self, messages: List[Dict]) - str: “”“调用LLM并返回其回复内容。”“” try: response openai.ChatCompletion.create( modelself.model, messagesmessages, temperature0.1, # 低温度保证输出格式稳定 max_tokens500 ) return response.choices[0].message.content.strip() except Exception as e: return f”LLM API Error: {str(e)}“ def _parse_llm_response(self, response: str) - Dict[str, Any]: “”“解析LLM的回复提取Thought, Action, Action Input等部分。”“” result {“thought”: “”, “action”: None, “action_input”: None, “final_answer”: None} lines response.split(‘\n’) current_section None for line in lines: line line.strip() if line.startswith(“Thought:”): current_section “thought” result[“thought”] line.replace(“Thought:”, “”).strip() elif line.startswith(“Action:”): current_section “action” result[“action”] line.replace(“Action:”, “”).strip() elif line.startswith(“Action Input:”): current_section “action_input” # 尝试解析JSON输入 input_str line.replace(“Action Input:”, “”).strip() try: # 处理可能的花括号 if input_str.startswith(“{“) and input_str.endswith(“}”): result[“action_input”] json.loads(input_str) else: # 如果不是标准JSON尝试将其作为字符串参数处理例如对于单个查询 result[“action_input”] {“query”: input_str} except json.JSONDecodeError: result[“action_input”] {“input”: input_str} elif line.startswith(“Observation:”): # 观察部分由系统提供不在本次解析中 current_section “observation” elif line.startswith(“Final Answer:”): result[“final_answer”] line.replace(“Final Answer:”, “”).strip() break elif current_section “thought” and result[“thought”]: # 处理多行Thought result[“thought”] “ “ line return result def run(self, user_input: str) - str: “”“执行ReAct循环处理用户输入。”“” print(f”\n 开始处理用户请求: ‘{user_input}’ “) self.iteration_count 0 # 将用户输入加入历史 self.conversation_history.append({“role”: “user”, “content”: user_input}) # 构建初始消息列表 messages [ {“role”: “system”, “content”: self._build_system_prompt()}, ] # 添加上下文历史如果实现持久化这里会加载历史 messages.extend(self.conversation_history) while self.iteration_count self.max_iterations: self.iteration_count 1 print(f”\n--- ReAct循环 第 {self.iteration_count} 轮 ---“) # 1. 调用LLM进行“思考” log_agent_step(“prompt_to_llm”, {“messages”: messages}) llm_response self._call_llm(messages) log_agent_step(“llm_raw_response”, {“response”: llm_response}) # 2. 解析LLM响应 parsed self._parse_llm_response(llm_response) log_agent_step(“parsed_response”, parsed) # 将LLM的完整回复包含Thought等加入对话历史作为assistant的回复 self.conversation_history.append({“role”: “assistant”, “content”: llm_response}) # 3. 检查是否已有最终答案 if parsed[“final_answer”] is not None: print(f”\n Agent 得出最终答案 “) log_agent_step(“final_answer”, {“answer”: parsed[“final_answer”]}) self.conversation_history.append({“role”: “assistant”, “content”: f”Final Answer: {parsed[‘final_answer’]}“}) return parsed[“final_answer”] # 4. 检查并执行行动 if parsed[“action”] is not None and parsed[“action_input”] is not None: tool_name parsed[“action”] if tool_name in TOOL_REGISTRY: tool TOOL_REGISTRY[tool_name] # 执行工具 action_input parsed[“action_input”] # 如果action_input是字典将其展开作为关键字参数否则作为单一参数传递 if isinstance(action_input, dict): observation tool.execute(**action_input) else: observation tool.execute(action_input) log_agent_step(“tool_execution”, {“tool”: tool_name, “input”: action_input, “output”: observation}) # 5. 将“观察”结果加入消息历史用于下一轮循环 observation_msg f”Observation: {observation}“ messages.append({“role”: “user”, “content”: observation_msg}) self.conversation_history.append({“role”: “user”, “content”: observation_msg}) else: error_msg f”Observation: Error: Unknown tool ‘{tool_name}’. Available tools are: {list(TOOL_REGISTRY.keys())}.“ messages.append({“role”: “user”, “content”: error_msg}) self.conversation_history.append({“role”: “user”, “content”: error_msg}) log_agent_step(“tool_error”, {“error”: error_msg}) else: # 如果既没有最终答案也没有行动可能格式错误注入一个错误观察 error_msg “Observation: Error: Your response format was incorrect. You must output ‘Thought:’, then ‘Action:’ and ‘Action Input:’, or ‘Final Answer:’.” messages.append({“role”: “user”, “content”: error_msg}) self.conversation_history.append({“role”: “user”, “content”: error_msg}) log_agent_step(“format_error”, {“error”: error_msg}) # 循环结束未得出答案 timeout_msg f”ReAct loop reached maximum iterations ({self.max_iterations}) without final answer.“ log_agent_step(“timeout”, {“message”: timeout_msg}) return timeout_msg代码解析与注意事项系统提示词_build_system_prompt这是引导LLM行为的关键。我们明确规定了输出格式Thought/Action/Action Input/Observation列出了可用工具及其描述并强调了关键规则如Action必须是给定工具之一。清晰的提示词是Agent稳定工作的基石。LLM调用_call_llm我们设置了较低的temperature0.1这是为了确保LLM尽可能严格地遵守我们指定的输出格式减少随机性。对于需要创造性的任务可以适当调高。响应解析_parse_llm_response这是一个脆弱的环节。我们编写了一个简单的解析器按行识别关键词。在实际复杂应用中可以考虑使用更鲁棒的方法比如要求LLM输出JSON格式或者使用Pydantic模型进行验证。循环控制设置了max_iterations默认为10以防止Agent陷入死循环。这是一个重要的安全措施。错误处理在工具调用和响应解析阶段都加入了基本的错误处理并将错误信息作为“Observation”反馈给LLM让它有机会自我纠正。3.4 可观测性实现记录每一步思维可观测性已经内嵌在上述run方法中。我们通过log_agent_step函数在控制台输出了每一步的关键信息prompt_to_llm: 发送给LLM的完整消息列表。llm_raw_response: LLM返回的原始文本。parsed_response: 解析后的结构化结果思考、行动、输入。tool_execution: 工具执行的输入和输出。final_answer: 最终答案。在实际项目中你可以将这些日志写入文件如JSONL格式或发送到监控系统如PrometheusGrafana以便进行长期分析和可视化。3.5 持久化实现保存对话历史持久化让我们能保存Agent的“记忆”。我们实现两个简单的方法来保存和加载对话历史到JSON文件。def save_history(self, filepath: str “agent_history.json”): “”“将当前对话历史保存到JSON文件。”“” with open(filepath, ‘w’, encoding‘utf-8’) as f: json.dump(self.conversation_history, f, indent2, ensure_asciiFalse) print(f”对话历史已保存至: {filepath}“) def load_history(self, filepath: str “agent_history.json”): “”“从JSON文件加载对话历史。”“” try: if os.path.exists(filepath): with open(filepath, ‘r’, encoding‘utf-8’) as f: self.conversation_history json.load(f) print(f”已从 {filepath} 加载对话历史共 {len(self.conversation_history)} 条记录。“) else: print(f”历史文件 {filepath} 不存在将从空历史开始。“) except Exception as e: print(f”加载历史文件失败: {e}将从空历史开始。“) self.conversation_history []现在我们的ReactAgent类在初始化后可以先调用load_history来恢复上次的对话状态。在run方法结束时或定期调用save_history来保存状态。这样即使程序重启Agent也能“记得”之前的对话。4. 运行示例与结果分析让我们写一个主函数来测试这个Agent。def main(): # 1. 初始化Agent agent ReactAgent(model“gpt-3.5-turbo”, max_iterations8) # 2. 可选加载之前的对话历史 # agent.load_history(“my_agent_history.json”) # 3. 运行一个示例任务 user_question “What is the weather in Beijing, and then calculate the square of 15?” final_answer agent.run(user_question) print(f”\n 最终返回给用户的结果 “) print(final_answer) # 4. 可选保存本次对话历史 # agent.save_history(“my_agent_history.json”) if __name__ “__main__”: main()运行这个脚本你将在控制台看到类似以下的输出具体内容因LLM响应而异 开始处理用户请求: ‘What is the weather in Beijing, and then calculate the square of 15?’ --- ReAct循环 第 1 轮 --- [2023-10-27 10:00:00] PROMPT_TO_LLM: { “messages”: [ {“role”: “system”, “content”: “You are a helpful AI assistant that can use tools…”}, {“role”: “user”, “content”: “What is the weather in Beijing, and then calculate the square of 15?”} ] } [2023-10-27 10:00:01] LLM_RAW_RESPONSE: Thought: The user is asking two things: the weather in Beijing and a calculation. I should use the tools sequentially. First, I need to get the weather for Beijing. Action: get_weather Action Input: {“city”: “Beijing”} [2023-10-27 10:00:01] PARSED_RESPONSE: { “thought”: “The user is asking two things: the weather in Beijing and a calculation. I should use the tools sequentially. First, I need to get the weather for Beijing.”, “action”: “get_weather”, “action_input”: {“city”: “Beijing”}, “final_answer”: null } [2023-10-27 10:00:01] TOOL_EXECUTION: { “tool”: “get_weather”, “input”: {“city”: “Beijing”}, “output”: “Sunny, 25°C, humidity 40%.” } --- ReAct循环 第 2 轮 --- [2023-10-27 10:00:02] PROMPT_TO_LLM: { “messages”: [ … (之前的消息) …, {“role”: “assistant”, “content”: “Thought: … Action: get_weather …”}, {“role”: “user”, “content”: “Observation: Sunny, 25°C, humidity 40%.”} ] } [2023-10-27 10:00:03] LLM_RAW_RESPONSE: Thought: I have the weather for Beijing. Now I need to calculate the square of 15. I’ll use the calculator tool. Action: calculator Action Input: {“expression”: “15**2”} [2023-10-27 10:00:03] PARSED_RESPONSE: { “thought”: “I have the weather for Beijing. Now I need to calculate the square of 15. I’ll use the calculator tool.”, “action”: “calculator”, “action_input”: {“expression”: “15**2”}, “final_answer”: null } [2023-10-27 10:00:03] TOOL_EXECUTION: { “tool”: “calculator”, “input”: {“expression”: “15**2”}, “output”: “225” } --- ReAct循环 第 3 轮 --- [2023-10-27 10:00:04] LLM_RAW_RESPONSE: Thought: I now have both pieces of information: the weather in Beijing and the result of 15 squared. I can provide the final answer. Final Answer: The weather in Beijing is sunny, 25°C, with 40% humidity. The square of 15 is 225. [2023-10-27 10:00:04] PARSED_RESPONSE: { “thought”: “I now have both pieces of information: the weather in Beijing and the result of 15 squared. I can provide the final answer.”, “action”: null, “action_input”: null, “final_answer”: “The weather in Beijing is sunny, 25°C, with 40% humidity. The square of 15 is 225.” } Agent 得出最终答案 [2023-10-27 10:00:04] FINAL_ANSWER: { “answer”: “The weather in Beijing is sunny, 25°C, with 40% humidity. The square of 15 is 225.” } 最终返回给用户的结果 The weather in Beijing is sunny, 25°C, with 40% humidity. The square of 15 is 225.结果分析可观测性我们清晰地看到了Agent的完整思考链。第一轮它“想”到要先用get_weather工具并正确输出了JSON格式的输入。执行后得到观察结果“Sunny, 25°C…”。第二轮它基于上一轮的观察“想”到要计算15的平方调用calculator工具。第三轮它“想”到信息已齐全输出最终答案。工具调用Agent成功解析了用户复杂的复合问题并将其分解为两个顺序执行的工具调用。ReAct循环完美展示了“Thought - Action - Observation - Thought - … - Final Answer”的循环过程。持久化如果我们在main函数中取消注释加载和保存历史的代码那么多次运行程序Agent就能基于之前的对话历史进行上下文理解。5. 常见问题、优化方向与避坑指南在实际搭建和运行过程中你可能会遇到以下问题。这里提供一些排查思路和优化建议。5.1 LLM不遵循指定格式这是最常见的问题。LLM可能会输出不符合Thought:/Action:/Final Answer:格式的文本导致解析失败。排查查看llm_raw_response日志检查LLM的原始输出。解决强化系统提示词在提示词中更严厉地强调格式要求使用“MUST”、“STRICTLY”等词。可以给出更具体的格式示例。调整温度Temperature将temperature设为0或一个很低的值如0.1减少随机性。使用更强大的模型GPT-4在遵循复杂指令方面通常比GPT-3.5更可靠。后处理与重试在解析失败时可以将错误信息连同之前的对话一起重新发送给LLM要求它纠正格式。例如在解析失败的分支里给messages追加一条内容“Your previous response was not in the correct format. Please strictly follow the format: Thought: … Action: … Action Input: … or Final Answer: …”。5.2 工具调用参数错误LLM可能生成了错误的工具参数格式比如Action Input不是有效的JSON。排查查看parsed_response日志中的action_input字段。解决在提示词中明确参数格式例如明确写出“Action Input must be a valid JSON string, like{\”query\”: \”python\”}”。在工具描述中说明在Tool的description里写明输入示例。编写更鲁棒的解析器像我们代码中做的那样对非标准JSON进行容错处理尝试将其包装成字典。对于更复杂的情况可以要求LLM直接输出JSON对象。5.3 Agent陷入循环或无法终止Agent可能在一个问题上反复调用工具或者无法判断何时应该输出最终答案。排查观察日志中多轮循环的thought内容是否重复或没有进展。解决设置最大迭代次数我们已经做了这是必须的安全网。在提示词中强调任务完成条件例如“When you believe you have fully answered the user’s original question based on the observations, you MUST output ‘Final Answer:’.”实现超时或外部中断除了迭代次数还可以设置总耗时限制。引入“反思”步骤在每轮或每几轮后让LLM评估当前进度是否足以回答问题或者是否需要换一种策略。5.4 性能与成本优化频繁调用LLM API会产生成本并且可能较慢。优化缓存Caching对相同的提示词或经过归一化处理后的提示词和LLM参数缓存其响应结果。这对于重复性查询非常有效。流式处理Streaming对于需要长时间运行的Agent可以考虑使用流式API来逐步输出提升用户体验。本地小模型对于简单、确定性的工具调用决策可以考虑使用更小、更快的本地模型通过Ollama等工具部署来分担部分任务。提示词压缩当对话历史很长时发送全部历史会给API带来大量tokens消耗。可以尝试对历史进行摘要Summarization只发送最重要的上下文。5.5 安全性与可靠性我们的示例代码为了简化在calculator工具中使用了eval这存在严重的安全风险。避坑永远不要在生产环境使用eval应使用安全的数学表达式解析库如ast.literal_eval配合自定义安全计算逻辑或numexpr。验证工具输入在执行任何工具前对输入参数进行严格的验证和清洗防止注入攻击。限制工具权限确保工具只能访问必要的资源。例如一个文件读取工具不应该能访问系统根目录。用户确认对于高风险操作如发送邮件、修改数据可以让Agent先输出计划等待用户确认后再执行。5.6 扩展性建议这个简化框架是一个完美的起点你可以从以下几个方向扩展它更复杂的工具集成真实的API如网络搜索SerpAPI、数据库查询、代码执行环境等。记忆与知识库实现更复杂的记忆机制如短期记忆对话历史、长期记忆向量数据库存储和检索相关知识。规划能力在ReAct循环开始前让LLM先制定一个多步骤的计划Plan然后按计划执行和调整。多智能体协作创建多个ReactAgent实例让它们扮演不同角色如研究员、写手、校对员并通过一个协调器Orchestrator让它们协同完成复杂任务。Web界面或API使用FastAPI或Gradio为你的Agent构建一个Web界面或API使其更容易被使用。这个最简化的AI Agent核心实现就像一副清晰的骨架。它可能没有现成框架那么丰富的肌肉和皮肤但它让你透彻地理解了神经ReAct循环、关节工具调用、感官可观测性和记忆持久化是如何协同工作的。基于此你可以根据自己的需求为其添加上任何你想要的“器官”和“功能”构建出真正强大且可控的智能体应用。