
1. 从一次工具调用失败说起Qwen-Agent 的 ReAct 与 Function Call 到底怎么选Qwen-Agent 是阿里通义千问团队开源的一套 Agent 开发框架它把大模型的指令遵循、工具调用、规划、记忆能力封装成可复用的组件让你用几十行代码就能搭出一个能读文档、能调工具、能多跳推理的智能体。它适合谁适合已经会写 Python、想快速验证 RAG 增强 Agent 的开发者也适合手里有一堆本地文档、想让模型自己决定“先查什么、再算什么”的工程同学。我最初接触它是因为一个很具体的需求手头有一批 PDF 报告想让模型回答跨文档的对比问题。直接塞进上下文会超长纯向量检索又经常召回不准。Qwen-Agent 给出的答案是把 Agent 分成三个复杂度级别——Lv1 检索、Lv2 分块阅读、Lv3 逐步推理每一层都建立在前一层之上。这个分层思路很实用因为它让你可以按问题难度选择成本。但真正上手时第一个卡点往往不是框架本身而是模型服务怎么接。Qwen-Agent 默认走 DashScope也支持 OpenAI 兼容接口。如果你本地跑的是 vLLM 或 Ollama或者想用一个统一的 Key 管理多个模型通道就需要一个稳定的 API 入口。我实测下来用 TaoToken 的统一通道接 Qwen-Agent 是可行的Base URL 填https://taotoken.net/apiKey 用平台生成的Model ID 按你实际调用的模型填。这样 ReAct 和 Function Call 两种模式都能跑通后面我会给出完整配置。ReAct 和 Function Call 的区别用一句话说ReAct 是让模型“边想边说”把推理过程写成文本框架再去解析动作Function Call 是模型直接输出结构化的函数调用参数框架按 schema 执行。前者对模型能力要求低、调试直观后者更稳定、更适合生产。Qwen-Agent 两种都支持你可以在同一个 Assistant 里混用。下面从环境准备开始一步步把链路跑通。2. TaoToken 前置准备统一 Key 与 API 通道接入 Qwen-Agent 的配置方法在写 Agent 代码之前先把模型通道准备好。Qwen-Agent 的llm_cfg支持两种写法一种是model_server: dashscope走 DashScope 原生协议另一种是 OpenAI 兼容模式填model_server为 base_url。后者通用性更强vLLM、Ollama、以及 TaoToken 的统一通道都走这个模式。你需要准备三样东西Base URL、API Key、Model ID。Base URL 用https://taotoken.net/api注意这里不带任何查询参数。API Key 在控制台的 API Keys 页面生成生成后复制保存页面关闭后不再完整显示。Model ID 取决于你要调用的模型比如qwen-max、qwen-plus或者你本地部署后映射到通道上的模型名。环境变量方式是最省事的避免把 Key 写进代码提交到仓库。在终端里执行export TAOTOKEN_API_KEYsk-你的key export TAOTOKEN_BASE_URLhttps://taotoken.net/api如果你用.env文件管理记得把.env加进.gitignore。Windows PowerShell 下用$env:TAOTOKEN_API_KEYsk-...CMD 下用set TAOTOKEN_API_KEYsk-...。这一步看起来简单但后面 401 报错十有八九是环境变量没生效或者拼写错了。安装 Qwen-Agent 本身pip install -U qwen-agent如果你要用code_interpreter工具它依赖 Docker 或本地 Python 执行环境建议先确认docker命令可用。如果只是跑 ReAct 文本推理和自定义工具不装 Docker 也能跑。另外json5是自定义工具里解析参数的常用库pip install json5一并装上。配置模板我建议单独放一个llm_config.py这样多个 Agent 脚本可以复用import os llm_cfg { model: qwen-max, model_server: os.getenv(TAOTOKEN_BASE_URL, https://taotoken.net/api), api_key: os.getenv(TAOTOKEN_API_KEY), generate_cfg: { top_p: 0.8, temperature: 0.5, } }这里有个细节Qwen-Agent 在 OpenAI 兼容模式下model_server填的是 base_url框架会自动拼接/chat/completions。如果你填成https://taotoken.net/api/v1反而可能重复。实测填https://taotoken.net/api即可。Model ID 必须和通道上可用的模型一致填错会返回模型不存在的错误。注意不要把 API Key 硬编码在llm_cfg里然后提交到公开仓库。用环境变量或本地配置文件是成本最低的安全习惯。准备好之后先别急着写复杂 Agent用一段最小代码验证通道是否通。下一节给出可复制的配置和验证脚本。3. 可复制配置ReAct 与 Function Call 双模式 Agent 模板这一节给两份可直接跑的模板。第一份是 Function Call 模式用自定义工具my_image_gen演示结构化调用第二份是 ReAct 模式用Assistant的默认推理循环演示文本式工具选择。两份都走 TaoToken 通道。先看 Function Call 模板。核心是继承BaseTool并注册description和parameters决定了模型能不能正确调用import os import json5 import urllib.parse from qwen_agent.agents import Assistant from qwen_agent.tools.base import BaseTool, register_tool register_tool(my_image_gen) class MyImageGen(BaseTool): description AI 绘画服务输入文本描述返回图像 URL。 parameters [{ name: prompt, type: string, description: 期望的图像内容描述, required: True }] def call(self, params: str, **kwargs) - str: prompt json5.loads(params)[prompt] prompt urllib.parse.quote(prompt) return json5.dumps( {image_url: fhttps://image.pollinations.ai/prompt/{prompt}}, ensure_asciiFalse ) llm_cfg { model: qwen-max, model_server: os.getenv(TAOTOKEN_BASE_URL, https://taotoken.net/api), api_key: os.getenv(TAOTOKEN_API_KEY), generate_cfg: {top_p: 0.8} } system_instruction 你是一个乐于助人的AI助手。 收到用户请求后先绘制图像得到 URL再用中文回复用户。 bot Assistant( llmllm_cfg, system_messagesystem_instruction, function_list[my_image_gen] ) messages [] query 画一只在草地上奔跑的柯基 messages.append({role: user, content: query}) response [] for response in bot.run(messagesmessages): pass print(response[0][content])这段代码里function_list只放了my_image_gen模型会在需要时输出函数调用参数框架解析后执行call。如果你把code_interpreter也加进去模型可能会选择写代码而不是直接调工具这取决于 system message 的引导。再看 ReAct 模式。ReAct 的关键是让模型在文本里显式写出“思考—行动—观察”的循环。Qwen-Agent 的Assistant在function_list非空时会自动进入工具调用循环但你可以通过 system message 引导它先输出推理文本system_instruction 你是一个善于逐步推理的助手。 面对复杂问题时先写出你的思考过程再决定调用哪个工具。 可用工具my_image_gen。 每次只调用一个工具观察结果后再继续。 bot Assistant( llmllm_cfg, system_messagesystem_instruction, function_list[my_image_gen] )ReAct 和 Function Call 在 Qwen-Agent 里并不是互斥的。底层都是bot.run()返回一个生成器每次 yield 的是当前累积的消息列表。你可以通过打印response[0][content]看到模型是输出了文本推理还是输出了function_call字段。实测下来qwen-max在 Function Call 模式下更稳定qwen-plus在 ReAct 文本推理下响应更快、成本更低。如果你要接 RAG把检索结果作为files或 system message 的一部分传进去即可。Qwen-Agent 的Assistant支持files参数内部会做分块和检索。对于 Lv2 分块阅读框架会并行评估每个块的相关性对于 Lv3 逐步推理它会把 Lv2 封装成工具供上层调用。你不需要自己实现这套逻辑但需要理解它的成本——每个块都要过一次模型块多了费用会上去。提示先用小文档几页 PDF验证链路再换大文档。大文档下 Lv2 的并行调用会明显变慢建议加超时和重试。配置写好后下一步是验证请求是否真的成功以及成功结果长什么样。4. 验证请求与成功结果一次完整的工具调用链路测试验证分三步通道连通性、单工具调用、多轮工具链。第一步用最小请求确认 Key 和 Base URL 没问题from openai import OpenAI import os client OpenAI( api_keyos.getenv(TAOTOKEN_API_KEY), base_urlos.getenv(TAOTOKEN_BASE_URL, https://taotoken.net/api) ) resp client.chat.completions.create( modelqwen-max, messages[{role: user, content: 回复两个字通了}] ) print(resp.choices[0].message.content)如果这里报 401说明 Key 无效或没读到环境变量如果报连接错误检查 Base URL 是否多了/v1或末尾斜杠。这一步通了再跑 Qwen-Agent 的完整链路。第二步跑上一节的my_image_gen模板。成功时你会看到类似输出好的我已经为你生成图像URL 如下 https://image.pollinations.ai/prompt/...同时终端里可能打印出工具调用的中间消息。Qwen-Agent 的bot.run()每次 yield 的response是一个消息列表最后一条是模型的最终回复。你可以遍历打印每条消息的role和content观察工具调用发生在哪一轮。第三步测多轮工具链。把 system message 改成需要两步操作的指令比如“先画图再描述图中内容”。模型可能会先调my_image_gen拿到 URL 后再生成描述文本。如果你加了code_interpreter它甚至可能写代码去下载图片。这一步能跑通说明 Function Call 的循环机制是正常的。对于 RAG 场景验证方式是给Assistant传一个本地 PDFbot Assistant( llmllm_cfg, system_message根据文档回答问题引用原文。, function_list[], files[./docs/report.pdf] )然后问一个文档里明确有答案的问题看回复是否引用了正确段落。如果模型答非所问先检查 PDF 是否被正确解析——Qwen-Agent 内部用pdfplumber或类似库提取文本扫描版 PDF 可能提取为空。这种情况需要先做 OCR再喂给 Agent。成功结果的判断标准工具被正确调用、参数符合 schema、最终回复包含工具返回的信息。如果模型只是“假装”调用了工具实际没触发call通常是description写得太模糊或者parameters的required没设对。把工具描述写具体比如“输入英文 prompt返回图片 URL”模型的选择准确率会明显提升。5. 本篇常见错排查401、local proxy failed、reading choices、OAuth 报错对照这一节按真实报错来。第一个高频错误是 401 Unauthorized。表现是请求直接返回{error: {message: Invalid API key}}。原因通常有三个环境变量名拼错、Key 复制时带了空格、或者用了已删除的 Key。排查方法是在 Python 里打印os.getenv(TAOTOKEN_API_KEY)[:8]确认前几位和平台上一致。如果用的是.env文件确认python-dotenv已加载。第二个是local proxy failed或连接超时。这类报错通常出现在 Base URL 写错、网络出口受限、或者本地开了某些网络工具导致请求被拦截。先确认https://taotoken.net/api能通用curl -I测一下。如果公司网络有出口限制换一个网络环境再试。注意不要在代码里配置任何代理地址保持直连即可。第三个是reading choices或KeyError: choices。这通常发生在响应体不是标准 OpenAI 格式时比如通道返回了错误 JSON但代码直接取resp.choices。排查方法是先打印原始响应import requests r requests.post( https://taotoken.net/api/chat/completions, headers{Authorization: fBearer {os.getenv(TAOTOKEN_API_KEY)}}, json{model: qwen-max, messages: [{role: user, content: hi}]} ) print(r.status_code, r.text[:500])如果返回的是{error: ...}根据错误信息定位。常见的是模型名不存在把 Model ID 换成通道上确认可用的。第四个是 OAuth 相关报错比如OAuth token expired或invalid_grant。如果你用的是某些需要 OAuth 的客户端比如 Claude Code 类工具注意 OAuth 和 API Key 是两套认证。Qwen-Agent 走的是 API Key不需要 OAuth。如果你在 CC Switch、Cline MCP 或 Codex 的auth.json里配置三件套要写全Base URL 填https://taotoken.net/apiKey 填生成的 API KeyModel ID 填实际模型名。缺任何一个都会认证失败。还有一个容易忽略的code_interpreter报 Docker 相关错误。如果你没装 Docker或者 Docker 服务没启动这个工具会失败。解决方法是启动 Docker Desktop或者把function_list里的code_interpreter去掉只保留自定义工具。注意报错信息里如果出现proxy字样先检查是不是环境变量HTTP_PROXY/HTTPS_PROXY被设置了。清掉这两个变量再跑。排查顺序建议先测通道连通性再测单工具最后测多轮。每步都打印原始响应不要只看异常堆栈。大部分问题出在配置层而不是框架层。6. 语义一致 CTA把 Qwen-Agent 接入你的日常开发流链路跑通之后下一步是把它用起来。如果你主要做模型能力验证和对话测试可以直接在模型对话页面里试不同 Model ID 的效果确认哪个模型在你的任务上表现最好。如果你要长期跑编码类 Agent或者需要稳定的工具调用通道Coding Plan 更适合它按周期提供额度不用每次手动充值。接入文档里有各语言和各客户端的配置示例包括 OpenAI SDK、LangChain、以及常见的 IDE 插件。API Keys 页面用来生成和管理 Key建议给不同项目建不同的 Key方便排查和吊销。如果你用 Claude Code 类工具做代码润色或重构注意它需要的是完整的 Base URL Key Model ID 三件套缺一不可。我自己的习惯是先用模型对话快速验证 prompt 和工具描述确认模型能正确选择工具后再写进 Qwen-Agent 脚本里跑批量任务。这样比直接写代码调试快很多因为对话页面能实时看到模型的推理过程。对于 RAG 场景先用小文档在对话里测检索质量再换大文档跑 Lv2/Lv3成本可控。最后留一个实用技巧Qwen-Agent 的bot.run()返回的是生成器你可以把每次 yield 的消息落盘成 JSONL方便回放和对比不同模型的表现。这个日志在调工具描述时特别有用能清楚看到模型是在哪一轮决定调用哪个工具的。