
1. 从一次工具调用失败说起Qwen-Agent 到底解决什么问题如果你正在用 Qwen 系列模型做应用大概率遇到过这种场景模型明明知道该调用哪个工具但参数拼错了或者工具返回结果后模型不知道怎么把结果接回对话里再或者多个工具需要按顺序执行中间状态全丢了。这些问题不是模型能力不够而是缺少一层工程化的编排框架。Qwen-Agent 就是干这个的。它是一个专门为基于 LLM 的应用设计的开发框架核心能力包括函数调用、代码解释器、RAG 检索、记忆管理和多智能体协作。适合谁用适合那些不想从零手写 tool call 解析、不想自己维护对话状态、但又需要灵活扩展自定义工具的开发者。你可以把它理解成 LLM 和真实业务逻辑之间的一层“胶水层”把模型输出的自然语言意图翻译成可执行的动作再把执行结果翻译回模型能理解的格式。我试过直接裸调 Qwen 的 function calling 接口光是处理流式输出里的 tool_calls 增量拼接就够写两百行代码。Qwen-Agent 把这些脏活累活都封装好了你只需要关注两件事定义工具、配置模型。框架会自动处理工具注册、参数解析、结果回填、多轮对话状态维护。这篇文章会从源码层面拆解 Qwen-Agent 的调用链路给出可复制的 Agent 配置和工具接入代码最后用 sqlite 数据库助手和思维导图生成两个完整例子验证整个流程。如果你手头有 Qwen 的 API Key跟着做大概 20 分钟能跑通第一个 Agent。2. 前置准备模型接入与 TaoToken 配置Qwen-Agent 本身不绑定任何模型服务商它通过BaseChatModel抽象层支持多种后端。官方示例默认用 DashScope但如果你手头没有 DashScope 的 Key或者想统一管理多个模型的调用凭证可以用 TaoToken 作为模型接入层。TaoToken 提供 OpenAI 兼容的 API 接口Qwen-Agent 可以直接通过model_server参数指向它。先装依赖。Python 环境建议 3.10 以上Qwen-Agent 对异步和类型注解用得比较多pip install -U qwen-agent[gui,rag,code_interpreter,mcp]这个命令会装上核心框架以及 GUI、RAG、代码解释器和 MCP 支持的额外依赖。如果你只需要最基础的功能pip install -U qwen-agent就够了。接下来配置模型。Qwen-Agent 的 LLM 配置是一个字典关键字段包括model、model_server、api_key和generate_cfg。用 TaoToken 的话model_server填https://taotoken.net/apiapi_key填你在控制台生成的 Key。模型名根据你实际要用的填比如qwen-max-latest或qwen3-235b-a22b。llm_cfg { model: qwen-max-latest, model_server: https://taotoken.net/api, api_key: sk-你的TaoToken密钥, generate_cfg: { top_p: 0.8 } }如果你不想把 Key 硬编码在代码里可以设环境变量DASHSCOPE_API_KEY或OPENAI_API_KEYQwen-Agent 会自动读取。但用 TaoToken 的时候建议显式传api_key避免和其他服务的环境变量冲突。这里有个细节Qwen-Agent 的model_server参数在不同版本里行为略有差异。有些版本要求填完整的 base URL 包括/v1有些版本会自动补。实测下来填https://taotoken.net/api是能正常工作的框架内部会拼接/chat/completions路径。如果你遇到 404先检查一下是不是多写了或少写了/v1。配置好之后可以先用一个最简单的对话测试连通性from qwen_agent.agents import Assistant bot Assistant(llmllm_cfg) messages [{role: user, content: 用一句话解释什么是函数调用}] for response in bot.run(messages): print(response)如果能看到流式输出说明模型接入没问题。这一步跑不通的话后面所有工具调用都会失败所以务必先确认基础对话能通。3. 可复制配置工具注册与 Agent 编排的完整代码Qwen-Agent 的工具系统基于注册表模式。你定义一个继承BaseTool的类用register_tool装饰器注册然后在创建 Agent 时把工具名放进function_list列表里。框架会自动把工具的description和parameters拼进系统提示词让模型知道有哪些工具可用。先看一个自定义图像生成工具的完整定义import json5 import urllib.parse from qwen_agent.agents import Assistant from qwen_agent.tools.base import BaseTool, register_tool from qwen_agent.utils.output_beautify import typewriter_print 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 )这个类里有两个关键点description是给模型看的工具说明写得越清楚模型越知道什么时候该调用parameters是 JSON Schema 格式的参数定义框架会把它转成模型能理解的 function calling 格式。call方法接收的params是模型生成的 JSON 字符串你需要自己解析。然后创建 Agent。Assistant是最常用的 Agent 类它支持工具调用、文件读取和 RAG。system_message用来指导模型的行为function_list指定可用的工具files可以传入本地文件让 Agent 读取。system_instruction 在收到用户的请求后你应该 - 首先绘制一幅图像得到图像的url - 然后运行代码下载该图像 - 最后用 plt.show() 展示图像。 你总是用中文回复用户。 tools [my_image_gen, code_interpreter] files [./examples/resource/doc.pdf] bot Assistant( llmllm_cfg, system_messagesystem_instruction, function_listtools, filesfiles )code_interpreter是框架自带的工具用于执行 Python 代码。它会在沙箱环境里运行模型生成的代码并把执行结果返回给模型。这个工具在数据分析、图表绘制、文件处理场景里特别有用。运行对话的代码messages [] while True: query input(\n用户请求: ) messages.append({role: user, content: query}) response [] response_plain_text print(机器人回应:) for response in bot.run(messagesmessages): response_plain_text typewriter_print(response, response_plain_text) messages.extend(response)typewriter_print是框架提供的流式打印工具它会处理函数调用和普通对话的不同输出格式。对于推理类模型它还会识别reasoning_content字段并单独展示思考过程。如果你不需要流式效果可以直接遍历bot.run()的返回值每次拿到的是完整的消息列表。这里有个容易踩的坑messages.extend(response)这一步不能省。bot.run()返回的是增量消息包括 assistant 的工具调用消息和 tool 角色的结果消息。如果你不把这些消息追加到历史里下一轮对话模型就看不到之前的工具调用记录会导致重复调用或上下文断裂。4. 验证请求从日志看工具调用链路跑通第一个例子之后你需要知道怎么验证工具调用是否真的发生了。Qwen-Agent 的流式输出里包含了完整的消息结构你可以通过打印原始 response 来观察。把typewriter_print换成直接打印for response in bot.run(messagesmessages): print(json5.dumps(response, ensure_asciiFalse, indent2))你会看到类似这样的输出[ { role: assistant, content: , function_call: { name: my_image_gen, arguments: {\prompt\: \一只在草地上奔跑的狗\} } } ]这说明模型决定调用my_image_gen工具参数是{prompt: 一只在草地上奔跑的狗}。框架会自动执行这个工具的call方法然后把返回结果作为role: tool的消息追加到对话里再让模型继续生成。完整的调用链路是这样的用户输入 → 模型生成 function_call → 框架解析并执行工具 → 工具返回结果 → 结果回填到 messages → 模型基于结果生成最终回复。这个循环会一直持续直到模型不再生成 function_call 为止。如果你想看框架最终拼给模型的 prompt 长什么样可以在qwen_agent/llm/fncall_prompts目录下找到模板文件。以 ReAct 风格的 prompt 为例工具描述部分是这样的TOOL_DESC ( {name_for_model}: Call this tool to interact with the {name_for_human} API. What is the {name_for_human} API useful for? {description_for_model} Parameters: {parameters} {args_format} )框架会把所有注册的工具按这个模板拼成一段文本插入到系统提示词里。模型看到这段描述后就知道有哪些工具可用、每个工具接受什么参数。对于多轮对话最终拼给模型的 prompt 会包含完整的消息历史。以 Qwen3 的 chat template 为例工具调用和结果会被特殊标记包裹|im_start|system 你是一个能调用计算器和搜索工具的助手。 |im_end| |im_start|user 1020等于多少 |im_end| |im_start|assistant tool_call {name: calculator, arguments: {expr: 1020}} /tool_call |im_end| |im_start|user tool_response 30 /tool_response |im_end| |im_start|assistant 1020等于30。 |im_end|这个格式是模型训练时就见过的所以模型能正确理解工具调用的上下文。Qwen-Agent 的职责就是确保你传入的 messages 列表能被正确地转换成这个格式。验证多智能体协作的时候日志会更复杂一些。MultiAgentHub会为每个 Agent 维护独立的消息历史然后根据 mention 规则决定当前该哪个 Agent 发言。你可以在examples/group_chat_demo.py里看到完整的实现核心逻辑是根据消息内容里的agent_name来路由。5. 常见报错排查401、local proxy failed 与 OAuth 问题接入过程中最容易遇到的几个报错我按出现频率排个序。401 Unauthorized这个最常见九成是 API Key 的问题。先检查 Key 有没有复制完整有没有多余的空格。如果用 TaoToken确认model_server填的是https://taotoken.net/api而不是其他路径。有些开发者会把 Key 放在环境变量里但忘了 export或者用了错误的变量名。Qwen-Agent 读取环境变量的顺序是DASHSCOPE_API_KEY优先然后是OPENAI_API_KEY。如果你显式传了api_key参数框架会优先用参数值。local proxy failed这个报错通常出现在你配置了 HTTP 代理但代理不可用的时候。Qwen-Agent 底层用 requests 或 httpx 发请求会读取HTTP_PROXY和HTTPS_PROXY环境变量。如果你不需要代理把这两个变量清掉unset HTTP_PROXY unset HTTPS_PROXY如果你确实需要走代理确保代理地址和端口正确并且代理服务本身是通的。这个报错和模型服务商无关纯粹是网络层的问题。reading choices 报错这个通常出现在流式响应解析阶段。报错信息类似KeyError: choices或IndexError: list index out of range。原因是模型服务返回的响应格式和 Qwen-Agent 预期的格式不一致。Qwen-Agent 默认按 OpenAI 兼容格式解析期望响应里有choices[0].delta.content或choices[0].delta.tool_calls。如果你用的模型服务返回格式有差异就会在这里报错。解决办法是确认model_server指向的是 OpenAI 兼容接口或者检查模型名是否正确。OAuth 相关报错如果你用 MCP 工具可能会遇到 OAuth 认证失败。MCP 的 stdio 模式不需要 OAuth但 SSE 模式可能需要。以高德 MCP 为例配置里需要填key参数{ mcpServers: { amap-amap-sse: { url: https://mcp.amap.com/sse?keyYOUR_KEY } } }如果 Key 无效或过期就会报 OAuth 或 401 错误。检查 Key 是否在对应平台申请、是否有权限访问该 MCP 服务。工具调用死循环模型反复调用同一个工具或者工具返回结果后模型不生成最终回复。这通常是system_message写得不够明确模型不知道什么时候该停止调用工具。解决办法是在系统提示里加一句“当你已经获得足够信息时直接生成最终回复不要再调用工具”。另外检查工具的description是否清晰模型可能因为不理解工具用途而反复尝试。MCP 子进程启动失败Qwen-Agent 接入 MCP 采用 stdio 模式把 MCP 服务作为子进程启动。如果报FileNotFoundError或command not found说明对应的命令不在 PATH 里。比如npx需要 Node.js 环境uvx需要 uv 工具。先确认这些命令在终端里能直接运行npx --version uvx --version如果命令不存在按官方文档安装 Node.js 和 uv。Windows 用户可以用 winget 安装winget install --idastral-sh.uv -e winget install git.git sqlite.sqlitemacOS 用户用 Homebrewbrew install uv git sqlite36. 从单 Agent 到多智能体MCP 工具接入与协作编排单 Agent 跑通之后下一步是接入 MCP 工具和实现多智能体协作。MCP 是模型上下文协议它定义了一套标准接口让 Agent 能以统一的方式调用外部工具服务。Qwen-Agent 已经集成了 MCP 客户端你只需要写配置。以 sqlite 数据库助手为例配置如下tools [{ mcpServers: { sqlite: { command: uvx, args: [ mcp-server-sqlite, --db-path, test.db ] } } }] bot Assistant( llmllm_cfg, name数据库管理员, description你是一位数据库管理员具有对本地数据库的增删改查能力, system_message你扮演一个数据库助手你具有查询数据库的能力, function_listtools )运行之后模型会自动发现sqlite服务提供的工具比如sqlite-create_table、sqlite-write_query、sqlite-read_query。当你输入“帮我创建一个学生表包含 id、name、age、gender、score 字段然后插入一条数据”时模型会先生成sqlite-create_table的调用等结果返回后再生成sqlite-write_query的调用。思维导图生成的配置类似需要同时挂载 filesystem 和 mindmap 两个 MCP 服务tools [{ mcpServers: { filesystem: { command: npx, args: [ -y, modelcontextprotocol/server-filesystem, . ] }, mindmap: { command: uvx, args: [mindmap-mcp-server, --return-type, filePath] } } }]filesystem 服务让 Agent 能读写本地文件mindmap 服务负责把文本转换成思维导图文件。两个服务配合就能实现“读取文档 → 提取结构 → 生成导图”的完整流程。多智能体协作通过MultiAgentHub实现。它的核心思路是每个 Agent 有自己的角色和工具集消息里用agent_name来指定当前该谁发言。框架会解析 mention把消息路由到对应的 Agent然后把该 Agent 的回复追加到共享的对话历史里。一个典型的多 Agent 配置from qwen_agent.agents import Assistant from qwen_agent.agents import MultiAgentHub agent1 Assistant(llmllm_cfg, name研究员, system_message你负责搜集和整理信息) agent2 Assistant(llmllm_cfg, name写手, system_message你负责根据研究员提供的信息撰写文章) agent3 Assistant(llmllm_cfg, name审校, system_message你负责检查文章的事实准确性和语言流畅度) hub MultiAgentHub(agents[agent1, agent2, agent3])运行时用户输入“研究员 帮我查一下 Qwen-Agent 的最新版本特性”框架会把消息发给研究员 Agent研究员的回复里如果包含“写手 请根据以上信息写一篇简介”框架会自动路由到写手 Agent。这种基于 mention 的编排方式比硬编码的流水线灵活得多你可以随时调整 Agent 之间的协作关系。最后如果你想快速给 Agent 部署一个 Web 界面Qwen-Agent 内置了 Gradio 支持from qwen_agent.gui import WebUI WebUI(bot).run()这会启动一个本地 Web 服务你可以在浏览器里和 Agent 对话支持流式输出和文件上传。对于演示和内部测试来说这个功能省去了自己写前端的麻烦。整个流程跑下来我的体会是Qwen-Agent 的价值不在于它封装了多少功能而在于它把 LLM 应用开发中最琐碎的部分——工具注册、参数解析、消息拼接、状态维护——标准化了。你只需要关注业务逻辑本身剩下的交给框架。如果你正在做基于 Qwen 的 Agent 应用建议先从单 Agent 加一个自定义工具开始跑通之后再逐步接入 MCP 和多智能体。