1. 项目概述从零构建AI智能体的核心价值最近几年AI领域最激动人心的进展之一无疑是智能体AI Agent技术的崛起。从能自主完成复杂任务的AutoGPT到能理解并执行自然语言指令的各类AI助手智能体正在从一个学术概念迅速演变为改变我们与数字世界交互方式的核心技术。然而对于大多数开发者而言智能体技术似乎笼罩着一层神秘的面纱充斥着复杂的框架、晦涩的论文和看似遥不可及的工程实践。这正是“pguso/ai-agents-from-scratch”这个项目吸引我的地方——它承诺带领我们“从零开始”构建AI智能体剥开层层封装直抵技术内核。这个项目的核心价值在于其“从零开始”的实践路径。它不依赖于OpenAI的Assistant API、LangChain或AutoGen这类重型框架而是引导我们使用基础的LLM API如OpenAI的GPT-4/3.5、Anthropic的Claude等作为“大脑”结合清晰的编程逻辑亲手搭建起一个具备规划、执行、反思能力的智能体系统。这个过程就像是从学习如何用砖块和水泥砌墙开始最终自己设计并盖起一栋房子而不是直接搬进一个精装修的样板间。对于希望深入理解智能体工作原理、掌握其核心设计模式并最终能够根据特定业务场景定制专属智能体的开发者来说这是一条无比宝贵的“硬核”学习路径。通过这个项目你将不仅仅学会调用一个API而是真正理解一个智能体是如何“思考”和“行动”的。你会亲手实现任务分解、工具调用、记忆管理、反思修正等关键模块并深刻体会到提示工程Prompt Engineering在串联整个工作流中的决定性作用。无论你是想为你的产品添加一个智能客服构建一个自动化数据分析助手还是探索AI在游戏NPC或流程自动化中的应用掌握从零构建智能体的能力都将为你打开一扇全新的大门。接下来我将基于常见的实践模式为你拆解这个项目的核心设计思路、关键实现细节以及那些只有亲手做过才会知道的“坑”与技巧。2. 智能体架构的核心设计思路拆解一个典型的、功能完整的AI智能体其架构可以抽象为几个核心循环组件感知Perception、规划Planning、执行Execution和反思Reflection。ai-agents-from-scratch项目所倡导的“从零构建”正是要求我们清晰地定义并实现这些组件之间的数据流与控制逻辑。2.1 大脑、工具与记忆智能体的三大支柱首先我们需要确立智能体的基本构成要素。在我看来任何智能体都离不开三大支柱大脑The Brain即大型语言模型LLM。它是智能体的推理和决策中心。我们通过向LLM发送精心构造的提示Prompt来驱动它进行分析、规划和生成下一步动作。这里的关键在于我们不能把LLM当作一个“问答机”而要将其视为一个具有状态和上下文的“推理引擎”。项目的核心之一就是设计一套能与LLM高效“对话”的提示模板系统。工具Tools智能体作用于外部世界的“手”和“脚”。LLM本身无法直接执行代码、查询数据库、调用API或操作文件系统。因此我们需要为智能体装备一系列工具。一个工具通常由一个描述LLM用来理解工具能做什么、一个函数实现具体的代码逻辑以及输入输出规范组成。智能体在规划时会决定调用哪个工具并生成调用该工具所需的参数。记忆Memory智能体的“经验簿”。记忆系统负责存储和管理智能体与环境的交互历史。这通常包括对话历史用户与智能体的完整对话记录。任务历史智能体已执行过的动作调用了什么工具、输入输出是什么及其结果。长期记忆可能是一个向量数据库用于存储和检索更持久、更结构化的知识。项目的架构设计就是围绕着如何让这三大支柱协同工作而展开。一个经典的工作流是用户提出目标 - LLM大脑结合记忆当前对话和任务历史进行规划决定下一步行动例如调用某个工具- 系统执行该工具 - 将执行结果存入记忆 - LLM结合新结果进行下一步规划或直接生成最终答案给用户。2.2 规划-执行-观察-循环ReAct模式的落地实现在众多智能体范式中ReActReasoning Acting模式因其简洁有效而备受推崇也是ai-agents-from-scratch这类项目最常实现的模式。它的核心思想是让智能体循环进行“思考-行动-观察”的步骤。Reasoning思考/规划LLM根据当前目标、已有的记忆历史观察和可用的工具列表分析现状推理出为了达成目标下一步应该做什么。输出通常是一个结构化的“动作”例如Action: search_web, Action Input: {query: 今天北京的天气}。Acting行动/执行系统解析LLM输出的动作找到对应的工具函数传入参数并执行。Observation观察工具执行的结果成功或失败被捕获作为“观察”反馈给LLM。循环LLM接收到新的“观察”后将其加入上下文记忆开始下一轮的“思考”决定是继续执行下一个动作还是已经收集到足够信息可以生成最终答案Final Answer。在项目中实现ReAct关键在于设计一个稳定的主循环和一套清晰的动作解析规则。主循环需要设定终止条件比如达到最大步数、LLM输出了“Final Answer”、或者任务明确失败。动作解析则要求LLM的输出必须严格遵循预定义的格式如JSON以便程序能准确提取出工具名和参数。注意让LLM稳定输出结构化内容是一大挑战。除了在提示词中反复强调格式使用LLM的“函数调用”Function Calling或“JSON模式”JSON Mode等原生功能可以极大提高可靠性。如果使用的模型不支持这些功能则需要在解析逻辑中加入健壮的错误处理和重试机制。2.3 分层任务分解与动态规划对于复杂任务单步的ReAct循环可能不够。智能体需要具备将宏大目标拆解为可执行子任务的能力。这就引入了分层任务分解Hierarchical Task Decomposition的概念。在项目实现中这通常意味着设计两种或多种不同的LLM调用角色规划者Planner接收用户的初始指令生成一个高层次的任务列表或流程图。例如用户说“帮我分析一下公司上季度的销售数据并写一份报告”规划者可能输出[1. 从数据库获取销售数据, 2. 清理和预处理数据, 3. 计算关键指标销售额、增长率等, 4. 生成图表, 5. 撰写报告摘要]。执行者Executor也就是我们上面提到的ReAct智能体。它负责具体执行规划者生成的每一个子任务。执行者不需要关心全局只需专注于完成当前被分配的子任务。更高级的动态规划还会允许执行者在遇到意外如工具调用失败、数据不符合预期时将问题反馈给规划者请求调整任务计划。这种“规划-执行-反馈-重规划”的闭环使得智能体具备了更强的鲁棒性和适应性。在ai-agents-from-scratch的实现中你可能会用一个专门的“规划”提示模板来初始化规划者而执行者则使用另一套包含工具列表的“执行”提示模板。两者通过共享的任务队列和状态存储器进行通信。3. 从零开始的关键模块实现细节理解了宏观架构我们进入实战环节。下面我将逐一拆解各个核心模块的实现要点、代码结构和需要注意的陷阱。3.1 构建稳定可靠的工具调用系统工具系统是智能体与真实世界交互的桥梁其稳定性和易用性至关重要。工具的定义与注册一个典型的工具可以用一个Python类或字典来定义。我更喜欢使用Pydantic模型来定义因为它能提供清晰的类型提示和验证。from pydantic import BaseModel, Field from typing import Type, Any import inspect class Tool(BaseModel): 工具基类定义 name: str Field(..., description工具的唯一名称) description: str Field(..., description给LLM看的工具功能描述) func: callable Field(..., description实际执行的函数) args_schema: Type[BaseModel] Field(None, description参数验证模型) def run(self, **kwargs) - str: 执行工具返回结果字符串 try: result self.func(**kwargs) # 将结果转换为字符串便于LLM理解 return str(result) except Exception as e: return fError executing tool {self.name}: {str(e)} # 示例定义一个网络搜索工具 class SearchInput(BaseModel): query: str Field(..., description搜索查询词) def search_web(query: str) - str: # 这里模拟搜索实际可接入Serper API、Google Custom Search等 return f模拟搜索结果关于{query}的信息... web_search_tool Tool( namesearch_web, description用于在互联网上搜索最新信息。输入一个搜索查询词。, funcsearch_web, args_schemaSearchInput ) # 工具注册表 class ToolRegistry: def __init__(self): self._tools: dict[str, Tool] {} def register(self, tool: Tool): self._tools[tool.name] tool def get_tool(self, name: str) - Tool: return self._tools.get(name) def get_tools_description(self) - str: 生成给LLM看的工具列表描述 desc [] for name, tool in self._tools.items(): # 解析函数签名帮助LLM理解参数 sig inspect.signature(tool.func) params list(sig.parameters.keys()) desc.append(f- {name}: {tool.description} 参数: {params}) return \n.join(desc) registry ToolRegistry() registry.register(web_search_tool)工具调用的流程提示词集成在给LLM的提示词中动态插入ToolRegistry.get_tools_description()生成的工具列表。动作解析LLM返回类似Action: search_web, Action Input: {query: Python最新版本}的文本。你需要编写一个解析器可以用正则表达式或简单的字符串分割来提取动作名和参数字符串。参数验证与执行根据动作名从注册表获取Tool实例。将参数字符串解析为字典并用args_schema如果存在进行验证。最后调用tool.run(**kwargs)。结果格式化将工具执行结果格式化为统一的Observation: ...格式供下一轮循环使用。实操心得工具的描述description至关重要它是LLM理解工具能力的唯一途径。描述要具体、明确说明输入是什么、输出是什么、能解决什么问题。避免使用“处理数据”这种模糊描述而要用“根据给定的CSV文件路径读取文件并返回前5行内容作为字符串”。3.2 设计高效的记忆与上下文管理系统记忆管理直接决定了智能体的“智商”上限和API成本。我们需要在有限的上下文窗口内放入最相关的信息。记忆的组成短期记忆/对话历史一个简单的列表存储每条消息用户消息、AI消息、系统消息、工具观察消息。但直接存储所有历史很快会耗尽上下文。长期记忆/向量存储对于需要跨对话记忆的知识可以使用向量数据库如Chroma、Weaviate、FAISS。将重要的信息片段如用户偏好、任务结果总结转换为向量并存储。在需要时根据当前对话的语义进行检索将最相关的几条信息注入上下文。关键实现策略摘要压缩这是管理长对话历史的核心技术。当对话轮数或token数达到阈值时触发一个摘要过程。调用LLM让它将之前的对话历史总结成一段简洁的摘要。然后用这个摘要替换掉大部分旧的历史记录只保留最近几轮原始对话。这样既保留了关键信息又大幅节省了token。def summarize_conversation(messages: List[dict], model: str) - str: 调用LLM生成对话摘要 prompt f 请将以下对话内容总结成一段简洁的摘要保留核心事实、用户要求和决策结果。 对话记录 {json.dumps(messages, ensure_asciiFalse)} 摘要 # 调用LLM API获取摘要 summary call_llm_api(prompt, model) return summary滑动窗口只保留最近N轮对话或最近K个token的原始内容。这是一种简单粗暴但有效的方法适用于对历史连贯性要求不高的场景。重要性评分为每一条消息尤其是工具观察结果赋予一个重要性分数。在需要裁剪上下文时优先保留高分消息。分数可以通过规则例如包含最终答案的消息分数高或通过一个小型LLM来评判。在ai-agents-from-scratch项目中你可能会实现一个MemoryManager类它内部维护着原始消息列表、摘要和向量存储客户端并提供add_message(),get_context_for_llm(max_tokens)等方法自动处理摘要、检索和裁剪逻辑。3.3 提示工程驱动智能体的“隐形代码”提示词是智能体的“软件”直接编写着智能体的行为逻辑。一个好的提示模板系统是项目成功的关键。系统提示词System Prompt的设计这是智能体的“人格设定”和核心行为准则。它应该包含身份与角色你是一个什么类型的助手核心能力与限制你可以使用哪些工具你不能做什么例如“除非用户明确要求否则你不能直接执行涉及修改或删除文件的操作。”输出格式指令必须严格遵守的响应格式。这是实现稳定ReAct循环的基石。推理过程要求鼓励LLM展示其思考链Chain-of-Thought。示例你是一个高效、可靠的AI助手可以调用工具来帮助用户解决问题。 你拥有以下工具 {tools_description} 请严格按照以下格式响应 Thought: 首先我需要思考用户的目标和我现有的信息。我需要使用工具吗如果需要用哪个 Action: 要调用的工具名必须是[{tool_names}]中的一个。 Action Input: 调用工具所需的输入必须是一个有效的JSON字符串。 Observation: 工具返回的结果。 或者如果你已经能给出最终答案 Thought: 我已经获得了所有必要信息。 Final Answer: [你的最终回答尽量详细] 你必须遵守以下规则 1. 如果用户请求需要真实世界数据如天气、新闻你必须使用工具。 2. 不要编造工具结果。 3. 一次只执行一个动作。动态上下文构建每次调用LLM前都需要动态组装最终的提示消息列表。顺序通常是系统消息包含角色设定和当前工具描述。记忆管理器提供的浓缩后的对话历史可能包含摘要和最近消息。当前用户的查询或上一步的观察结果。可选在最后追加一句强格式提醒如“请严格按格式响应”。少样本示例Few-shot Examples的运用在系统提示词或初始对话历史中提供1-2个完整的、格式完美的“用户提问-智能体思考-动作-观察-最终答案”的示例能极其有效地引导LLM遵循你设定的格式和行为模式。这比单纯用文字描述格式要有效得多。4. 核心工作流的完整实现与代码剖析现在让我们将上述模块组合起来实现一个完整的、可运行的ReAct智能体主循环。这里我会提供一个高度精简但功能完整的代码框架并附上详细注释。4.1 主循环引擎的实现import json import re from typing import List, Dict, Any, Optional from dataclasses import dataclass from enum import Enum class AgentStatus(Enum): 智能体状态枚举 RUNNING running STOPPED stopped # 正常停止输出最终答案 ERROR error # 出错停止 MAX_ITER max_iteration # 达到最大迭代次数停止 dataclass class AgentStep: 记录智能体每一步的完整信息 thought: str action: Optional[str] None action_input: Optional[dict] None observation: Optional[str] None final_answer: Optional[str] None class ReactAgent: ReAct模式智能体核心类 def __init__(self, llm_client, tool_registry, memory_manager, max_iterations10): self.llm llm_client self.tools tool_registry self.memory memory_manager self.max_iterations max_iterations self.status AgentStatus.STOPPED self.steps: List[AgentStep] [] def run(self, user_input: str) - str: 运行智能体的主入口 self.status AgentStatus.RUNNING self.steps [] self.memory.add_message(user, user_input) iteration 0 final_output None while self.status AgentStatus.RUNNING and iteration self.max_iterations: iteration 1 print(f\n--- 迭代第 {iteration} 步 ---) # 1. 构建当前上下文并调用LLM进行“思考” prompt_messages self._build_messages() llm_response self.llm.chat_completion(prompt_messages) llm_content llm_response[choices][0][message][content] # 2. 解析LLM的响应 step self._parse_llm_response(llm_content) self.steps.append(step) self.memory.add_message(assistant, llm_content) # 3. 根据解析结果决定下一步 if step.final_answer is not None: # 智能体认为可以给出最终答案 self.status AgentStatus.STOPPED final_output step.final_answer self.memory.add_message(assistant, fFinal Answer: {final_output}) break elif step.action is not None: # 智能体决定调用工具 print(f动作: {step.action}, 输入: {step.action_input}) observation self._execute_action(step.action, step.action_input) step.observation observation self.memory.add_message(system, fObservation: {observation}) else: # 无法解析出有效动作或最终答案可能是LLM格式错误 self.status AgentStatus.ERROR final_output f智能体响应解析失败: {llm_content} break if self.status AgentStatus.RUNNING: self.status AgentStatus.MAX_ITER final_output f达到最大迭代次数({self.max_iterations})仍未完成。 return final_output or 智能体运行结束无输出。 def _build_messages(self) - List[Dict[str, str]]: 构建发送给LLM的消息列表 messages [] # 系统消息包含角色设定、工具描述和格式要求 system_prompt self._get_system_prompt() messages.append({role: system, content: system_prompt}) # 从记忆管理器获取对话历史可能已压缩 history_messages self.memory.get_context_messages() messages.extend(history_messages) # 可以在这里添加一个强制的格式提醒 # messages.append({role: user, content: 请严格按要求的格式回复。}) return messages def _get_system_prompt(self) - str: 生成系统提示词 tools_desc self.tools.get_tools_description() tool_names list(self.tools._tools.keys()) prompt f你是一个AI助手可以使用工具来解决问题。 你可以使用的工具 {tools_desc} 你必须严格按照以下格式回复 Thought: 你的思考过程 Action: 工具名必须是[{tool_names}]中的一个 Action Input: 工具的输入参数必须是JSON字符串 Observation: 工具执行的结果 或者当你已经得到最终答案时 Thought: 你的思考过程 Final Answer: 你的最终答案 现在开始。 return prompt def _parse_llm_response(self, content: str) - AgentStep: 解析LLM的回复提取Thought, Action, Final Answer等 step AgentStep(thought) # 使用正则表达式匹配关键部分 thought_match re.search(rThought:\s*(.*?)(?\nAction:|\nFinal Answer:|\n?$), content, re.DOTALL) if thought_match: step.thought thought_match.group(1).strip() action_match re.search(rAction:\s*(\w), content) action_input_match re.search(rAction Input:\s*(.*?)(?\nObservation:|\n?$), content, re.DOTALL) if action_match and action_input_match: step.action action_match.group(1) try: # 尝试解析JSON输入 input_str action_input_match.group(1).strip() # 处理可能存在的json代码块标记 input_str input_str.replace(json, ).replace(, ).strip() step.action_input json.loads(input_str) except json.JSONDecodeError as e: step.observation fAction Input 不是有效的JSON: {input_str}. 错误: {e} step.action None # 解析失败视为无效动作 final_answer_match re.search(rFinal Answer:\s*(.*?)(?\n?$), content, re.DOTALL) if final_answer_match: step.final_answer final_answer_match.group(1).strip() return step def _execute_action(self, action_name: str, action_input: dict) - str: 执行工具调用 tool self.tools.get_tool(action_name) if not tool: return f错误未知工具 {action_name}。 try: # 这里可以加入参数验证如果tool定义了args_schema result tool.run(**action_input) return str(result) except Exception as e: return f执行工具 {action_name} 时出错: {str(e)}这个ReactAgent类实现了一个最基础的ReAct循环。它包含了状态管理、消息构建、响应解析和工具执行的核心逻辑。你可以通过继承这个类并重写_build_messages、_get_system_prompt等方法来定制不同风格的智能体。4.2 一个完整的端到端示例天气查询助手让我们用一个具体的例子串联起所有组件。假设我们要构建一个能查询天气的智能体。1. 定义工具我们创建一个调用真实天气API的工具这里用模拟函数代替。import requests class WeatherInput(BaseModel): city: str Field(..., description城市名称例如北京、Shanghai) def get_weather(city: str) - str: # 模拟API调用实际应替换为如OpenWeatherMap的API # response requests.get(fhttps://api.openweathermap.org/...q{city}) # data response.json() # return f{city}的天气是{data[weather][0][description]}, 温度{data[main][temp]}°C return f{city}的天气模拟结果晴温度25°C。 weather_tool Tool( nameget_weather, description获取指定城市的当前天气情况。, funcget_weather, args_schemaWeatherInput )2. 初始化并运行# 1. 初始化组件这里需要你实现LLMClient和SimpleMemoryManager llm_client OpenAILLMClient(api_keyyour_key, modelgpt-3.5-turbo) registry ToolRegistry() registry.register(weather_tool) memory SimpleMemoryManager(max_messages10) # 一个简单的只保存最近N条消息的记忆管理器 # 2. 创建智能体 agent ReactAgent(llm_client, registry, memory, max_iterations5) # 3. 运行 user_query 上海和北京的天气怎么样哪个更热 result agent.run(user_query) print(\n 最终答案 ) print(result) # 4. 查看执行步骤 print(\n 执行步骤追踪 ) for i, step in enumerate(agent.steps): print(f步骤{i1}:) print(f 思考: {step.thought[:100]}...) if step.action: print(f 动作: {step.action}({step.action_input})) print(f 观察: {step.observation[:100]}...) if step.final_answer: print(f 最终答案: {step.final_answer})在这个例子中智能体可能会这样运行思考用户问了两个城市的天气并要比较。我需要先获取两地的天气信息。我有get_weather工具。动作Action: get_weather, Action Input: {city: 上海}观察Observation: 上海的天气模拟结果晴温度28°C。思考我得到了上海的天气。现在需要北京的天气。动作Action: get_weather, Action Input: {city: 北京}观察Observation: 北京的天气模拟结果多云温度25°C。思考上海28°C北京25°C。上海更热。最终答案Final Answer: 根据查询上海天气晴28°C北京天气多云25°C。因此上海比北京更热。通过这个流程你可以清晰地看到智能体是如何一步步推理、行动并最终解决问题的。5. 进阶技巧与性能优化实战当基础智能体跑通后你会面临真实世界的挑战成本、速度、稳定性。下面分享一些进阶优化技巧。5.1 降低LLM调用成本的策略LLM API调用是智能体运行的主要成本。优化策略包括精细化上下文管理如前所述积极使用摘要压缩和滑动窗口尽可能减少每次请求的token数量。定期清理记忆中的冗余信息。使用更小的模型进行简单任务并非所有步骤都需要最强的GPT-4。你可以设计一个路由机制让一个“调度员”模型如GPT-3.5-turbo先判断任务的复杂性。如果是简单的信息提取或格式化就用更便宜、更快的模型如Claude Haiku甚至本地小模型只有复杂推理和规划才用大模型。缓存Caching对于相同的用户查询和工具结果其LLM的“思考”过程很可能是相同的。可以建立一个缓存系统将(prompt_hash, model)作为键将LLM的完整响应作为值存储起来。下次遇到相同请求时直接返回缓存结果。这对于工具结果固定的查询如“北京的天气”效果显著。批量处理Batching如果智能体需要处理大量独立的小任务可以考虑将多个任务的提示词批量发送给LLM API如果API支持这通常比逐个请求更便宜、更快。5.2 提升智能体可靠性与稳定性的方法智能体在野外“失控”是常见问题以下是加固方法动作验证与沙盒Sandboxing在工具真正执行前进行预验证。特别是对于文件操作、数据库写入、网络请求等有副作用的工具。例如可以设置一个“模拟模式”或“确认模式”在首次运行时只报告将要执行的操作等待用户确认后再实际执行。设置安全护栏Guardrails在系统提示词中明确禁止性条款并可在解析LLM响应后加入规则检查。例如检查动作是否在允许的工具列表内检查输入参数是否包含敏感词如“rm -rf”、“DROP TABLE”等。实现自动重试与回退网络波动、API限流、工具临时失败是常态。在主循环中对于工具调用失败或LLM返回格式错误的情况不应立即失败。可以设计重试逻辑例如最多重试3次或者在多次失败后尝试让LLM换一种思路或使用备用工具。超时与看门狗Watchdog为每次LLM调用和工具执行设置超时。如果某个步骤卡住看门狗线程可以中断它并将控制权交还给主循环报告错误或尝试恢复。5.3 扩展性与多智能体协作的初步探索单一智能体的能力有限。ai-agents-from-scratch项目可以进一步扩展为多智能体系统。角色分工你可以创建多个具有不同专长的智能体实例每个实例拥有不同的系统提示词和工具集。例如一个“研究员”智能体擅长搜索和总结一个“程序员”智能体擅长写代码和调试一个“分析师”智能体擅长处理数据。管理者-工作者Manager-Worker模式引入一个“管理者”智能体。用户向管理者提出需求管理者将任务分解并分配给不同的“工作者”智能体去执行然后汇总结果。管理者自身也可以是一个ReAct智能体它的“工具”就是调用其他工作者智能体。智能体间通信智能体之间如何交换信息简单的可以通过一个共享的“黑板”Blackboard或消息队列。每个智能体将产出写入黑板其他智能体从中读取所需信息。更复杂的可以定义一套通信协议让智能体能够相互请求和提供帮助。实现多智能体协作是高级主题会引入并发、锁、通信一致性等复杂问题。但从ai-agents-from-scratch这个项目出发你可以先尝试实现一个简单的“规划者-执行者”双智能体模型这是向多智能体世界迈出的坚实第一步。6. 常见问题排查与实战调试心得即使按照最佳实践构建你的智能体在初期也难免会“行为怪异”。下面是我在实战中遇到的一些典型问题及解决方法。6.1 LLM不按格式输出怎么办这是最常见的问题。症状LLM回复了自然语言而不是Thought/Action/Final Answer格式。检查系统提示词确保格式指令清晰、醒目。使用三个反引号包裹格式示例或者用### 格式要求 ###这样的标题突出它。使用Few-shot示例这是最有效的方法。在系统消息或对话历史开头提供1-2个完美的示例。LLM的模仿能力很强。启用模型原生功能如果使用OpenAI API强烈建议使用function_call参数或将response_format设置为{ type: json_object }。这能从根本上保证输出的结构化。对于其他API查看是否支持类似功能。后处理与重试在解析逻辑中加入容错。如果第一次解析失败可以将错误信息如“你的回复格式不正确请严格按照要求格式重新回答”连同原始问题再次发送给LLM让它重试。通常第二次就会改正。调整温度Temperature将温度参数调低如0.1或0减少输出的随机性使其更倾向于遵循指令。6.2 智能体陷入死循环或无效动作症状智能体反复调用同一个工具或者在不该调用工具的时候调用。增强“思考”要求在系统提示词中明确要求LLM在Thought部分评估上一步的Observation是否已经足够回答问题。例如“在决定下一个动作前请仔细评估当前的Observation是否已经包含了回答用户问题所需的全部信息。如果已经足够请直接给出Final Answer。”引入循环检测在Agent类中维护一个最近动作的历史列表。如果检测到完全相同的(action, action_input)组合在短时间内重复出现例如最近3步内出现2次则中断循环并强制LLM重新评估或直接报错。给工具结果“打分”让LLM在Thought中对上一个工具的结果进行简短评价如“这个结果完全回答了子问题A”、“这个结果不相关”这能促使它进行更深入的元认知。限制迭代次数这是最后的安全网。务必设置一个合理的max_iterations如10-15步防止无限循环消耗大量API费用。6.3 工具调用结果不佳导致任务失败症状工具执行成功但返回的结果质量差如搜索不到相关信息导致智能体基于错误信息推理。工具设计的健壮性工具函数内部应该有完善的错误处理和结果验证。例如网络搜索工具在返回空结果或错误时不应返回“Error: ...”而应返回一个对LLM友好的提示如“Observation: 未能找到关于‘XXX’的有效信息请尝试更换搜索词或描述得更具体。”让智能体评估工具结果在提示词中要求LLM判断工具结果是否可靠。例如“如果Observation表明工具未能提供有效信息你应该尝试换一种方式使用该工具或者尝试使用其他工具。”实现工具链Tool Chaining有时一个工具的输出是另一个工具的输入。例如先调用search_web获取一些文章链接再调用read_webpage工具去提取链接中的具体内容。设计好工具间的协作流程。人工干预点在关键节点例如在执行具有永久性影响的操作前或在多次尝试失败后设计“询问用户”的机制。让智能体学会说“我不太确定根据X和Y信息我倾向于A方案您认为可以继续吗”构建一个稳定、高效的AI智能体是一个持续迭代的过程。从ai-agents-from-scratch出发亲手实现每一个模块踩过每一个坑你获得的将不仅仅是一个可运行的项目而是对智能体技术底层逻辑的深刻洞察。这份洞察力是未来无论使用多么高级的框架都能游刃有余地进行定制和优化的坚实基础。