news 2026/8/13 1:37:07

从零手写AI Agent:深入理解智能体核心原理与实现

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
从零手写AI Agent:深入理解智能体核心原理与实现

1. 项目缘起:为什么我们要“徒手”造一个AI Agent?

最近AI Agent这个概念火得不行,感觉是个技术分享会或者技术文章不提两句Agent,就跟不上时代了。市面上也确实涌现了像LangChain、AutoGPT、CrewAI这样优秀的框架,它们封装了大量工具调用、记忆管理、任务拆解的复杂逻辑,让开发者能快速搭建起一个看起来挺智能的“代理”。但不知道你有没有过这种感觉:用框架搭出来的东西,跑起来是能跑,但一旦出了问题,或者想深度定制某个行为,就感觉像在碰一个黑盒,调试起来无从下手,文档翻遍了也找不到那个关键的“开关”。

这正是我决定抛开所有现成框架,从零开始手写一个AI Agent的核心动机。这绝不是为了标新立异或者重复造轮子,而是想通过这个“笨办法”,彻底搞明白一个AI Agent到底是怎么“思考”和“行动”的。它的“大脑”(大模型)是如何被调用的?它如何记住之前的对话?又如何决定下一步是去查资料、写代码还是直接回答?当你亲手用最基础的代码把这些流程串起来之后,你对Agent的理解会从“用户”层面深入到“设计者”层面。以后再遇到任何Agent相关的复杂问题,你脑子里会自然浮现出它的工作流程图,而不是一堆模糊的API调用。

这个过程,很像学编程时不直接上SpringBoot,而是先手写一个简单的IoC容器;学数据结构时不满足于调用HashMap,而是去琢磨它的哈希冲突解决和扩容机制。它锻炼的是一种“第一性原理”的思维。所以,这篇文章就是一次这样的实践记录。我们将只用Python标准库和OpenAI的API(作为“大脑”),从定义一个最简单的Agent循环开始,一步步构建起它的记忆、工具使用和决策能力。你会发现,Agent的核心逻辑,远没有想象中那么神秘。

2. 核心蓝图:一个最小可运行AI Agent的解剖图

在动手写代码之前,我们必须先在大脑里勾勒出这个AI Agent的骨架。一个能独立完成任务的Agent,无论框架如何包装,都离不开以下几个核心组件,它们共同构成了一个经典的“感知-思考-行动”循环。

大脑 (The Brain): 这是Agent的智能核心,通常是一个大语言模型。它负责理解用户的输入(感知),结合上下文进行推理(思考),并生成下一步的指令或回答。在我们的手写版本里,它就是通过API调用的OpenAI GPT模型。我们发送给它一段精心设计的提示词,它返回文本形式的“想法”。

记忆 (Memory): Agent不能是“金鱼”,它必须能记住之前发生过的事情。记忆分为短期和长期。短期记忆通常指当前对话的上下文,即我们发送给大模型的那一串消息历史。长期记忆则可以更复杂,比如一个向量数据库,用于存储和检索过去的经验。初始版本,我们先实现一个简单的对话历史记忆。

工具 (Tools): 这是Agent与外部世界交互的“手脚”。大模型本身无法获取实时信息、无法执行计算、无法操作文件系统。工具就是赋予它这些能力的函数。例如,一个搜索工具、一个计算器工具、一个读写文件的工具。Agent在思考后,如果认为需要,可以“决定”调用某个工具。

执行器 (Executor): 也可以称为“运行时”或“循环控制器”。这是驱动整个Agent运转的引擎。它负责管理整个工作流:接收用户输入,将其与记忆组合成提示词,调用大脑,解析大脑的输出,判断是否需要调用工具,如果需要则调用工具并获取结果,再将结果反馈给大脑进行下一轮思考,直到大脑认为可以给出最终答案,再由执行器输出给用户。

它们之间的关系,可以用一个简单的循环来描述:

  1. 接收输入:用户提出请求。
  2. 组织上下文:执行器从记忆模块获取历史对话,和当前输入一起,组装成发送给大脑的提示词。
  3. 大脑思考:调用大模型API,获得模型的回复文本。
  4. 解析与决策:执行器解析模型回复。如果回复中包含调用工具的指令,则进入步骤5;否则,直接进入步骤6。
  5. 执行工具:根据解析出的指令,调用对应的工具函数,并获得工具执行的结果。
  6. 更新与循环:将工具执行的结果(或模型的直接回复)作为新的上下文,追加到记忆(对话历史)中。如果需要调用工具,则带着工具的结果跳回步骤3,开始新一轮思考;如果不需要,则跳转至步骤7。
  7. 输出结果:将模型的最终回复返回给用户,并更新记忆。

这个循环就是Agent自主性的来源。下面,我们就将这个蓝图转化为具体的代码。

3. 从零搭建:逐步实现Agent的每一个器官

我们将采用自底向上的方式构建,先实现基础部件,最后组装成完整的Agent。请确保你有一个可用的OpenAI API密钥。

3.1 第一步:构建“大脑”——与大模型对话

大脑的核心功能是接收一段提示词,返回模型的思考结果。我们将其封装成一个类,便于管理API密钥、模型选择等配置。

import openai from typing import List, Dict, Any class OpenAIBrain: """ Agent的大脑,封装与OpenAI API的交互。 为了简化,我们暂时只使用ChatCompletion接口。 """ def __init__(self, api_key: str, model: str = "gpt-3.5-turbo"): openai.api_key = api_key self.model = model # 系统提示词,用于设定Agent的角色和基础行为准则 self.system_prompt = """你是一个有帮助的AI助手。你可以思考,并且在需要时使用工具来帮助你完成任务。 如果你需要使用工具,请严格按照以下格式回复: 思考:[你的推理过程] 行动:`工具名称`(`参数1`, `参数2`, ...) 例如:行动:`search_web`(`什么是量子计算`) 如果你不需要使用工具,或者已经通过工具获得了足够信息,请直接给出最终答案。 最终答案:[你的回答] """ def think(self, messages: List[Dict[str, str]]) -> str: """ 核心思考函数。将消息历史发送给大模型,并返回它的回复文本。 """ # 在消息列表开头插入系统提示 conversation = [{"role": "system", "content": self.system_prompt}] + messages try: response = openai.ChatCompletion.create( model=self.model, messages=conversation, temperature=0.7, # 控制创造性,对于任务执行可以调低 max_tokens=500 ) return response.choices[0].message['content'].strip() except Exception as e: return f"思考过程出错: {e}"

这里有几个关键点:

  1. 系统提示词:这是“调教”Agent行为的关键。我们明确规定了它何时以及如何调用工具(行动:工具名(...)),并要求它在不需要工具或得到答案后,以最终答案:开头回复。这种结构化的输出约定,是后续解析的基础。
  2. 消息格式:遵循OpenAI Chat API的格式,每条消息是一个字典,包含rolesystem,user,assistant)和content
  3. 错误处理:简单的try-catch,在实际项目中需要更健壮的处理,比如重试、降级等。

3.2 第二步:赋予“记忆”——记住对话的历史

我们实现一个简单的对话历史记忆,它只保存最近的若干轮对话,防止上下文过长。

class SimpleMemory: """ 简单的对话记忆,使用列表保存消息历史。 可以添加限制最大轮数的功能以防止上下文过长。 """ def __init__(self, max_turns: int = 10): self.messages = [] # 格式:[{"role": "user", "content": "..."}, {"role": "assistant", "content": "..."}] self.max_turns = max_turns * 2 # 因为一轮包含user和assistant两条消息 def add_message(self, role: str, content: str): """添加一条消息到历史记录""" self.messages.append({"role": role, "content": content}) # 如果超出最大限制,从头部移除最旧的消息 if len(self.messages) > self.max_turns: self.messages = self.messages[-self.max_turns:] def get_context(self) -> List[Dict[str, str]]: """获取当前的完整对话上下文""" return self.messages.copy() def clear(self): """清空记忆""" self.messages = []

这个记忆模块非常简单,就是维护一个列表。add_message方法负责追加并管理长度,get_context方法在组织提示词时被大脑调用。在更复杂的Agent中,这里可能会接入向量数据库,实现基于语义的长期记忆检索。

3.3 第三步:打造“工具”——Agent的手和脚

工具是函数,我们需要一种方式让Agent知道有哪些工具可用,以及如何调用它们。我们创建一个工具注册表。

class ToolRegistry: """ 工具注册表。管理所有可用的工具,并提供调用接口。 """ def __init__(self): self.tools = {} # 工具名 -> 工具函数 def register(self, name: str, func: callable, description: str = ""): """注册一个工具""" self.tools[name] = { 'function': func, 'description': description } def execute(self, tool_name: str, *args, **kwargs) -> str: """执行一个工具""" if tool_name not in self.tools: return f"错误:未找到工具 '{tool_name}'。" try: result = self.tools[tool_name]['function'](*args, **kwargs) return str(result) except Exception as e: return f"工具执行出错: {e}" def get_tools_description(self) -> str: """生成工具描述文本,用于拼接到系统提示词中,让Agent知道有哪些工具可用""" desc = [] for name, info in self.tools.items(): desc.append(f"- `{name}`: {info['description']}") return "\n".join(desc) # 定义几个示例工具 def calculator(expression: str) -> str: """计算一个数学表达式。例如:`calculator('3 + 5 * 2')`""" try: # 警告:使用eval有安全风险,此处仅用于演示。生产环境应用安全计算库。 result = eval(expression, {"__builtins__": {}}, {}) return f"计算结果: {result}" except Exception as e: return f"计算错误: {e}" def get_current_time(*args) -> str: """返回当前的日期和时间。""" from datetime import datetime return f"当前时间是: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}" def search_web(query: str) -> str: """模拟网络搜索。在实际应用中,这里会调用真正的搜索API。""" # 这里只是一个模拟返回 simulated_results = { "python": "Python是一种高级、解释型的通用编程语言。", "天气": "模拟天气:北京,晴,25摄氏度。", "量子计算": "量子计算是利用量子力学原理(如叠加和纠缠)进行数据处理的计算范式。" } return simulated_results.get(query.lower(), f"未找到关于 '{query}' 的模拟信息。")

关键设计解析

  1. 注册机制ToolRegistry是一个中心化的管理器。任何函数只要通过register方法注册,就成为了Agent可用的工具。这比在代码中硬编码工具列表要灵活得多。
  2. 工具描述get_tools_description方法生成一段文本描述。这个描述必须被动态地插入到大脑的system_prompt中,这样Agent在思考时才知道自己“拥有”哪些能力。这是连接工具定义与Agent认知的桥梁。
  3. 安全警告calculator工具使用了eval,这在演示中可行,但在真实、开放的环境中极其危险,因为它允许执行任意代码。实际项目中,必须使用像ast.literal_eval这样的安全解析器,或者自己实现一个算术表达式解析器。

3.4 第四步:组装“躯干”——创建主循环与执行器

这是最核心的部分,它将大脑、记忆和工具连接起来,并实现之前描述的推理循环。

import re class SimpleAgent: """ 简单的AI Agent执行器。 """ def __init__(self, brain: OpenAIBrain, memory: SimpleMemory, tool_registry: ToolRegistry): self.brain = brain self.memory = memory self.tools = tool_registry # 更新大脑的系统提示词,加入具体的工具描述 self.brain.system_prompt += f"\n\n你可以使用的工具有:\n{self.tools.get_tools_description()}" def parse_model_response(self, response: str) -> tuple: """ 解析模型的回复。 返回一个元组:(类型, 内容)。 类型可以是 'thought', 'action', 'final_answer'。 """ # 匹配最终答案 final_match = re.search(r'最终答案:\s*(.*)', response, re.DOTALL) if final_match: return ('final_answer', final_match.group(1).strip()) # 匹配工具调用动作 action_match = re.search(r'行动:\s*`(\w+)`\s*\((.*)\)', response) if action_match: tool_name = action_match.group(1) # 解析参数,这是一个简化版,假设参数是逗号分隔的字符串 args_str = action_match.group(2).strip() # 更健壮的解析应该处理引号、嵌套等,这里简单按逗号分割并去除空格 args = [arg.strip().strip('\'\"') for arg in args_str.split(',')] if args_str else [] return ('action', (tool_name, args)) # 如果既不是最终答案也不是行动,则视为纯思考或对话 return ('thought', response) def run(self, user_input: str) -> str: """ 运行Agent的主循环。 """ print(f"用户: {user_input}") # 1. 将用户输入存入记忆 self.memory.add_message("user", user_input) max_iterations = 5 # 防止无限循环 for i in range(max_iterations): # 2. 获取当前对话上下文 context = self.memory.get_context() # 3. 让大脑思考 model_response = self.brain.think(context) print(f"\n[Agent第{i+1}轮思考]") print(f"大脑原始输出: {model_response}") # 4. 解析大脑的输出 resp_type, content = self.parse_model_response(model_response) if resp_type == 'final_answer': # 获得最终答案,循环结束 final_answer = content self.memory.add_message("assistant", final_answer) print(f"最终答案: {final_answer}") return final_answer elif resp_type == 'action': # 需要执行工具 tool_name, tool_args = content print(f"决定调用工具: {tool_name}, 参数: {tool_args}") # 5. 执行工具 tool_result = self.tools.execute(tool_name, *tool_args) print(f"工具执行结果: {tool_result}") # 将工具执行结果作为一条“系统”或“用户”消息加入记忆,供下一轮思考使用 # 这里我们将其模拟为“用户”提供的新信息 self.memory.add_message("user", f"[工具 {tool_name} 的结果] {tool_result}") elif resp_type == 'thought': # 纯思考内容,可以将其加入记忆作为助理的“自言自语”,也可以不加入。 # 这里我们选择加入,让思考过程也形成上下文。 self.memory.add_message("assistant", content) # 如果模型只是思考而没有行动或最终答案,我们需要手动提示它继续。 # 更优的做法是在系统提示词中要求它必须输出行动或最终答案。 # 这里简单处理:添加一个用户提示,推动它继续。 self.memory.add_message("user", "请根据你的思考,继续下一步(使用工具或给出答案)。") else: # 未知响应类型 error_msg = "无法解析模型的响应。" self.memory.add_message("user", error_msg) # 如果循环达到最大次数仍未得到最终答案 timeout_msg = "抱歉,我在处理您的问题时思考过久,未能得出最终结论。" self.memory.add_message("assistant", timeout_msg) return timeout_msg

循环逻辑深度解析

  1. 初始化与提示词增强:在__init__中,我们将工具描述动态添加到了大脑的系统提示词里。这是至关重要的一步,它让模型知道了自己可以调用哪些工具及其功能。
  2. 解析器parse_model_response函数使用正则表达式匹配模型输出。这是整个Agent稳定性的关键。我们约定了行动:工具名(参数)最终答案:的格式,解析器必须准确识别。在实际应用中,可以要求模型输出JSON等更结构化的格式来降低解析难度。
  3. 主循环run方法实现了完整的“思考-行动”循环。
    • 防呆设计:设置了max_iterations(这里为5),防止模型陷入无限循环的“思考-调用-再思考”中。
    • 工具结果反馈:将工具执行结果以[工具 X 的结果] ...的格式作为新的用户消息加入历史。这模拟了Agent通过工具感知到的世界变化,并基于此进行下一轮思考。这个格式可以根据需要调整。
    • 纯思考处理:如果模型只输出了思考过程(thought),我们将其记录后,主动添加一个用户消息推动它继续。更好的做法是在系统提示词中强制要求模型在每轮输出中必须包含行动最终答案
  4. 状态管理:所有的交互状态都保存在memory.messages中。每一轮循环,我们都将最新的上下文(包含所有历史对话和工具结果)发送给模型,这使得模型具备了“记忆”能力。

3.5 第五步:让Agent跑起来——完整的示例代码

现在,我们把所有部件组装起来,并运行一个完整的示例。

def main(): # 0. 配置你的OpenAI API Key OPENAI_API_KEY = "你的-OpenAI-API-Key" # 1. 初始化各个组件 brain = OpenAIBrain(api_key=OPENAI_API_KEY, model="gpt-3.5-turbo") memory = SimpleMemory(max_turns=5) tool_registry = ToolRegistry() # 2. 注册工具 tool_registry.register("calculator", calculator, "计算一个数学表达式,例如:'3 + 5 * 2'。") tool_registry.register("get_current_time", get_current_time, "获取当前的日期和时间。") tool_registry.register("search_web", search_web, "模拟网络搜索,获取信息。") # 3. 创建Agent agent = SimpleAgent(brain, memory, tool_registry) # 4. 运行测试 queries = [ "北京现在的天气怎么样?", "计算一下 (15 + 7) * 3 等于多少?", "先查一下量子计算是什么,然后告诉我它和传统计算的主要区别。" ] for query in queries: print("\n" + "="*50) print(f"处理查询: {query}") print("="*50) final_response = agent.run(query) print("="*50) # 清空记忆,开始下一个独立对话(可选) # memory.clear() if __name__ == "__main__": main()

运行这段代码,你将在控制台看到Agent完整的思考过程。对于第三个复杂问题,它会先调用search_web工具获取“量子计算”的信息,然后将搜索结果作为上下文进行第二轮思考,最终给出一个结合了搜索结果的答案。这就是一个具备自主工具调用能力的AI Agent的完整工作流程。

4. 从玩具到工具:手写Agent的进阶思考与优化

完成一个基础可运行的Agent只是第一步。要让它在实际项目中发挥作用,我们还需要考虑很多工程化和优化问题。这部分才是从“知道原理”到“能用好用”的关键。

4.1 解析器的稳健性:告别脆弱的正则表达式

我们之前用正则表达式r'行动:\s*(\w+)\s*\((.*)\)来解析工具调用。这非常脆弱。如果模型输出的括号不匹配、参数里包含逗号或引号,解析就会失败。更可靠的做法是:

  1. 结构化输出:在调用大模型时,使用function calling(如果API支持)或要求模型输出严格的JSON格式。例如,系统提示词可以改为:“请以以下JSON格式回复:{"thought": "...", "action": {"name": "...", "args": [...]}, "final_answer": "..."}”。然后在解析时使用json.loads
  2. 使用Pydantic模型验证:即使输出JSON,其结构也可能不对。可以定义一个Pydantic模型来描述期望的响应结构,在解析后进行验证和类型转换,这能极大提高代码的健壮性。
  3. 后备与重试:当解析失败时,不应直接报错退出。可以将解析失败的信息反馈给模型(例如,“你上次的回复格式不正确,请严格按照要求输出”),并让其重试。这需要在主循环中增加相应的错误处理逻辑。

4.2 记忆系统的演进:从列表到向量数据库

SimpleMemory只保留了最近的对话,这对于长篇幅、多回合的复杂任务远远不够。真正的长期记忆需要解决两个问题:存储容量相关性检索

  • 分窗口记忆:可以维护多个记忆窗口,如“超短期”(最后几轮对话)、“短期”(当前会话主题)、“长期”(跨会话的持久化存储)。大脑在思考时,从不同的窗口中抽取最相关的信息组合成上下文。
  • 向量记忆:这是目前的主流方案。将对话中的关键信息(如用户的问题、助理的结论、工具的结果)转换成向量(Embedding),存储到向量数据库(如Chroma、Pinecone、Weaviate)中。当新问题到来时,将问题也转换成向量,并在数据库中搜索最相似的过往记忆片段,将其作为上下文注入。这模拟了人类的“联想记忆”。
  • 记忆摘要:对于非常长的对话,可以在每N轮后,让大模型自动生成一个对话摘要,然后将摘要存入长期记忆,并清空或压缩短期记忆列表。这样可以保留核心信息,同时节省上下文令牌。

4.3 工具调用的高级模式:并行、流式与验证

我们实现的工具调用是串行、同步的。在实际场景中,可能需要更复杂的模式:

  • 并行工具调用:有些任务可以同时进行。例如,Agent可以同时调用“获取天气”和“查询航班”两个工具。最新的OpenAI API已经支持在单次请求中要求模型并行调用多个函数。在我们的手写框架中,可以通过解析出多个行动指令,然后使用asyncio或线程池来并发执行。
  • 流式思考:为了让用户体验更好,可以支持流式输出模型的“思考”过程,就像ChatGPT那样一个字一个字地显示。这需要处理OpenAI API的流式响应。
  • 参数验证与类型转换:在ToolRegistry.execute中,我们直接传递字符串参数。更安全的做法是,在注册工具时,同时注册其参数的类型(如int,str,bool)。在执行前,先进行类型转换和验证。例如,calculator工具期望一个字符串表达式,但如果模型传入了数字,可以尝试转换。
  • 工具链:一个工具的输出可以作为另一个工具的输入。这需要更复杂的规划能力。可以在系统提示词中说明工具之间的关系,或者设计一个更高级的“规划模块”,让模型先输出一个步骤计划,再逐步执行。

4.4 规划与反思:让Agent更“智能”

基础的Agent是反应式的:收到输入,思考,行动,再思考。更高级的Agent应该具备规划和反思能力。

  • 任务分解:面对复杂任务(如“为我制定一个周末旅行计划”),Agent应该能先将其分解为子任务(查目的地天气、找酒店、排行程)。这可以通过在系统提示词中要求模型“先制定一个计划”来实现,或者单独调用一个“规划模型”。
  • 自我反思:Agent在执行完一系列步骤后,应该能评估结果是否达到了目标。如果没有,它应该能调整策略。这可以通过在循环中引入一个“反思”步骤来实现。例如,在得到最终答案前,让模型先回答:“当前的结果是否已充分解答用户问题?如果否,还缺少什么信息?下一步应该做什么?”
  • 试错与学习:理论上,Agent可以将成功和失败的经验存储到长期记忆中,在未来遇到类似任务时参考。这涉及到更复杂的强化学习机制,但在我们的框架中,可以通过精心设计记忆的存储和检索来初步实现。

5. 避坑指南:手写Agent过程中常见的“坑”与对策

在亲手实现和迭代这个Agent的过程中,我踩过不少坑。这里分享几个最典型的,希望能帮你绕过去。

坑一:提示词工程是成败的关键,但极易失控

  • 现象:Agent要么不调用工具,要么乱调用工具;或者输出的格式总是不对,导致解析失败。
  • 根因:系统提示词(system_prompt)写得不够清晰、具体,或者与模型的能力不匹配。不同的模型(如GPT-3.5-Turbo和GPT-4)对指令的遵循能力有差异。
  • 对策
    1. 分步调试:不要一次性写很长的复杂提示词。先让模型能正确识别“需要调用工具”的场景。可以先用一个简单的例子测试:“如果我问你一个需要计算的问题,请调用计算器工具。”
    2. 提供大量示例:在提示词中使用Few-Shot示例。直接给出2-3个完整的、格式正确的输入输出对。例如:
      用户:123乘以456等于多少? 助理:思考:这是一个数学计算问题,我需要使用计算器工具。 行动:`calculator`(`123 * 456`)
      这比单纯用文字描述格式有效得多。
    3. 结构化输出:如前所述,强烈建议使用JSON等结构化输出格式,并在提示词中提供JSON Schema示例。
    4. 迭代优化:将提示词单独保存在一个文件中,方便修改和版本管理。根据测试结果不断调整措辞和示例。

坑二:上下文管理不当,导致成本激增或信息丢失

  • 现象:对话轮次一多,API调用费用飙升,或者Agent“忘记”了很早之前的重要信息。
  • 根因:无限制地将所有历史对话都塞进上下文。OpenAI的API按Token收费,上下文越长越贵。而且模型对上下文中间部分的信息关注度会下降。
  • 对策
    1. 设置合理的记忆窗口:像我们的SimpleMemory一样,限制保存的对话轮数。
    2. 关键信息摘要:在对话中,主动让模型对当前讨论的核心点进行总结,并将摘要作为一条独立消息存入记忆,替代冗长的原始对话。
    3. 向量检索作为补充:对于需要长期记忆的知识,采用“向量数据库+摘要”的方式。只将最重要的信息片段向量化存储,需要时通过检索召回,而不是把全部历史都放进上下文。

坑三:工具执行的安全性与可靠性隐患

  • 现象:Agent调用的工具导致系统文件被误删、服务器负载过高,或者因为网络问题长时间挂起。
  • 根因:工具函数没有进行权限控制、输入验证和超时处理。
  • 对策
    1. 最小权限原则:工具函数运行在沙箱环境或受限权限下。特别是文件操作、系统命令执行类工具。
    2. 严格的输入清洗与验证:对所有传入工具的参数进行类型检查、长度限制、危险字符过滤。绝对不要相信模型直接输出的参数。
    3. 超时与重试机制:为每个工具调用设置超时时间。对于可能失败的操作(如网络请求),实现指数退避的重试逻辑。
    4. 用户确认:对于高风险操作(如删除文件、发送邮件),可以让Agent先征求用户确认,再将确认结果作为参数调用工具。

坑四:循环失控,陷入死循环

  • 现象:Agent在“思考-调用工具-再思考”的循环中出不来,永远无法输出最终答案。
  • 根因:模型可能陷入逻辑怪圈,或者工具返回的结果始终无法让它满意。
  • 对策
    1. 强制循环上限:像我们代码中的max_iterations一样,这是必须的保险丝。
    2. 超时总结:在达到循环上限后,不是简单报错。可以让模型基于已有的所有信息,强制输出一个当前能给出的最佳答案或阶段性总结。
    3. 反思中断:在每轮循环后,可以引入一个简单的“反思”步骤,判断是否进展停滞。例如,检查最近两轮的工具调用和思考内容是否高度重复。

手写一个AI Agent的过程,是一个绝佳的深度学习机会。它迫使你去关注每一个细节,从提示词设计、API调用、状态管理到错误处理。当你成功运行起第一个自己打造的Agent,并看着它调用工具完成任务时,那种对技术原理的透彻理解所带来的成就感,是单纯使用框架无法比拟的。这个简单的骨架,已经包含了智能体最核心的思想。你可以在此基础上,根据自己的需求,为其添加更强大的记忆、更丰富的工具、更稳健的循环逻辑,逐步构建出属于你自己的、功能独特的AI智能体。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/8/13 1:35:10

外贸GEO03|ChatGPT/Gemini时代,客户怎么找供应商?

引言:当AI成为采购商的“第一双眼睛” “林芳老师,我们最近发现,很多询盘的质量明显下降了。客户问的问题都很基础,甚至有些问题在官网上写得清清楚楚。”一位做工业阀门的外贸老板最近向我吐槽。 这不是个例。在ChatGPT、Gemini等…

作者头像 李华
网站建设 2026/8/13 1:34:42

TIR棱镜设计全流程:从全内反射原理到光学仿真与工程实践

1. 项目缘起:为什么我们要亲手建模一个TIR棱镜?如果你从事光学设计、激光加工、机器视觉,甚至是消费电子(比如手机闪光灯、扫地机的激光雷达),那么“全内反射棱镜”这个名字你一定不陌生。它不像透镜那样直…

作者头像 李华
网站建设 2026/8/13 1:33:03

CUTTag与RNA-seq多组学关联分析:5大核心套路与实操指南

1. 项目概述:从单组学到多组学关联的必然之路在表观遗传学和转录组学研究领域,我们早已不满足于“单打独斗”式的数据分析。过去,我们可能分别做一次CUT&Tag实验来描绘组蛋白修饰或转录因子的结合图谱,再单独做一次RNA-seq来测…

作者头像 李华
网站建设 2026/8/13 1:30:12

RePKG完全指南:3步解锁Wallpaper Engine资源包的终极教程

RePKG完全指南:3步解锁Wallpaper Engine资源包的终极教程 【免费下载链接】repkg Wallpaper engine PKG extractor/TEX to image converter 项目地址: https://gitcode.com/gh_mirrors/re/repkg RePKG是一款专门用于提取和转换Wallpaper Engine资源包的开源工…

作者头像 李华
网站建设 2026/8/13 1:29:13

FS8025BH协议诱骗芯片:一站式解决多协议快充兼容难题

1. 项目概述:一颗芯片如何终结无线充的“协议之痛”如果你和我一样,是个喜欢折腾各种电子设备的玩家,或者是个需要为不同设备寻找合适充电方案的工程师,那你一定对“充电协议”这四个字又爱又恨。爱的是,快充协议确实让…

作者头像 李华
网站建设 2026/8/13 1:27:41

GitLab私有化部署全攻略:从架构解析到CI/CD实战

1. 项目概述:为什么我们需要一个自己的GitLab?如果你是一名开发者,或者正在管理一个技术团队,那么“代码放哪里”这个问题,可能比“今天吃什么”更让你头疼。用公共的GitHub?私有仓库要付费,而且…

作者头像 李华