ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

千问 function call 实战:qwen agent 搭建 MCP server 并集成第三方 MCP 服务

千问 function call 实战:qwen agent 搭建 MCP server 并集成第三方 MCP 服务 1. 从 function call 到 MCP server千问 agent 接工具的完整链路千问的 function call 和 qwen agent 的 MCP server 能力解决的是同一个问题让模型不再只会聊天而是能真正调用外部工具。function call 是最基础的形态你定义工具列表模型判断该不该调、调哪个、传什么参数MCP server 则把这套逻辑标准化让工具可以跨进程、跨语言、跨服务被复用。适合谁需要让千问接入数据库、时间服务、天气接口或者想直接挂载第三方 MCP 服务的开发者。我试过把这两条链路串起来跑通中间踩的坑主要集中在工具描述写得太随意、MCP 配置路径不对、以及第二次调用时消息拼接格式出错。下面按「先跑通 function call再升级到 MCP server最后挂第三方服务」的顺序把可复制的配置和验证动作都写清楚。整个过程你只需要一个能调通的 API 通道我用 TaoToken 统一管理 Key省得在多个模型服务之间来回切。2. TaoToken 前置统一 Key 与 API 通道在写任何工具代码之前先把模型调用通道固定下来。qwen agent 默认走 DashScope但如果你同时要试 qwen3-32b 本地模型、qwen-max-latest 在线模型甚至后面接第三方 MCP 服务Key 和 base_url 分散在各处会很乱。TaoToken 的作用就是给你一个统一的 API 入口模型对话、coding plan、API Keys 都在一个控制台里管。你需要先拿到一个可用的 Key然后确认两件事base_url 指向https://taotoken.net/api模型名按你实际开通的填。这一步不涉及任何复杂配置但它是后面所有请求能通的前提。如果你还没建 Key直接去控制台的 API Keys 页面生成一个复制出来备用。注意base_url 只写到/api不要自己拼/v1之类的后缀qwen agent 和 OpenAI 兼容客户端会自动补路径。3. 可复制配置config.toml 与 settings.json 骨架3.1 config.toml模型与通道配置qwen agent 的 Assistant 初始化时llm_cfg 决定了模型走哪条通道。下面这份 config.toml 是我实际用的骨架把模型名、base_url、api_key 分开写方便你替换[llm] model qwen-max-latest model_type qwen_dashscope base_url https://taotoken.net/api api_key sk-你的TaoToken密钥 [llm.local] model qwen3-32b model_server http://127.0.0.1:8000/v1 api_key EMPTY对应到 Python 里llm_cfg 这样构造llm_cfg { model: qwen-max-latest, model_type: qwen_dashscope, base_url: https://taotoken.net/api, api_key: sk-你的TaoToken密钥, }如果你不想联网把 model 换成qwen3-32bmodel_server 指向本地推理服务即可。注意即使使用 Mock 工具模型仍需被调用以生成 tool_calls所以本地模型也要能正常返回结构化输出。3.2 settings.jsonMCP server 注册骨架MCP server 的注册信息放在 function_list 里本质是一个 mcpServers 字典。下面这份 settings.json 同时注册了 sqlite、自定义时间服务和一个第三方 fetch 服务{ mcpServers: { sqlite: { command: uvx, args: [mcp-server-sqlite, --db-path, test.db] }, custom_time: { command: python, args: [/data/mcp/mcp_time_server.py] }, fetch: { type: sse, url: https://mcp.api-inference.modelscope.net/1f45bb7e33754c/sse } } }本地 stdio 类型的服务用 command args远程 SSE 类型的服务用 type url。两种可以混在同一个 mcpServers 里qwen agent 会分别处理。4. 可复制配置function call 工具定义与两次调用4.1 定义工具列表先写一个最简单的工具获取当前时间。parameters 为空字典因为不需要入参tools [ { type: function, function: { name: get_current_time, description: 当你想知道现在的时间时非常有用。, parameters: {}, }, }, ]工具函数本身from datetime import datetime def get_current_time(): current_datetime datetime.now() formatted_time current_datetime.strftime(%Y-%m-%d %H:%M:%S) return f当前时间{formatted_time}。4.2 创建带 tools 的客户端关键点只有把 tools 参数传进去客户端返回的结果才会被标准化成 function call 格式。不传 tools模型就只会返回普通文本。from openai import OpenAI client OpenAI( base_urlhttps://taotoken.net/api, api_keysk-你的TaoToken密钥, ) response client.chat.completions.create( modelqwen-max-latest, messages[{role: user, content: 现在几点了}], toolstools, )4.3 第一次调用与第二次调用第一次调用模型判断是否存在可调用的工具不管存不存在都把结果返回。你检查返回结果里的 tool_calls 字段msg response.choices[0].message if msg.tool_calls: tool_call msg.tool_calls[0] func_name tool_call.function.name if func_name get_current_time: result get_current_time()第二次调用把工具执行结果拼到消息后面再送给模型messages [ {role: user, content: 现在几点了}, msg, { role: tool, tool_call_id: tool_call.id, content: result, }, ] final client.chat.completions.create( modelqwen-max-latest, messagesmessages, toolstools, ) print(final.choices[0].message.content)实测下来最容易出错的是 tool_call_id 没对上或者 role 写成了 function 而不是 tool。qwen 系列现在统一用 tool 角色。5. 可复制配置qwen agent 搭建 MCP server 并集成第三方服务5.1 用 FastMCP 写一个时间 MCP serverqwen-agent 内置了 FastMCP基于 fastmcp 简化开发。用mcp.tool装饰器把功能注册成 MCP 服务然后启动服务器from qwen_agent.tools.mcp import FastMCP mcp FastMCP(time-server) mcp.tool() def get_current_time() - str: 获取当前时间 from datetime import datetime return f当前时间{datetime.now().strftime(%Y-%m-%d %H:%M:%S)} if __name__ __main__: mcp.run()保存为mcp_time_server.py这就是一个标准的 stdio 类型 MCP server。5.2 在 Assistant 中注册 MCP 服务把 MCP 配置塞进 function_list然后初始化 Assistantfrom qwen_agent.agents import Assistant def init_agent_service(): llm_cfg { model: qwen-max-latest, model_type: qwen_dashscope, base_url: https://taotoken.net/api, api_key: sk-你的TaoToken密钥, } system 你扮演一个数据库助手你具有查询数据库的能力你也要查询时间的能力 tools [ { mcpServers: { sqlite: { command: uvx, args: [mcp-server-sqlite, --db-path, test.db], }, custom_time: { command: python, args: [/data/mcp/mcp_time_server.py], }, }, }, ] bot Assistant( llmllm_cfg, name数据库和时间查询助手, description数据库查询和时间查询, system_messagesystem, function_listtools, ) return bot5.3 集成第三方 MCP 服务第三方 MCP 服务通常给你一段配置直接放到 mcpServers 下面即可。stdio 类型{ mcpServers: { fetch: { args: [mcp-server-fetch], command: uvx } } }SSE 类型{ mcpServers: { fetch: { type: sse, url: https://mcp.api-inference.modelscope.net/1f45bb7e33754c/sse } } }两种都直接贴进 function_list 的 mcpServers 里不需要改代码结构。6. 验证请求与成功结果启动 MCP server 后发一条测试消息验证工具调用链路bot init_agent_service() messages [{role: user, content: 现在几点了顺便查一下 test.db 里有哪些表}] for response in bot.run(messages): print(response)成功的结果会分两段第一段模型返回 tool_calls指明调用 custom_time 和 sqlite第二段工具执行完模型把结果整合成自然语言输出。你会在终端看到类似「当前时间2024-04-15 17:15:18test.db 中有 users、orders 两张表」的回复。如果只想验证模型对话通道是否通可以直接用模型对话页面发一条消息确认 base_url 和 Key 没问题。长期做编码和 Agent 的话Coding Plan 更适合高频调用场景。7. 本篇常见错排查7.1 工具描述太模糊导致模型不调用description 写「获取时间」和写「当你想知道现在的时间时非常有用」调用率差别很大。模型靠 description 判断该不该调写清楚使用场景。7.2 MCP server 路径写错custom_time 的 args 里如果写相对路径qwen agent 的工作目录可能和你预期不一致。统一用绝对路径或者确认启动目录。7.3 SSE 类型服务连不上检查 url 是否完整有些第三方服务需要带 token 参数。另外 SSE 类型不需要 command 和 args写了反而报错。7.4 第二次调用消息格式错误tool 角色的消息必须带 tool_call_id且要和第一次返回的 id 完全一致。少这个字段模型会报 400。7.5 base_url 拼错TaoToken 的 base_url 是https://taotoken.net/api不要写成/api/v1或漏掉/api。qwen agent 内部会补全路径写多了会 404。8. 下一步把 Key 和通道固定下来整条链路跑通后你会发现最花时间的不是写工具而是调通模型通道和 MCP 配置。建议先把 TaoToken 的 Key 建好base_url 固定成https://taotoken.net/api然后按上面的 config.toml 和 settings.json 骨架替换模型名和路径。工具定义从最简单的 get_current_time 开始确认两次调用能跑通再逐步加 sqlite 和第三方 fetch 服务。遇到报错优先查 tool_call_id、MCP 路径和 base_url 这三处基本能覆盖八成问题。
返回列表