ARTICLE DETAIL

资讯详情

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

从 ReAct 到自主规划 Agent:用 TaoToken 统一 Key 跑通 Plan-and-Execute 与 Reflection 全流程

从 ReAct 到自主规划 Agent:用 TaoToken 统一 Key 跑通 Plan-and-Execute 与 Reflection 全流程 1. 为什么单步 ReAct 撑不起复杂任务从「边走边看」到「先谋后动」的 Agent 演进如果你最近在写 LLM Agent大概率绕不开一个尴尬Demo 里 ReAct 跑得挺顺一换到真实任务就开始翻车。问它「帮我分析这份销售数据并生成周报」它会先查一下数据、再算个平均值、然后突然忘了自己要干嘛最后给你一段看起来像周报但数字对不上的文字。这不是模型不够聪明而是 ReAct 这种「思考—行动—观察」的单步循环天生缺少全局视角。ReAct 的核心价值在于把推理和工具调用交织在一起模型先输出 Thought 分解当前该做什么再输出 Action 调用搜索或计算器拿到 Observation 后继续下一轮。它解决了 Chain-of-Thought 只能空想、Act-Only 只会蛮干的问题在知识密集型问答里表现很稳。但它的局限也很明显——每一步都只看眼前没有一份「任务地图」步骤一多就容易跑偏而且它不会记住上一轮为什么失败。Plan-and-Execute 换了个思路先让 Planner 把目标拆成结构化步骤序列再由 Executor 逐步执行Replanner 根据执行结果决定继续、调整还是终止。这就像出门前先看地图规划路线而不是走到路口才想要往哪拐。再往上ReflectionReflexion给 Agent 加了「复盘」能力Actor 产生轨迹Evaluator 打分Self-Reflection 生成语言反馈存进记忆下次别再犯同样的错。这篇会带你用一套统一的 Key把 ReAct、Plan-and-Execute、Reflection 三种范式串成一个可运行 Demo跑通「规划—执行—反思」全流程。适合已经会调 LLM API、想往 Agent 方向深入的同学。下面先从接入配置讲起再给代码骨架最后用三类任务做对比验证。2. TaoToken 统一 Key 前置准备Base URL、API Key 与模型 ID 三件套怎么配不管你最后选哪种 Agent 范式第一步都是让代码能稳定调到大模型。我试过在多个项目里各配一套 Key结果环境变量互相覆盖排查半天。后来统一用 TaoToken 做接入层一个 Key 走通对话、代码、Agent 循环省掉不少切换成本。你需要准备的三件套是Base URL、API Key、Model ID。Base URL 用https://taotoken.net/api这是 OpenAI 兼容协议的入口绝大多数 SDK 直接改base_url就能用。API Key 在控制台的 API Keys 页面创建建议按项目建不同的 Key方便后面看用量。Model ID 按任务选规划类任务用推理强一点的模型执行类任务用响应快的模型评估/反思类可以用便宜模型来打分。先建一个.env文件把配置集中管理# .env TAOTOKEN_BASE_URLhttps://taotoken.net/api TAOTOKEN_API_KEYsk-你的key MODEL_PLANNERclaude-sonnet-4-20250514 MODEL_EXECUTORclaude-sonnet-4-20250514 MODEL_EVALUATORclaude-3-5-haiku-20241022如果你用 Python装好依赖后这样初始化客户端# llm_client.py import os from openai import OpenAI from dotenv import load_dotenv load_dotenv() client OpenAI( base_urlos.getenv(TAOTOKEN_BASE_URL), api_keyos.getenv(TAOTOKEN_API_KEY), ) def chat(model: str, messages: list, temperature: float 0.2) - str: resp client.chat.completions.create( modelmodel, messagesmessages, temperaturetemperature, ) return resp.choices[0].message.content如果你用 Node/TypeScript配置逻辑一样只是换成openai包// llmClient.ts import OpenAI from openai; const client new OpenAI({ baseURL: process.env.TAOTOKEN_BASE_URL, apiKey: process.env.TAOTOKEN_API_KEY, }); export async function chat(model: string, messages: any[]) { const resp await client.chat.completions.create({ model, messages, temperature: 0.2, }); return resp.choices[0].message.content; }这里有个容易踩的坑Base URL 末尾不要多加/v1OpenAI SDK 会自己拼路径多写一层会 404。另外 Key 别硬编码进代码提交到仓库用环境变量或密钥管理服务。配好之后先跑一个最小请求验证连通性确认没问题再往上搭 Agent 循环不然报错了你分不清是网络问题还是逻辑问题。3. 三种范式的可复制配置与代码骨架ReAct、Plan-and-Execute、Reflection 全流程这一节是核心我会给出三种范式的可运行骨架。为了让你能直接复现配置部分用 JSON 描述工具注册代码用 Python 写逻辑清晰、方便移植。先定义工具层。Agent 要能「动手」就得有工具。这里用两个简单工具演示计算器和模拟搜索。# tools.py import json def calculator(expression: str) - str: 计算数学表达式例如 3 5 * 12 try: result eval(expression, {__builtins__: {}}, {}) return str(result) except Exception as e: return f计算错误: {e} def search(query: str) - str: 模拟搜索返回预设知识 knowledge { 北京人口: 北京常住人口约 2184 万2023 年数据, 上海人口: 上海常住人口约 2487 万2023 年数据, } for k, v in knowledge.items(): if k in query: return v return f未找到关于「{query}」的信息 TOOLS { calculator: calculator, search: search, } TOOL_SCHEMA [ { type: function, function: { name: calculator, description: 计算数学表达式, parameters: { type: object, properties: {expression: {type: string}}, required: [expression], }, }, }, { type: function, function: { name: search, description: 搜索信息, parameters: { type: object, properties: {query: {type: string}}, required: [query], }, }, }, ]ReAct 骨架核心是循环「让模型输出 Thought Action执行工具把 Observation 塞回上下文」。# react_agent.py import json from llm_client import chat, client from tools import TOOLS, TOOL_SCHEMA import os REACT_PROMPT 你是一个 ReAct Agent。每轮按以下格式输出 Thought: 你的推理 Action: 工具名 Action Input: JSON 参数 当可以回答时输出 Thought: 我已经知道答案 Final Answer: 最终答案 可用工具calculator, search def run_react(question: str, max_steps: int 6) - str: messages [ {role: system, content: REACT_PROMPT}, {role: user, content: question}, ] for step in range(max_steps): resp client.chat.completions.create( modelos.getenv(MODEL_EXECUTOR), messagesmessages, temperature0.1, ) text resp.choices[0].message.content messages.append({role: assistant, content: text}) if Final Answer: in text: return text.split(Final Answer:)[-1].strip() # 解析 Action try: action_line [l for l in text.split(\n) if l.startswith(Action:)][0] input_line [l for l in text.split(\n) if l.startswith(Action Input:)][0] tool_name action_line.replace(Action:, ).strip() tool_input json.loads(input_line.replace(Action Input:, ).strip()) observation TOOLS[tool_name](**tool_input) except Exception as e: observation f解析或执行失败: {e} messages.append({role: user, content: fObservation: {observation}}) return 达到最大步数仍未得出答案Plan-and-Execute 骨架先规划再逐步执行最后重规划。# plan_execute_agent.py import json from llm_client import chat import os PLANNER_PROMPT 你是规划器。把用户目标拆成 2-5 个可执行步骤。 只输出 JSON 数组每项包含 step 和 tool_hint。 示例[{step: 查询北京人口, tool_hint: search}] def plan(goal: str) - list: raw chat(os.getenv(MODEL_PLANNER), [ {role: system, content: PLANNER_PROMPT}, {role: user, content: goal}, ]) raw raw.strip().replace(json, ).replace(, ) return json.loads(raw) def execute_step(step: dict, context: str) - str: prompt f当前上下文{context}\n请执行这一步{step[step]}\n只输出执行结果。 return chat(os.getenv(MODEL_EXECUTOR), [{role: user, content: prompt}]) def run_plan_execute(goal: str) - str: steps plan(goal) context for i, step in enumerate(steps): result execute_step(step, context) context f\n步骤{i1}结果{result} final chat(os.getenv(MODEL_EXECUTOR), [ {role: user, content: f根据以下执行记录给出最终答案\n{context}}, ]) return finalReflection 骨架在 ReAct 基础上加评估和反思失败则带着反思重试。# reflection_agent.py from llm_client import chat from react_agent import run_react import os EVALUATOR_PROMPT 评估以下回答是否正确解决了问题。 输出 JSON{score: 0-10, reason: 原因} def evaluate(question: str, answer: str) - dict: raw chat(os.getenv(MODEL_EVALUATOR), [ {role: system, content: EVALUATOR_PROMPT}, {role: user, content: f问题{question}\n回答{answer}}, ]) raw raw.strip().replace(json, ).replace(, ) return json.loads(raw) def run_reflection(question: str, max_rounds: int 3) - str: reflections [] for r in range(max_rounds): enriched question if reflections: enriched \n\n历史反思\n \n.join(reflections) answer run_react(enriched) ev evaluate(question, answer) if ev[score] 8: return answer reflections.append(f第{r1}轮得分{ev[score]}{ev[reason]}) return answer这三段代码共用同一套llm_client和tools切换范式只改入口函数。配置上唯一要动的是.env里的模型 ID规划用强模型、评估用快模型成本和质量能兼顾。4. 验证请求与成功结果三类任务对比跑通规划型智能体代码写完了得用真实任务验证。我准备了三个难度递增的任务分别对应三种范式的强项你可以照着跑一遍看输出差异。任务一单步计算ReAct 强项问题「(3 5) * 12 等于多少」 预期ReAct 一轮内调用 calculator返回 96。Plan-and-Execute 会先规划「计算括号内、再乘」多花一次规划调用结果一样但更慢。这说明简单任务上 ReAct 更经济。任务二多步信息整合Plan-and-Execute 强项问题「北京和上海的人口总和是多少」 ReAct 可能先搜北京、再搜上海、再算总和但如果中间忘了已搜过什么会重复搜索。Plan-and-Execute 会先输出计划[{step:查北京人口},{step:查上海人口},{step:求和}]执行器逐步跑上下文里始终有完整记录不容易漏步。跑这个任务时你可以打印 Planner 的输出确认计划合理steps plan(北京和上海的人口总和是多少) print(json.dumps(steps, ensure_asciiFalse, indent2))正常输出类似[ {step: 查询北京人口, tool_hint: search}, {step: 查询上海人口, tool_hint: search}, {step: 计算两地人口总和, tool_hint: calculator} ]任务三易错任务Reflection 强项问题「北京人口是上海人口的多少倍保留两位小数。」 这个任务容易出错模型可能算反、可能忘记保留小数。Reflection 的价值在这里体现——第一轮 ReAct 给出答案后Evaluator 打分如果发现「未保留两位小数」或「倍数算反」Self-Reflection 生成反馈第二轮带着反馈重跑通常能修正。验证时打印每轮评估结果answer run_reflection(北京人口是上海人口的多少倍保留两位小数) print(answer)成功的话最终答案应该是0.88左右2184/2487。如果第一轮就对了说明模型够强如果第一轮错、第二轮对正好证明 Reflection 有效。三类任务跑下来你会直观感受到ReAct 适合步骤少、反馈快的任务Plan-and-Execute 适合步骤多、需要全局视角的任务Reflection 适合对正确率要求高、允许重试的任务。实际项目里三者常常组合使用比如用 Plan-and-Execute 做外层规划每个步骤内部用 ReAct 执行关键步骤加 Reflection 校验。5. 本篇常见报错排查401、local proxy failed、reading choices、OAuth 逐条对照Agent 跑不起来八成是接入层的问题。我把这几类真实报错和排查路径列出来你对着查能省不少时间。401 Unauthorized最常见。原因通常是 Key 没读到、Key 失效、或者 Base URL 写错导致请求打到了别的服务。先确认.env被正确加载import os print(os.getenv(TAOTOKEN_API_KEY)[:8]) # 只打印前8位确认非空如果打印出None说明load_dotenv()没生效或路径不对。如果 Key 有值仍 401去控制台确认 Key 是否被禁用以及 Base URL 是否为https://taotoken.net/api。local proxy failed / connection error这类报错说明请求根本没发出去通常是本地网络环境或代理配置干扰。检查你的运行环境有没有设置HTTP_PROXY、HTTPS_PROXY环境变量如果有且指向不可用的地址SDK 会尝试走它然后失败。临时清掉再试unset HTTP_PROXY HTTPS_PROXY另外确认防火墙没拦截出站请求。如果你在公司内网可能需要找网管确认出口策略。reading choices of undefined这个报错说明resp.choices是 undefined即返回体结构和你预期的不一样。常见原因是请求其实失败了但你没检查状态码或者模型 ID 写错导致服务返回了错误对象。加一层防御resp client.chat.completions.create(...) if not resp.choices: raise RuntimeError(f返回异常: {resp})同时打印完整响应看看服务到底返回了什么。模型 ID 拼写错误比如把claude-sonnet-4-20250514写成claude-sonnet-4也会触发类似问题对照控制台模型列表核对。OAuth / authentication 相关报错如果你用的是某些 CLI 工具或 IDE 插件可能会遇到 OAuth 流程失败。这类工具通常要求填 Base URL、API Key、Model ID 三件套缺一不可。以 Claude Code 类工具为例配置里要明确写全{ baseUrl: https://taotoken.net/api, apiKey: sk-你的key, model: claude-sonnet-4-20250514 }如果工具提示 OAuth 失败先确认它是否支持 API Key 模式支持的话优先用 Key比 OAuth 少一层跳转排查也简单。Cline、CC Switch 这类工具同理Base URL 和 Model ID 必须和你的 Key 权限匹配用错模型会报权限或不存在错误。排查顺序建议先确认 Key 非空 → 再确认 Base URL 正确 → 再确认模型 ID 存在 → 最后看网络环境。按这个顺序走大部分接入问题十分钟内能定位。6. 把三种范式用起来从 Demo 到项目的落地建议跑通 Demo 只是开始真正落地时还有几个经验值得分享。第一别一上来就上多 Agent。我见过不少项目任务明明用 Plan-and-Execute 加几个工具就能解决非要拆成五个 Agent 互相调用结果调试成本翻倍、延迟高得离谱。先用单 Agent 加规划撑不住了再拆。第二Reflection 的评估器别用太贵的模型。评估本质是打分和给反馈不需要最强推理能力用快模型能省不少成本而且评估轮次多积少成多。规划器才值得用强模型因为计划错了后面全错。第三工具描述要写清楚。Agent 选错工具很多时候不是模型笨而是你的description太模糊。比如search写成「搜索信息」就不如「根据关键词搜索事实性知识返回简短答案」来得明确。工具参数也要给示例模型照着填准确率高很多。第四给 Agent 循环设上限。不管是 ReAct 的max_steps还是 Reflection 的max_rounds都要有硬上限否则遇到死循环会一直烧 token。上限值按任务复杂度定一般 ReAct 给 6-10 步Reflection 给 2-3 轮就够。最后把每次运行的轨迹存下来。Thought、Action、Observation、评估分数这些数据是你后续优化的金矿。哪类任务经常失败、哪个工具老被误用看轨迹一目了然。等积累够了你甚至可以用这些数据微调一个专属的规划模型。Agent 这条路从 ReAct 到自主规划核心就一句话让模型先想清楚再动手动完手能复盘。把这三件事用代码串起来你就已经超过大多数只会调 API 的人了。
返回列表