Qwen-Agent实战入门:从工具调用到多智能体协作
一文入门Qwen-Agent附可直接跑的实战代码最近在折腾LLM应用开发频繁被问到一个问题想给大模型加工具调用、做Agent到底应该用哪套框架LangChain太重自研又太费劲OpenAI Function Calling倒是好用但被生态绑死。我现在的答案是如果你在中文场景、想快速落地、又想把通义千问的能力吃透Qwen-Agent是个非常值得花一个下午试一把的选择。Qwen-Agent是阿里通义实验室开源的智能体开发框架专门围绕通义千问模型打造核心解决两个事情一是把模型接入外部工具搜索、代码执行、自定义API变成标准流程二是把多步骤的任务拆解交给模型自动编排执行。换句话说你给它一个目标它自己规划、调工具、看结果、再规划直到任务完成。本文会从一个纯小白的视角从安装到跑通再到自定义工具和多智能体协作用实战代码带你完整走一遍顺便把我踩过的坑和绕过的弯路都交代清楚。先说清楚这篇文章适合谁已经会用Python、调过OpenAI或其他大模型API、但对Agent框架还比较陌生的开发者。不用你提前精通Prompt Engineering也不用你啃完官方文档——你跟着敲一遍代码基本就能在日常项目里上手了。1. Qwen-Agent核心概念与设计思路1.1 它到底解决了什么问题在Qwen-Agent出现之前很多人给LLM做工具调用是这么干的自己写一个函数列表把函数描述拼进System Prompt让模型输出JSON格式的调用意图再写一堆正则和解析逻辑去抽参数然后手动调用函数、回填结果、再拼回上下文。这套流程本身没问题但一旦函数多了、流程长了维护成本会迅速失控。Qwen-Agent把这些繁琐工作全部内化成了框架能力。它约定了“工具描述”、“调用结果回填”、“循环控制”的标准机制。你只需要把Python函数定义好、写好docstring框架会自动把函数签名和描述序列化成模型可理解的格式。模型决定调用哪个函数、传什么参数框架负责解析、执行、把结果格式化成模型能读的文本然后再次交给模型做下一步决策。这个“观察-行动-反思”的循环就是Agent的核心。这套设计带来的直接好处有三个。第一你不用再手工维护任何JSON Schema函数的docstring就是工具描述模型能看懂自然语言说明第二多轮工具调用的状态管理由框架内部维护ChatHistory对象帮你记住每一轮的上下文不会越跑越乱第三底层模型可以随时切换同一个Agent代码从Qwen-Max换到Qwen-Plus或者换到开源版Qwen模型几乎不用改业务逻辑。1.2 核心模块拆解LLM、Agent、Tool、ChatHistoryQwen-Agent的模块划分非常清晰上手前花十分钟理解这几个概念后面会顺畅很多。首先是LLM它是对模型API的统一封装。框架支持HTTP接口调用也兼容OpenAI格式的接口所以如果你有自己部署的模型服务只要暴露成OpenAI兼容的接口Qwen-Agent也能接。其次是Tool所有外部能力的统一抽象。框架内置了一些常用工具比如代码执行器、浏览器搜索、数学计算等等同时更推荐你自己定义工具函数——只需要一个普通Python函数加一段清晰的docstring。然后是Agent这是整个框架的调度大脑。它接收一个任务目标不断触发LLM去决策调用哪个工具直到得到最终答案。你不需要自己写While循环Agent内部已经实现了完整的循环控制你只需要设置max_iterations来控制最大轮数防止任务陷入死循环。最后是ChatHistory管理多轮对话状态的核心。它记录用户消息、助手消息、工具调用消息和工具结果消息并且内置了消息压缩和截断策略。实测下来当上下文变得很长时合理的消息管理比堆Prompt更影响最终效果。这四个对象的关系可以这样理解ChatHistory是笔记本Agent是执行者Tool是工具箱LLM是大脑。大脑看笔记本决定拿哪个工具执行者去拿把结果写回笔记本大脑接着看直到任务完成。2. 环境准备与快速安装2.1 一条命令完成安装Qwen-Agent的安装非常省心Python 3.9以上版本即可用pip直接装pip install qwen-agent如果你需要跑多智能体协作的示例还会用到一些浏览器自动化相关的依赖这时候可以装完整版pip install qwen-agent[all]我建议直接装完整版因为后面很多示例代码需要浏览器工具缺依赖再回来补装比较折腾。装完之后可以用一段最简单的代码验证安装是否成功from qwen_agent.llm import LLM llm LLM(modelqwen-plus, api_key你的API-KEY) response llm.chat(messages[{role: user, content: 你好}]) print(response)如果你的API-KEY配置正确这里应该能正常返回一段模型回复。如果报错绝大多数情况是API-KEY没配好或者网络代理干扰了请求这个问题我会在后面的章节专门讲排查。2.2 用环境变量替代硬编码API Key很多入门教程喜欢把API-KEY直接写在代码里但我不建议你在任何工程化项目里这么干尤其当代码要提交到Git仓库的时候。正确做法是用环境变量管理。在项目根目录创建一个.env文件DASHSCOPE_API_KEYsk-你的完整Key然后修改代码让它优先读取环境变量import os from qwen_agent.llm import LLM api_key os.getenv(DASHSCOPE_API_KEY) if not api_key: raise ValueError(请先在环境变量中配置DASHSCOPE_API_KEY) llm LLM(modelqwen-plus, api_keyapi_key)实测下来这样管理密钥的另一个隐形好处是当你需要切换测试环境时不用改动任何代码只要重新export环境变量就好。我见过不少同学因为多个项目的Key不同每次切换都要全局搜索替换非常容易出错。3. 实战代码一5分钟跑通你的第一个Agent3.1 从最简单的人机对话开始很多人误以为Agent框架只能做复杂的工具调度其实它本身就是一个对话框架。我们先用最简单的方式跑通一个对话Agentimport os from qwen_agent.llm import LLM from qwen_agent.agent import Agent # 初始化模型 llm_cfg { model: qwen-plus, api_key: os.getenv(DASHSCOPE_API_KEY), } # 创建一个最基础的Agent让模型直接回答用户问题 bot Agent( nameassistant, llmllm_cfg, system_message你是一个乐于助人的助手。, ) # 执行对话并打印结果 responses bot.run(messages[{role: user, content: 介绍一下你自己}]) for response in responses: print(response)这段代码的逻辑很简单LLM只负责对话Agent没有挂载任何工具所以它就是一个普通聊天机器人。但注意看Agent的run方法返回的是一个消息序列这正是Agent循环的体现——即使是最简单的场景框架也保持了统一的对话-响应数据结构。如果你只想拿到最终的文本结果可以加上一句final_text responses[-1][content] print(final_text)3.2 接入代码执行器让Agent真正“动手”光聊天没什么意思Agent的价值在于行动。我们接入框架内置的代码执行工具让模型可以自己写代码、跑代码、根据结果调整方案。比如问它“计算斐波那契数列前20项的和”模型就会自动编写Python代码并执行。import os from qwen_agent.agent import Agent from qwen_agent.tools import CodeExecutor llm_cfg { model: qwen-plus, api_key: os.getenv(DASHSCOPE_API_KEY), } # 将代码执行器注册到工具列表 bot Agent( namecoder, llmllm_cfg, tools[CodeExecutor()], system_message你是一个编程助手需要通过编写并执行代码来回答问题。, ) responses bot.run( messages[{role: user, content: 用python计算斐波那契数列前20项的和是多少}] ) for response in responses: print(response)这里最值得说道的是CodeExecutor这个工具。它会在一个沙箱环境里执行模型生成的Python代码并把执行结果回传给模型。如果代码报错模型会看到错误信息并尝试修复再执行一次直到跑通或者达到最大迭代次数。需要注意的是内置的代码执行器虽然方便但并非完全隔离的沙箱它只是提供了一个受限的Python环境并不适合执行不信任来源的代码。如果要做严格的代码隔离还是需要接入Docker等外部容器方案。4. 实战代码二自定义工具把你的API变成Agent能力4.1 用docstring定义工具接口框架最核心的扩展点就是自定义工具。它遵循一个非常优雅的设计工具函数定义本身就是模型感知的接口描述框架会把你的函数名、docstring和参数注释自动序列化成模型能理解的工具列表。下面是我在一个天气查询项目里实际用过的例子定义一个查询城市天气的工具import json import urllib.request from qwen_agent.tools import BaseTool class WeatherQuery(BaseTool): 查询指定城市的实时天气信息 def call(self, params: str) - str: 基于城市名查询实时天气。 Args: city: 城市名称如‘北京’、‘上海’。 # 这里的params是JSON字符串 try: params_dict json.loads(params) city params_dict.get(city, 北京) except json.JSONDecodeError: city 北京 # 调用公开天气API这里示意写法实际请替换成你自己的天气服务 url fhttps://example-weather-api.com/api/v1/weather?city{city} try: with urllib.request.urlopen(url, timeout10) as resp: data json.loads(resp.read().decode(utf-8)) return json.dumps(data, ensure_asciiFalse) except Exception as e: return f天气查询失败{str(e)}关键点在于类名就是工具名call方法就是调用入口docstring就是模型识别工具的说明书。尤其是这段docstring里面的“Args”部分就是触发模型传参的钥匙。写清楚参数名和含义模型才知道应该传什么值。4.2 把多个工具组合进一个Agent单个工具能力有限把多个工具组合起来才真正体现出Agent的调度价值。这里我结合一个“会议纪要生成”的场景让Agent同时具备搜索百科和总结文本两种能力import os import json from qwen_agent.agent import Agent from qwen_agent.tools import DocParser, CodeExecutor from qwen_agent.tools import BaseTool # 假设我们已经定义好了MeetingNotesTool class MeetingNotesTool(BaseTool): 提取会议纪要中的关键信息 def call(self, params: str) - str: params_dict json.loads(params) text params_dict.get(text, ) # 这里可以接入你的NLP处理逻辑或直接让LLM做信息提取 # 本示例简单返回文本长度作为演示 return json.dumps({status: success, text_length: len(text)}, ensure_asciiFalse) llm_cfg { model: qwen-plus, api_key: os.getenv(DASHSCOPE_API_KEY), } bot Agent( namemeeting-assistant, llmllm_cfg, tools[MeetingNotesTool(), CodeExecutor()], system_message你是一个高效的项目助理负责整理会议纪要提取关键结论。, ) user_input 请帮我提取以下会议纪要中的关键决定和负责人\n项目进度会议王工负责前端重构预计两周完成李工负责后端接口改造一周内出方案。 responses bot.run(messages[{role: user, content: user_input}]) for response in responses: print(response)运行这个Agent它会自主决定调用MeetingNotesTool而不是CodeExecutor因为它能理解任务本质是信息提取而非代码计算。这种“根据任务目标自动选择合适的工具”的能力正是Agent和普通的规则引擎的最大区别。4.3 工具设计的核心原则实战中我发现工具设计直接影响Agent效果的比重甚至超过Prompt。有几个经验想分享。第一docstring里的TODO和示例代码越多越好。模型是少样本学习高手你在docstring里写两到三个“参数示例”它几乎不会传错参。第二工具返回结果尽量使用结构化JSON。你返回纯文本模型也能读但JSON的层次感更强尤其当结果需要再次被程序处理时。实测中纯文本返回比JSON返回更容易让模型在后续轮次“误解”结果。第三一个工具只做一件事。千万不要把“查询天气并推荐穿衣”写进一个工具应该拆成“查询天气”和“推荐穿衣”两个工具让模型自己决定如何串联。模块化粒度越细组合能力越强Agent的灵活性越高。5. 实战代码三用多智能体协作完成复杂任务5.1 理解多智能体的协作模式当单一Agent面对一个涉及多领域知识的复杂任务时常常会出现能力瓶颈。比如你让一个Agent既当“数据分析师”又当“文案写手”它在工具选择上经常纠结上下文也容易被干扰。多智能体架构就是把大任务拆解成子任务每个Agent只专注一个方向再通过“管理者-执行者”的层级结构协作完成整体目标。Qwen-Agent实现多智能体的方式并不神秘本质上还是Agent的组合。你可以创建一个“管理者Agent”让它把任务拆给“数据分析Agent”和“文案Agent”再汇总各Agent的输出。框架并没有给你封装一个黑盒的“多智能体类”你需要用代码显式地编排协作逻辑而这恰恰是最灵活的地方。5.2 完整的协作代码示例下面这个例子我让一个“数据Agent”负责生成某个数据的分析结论另一个“文案Agent”负责把这个结论改写成一篇适合公众号发布的口播稿。两者各司其职最后合并import os from qwen_agent.agent import Agent from qwen_agent.tools import CodeExecutor llm_cfg { model: qwen-maxi, api_key: os.getenv(DASHSCOPE_API_KEY), } # 数据分析Agent data_agent Agent( namedata-analyst, llmllm_cfg, tools[CodeExecutor()], system_message你是一个资深数据分析师。请基于给定的用户数据进行统计并输出关键洞察。, ) # 文案Agent writer_agent Agent( namecopywriter, llmllm_cfg, system_message你是一个资深新媒体编辑。请根据数据分析师的洞察写出一段150字左右的公众号口播文案要求通俗易懂、有传播力。, ) # 管理者Agent负责编排整个流程 manager_agent Agent( namemanager, llmllm_cfg, system_message你是一个项目负责人负责协调团队成员完成用户任务。, ) def run_team(task: str): # 第一步数据Agent分析数据 data_responses data_agent.run(messages[{role: user, content: task}]) data_result data_responses[-1][content] print(数据代理结果:, data_result) # 第二步文案Agent基于分析结果创作 writer_input f任务描述{task}\n数据洞察{data_result} writer_responses writer_agent.run(messages[{role: user, content: writer_input}]) final_copy writer_responses[-1][content] print(文案代理结果:, final_copy) return final_copy if __name__ __main__: final_result run_team(分析以下销售数据并生成一份向管理层汇报的口播稿\n1月销售额200万2月190万3月230万4月280万。) print(最终产出:, final_result)这段代码展示了最朴素的流水线式多智能体协作。数据Agent只负责算和总结文案Agent只负责写作。好处是每个Agent的上下文都很干净不会被大量原始数据或者写作指令污染实际效果比我早期尝试的“一个大Agent处理所有事”要好很多。如果你想做更复杂的并行协作比如多个Agent同时调研不同方向的资料再统一汇总可以自行用线程池或异步任务管理发起多个Agent实例。Qwen-Agent的Agent实例是可以并发调用的只要注意每个Agent用独立的ChatHistory避免消息串台就行。5.3 多智能体的适用场景和成本控制多智能体不是银弹它有代价。每多一个Agent就意味着多一轮甚至多轮模型调用。我用上面的例子做过粗略统计单Agent方案调用模型约1到2次而双Agent协作方案至少4到6次。如果你的场景非常简单硬上多智能体只会增加延迟和费用。适用多智能体的典型场景有这么几类一是任务需要多种完全不同领域的专业技能比如既需要代码能力又需要写作能力二是任务流程天然分阶段比如先调研、再分析、再输出三是单一Agent处理长上下文时效果明显下降需要拆分成多个短上下文场景。成本控制方面我建议不同Agent可以使用不同规格的模型。比如数据Agent用qwen-max保证分析质量文案Agent用qwen-plus就够了。Qwen-Agent的LLM配置支持按Agent单独设置你可以直接在每个Agent的llm参数里指定不同模型成本能省下不少。6. 常见问题与排查技巧实录6.1 高频报错速查表我把实战中遇到的高频问题整理成了一张表基本覆盖了初学者会踩的绝大多数坑现象可能原因解决方法调用LLM时返回401认证失败API Key错误或已过期检查环境变量重新生成Key返回404模型不存在模型名拼写错误或没有该模型权限确认模型可用列表检查是否有白名单权限工具调用时模型总是传错参数docstring描述不够清晰在docstring中添加参数示例和取值范围说明Agent循环一直不停止任务复杂但max_iterations设置过小适当增大max_iterations同时检查工具返回是否被模型正确理解工具返回内容过大上下文超限工具返回了过长文本在工具内部做结果截断或摘要减少回传内容并发调用多个Agent报错共享了同一个ChatHistory对象每个Agent实例使用独立的历史记录对象网络超时或连接重置本机网络代理影响了API请求关闭全局代理或给API域名配置直连6.2 调试Agent的一线经验老实说Agent开发调试起来比传统的API开发要麻烦因为它可能每一次运行结果都不同。我调试时最常用的是“逐轮日志法”先打开框架的流式输出观察每一轮模型决策、工具调用和返回结果。开启流式输出的方法很简单在Agent的run方法里传入streamTrueresponses bot.run(messagesmsg, streamTrue) for chunk in responses: print(chunk)流式输出能看到模型每一步的思考过程和部分生成结果。如果模型走到了错误的方向你能第一时间看出来比事后翻log高效得多。另一个经验是我强烈建议在写自定义工具时自己先在工具内部把返回结果打印或记录出来。很多问题并不出在Agent逻辑上而是工具本身返回了空数据或者非预期格式模型拿到错误输入后续决策自然全歪。6.3 性能优化的三个方向任务跑通了接着就是优化。我在生产环境调优时主要从三个方向入手。第一Prompt瘦身。Agent的system_message越冗长模型响应延迟越高也越容易“跑偏”。我的经验是system_message只保留角色设定和任务边界把详细规则尽可能写入工具docstring里让工具描述承担更多引导职责。这样既能保持Agent目标清晰又能在工具层面做精细引导。第二工具结果压缩。如果业务工具会返回大段的日志、长文本或数据库记录一定要提前在工具内部做截断或摘要。一个5万字的数据库查询结果返回给模型不仅浪费token还会让模型无法聚焦。我在很多工具里加了一个“只返回前N条记录和统计摘要”的逻辑效果立竿见影。第三控制max_iterations。这个参数决定了Agent最多能执行几轮工具调用循环。设置太小复杂任务做不完设置太大模型可能陷入无效循环白白消耗API额度。我的经验是普通任务设置3到5涉及多步骤数据处理的设置8到10同时通过prompt引导模型“尽量在较少的步骤内完成任务”。6.4 几个值得留意的风水宝地最后分享几个我实际操作中摸索出来的小技巧全都是文档里不会写但我反复验证过的。第一个是关于模型选择同一个Agent逻辑用qwen-max和qwen-plus的效果差距有时候比你想的还大。qwen-max在复杂工具调度上的表现明显更好但价格也更高。我的建议是开发阶段用qwen-plus调通逻辑上线前再根据任务复杂度决定是否升级到qwen-max。第二个是关于API Key的权限如果你用的是企业版账号注意给不同环境的API Key设置不同的权限范围避免生产环境的Key意外泄漏也便于审计调用量。第三个是关于工具命名工具名和工具描述中的名词会直接影响模型调用时的“直觉”。比如你命名一个工具为weather_query模型倾向于在城市类任务中触发它如果你命名为tool_001模型触发的准确率就会下降。所以工具命名要见名知意别偷懒用编号。我一直觉得Agent开发的本质工作量其实有六七成花在“把工具定义清楚、把边界画清楚”上——这和传统软件开发的理念是一致的只不过Agent让这些工具变成了可以自主调用的“技能”。等你用顺手了就会理解为什么框架设计者如此强调docstring和工具描述因为对LLM来说清晰就是生产力。
上一篇/下一篇内容由系统自动关联
返回资讯列表 →