尧图精选

关于Agent的一些思考:从ReAct到Function Call的工程落地

🕒 发布时间:2026/10/2 16:23:50 📁 来源:尧图网络
1. 从 ReAct 到 Function CallAgent 工程落地到底难在哪如果你正在做 Agent 相关开发大概率绕不开这几个词ReAct、Function Call、RAG、SSE。单独看每个概念都不复杂但真正把它们拼成一个能跑、能观测、能上线的流程时问题就来了——模型不按格式输出、工具调用参数解析失败、流式响应断在半路、检索召回的内容和问题对不上。我自己在做告警处理类 Agent 时踩过一圈坑之后发现Agent 工程化的核心不是让模型更聪明而是把不确定的推理过程约束成可解析、可观测、可回放的工程结构。ReAct 解决的是模型怎么想Function Call 解决的是模型怎么调工具RAG 解决的是模型依据什么回答SSE 解决的是用户怎么看到过程。这四件事串起来才是一个完整的 Agent 编排思路。这篇文章会按工程落地的顺序展开先讲 ReAct 循环的本质和它为什么需要 Function Call 来标准化再给出可复制的工具定义 JSON 和 Agent 配置片段然后讲 RAG 检索增强的接入要点和相似度阈值控制接着用 SSE 流式输出做验证请求最后把常见报错对照排查一遍。适合已经了解大模型基础调用、准备把 Agent 从 demo 推进到真实项目的开发者。核心检索词先明确Agent 从 ReAct 推理范式到 Function Call 工具调用的工程化路径以及 RAG 检索增强与 SSE 流式输出在真实项目中的接入方式。下面按可跟做的步骤来。2. TaoToken 前置准备模型接入与 Key 配置在写 Agent 编排代码之前先把模型接入层跑通。我用的是 TaoToken 作为模型调用入口它兼容 OpenAI 风格的接口Function Call 和流式输出都能直接用。官网地址是 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content API 基础地址是 https://taotoken.net/api 。第一步是拿到 API Key。进入控制台页面 https://taotoken.net/console?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 在 API Keys 管理页创建一个新的 Key。创建时建议按项目命名比如agent-alert-prod方便后续区分不同环境的调用量。Key 只在创建时完整显示一次复制后存到环境变量里不要硬编码进代码。第二步是确认模型 ID。在模型对话页面 https://taotoken.net/chat?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 可以先手动试一下目标模型是否支持 Function Call。我用的语言模型是 qwen3-max向量模型是 text-embedding-v4。你可以在对话页发一条带工具定义的请求看返回里有没有tool_calls字段这是判断模型是否支持 Function Call 最直接的方式。第三步是配置环境变量。在项目根目录建一个.env文件写入TAOTOKEN_API_KEYsk-你的实际Key TAOTOKEN_BASE_URLhttps://taotoken.net/api然后在 Python 代码里读取。如果你用 LangChain可以直接用ChatOpenAI并指定base_urlimport os from langchain_openai import ChatOpenAI llm ChatOpenAI( modelqwen3-max, api_keyos.getenv(TAOTOKEN_API_KEY), base_urlos.getenv(TAOTOKEN_BASE_URL), temperature0, streamingTrue, )这里temperature0是为了让工具调用的参数更稳定streamingTrue是为后面 SSE 流式输出做准备。如果你需要长期跑编码类或 Agent 类任务可以看下 Coding Plan 页面 https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 按调用量规划会更清晰。接入文档在 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 里面有完整的接口说明和参数列表。API Keys 管理页在 https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 需要轮换 Key 时从这里操作。这一步做完你应该能跑通一次普通的模型对话。接下来才是把 ReAct 循环和 Function Call 接进去。3. 可复制配置ReAct 循环与 Function Call 工具定义ReAct 的本质是 Reasoning Acting也就是思考→行动→观察→再思考的闭环。早期实现靠的是 Prompt 工程加字符串解析在 System Prompt 里严格规定输出格式比如要求模型输出Action: 工具名: 输入然后后端用正则去解析。这种方式的问题是格式脆弱模型稍微换个说法就解析失败。Function Call 把这件事标准化了工具用 JSON Schema 定义模型要调用工具时直接返回结构化的tool_calls后端不需要再做字符串解析。下面是一个完整的工具定义示例以查询行星质量为例{ type: function, function: { name: planet_mass, description: 查询太阳系行星的质量返回单位为地球质量, parameters: { type: object, properties: { planet: { type: string, description: 行星名称如 Earth、Mars、Jupiter } }, required: [planet] } } }模型返回的调用请求长这样{ tool_calls: [ { id: call_abc123, type: function, function: { name: planet_mass, arguments: {\planet\: \Earth\} } } ] }注意arguments是一个 JSON 字符串需要二次json.loads才能拿到参数字典。这是很多人第一次接 Function Call 时容易漏掉的一步。如果你用 LangChain 的create_agent()工具定义可以用装饰器写from langchain_core.tools import tool tool def planet_mass(planet: str) - str: 查询太阳系行星的质量返回单位为地球质量。 masses {Earth: 1.0, Mars: 0.107, Jupiter: 317.8} return f{planet} 的质量约为 {masses.get(planet, 未知)} 地球质量然后创建 Agentfrom langgraph.checkpoint.memory import MemorySaver from langchain.agents import create_agent agent create_agent( modelllm, tools[planet_mass], checkpointerMemorySaver(), )create_agent()底层基于 LangGraph会自动构建 Agent 执行图用户消息先进模型节点模型判断是否调用工具如果返回tool_calls进入工具节点执行结果作为 ToolMessage 追加到上下文再回到模型节点继续推理。这个循环一直跑到模型不再返回工具调用为止。代码里看不到显式while循环是因为被封装了。如果你不用 LangChain手写循环也不复杂messages [{role: user, content: 地球和火星哪个重}] while True: resp client.chat.completions.create( modelqwen3-max, messagesmessages, tools[planet_mass_schema], ) msg resp.choices[0].message if not msg.tool_calls: print(msg.content) break messages.append(msg) for tc in msg.tool_calls: args json.loads(tc.function.arguments) result planet_mass.invoke(args) messages.append({ role: tool, tool_call_id: tc.id, content: str(result), })这段循环就是 ReAct 的工程化形态模型推理、工具执行、结果回填、再推理。Function Call 让每一步都有明确的数据结构方便打日志和排查。4. RAG 检索增强接入与 SSE 流式验证RAG 解决的是模型回答缺少依据的问题。如果不做检索把大量知识直接塞进上下文会导致注意力偏移真实问题的权重被稀释幻觉率上升。RAG 的思路是先从知识库召回最相关的片段再和问题一起发给模型。RAG 有两条链路。提问前是数据准备上传文件、分片、Embedding 向量化、存入向量库。提问后是回答生成问题向量化、召回、重排、增强、生成。向量就是一组数字比如[0.8, 0.2, -0.5]是三维向量。Embedding 模型生成的向量维度通常几百到上千维维度越高对文本特征的刻画越细腻。意思相近的文本向量也相近。召回时用余弦相似度计算只看方向不看长度适合文本语义匹配。重排阶段用 Cross Encoder 把问题和片段拼在一起输入模型让模型直接判断相关性精度比召回高但速度慢。接入时关键参数是相似度阈值。我在告警处理场景里设的是 0.8因为告警处理要求准确优先。低于阈值的片段直接过滤不进入生成阶段。同时在 System Prompt 里加约束严格按照文档的内容回答不允许使用文档外的任何信息。 如果请求超出了你的能力范围清晰地说明你的局限性。这样即使没有召回任何片段模型也不会编造答案。SSE 流式输出是让用户看到 Agent 推理过程的关键。SSE 基于 HTTP客户端发起普通请求后服务器不关闭连接按text/event-stream格式持续推送数据。相比 WebSocketSSE 更轻量适合服务器向客户端单向推送文本的场景。SSE 消息格式很简单id: 1 event: message data: {delta: 地球} id: 2 event: message data: {delta: 的质量}每条消息由字段名、冒号、字段值组成以换行分隔消息之间用空行隔开。后端实现时把响应头Content-Type设为text/event-stream然后逐段写入即可。验证流式输出是否正常可以用 curlcurl -N -X POST https://taotoken.net/api/chat/completions \ -H Authorization: Bearer $TAOTOKEN_API_KEY \ -H Content-Type: application/json \ -d { model: qwen3-max, stream: true, messages: [{role: user, content: 用一句话介绍地球}] }-N参数关闭缓冲能实时看到数据块。正常返回是一行行data: {...}最后以data: [DONE]结束。如果卡住不动检查是不是中间有代理层做了缓冲。5. 常见报错排查401、local proxy failed、reading choices、OAuth接入过程中遇到的报错基本集中在几类下面按真实错误信息对照排查。401 Unauthorized最常见的是 Key 没读到或格式不对。先确认环境变量是否生效echo $TAOTOKEN_API_KEY看有没有值。如果用的是.env文件确认代码里调了load_dotenv()。另外检查请求头是不是Authorization: Bearer sk-xxx少了Bearer前缀也会 401。local proxy failed / connection refused这类报错通常是本地网络层的问题。检查base_url是不是写成了https://taotoken.net/api末尾不要多加斜杠。如果公司网络有出口限制确认 API 域名在允许列表里。还有一种情况是本地开了某些网络工具导致请求被拦截关掉后重试。Error reading choices / choices is null这个报错说明请求发出去了但返回结构里没有choices字段。常见原因是模型 ID 写错比如把qwen3-max写成了qwen-max。另一个原因是请求体里messages格式不对比如 role 用了assistant但 content 为空。建议先用模型对话页面 https://taotoken.net/chat?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 手动发一条确认模型可用后再回到代码。OAuth / token expired如果你用的是 Claude Code 或类似工具接入报 OAuth 相关错误通常是认证方式没配对。Claude Code 接入时需要配置三件套Base URL、API Key、Model ID。Base URL 填https://taotoken.net/apiAPI Key 填控制台创建的 KeyModel ID 填qwen3-max或你实际使用的模型。配置路径参考接入文档 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 。工具调用参数解析失败如果报json.decoder.JSONDecodeError检查tc.function.arguments是不是被当成字典直接用了。它永远是字符串需要json.loads。另外有些模型在 arguments 里会带多余空格或换行解析前先strip()。SSE 流中断如果流式输出到一半停了先看后端有没有设置超时。SSE 连接需要保持长连接Nginx 默认 60 秒超时会断开需要调大proxy_read_timeout。另外检查有没有在循环里做了阻塞操作导致数据块没及时 flush。排查时建议打开详细日志把每次请求的model、messages长度、tools数量、返回的finish_reason都打出来。finish_reason是tool_calls说明模型要调工具是stop说明正常结束是length说明被截断了。6. 继续深入Agent 编排的下一步把 ReAct、Function Call、RAG、SSE 串起来之后一个可观测的 Agent 流程基本成型了。上下文管理用 LangGraph 的 checkpointer 机制MemorySaver()把会话状态存在进程内存里通过thread_id区分不同会话。注意它受两个限制服务器内存大小和模型上下文窗口。实际项目里不会把所有历史都传给模型一般只保留最近几轮或者对更早的历史做摘要。如果你想把 Agent 能力扩展到更多工具MCP 是一个值得关注的方向。可以把 MCP 理解成 USB-C 接口Agent 是电脑MCP Server 是外设只要都支持 MCP 协议就能即插即用。相同的工具可以给多个 Agent 复用不需要重复写代码。下一步可以做的事把工具定义抽成独立的 schema 文件方便版本管理给每次工具调用加 trace_id方便回放把相似度阈值做成可配置参数不同场景用不同值。这些工程细节决定了 Agent 能不能从 demo 走到生产。需要继续调试模型或验证 Function Call 返回结构的可以从模型对话页面 https://taotoken.net/chat?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 直接试需要管理 Key 和查看调用量的去 API Keys 页面 https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 接口参数细节看接入文档 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 。长期跑 Agent 任务的可以了解 Coding Plan https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 。
上一篇/下一篇内容由系统自动关联 返回资讯列表 →