从零手写AI Agent:深入理解ReAct循环与核心架构设计

📅 2026/8/12 16:17:38
从零手写AI Agent:深入理解ReAct循环与核心架构设计
1. 项目缘起为什么我们要“徒手”造一个AI Agent最近AI Agent这个概念火得不行感觉是个技术分享会或者社区帖子不提两句Agent就跟不上时代了。铺天盖地的框架——LangChain、AutoGen、CrewAI还有各种大厂开源的、创业公司闭源的看得人眼花缭乱。这些框架当然好它们封装了复杂的流程提供了丰富的工具集成让开发者能快速搭建出功能强大的智能体。但不知道你有没有这种感觉用框架搭出来的东西跑是能跑起来可一旦出了点预期外的状况或者想深入定制某个环节就有点抓瞎了感觉像是在一个黑盒外面敲敲打打。这正是我决定动手“徒手”实现一个最基础AI Agent的原因。这不是为了否定框架的价值恰恰相反这是一次“回溯本源”的深度练习。就像学编程你不能只靠调用库函数总得知道print(“Hello World”)背后数据是怎么流向标准输出流的。通过从零开始用最纯粹的Python不依赖LangChain等专门框架我们将彻底弄明白一个AI Agent的核心循环究竟是如何运转的它如何理解指令、如何调用工具、如何根据结果进行思考并决定下一步行动。这个过程会让你对ReActReasoning and Acting、Chain of Thought等听起来高大上的概念有刻骨铭心的理解。当你再使用那些成熟框架时你会清楚地知道每一行配置代码对应着底层哪个模块的什么操作调试和优化都将变得心中有数。这个项目适合谁呢如果你是对AI应用开发感兴趣的Python开发者已经了解过一些大模型API比如OpenAI的GPT系列、 Anthropic的Claude或者国内的一些大模型的基本调用但觉得Agent框架有些“重”或者“神秘”想揭开这层迷雾那么这篇手把手的指南就是为你准备的。我们不需要任何前置的Agent框架知识只需要有Python基础和对大模型API的基本了解即可。我们将从设计思路开始一步步用代码构建出一个能真正执行多步骤任务的、具备基础推理和行动能力的智能体。2. 核心蓝图拆解一个AI Agent的四大支柱在写第一行代码之前我们必须先画好蓝图。一个能够自主工作的AI Agent无论其外表多么复杂其核心架构通常都离不开以下几个基本组成部分。我们的“徒手”建造也将围绕它们展开。2.1 大脑大语言模型这是Agent的“大脑”负责所有的推理、规划和决策。它接收来自外部的指令用户输入、来自自身的思考历史以及来自“工具”的执行结果然后输出两种内容一是“思考过程”即它下一步打算做什么以及为什么二是具体的“行动指令”比如调用哪个工具、传入什么参数。在我们的手写版本中大脑就是一个通过API调用的LLM大语言模型。我们会精心设计发送给它的“提示词”来引导它按照我们期望的格式进行思考和输出。注意选择大模型时推理能力是关键。对于这种需要多步规划和逻辑判断的任务建议使用能力较强的模型如GPT-4、Claude 3系列等。虽然成本可能更高但它们在遵循复杂指令和逻辑链方面表现更稳定能极大降低我们构建“思维框架”的难度。2.2 记忆体对话历史与上下文管理Agent不能得“健忘症”。它必须记住和用户的整个对话历史以及自己之前都思考了什么、做了什么、得到了什么结果。这是它进行连贯推理的基础。记忆体本质上就是一个有序的列表保存着所有的消息记录。但这里有个关键挑战大模型的上下文长度是有限的。我们不能无限制地堆积历史消息。因此一个基础的记忆体模块还需要包含“摘要”或“选择性记忆”的简单策略例如当对话轮次太多时将早期不那么重要的对话压缩成一段摘要只保留最近的关键交互细节。2.3 工具箱函数调用与执行这是Agent的“手脚”。大脑想好了要做什么最终需要工具箱里的工具来执行。一个工具就是一个Python函数它可能用于搜索网络、查询数据库、执行计算、操作文件等等。我们的Agent需要具备两个能力第一向大脑“描述”自己有哪些工具可用工具的名称、功能描述、所需参数第二当大脑发出调用某个工具的命令时能够准确地找到对应的函数传入正确的参数并执行它然后将执行结果返回给大脑进行下一步分析。2.4 控制中枢ReAct循环与解析器这是连接大脑、记忆和工具箱的“中枢神经系统”。它定义了Agent的工作流程也就是著名的ReAct范式思考-行动-观察的循环。思考控制中枢将当前的用户问题、相关的记忆历史以及可用的工具描述组合成一个完整的提示提交给“大脑”。解析行动“大脑”返回一段文本。控制中枢需要从这段文本中精确地解析出模型是否决定使用工具如果要使用是哪个工具参数是什么如果任务已完成最终答案又是什么这需要一个稳定的解析逻辑。行动如果解析出工具调用控制中枢就调用对应的“工具”函数。观察将工具执行的结果成功或失败作为新的“观察”信息连同之前的记录一起追加到“记忆”中。循环将新的、包含了“观察”的记忆再次交给“大脑”进行下一轮“思考”直到大脑输出最终答案或达到循环上限。这个循环是Agent自主性的核心。我们的代码主要就是在实现这个循环的调度和状态管理。3. 从零开始逐步构建核心模块现在我们开始动手编码。请确保你有一个可用的Python环境3.8以及一个能访问大模型API的密钥例如OpenAI的API Key。我们将从最基础的模块开始搭建。3.1 第一步打造智能体的大脑与记忆首先我们需要一个模块来与大模型对话并管理记忆。创建一个名为simple_agent.py的文件。import json from typing import List, Dict, Any, Optional import openai # 或其他大模型SDK这里以OpenAI为例 class AgentBrain: Agent的大脑负责与LLM交互。 def __init__(self, model: str gpt-4, api_key: Optional[str] None): self.model model # 初始化客户端这里以OpenAI为例 self.client openai.OpenAI(api_keyapi_key) # 系统提示词这是塑造Agent行为的关键 self.system_prompt 你是一个智能助手能够通过使用工具来解决问题。请遵循以下步骤 1. 分析用户的问题。 2. 如果需要使用工具来获取信息或执行操作请严格按照以下JSON格式回应 { thought: 你的思考过程解释为什么需要这个工具以及如何使用它。, action: { name: 工具名称, args: {参数名1: 值1, 参数名2: 值2} } } 3. 如果不需要工具或者已经通过工具获得了所有必要信息可以给出最终答案时请用以下格式 { thought: 你的思考过程总结已有信息并得出结论。, final_answer: 给用户的最终答案 } 请确保你的回应是有效的JSON且只包含上述两种格式之一。 def think(self, messages: List[Dict[str, str]]) - Dict[str, Any]: 接收消息历史返回LLM的思考结果。 try: # 将系统提示词和消息历史组合 chat_messages [{role: system, content: self.system_prompt}] messages response self.client.chat.completions.create( modelself.model, messageschat_messages, temperature0.1, # 低温度保证输出更稳定、更符合格式 response_format{ type: json_object } # 强烈要求返回JSONGPT-4等模型支持 ) content response.choices[0].message.content return json.loads(content) except json.JSONDecodeError as e: print(fLLM返回了非JSON内容: {content}) # 提供一个兜底的错误结构 return {thought: 解析响应失败。, final_answer: 抱歉我处理请求时出现了内部错误。} except Exception as e: print(f调用LLM API失败: {e}) return {thought: API调用失败。, final_answer: 服务暂时不可用。} class AgentMemory: Agent的记忆体管理对话上下文。 def __init__(self, max_turns: int 20): self.messages: List[Dict[str, str]] [] self.max_turns max_turns # 最大对话轮次限制 def add(self, role: str, content: str): 添加一条消息。角色可以是 user, assistant, 或 tool。 self.messages.append({role: role, content: content}) # 简单的记忆窗口如果超过最大轮次移除最早的一轮对话userassistant # 这是一个非常基础的策略实际应用中可能需要更复杂的摘要或向量化记忆。 while len(self.messages) self.max_turns * 2: # 粗略估计 # 通常移除最早的系统提示之后的消息 if len(self.messages) 1: # 保留系统提示 self.messages.pop(1) # 移除第一条非系统消息 def get_context(self) - List[Dict[str, str]]: 获取当前的对话上下文。 return self.messages.copy() def reset(self): 清空记忆除了系统提示。 # 通常我们会保留系统提示词 system_msg self.messages[0] if self.messages and self.messages[0].get(role) system else None self.messages [] if system_msg: self.messages.append(system_msg)关键点解析系统提示词这是引导Agent行为的核心。我们明确规定了它的输出必须是两种严格的JSON格式之一。这种“结构化输出”要求对于构建可靠的Agent至关重要。我们利用了较新模型对response_format参数的支持来进一步提高JSON输出的稳定性。记忆管理AgentMemory类目前实现了一个简单的“滑动窗口”记忆。在实际复杂任务中当对话很长时你需要实现更高级的记忆策略比如将过去的对话总结成一段摘要或者使用向量数据库检索最相关的历史片段。错误处理在think方法中我们捕获了JSON解析错误和API调用错误。在原型阶段健壮的错误处理能让你快速定位问题是出在模型输出还是代码逻辑。3.2 第二步构建灵活的工具箱工具是Agent能力的延伸。我们需要一个统一的方式来定义、描述和调用工具。在simple_agent.py中继续添加。class Tool: 工具基类定义工具的接口。 def __init__(self, name: str, description: str, func: callable): self.name name self.description description # 用于向LLM描述工具功能 self.func func # 实际执行的函数 def run(self, **kwargs): 执行工具并返回字符串格式的结果。 try: result self.func(**kwargs) # 确保返回结果是字符串方便后续处理 return str(result) except Exception as e: return f工具执行出错: {e} def to_dict(self) - Dict[str, Any]: 将工具描述转换为字典方便发送给LLM。 # 这是一个简化版。更复杂的实现可以解析函数签名来自动生成参数描述。 return { name: self.name, description: self.description, # 在实际中这里最好能附上参数的类型和说明帮助LLM更好地调用。 } class Toolbox: 工具箱管理所有可用工具。 def __init__(self): self.tools: Dict[str, Tool] {} def register(self, tool: Tool): 注册一个工具。 self.tools[tool.name] tool def get_tool(self, name: str) - Optional[Tool]: 根据名称获取工具。 return self.tools.get(name) def get_descriptions(self) - List[Dict[str, str]]: 获取所有工具的描述列表用于提示词。 return [tool.to_dict() for tool in self.tools.values()] # 示例工具定义 def search_web(query: str) - str: 模拟一个网络搜索工具。在实际应用中这里会调用SerpAPI等真实搜索接口。 # 这里是模拟数据 mock_data { Python安装: 访问python.org官网下载对应操作系统的安装包运行安装程序并勾选‘Add Python to PATH’。, 今天天气: 北京晴15-25度上海多云18-28度。, 计算圆周率: 圆周率π的近似值是3.1415926535。 } return mock_data.get(query, f未找到关于 {query} 的模拟信息。) def calculator(expression: str) - str: 一个简单的计算器工具。注意直接eval有安全风险此处仅用于演示。 try: # 警告在生产环境中绝对不要对用户输入使用eval # 这里仅为演示应使用更安全的表达式解析库如ast.literal_eval。 result eval(expression, {__builtins__: None}, {}) return f{expression} {result} except Exception as e: return f计算表达式 {expression} 时出错: {e}实操心得工具描述的重要性description字段是LLM理解工具用途的唯一依据。描述要清晰、准确说明输入是什么、输出是什么。例如“计算器计算一个数学表达式的结果。输入是一个字符串格式的数学表达式如‘3 5 * 2’。”工具的安全性calculator工具使用了eval这在演示中很方便但在真实、开放的环境中极其危险。如果Agent能调用这样的工具恶意用户可能通过它执行任意代码。因此真实场景下的工具函数必须进行严格的输入验证和沙箱化处理或者使用安全的解析库。工具的可发现性我们通过get_descriptions方法将所有工具信息暴露给LLM。在更复杂的系统中你可能需要根据当前对话上下文动态选择最相关的工具子集以减少LLM的干扰。3.3 第三步实现核心控制循环与解析器这是最核心的部分我们将把大脑、记忆和工具箱串联起来实现ReAct循环。继续在simple_agent.py中添加。class SimpleAgent: 简单的AI Agent整合大脑、记忆和工具箱。 def __init__(self, brain: AgentBrain, memory: AgentMemory, toolbox: Toolbox): self.brain brain self.memory memory self.toolbox toolbox self.max_iterations 10 # 防止无限循环 def _format_tools_for_prompt(self) - str: 将工具描述格式化为提示词的一部分。 tools self.toolbox.get_descriptions() if not tools: return 目前没有可用的工具。 tools_text 你可以使用以下工具\n for tool in tools: tools_text f- {tool[name]}: {tool[description]}\n return tools_text def run(self, user_input: str) - str: 运行Agent处理用户输入返回最终答案。 print(f\n[用户] {user_input}) # 1. 将用户输入加入记忆 self.memory.add(user, user_input) for i in range(self.max_iterations): print(f\n--- 第 {i1} 轮思考 ---) # 2. 准备当前上下文记忆 context self.memory.get_context() # 3. 获取工具描述并动态插入到本次对话的末尾作为提示 # 更优的做法是将工具描述放在系统提示词中这里为了清晰采用动态附加。 current_context context [{role: user, content: self._format_tools_for_prompt() \n请基于以上信息和工具回应我的上一个问题。}] # 4. 大脑思考 response self.brain.think(current_context) print(f[大脑思考] {response.get(thought, N/A)}) # 5. 解析响应 if final_answer in response: # 有最终答案任务完成 final_answer response[final_answer] self.memory.add(assistant, final_answer) print(f[最终答案] {final_answer}) return final_answer elif action in response: # 需要执行工具 action response[action] tool_name action.get(name) args action.get(args, {}) if not tool_name or not isinstance(args, dict): error_msg f解析出的动作格式无效: {action} self.memory.add(assistant, error_msg) print(f[错误] {error_msg}) continue tool self.toolbox.get_tool(tool_name) if not tool: error_msg f未知的工具: {tool_name} self.memory.add(assistant, error_msg) print(f[错误] {error_msg}) continue # 6. 执行工具 print(f[执行工具] {tool_name}参数: {args}) try: # 将args字典展开作为关键字参数传入 tool_result tool.run(**args) except TypeError as e: tool_result f调用工具参数错误: {e} print(f[工具结果] {tool_result}) # 7. 将工具执行结果作为“工具”角色的消息加入记忆 # 这是ReAct中“观察”的关键一步让大脑知道行动的结果。 self.memory.add(tool, f工具 {tool_name} 返回: {tool_result}) else: # 响应格式不符合预期 error_msg f无法解析LLM的响应: {response} self.memory.add(assistant, error_msg) print(f[错误] {error_msg}) # 可以选择跳出循环或继续尝试 break # 循环结束仍未得到最终答案 timeout_msg f经过{self.max_iterations}轮尝试仍未解决问题。 self.memory.add(assistant, timeout_msg) return timeout_msg核心循环解析动态上下文构建在每一轮循环中我们不仅传递原始对话记忆还附加了当前可用的工具列表。这相当于在每一步都提醒大脑“你现在有这些工具可以用。”严格的响应解析我们根据系统提示词中定义的两种JSON格式来解析大脑的输出。解析逻辑必须健壮要处理缺失字段、格式错误等情况。工具结果的反馈将工具执行结果以role: “tool”的身份加入记忆这是实现“观察”步骤的标准做法。大脑在下一轮思考时就能看到“我上一步用了搜索工具它返回了XXX这个结果”。循环终止条件有两个终止条件一是大脑输出了final_answer二是达到了最大迭代次数max_iterations防止问题复杂或模型“卡住”导致无限循环。4. 让Agent动起来完整示例与调试现在让我们把所有模块组装起来并运行一个完整的示例。在同一个目录下创建一个main.py文件。# main.py from simple_agent import AgentBrain, AgentMemory, Tool, Toolbox, SimpleAgent import os def main(): # 0. 配置请替换为你的API Key api_key os.getenv(OPENAI_API_KEY) # 建议从环境变量读取 if not api_key: # 仅为演示硬编码Key极不安全 print(请设置 OPENAI_API_KEY 环境变量) # 为了演示这里假设有一个Key实际请勿这样做。 api_key your-api-key-here # 1. 初始化核心组件 brain AgentBrain(modelgpt-4, api_keyapi_key) memory AgentMemory(max_turns10) toolbox Toolbox() # 2. 注册工具 toolbox.register(Tool(namesearch, description根据查询词获取信息。参数: query (字符串)。, funcsearch_web)) toolbox.register(Tool(namecalculate, description计算一个数学表达式的结果。参数: expression (字符串如35*2)。, funccalculator)) # 3. 创建Agent agent SimpleAgent(brain, memory, toolbox) # 4. 运行测试 test_queries [ Python怎么安装, 先查一下北京的天气然后告诉我如果去户外需要穿什么衣服, 计算一下15乘以28再加上77等于多少, 先搜索‘圆周率’然后用计算器计算一下圆的面积假设半径是5。 ] for query in test_queries: print(\n *50) print(f处理查询: {query}) print(*50) answer agent.run(query) print(f\nAgent最终回复: {answer}) # 可选每轮测试后重置记忆避免上下文干扰 # memory.reset() if __name__ __main__: main()运行这个脚本你会看到Agent的思考过程、工具调用和最终答案在控制台打印出来。例如对于“先查一下北京的天气...”这个查询输出可能类似于 处理查询: 先查一下北京的天气然后告诉我如果去户外需要穿什么衣服 [用户] 先查一下北京的天气然后告诉我如果去户外需要穿什么衣服 --- 第 1 轮思考 --- [大脑思考] 用户想知道北京的天气然后根据天气给出穿衣建议。我需要先获取天气信息。有一个搜索工具可以使用。 [执行工具] search参数: {query: 北京天气} [工具结果] 北京晴15-25度。 --- 第 2 轮思考 --- [大脑思考] 搜索结果显示北京天气是晴天温度在15到25度之间。这是一个比较舒适的温度范围昼夜温差可能有点大。现在我可以基于这个信息给出穿衣建议了。 [最终答案] 根据查询到的信息北京今天天气晴朗气温在15至25摄氏度之间。这样的天气比较舒适建议穿着 - 上衣长袖T恤、薄衬衫或卫衣。 - 下装长裤、牛仔裤或休闲裤。 - 外套早晚温差可能较大建议带一件薄外套或风衣备用。 - 鞋子运动鞋或休闲鞋即可。 总体以舒适、便于活动的春装为主。避坑技巧实录API Key管理永远不要将API Key硬编码在代码中提交到版本控制系统如Git。务必使用环境变量os.getenv或专门的密钥管理工具。模型的选择与成本在开发调试阶段可以使用更便宜、速度更快的模型如gpt-3.5-turbo但要注意其在复杂推理和严格遵循输出格式上可能不如gpt-4稳定。正式使用时再切换。解析的稳定性即使我们要求模型返回JSON它偶尔也可能输出格式不正确或包含额外解释的文字。我们的代码中虽然有json.loads的异常捕获但更健壮的做法是使用一个“解析层”例如先尝试用正则表达式提取JSON块或者使用LLM本身来修复格式错误的JSON。循环失控务必设置max_iterations。有时模型可能会陷入“调用工具-得到结果-再次调用同一工具”的死循环。除了设置上限还可以在记忆中检测重复的工具调用并主动终止。5. 进阶思考与优化方向我们完成了一个最基础的、可运行的AI Agent。但要让它在实际项目中可用还有很长的路要走。以下是几个关键的优化方向你可以基于这个基础框架进行扩展。5.1 增强工具描述与动态规划我们目前的工具描述非常简单。一个强大的Agent需要更丰富的工具语义信息。我们可以改进Tool类使其能自动从函数签名和文档字符串中提取信息import inspect class EnhancedTool(Tool): def __init__(self, func: callable): # 从函数名获取工具名 name func.__name__ # 从文档字符串获取描述 description func.__doc__ or f执行函数 {name}。 super().__init__(name, description, func) # 解析函数签名获取参数信息 self.params self._parse_signature(func) def _parse_signature(self, func): sig inspect.signature(func) params {} for param_name, param in sig.parameters.items(): params[param_name] { type: param.annotation if param.annotation ! inspect.Parameter.empty else any, default: param.default if param.default ! inspect.Parameter.empty else None, required: param.default inspect.Parameter.empty } return params def to_dict(self): base_info super().to_dict() base_info[parameters] self.params return base_info这样在构造提示词时我们可以将每个工具需要的参数类型、是否必填等信息更精确地告诉LLM大大提高工具调用的准确率。5.2 实现更复杂的记忆与状态管理基础滑动窗口记忆在长对话中会丢失重要早期信息。我们可以引入向量数据库来实现“长期记忆”和“相关性检索”。记忆向量化将每一段对话或工具结果通过嵌入模型如OpenAI的text-embedding-3-small转换为向量。存储与检索使用ChromaDB、Pinecone或本地FAISS等向量数据库存储这些向量。相关性检索当Agent需要思考时将当前问题或上下文也转换为向量并从向量数据库中检索出最相关的几条历史记忆作为补充上下文发送给LLM。这模拟了人类的“联想记忆”能力。# 伪代码示例 class VectorMemory: def add(self, text: str): vector embedding_model.encode(text) vector_db.store(text, vector) def retrieve(self, query: str, k3) - List[str]: query_vector embedding_model.encode(query) relevant_texts vector_db.search(query_vector, top_kk) return relevant_texts5.3 多Agent协作与任务分解单个Agent能力有限。复杂的任务可以被分解成子任务由多个各司其职的Agent协作完成。这需要引入一个“管理者”或“协调者”角色。管理者Agent接收用户原始任务将其分解为一系列子任务并规划执行顺序。执行者Agent拥有特定技能如搜索、编程、写作负责完成管理者分配的具体子任务。协调循环管理者将子任务分配给执行者收集结果判断任务是否完成或是否需要进一步分解/调整。这本质上是在我们构建的SimpleAgent之上再构建一层更高级的调度和通信机制。每个子Agent都可以是我们已经实现的这个基础Agent的实例。5.4 处理复杂输出与流式响应我们的Agent目前只输出最终文本。但很多场景需要更丰富的输出比如生成图表、代码、结构化数据等。我们可以扩展响应格式让大脑能指示“生成一张图片描述”或“返回一个JSON列表”。同时对于耗时的任务可以考虑支持流式响应让用户能实时看到Agent的思考过程体验更好。6. 常见问题与排查技巧实录在开发和测试这个手写Agent的过程中你几乎一定会遇到下面这些问题。这里是我的排查笔记。问题1LLM不按我要求的JSON格式输出导致解析失败。可能原因系统提示词不够强硬模型能力不足如用了gpt-3.5-turbo温度参数temperature设置过高导致输出随机性太大。解决方案在系统提示词中反复强调“必须”、“只能”、“严格遵循”等字眼。可以要求模型“在回复前先在心里确认一遍输出是有效的JSON”。使用支持response_format: { “type”: “json_object” }的模型如GPT-4, GPT-4 Turbo, Claude 3等这能极大提升格式稳定性。将temperature设置为0或一个很低的值如0.1减少随机性。在代码中实现一个“后处理”函数尝试用正则表达式如r‘\{.*\}’从杂乱的输出中提取JSON或者将错误输出连同“请将以下内容修正为合规的JSON”的指令再次发送给LLM进行修复。问题2Agent陷入无限循环反复调用同一个工具。可能原因工具返回的结果不足以让LLM得出结论LLM对任务的理解有偏差记忆上下文混乱。解决方案检查工具结果工具返回的信息是否明确、完整如果工具返回“未找到信息”LLM可能因为没得到有效输入而反复尝试。确保工具在失败时返回清晰的错误信息。增强思考提示在系统提示词中加入“如果你已经尝试过某个工具但未能获得进展请尝试其他方法或承认无法解决”。在记忆中添加上下文除了工具结果还可以在记忆中加入“这是第N次尝试搜索”这样的元信息提醒LLM当前状态。实现循环检测在SimpleAgent.run的循环中记录每次调用的工具和参数。如果检测到完全相同的调用在最近3轮内重复出现则主动中断循环并返回一个错误信息。问题3工具调用参数总是出错比如类型不对或缺少参数。可能原因给LLM的工具描述不够精确LLM不理解参数格式。解决方案使用结构化的工具描述如前文EnhancedTool所示提供参数名称、类型、描述和示例。例如{“name”: “query”, “type”: “string”, “description”: “搜索关键词”, “example”: “Python教程”}。在提示词中提供示例在系统提示词里不仅描述格式还要给出一两个完整的、正确的调用示例。参数验证与修正在工具执行前增加一个参数验证和清洗的步骤。例如如果工具需要整数但LLM传了字符串“5”可以尝试自动转换。问题4处理复杂、多步骤任务时Agent表现不佳。可能原因单次提示的上下文不足以支撑复杂的规划Agent缺乏“反思”能力。解决方案引入“思维链”提示在提示词中明确要求LLM“逐步思考”。例如“请先分析任务需要几个步骤每一步需要什么工具然后再开始行动。”实现子目标分解可以设计一个专门的“规划器”模块可以是一个LLM调用先将大任务分解成清晰的子任务列表然后让执行Agent按顺序处理。增加反思步骤在每一轮行动后不仅观察结果还要求LLM简短评估当前进展“我们离目标更近了吗下一步最应该做什么”这可以通过在提示词中添加一个“反思”字段来实现。通过这次从零手写AI Agent的旅程你获得的不只是一个能运行的代码原型更是一张清晰的技术地图。你知道了大脑LLM如何被引导记忆如何流转工具如何被调度以及最核心的ReAct循环如何一步步推进。下次当你再面对LangChain那样功能繁多的框架时你看到的将不再是一个神秘的黑盒而是一组组你亲手实现过的、熟悉的核心模块的优雅封装。这种深度的理解是快速掌握和灵活运用任何上层框架的基石。