ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

从ReAct到Function Calling:构建能自动纠错的多步执行Agent

从ReAct到Function Calling:构建能自动纠错的多步执行Agent 引言Agent 的“原罪”与“救赎”2023年AutoGPT和BabyAGI的爆发让人们看到了通用人工智能AGI的雏形但也暴露了大语言模型LLM作为Agent核心的致命弱点幻觉与不稳定的工具调用。最初的ReActReasoning Acting范式通过让模型在“思考”和“执行”间循环解决了简单的工具调用问题。然而当遇到API密钥错误、参数类型不匹配或依赖缺失时传统的ReAct只会机械地重试直到耗尽Token。Function Calling函数调用的出现并非要取代ReAct而是对其实行了一次“外科手术式的升级”。它强制LLM输出结构化的JSON而不是自然语言中的“Action:”。但真正的质变发生在我们将“自我反思Self-Reflection”注入执行引擎的那一刻。今天我们要构建的Agent不是只会报错退出的“书呆子”而是一个会阅读错误日志、调整参数、甚至更换备用API的“老练工程师”。目录引言Agent 的“原罪”与“救赎”第一章ReAct 的辉煌与局限1.1 什么是 ReAct 范式1.2 为什么我们需要升级第二章Function Calling 深度解析2.1 不仅仅是 “JSON 输出”2.2 工具定义的艺术第三章构建“自动纠错”的核心引擎3.1 状态管理设计3.2 自我反思Self-Reflection的实现第四章多步执行与动态规划4.1 Plan-and-Solve 策略的融合4.2 关键难点中断与恢复第五章实战演练 —— 自动纠错的多步 Agent 代码5.1 环境准备5.2 定义工具集合5.3 核心 Agent 类实现5.4 测试运行见证自动纠错第六章进阶纠错策略与工程化思考6.1 指数退避与抖动Exponential Backoff with Jitter6.2 上下文窗口管理6.3 人类反馈强化学习RLHF风格的纠错6.4 与 RAG检索增强生成的结合第七章ReAct Function Calling 下一代架构第八章性能测试与监控第九章常见陷阱与避坑指南第十章未来展望 —— 从“纠错”到“容错”结语第一章ReAct 的辉煌与局限1.1 什么是 ReAct 范式ReAct 由 Yao 等人在 2022 年提出其核心逻辑极其简洁通过提示词工程迫使 LLM 交替输出Thought推理和Action行动并观察Observation观察结果。伪代码逻辑textwhile not finished: thought LLM.think(history) action LLM.choose_action(thought) observation execute(action) history.append(observation)1.2 为什么我们需要升级在实际生产中ReAct 存在三个致命伤解析脆弱性模型可能输出Action: Calculate但参数写成了Action Input: 22还是2 2纯文本解析的正则表达式极易失效。无状态重试如果执行execute(action)抛出ConnectionErrorReAct 只会把错误文本塞回给 LLM。LLM 可能据此产生新的幻觉而不是针对错误类型进行修正。Token 浪费思考链CoT中的大量思维文本不仅消耗上下文窗口还稀释了关键工具调用的注意力。转折点OpenAI 在 2023 年 6 月推出的gpt-4-0613率先引入了 Function Calling 能力正式宣告了结构化调用的时代来临。第二章Function Calling 深度解析2.1 不仅仅是 “JSON 输出”Function Calling 本质上是一个“强制语义补全”过程。在模型推理层系统根据你提供的 JSON Schema 约束 Logit 概率分布迫使生成结果必须是合法的函数名和参数。核心优势确定性输出直接是tool_calls对象无需正则清洗。并行性原生支持一次响应调用多个函数parallel_tool_calls。原生纠错基础当参数验证失败时我们可以利用BadRequestError信息重新发起补全请求这正是“自动纠错”的底层接口基础。2.2 工具定义的艺术在最新版的 OpenAI SDKv1.x中我们使用pydantic定义工具类。这不仅是类型提示更是生成 JSON Schema 的源头。pythonfrom pydantic import BaseModel, Field from typing import List, Optional class WebSearchTool(BaseModel): query: str Field(description经过优化的搜索引擎查询词) max_results: int Field(default5, description返回结果数量, ge1, le10) use_google: bool Field(defaultTrue, description是否优先使用Google)第三章构建“自动纠错”的核心引擎自动纠错并非简单的try...except而是一个三层防御体系语法层由 Function Calling 的 Schema 约束确保参数类型正确。运行时层捕获执行异常API超时、权限不足并将结构化的错误代码回传。策略层核心利用ReAct 的反思思维但作用于Function Calling 的结果上。3.1 状态管理设计我们引入AgentState来追踪执行路径这是实现多步回退的关键。pythonfrom typing import List, Dict, Any from dataclasses import dataclass, field from enum import Enum class Status(Enum): PENDING pending SUCCESS success RETRYABLE retryable # 可重试错误如限流 FATAL fatal # 致命错误如参数永久错误 dataclass class StepRecord: tool_name: str input_args: Dict[str, Any] output: Any None error: Optional[str] None status: Status Status.PENDING retry_count: int 03.2 自我反思Self-Reflection的实现当工具执行返回错误时我们不直接将错误扔回给 LLM而是先经过一个“反思编码器Reflection Encoder”。这个编码器会将原始错误翻译成带有解决建议的上下文。pythondef reflect_on_error(error_msg: str, tool_name: str) - str: 将机器错误翻译为LLM能够理解并修正的语义提示 if Connection refused in error_msg: return f⚠️ 工具 {tool_name} 网络连接被拒绝。建议请检查网络代理设置或尝试使用备用镜像源。 elif Invalid API key in error_msg: return f 工具 {tool_name} API密钥无效。建议请检查环境变量中的密钥配置或切换至测试模式。 elif Rate limit in error_msg: return f⏳ 工具 {tool_name} 触发限流。建议等待 {extract_retry_after(error_msg)} 秒后重试或降低并发数。 elif AttributeError in error_msg and NoneType in error_msg: return f 工具 {tool_name} 返回空对象可能是数据路径错误。建议尝试调整查询参数中的过滤条件。 else: return f❌ 工具 {tool_name} 执行失败{error_msg}。建议请检查输入参数是否符合预期格式。第四章多步执行与动态规划真正的 Agent 不是执行一个孤立函数而是完成一个目标Goal。比如“帮我统计上月销售额并生成可视化图表如果数据源API挂了就用缓存数据代替。”4.1 Plan-and-Solve 策略的融合我们采用“规划-执行-反思-再规划”的循环。初始时LLM 收到用户目标输出一个plan列表。但注意此计划是动态的。pythonfrom langchain_openai import ChatOpenAI from langgraph.graph import StateGraph, END from typing import TypedDict, Annotated import operator class AgentState(TypedDict): user_goal: str plan: List[str] past_steps: Annotated[List[StepRecord], operator.add] final_response: str # 定义节点函数 def planner(state: AgentState): # 这里使用带有结构化输出的LLM生成步骤列表 pass def executor(state: AgentState): # 执行计划中的下一个未完成步骤 pass def reflector(state: AgentState): # 检查上一步结果决定修正、重试还是重新规划 pass4.2 关键难点中断与恢复当reflector发现错误时它不立即报错而是修改plan列表插入一个新的纠正步骤例如修正参数使用宽松匹配重新搜索。代码片段动态插入纠错步骤pythondef handle_retry_logic(state: AgentState) - AgentState: last_step state[past_steps][-1] if last_step.status Status.RETRYABLE and last_step.retry_count 3: # 插入一个“修正”步骤而不是简单重试 correction_step f根据错误 {last_step.error}修改 {last_step.tool_name} 的参数增加容错性 state[plan].insert(0, correction_step) # 插到最前面优先执行 return state elif last_step.status Status.FATAL: # 致命错误启动备用方案 fallback_step f因 {last_step.tool_name} 失败切换到备用工具 DummyBackupTool state[plan].insert(0, fallback_step) return state else: return state第五章实战演练 —— 自动纠错的多步 Agent 代码以下是我们本次构建的核心 Agent 类。它整合了 OpenAI Function Calling、LangGraph 状态管理和自定义重试逻辑。5.1 环境准备bashpip install -U langchain langgraph openai python-dotenv5.2 定义工具集合为了让 Agent 有“纠错”的空间我们故意设计一个会定期报错的模拟工具和一个必定成功的备用工具。pythonimport random import time from langchain_core.tools import tool from pydantic import BaseModel, Field # ---------- 模拟不稳定的 API ---------- class SalesDataInput(BaseModel): month: str Field(description格式为 YYYY-MM) region: str Field(defaultEast, description销售区域) tool(args_schemaSalesDataInput) def fetch_sales_data(month: str, region: str East) - str: 模拟从数据库获取销售额。有 50% 概率模拟超时或数据格式错误。 # 模拟随机错误 if random.random() 0.4: raise ConnectionError(Database connection pool exhausted. Please retry with smaller time range.) if random.random() 0.3: raise ValueError(Unexpected data format: total field missing in response.) # 模拟成功 return fSales data for {month} in {region}: ${random.randint(10000, 99999)} # ---------- 稳定的备用工具 ---------- tool def get_cached_sales(month: str) - str: 从本地缓存获取销售数据永不失败 return fCached Sales for {month}: $42,000 (estimated) # ---------- 计算器工具用于多步逻辑 ---------- tool def calculate_average(total: float, months: int) - float: 计算平均值 return total / months if months 0 else 05.3 核心 Agent 类实现我们将使用langgraph来管理循环因为它允许我们在节点之间自由跳转这是实现“纠错回退”的最佳图结构。pythonimport os from typing import Literal from langchain_openai import ChatOpenAI from langgraph.graph import MessageGraph, END from langgraph.prebuilt import ToolExecutor, ToolInvocation from langchain_core.messages import HumanMessage, AIMessage, ToolMessage class ResilientAgent: def __init__(self, modelgpt-4o-mini): self.llm ChatOpenAI(modelmodel, temperature0) # 绑定工具 self.tools [fetch_sales_data, get_cached_sales, calculate_average] self.tool_executor ToolExecutor(self.tools) # 将工具定义转换为 OpenAI 格式 self.llm_with_tools self.llm.bind_tools(self.tools) # 构建图 self.graph MessageGraph() self.graph.add_node(agent, self._call_agent) self.graph.add_node(execute_tool, self._execute_tool) self.graph.add_node(reflect_and_retry, self._reflect_and_retry) self.graph.set_entry_point(agent) # 条件边判断是继续执行、反思还是结束 self.graph.add_conditional_edges(agent, self._should_continue) self.graph.add_edge(execute_tool, reflect_and_retry) self.graph.add_conditional_edges(reflect_and_retry, self._after_reflection) self.graph.add_edge(agent, END) # ---------- 节点实现 ---------- def _call_agent(self, state: List[BaseMessage]): Agent 推理节点调用 LLM 决定下一步 # 注入系统级纠错指令 system_prompt 你是一个具有自动纠错能力的Agent。 规则 1. 如果遇到工具报错不要直接放弃先分析错误类型。 2. 如果是网络或限流错误等待1秒后重试我会处理重试计数你只需在思考中提及。 3. 如果是参数错误如月份格式不对尝试修正格式。 4. 如果某个工具反复失败主动切换到备用工具如get_cached_sales。 # 由于MessageGraph只接受消息列表我们将system放入第一条 messages [(system, system_prompt)] state response self.llm_with_tools.invoke(messages) return [response] def _execute_tool(self, state: List[BaseMessage]): 执行工具节点 last_message state[-1] # 提取 tool_calls tool_calls last_message.tool_calls results [] for tool_call in tool_calls: try: # 执行工具 output self.tool_executor.invoke(ToolInvocation( tooltool_call[name], tool_inputtool_call[args] )) results.append(ToolMessage(contentstr(output), tool_call_idtool_call[id])) except Exception as e: # 捕获错误返回包含错误信息的 ToolMessage error_msg fERROR: {type(e).__name__}: {str(e)} results.append(ToolMessage(contenterror_msg, tool_call_idtool_call[id])) return results def _reflect_and_retry(self, state: List[BaseMessage]): 反思与纠错节点检查上一步的 ToolMessage决定是否注入修正指令 last_msg state[-1] if state else None if not isinstance(last_msg, ToolMessage): return state content last_msg.content if ERROR in content: # 根据错误类型构造一个“修正提示”插入到上下文中 if ConnectionError in content or pool exhausted in content: correction_hint HumanMessage(contentf[系统纠正] 检测到网络拥堵已自动增加超时等待。请重新尝试调用 fetch_sales_data但将参数 region 改为 Fallback。) # 同时为了强制重试我们在这里可以直接追加一条 AIMessage 表示“重试” retry_message AIMessage(content我将使用修正后的参数重试 fetch_sales_data。, tool_calls[{name: fetch_sales_data, args: {month: 2026-01, region: Fallback}, id: retry_001}]) return state [correction_hint, retry_message] elif Unexpected data format in content: correction_hint HumanMessage(contentf[系统纠正] 数据格式异常立即切换至备用工具 get_cached_sales。) retry_message AIMessage(content切换到缓存数据源。, tool_calls[{name: get_cached_sales, args: {month: 2026-01}, id: cached_001}]) return state [correction_hint, retry_message] # 如果没有错误正常返回 return state # ---------- 路由条件 ---------- def _should_continue(self, state: List[BaseMessage]): 判断 Agent 是否应该调用工具 last_msg state[-1] if hasattr(last_msg, tool_calls) and last_msg.tool_calls: return execute_tool return END def _after_reflection(self, state: List[BaseMessage]): 反思后的路由如果产生了新的 tool_calls 则继续执行否则结束 last_msg state[-1] if state else None if hasattr(last_msg, tool_calls) and last_msg.tool_calls and len(last_msg.tool_calls) 0: # 检查是否是我们注入的重试调用 return execute_tool # 检查是否含有错误但未处理的我们再次发给 Agent 进行最终决策 # 但为了简洁这里如果还有 ERROR 且没有新调用我们直接结束让用户看到错误。 return END def run(self, user_input: str): 运行 Agent initial_messages [HumanMessage(contentuser_input)] final_state self.graph.invoke(initial_messages) # 提取最终输出 for msg in reversed(final_state): if isinstance(msg, AIMessage) and msg.content: return msg.content # 如果是工具输出也可以展示 if isinstance(msg, ToolMessage): if ERROR not in msg.content: return f任务完成{msg.content} return 无法完成任务请查看详细日志。5.4 测试运行见证自动纠错pythonif __name__ __main__: agent ResilientAgent() # 测试用例1将触发连接错误然后自动修正参数重试 print( 测试自动纠错 ) result agent.run(请帮我获取 2026-01 月份东区的销售额如果失败则尝试修正。) print(result) # 测试用例2多步计算包含工具链 print(\n 测试多步执行 ) result agent.run(先获取2026-01东区销售额然后获取2026-02西区销售额最后计算这两个月平均销售额。) print(result)预期运行日志模拟text 测试自动纠错 [执行] fetch_sales_data(month2026-01, regionEast) - ERROR: ConnectionError: Database connection pool exhausted. [反思] 检测到连接错误注入修正参数 regionFallback。 [执行] fetch_sales_data(month2026-01, regionFallback) - $78,234 任务完成$78,234第六章进阶纠错策略与工程化思考6.1 指数退避与抖动Exponential Backoff with Jitter对于限流类错误我们的 Agent 不能简单地立即重试。在_reflect_and_retry中我们可以维护一个重试计数器并利用time.sleep实现指数退避。示例pythonimport time import random def backoff_retry(attempt): sleep_time (2 ** attempt) random.uniform(0, 1) time.sleep(sleep_time) return f等待 {sleep_time:.2f} 秒后重试6.2 上下文窗口管理多步 Agent 容易撑爆上下文。引入“滑动窗口”或“历史总结”机制。当past_steps超过 20 步时调用 LLM 对早期的对话进行摘要压缩。6.3 人类反馈强化学习RLHF风格的纠错在真正棘手的情况下Agent 自动纠错失败可以触发“人类介入Human-in-the-loop”。这时 Agent 会生成一个清晰的问题描述和两种备选方案等待用户点击确认而不是死循环。代码扩展pythonif attempt_count 3: return HumanMessage(contentf[需要人工介入] 工具 {tool_name} 连续失败 3 次。错误{error}。请选择1. 忽略继续 2. 使用缓存 3. 停止任务)6.4 与 RAG检索增强生成的结合当工具调用失败是因为“缺少必要知识”时例如不知道某个内部系统的 IPAgent 应自动调用 RAG 检索系统查询手册而不是报错。这构成了“知识纠错”闭环。第七章ReAct Function Calling 下一代架构在最新的LangGraph官方文档中ReAct 被重新诠释为一个循环图Cycle Graph。而我们的设计实际上是将 Function Calling 作为 ReAct 中的“行动触发器”但将“反思”提取为一个独立的仲裁节点Arbitrator。这种架构下反思不再依赖于 LLM 的随机生成而是由确定性逻辑LLM 辅助决策共同完成。例如确定性逻辑捕捉ConnectionError- 自动执行网络重试。LLM 辅助决策捕捉ValueError- 分析是数据格式变化提出新的解析策略。这是目前业界公认的最稳健的 Agent 落地形态。第八章性能测试与监控为了让生产环境的 Agent 可信我们必须埋点监控。建议添加Token 消耗追踪每次_call_agent记录response_metadata中的token_usage。步骤耗时柱状图记录每个工具的执行时长发现慢调用。纠错成功率统计_reflect_and_retry中从错误到成功的转化率。如果低于 80%说明工具定义或错误提示词需要优化。监控代码片段pythonfrom datetime import datetime import logging logging.basicConfig(levellogging.INFO) logger logging.getLogger(ResilientAgent) # 在 _execute_tool 中包装 start datetime.now() try: output self.tool_executor.invoke(...) logger.info(fTool {tool_call[name]} succeeded in {(datetime.now()-start).total_seconds():.2f}s) except Exception as e: logger.error(fTool {tool_call[name]} failed after {(datetime.now()-start).total_seconds():.2f}s: {e})第九章常见陷阱与避坑指南过度的自动纠错导致死循环务必在_reflect_and_retry中设置最大重试次数如 3 次。一旦超过强制转到FATAL状态并提请人工介入。工具返回的内容过长LLM 上下文有限如果ToolMessage返回了 10000 字的 HTML 页面直接传给 LLM 会炸掉窗口。建议对工具输出进行“截断与摘要”只保留前 2000 个字符。并发的副作用如果开启了parallel_tool_calls多个工具同时执行。如果其中一个失败是否全部回滚我们建议采用“乐观执行”只隔离失败的那个工具链不影响已成功的部分。第十章未来展望 —— 从“纠错”到“容错”我们已经成功构建了一个能自动纠错的多步 Agent。但 AI 的发展不会止步于此。下一个前沿是“主动容错Proactive Fault Tolerance”Agent 会在执行高风险操作前自动生成冗余方案如同时调用 GPT-4 和 Claude对比输出结果并在检测到输出分歧时启动投票机制。此外随着MCP模型上下文协议的普及未来的 Agent 将不再需要为每个 API 单独编写 Function Schema。标准化的工具接口将让“纠错逻辑”变成通用的中间件我们只需编写一次即可适用于所有 LLM。结语从 ReAct 到 Function Calling这不是技术的替代而是范式的进化。通过将结构化的工具调用与智能的自我反思相结合我们赋予了 Agent 真正意义上的“韧性”。在本文中你不仅学会了如何利用 LangGraph 构建状态机更深入理解了如何通过设计reflect_and_retry节点来实现业界领先的自动纠错机制。请记住优秀的 Agent 不是不犯错而是能以最低的成本从错误中快速恢复。
返回列表