资讯详情

千问大模型Function Calling实操:从聊天机器人到能干活的Agent

📅 2026/10/20 4:39:42 | 华诺云谱 👁 阅读
千问大模型Function Calling实操:从聊天机器人到能干活的Agent
这一两年我最大的一个感受是大模型的热闹劲儿过去了真正静下心来研究怎么让它干活的人开始吃到红利了。去年大家还在比谁的对话更流畅、更有人味儿今年风向明显变了——客户上来就问它能不能自己把活干了所谓干活不是让它写一首诗或者讲个笑话是让它能调你的接口、查你的数据库、操作你的系统、跑完一整条业务流程。就在这个节骨眼上千问这一系模型的能力升级让我觉得机会真正来了。之前你可能觉得大模型就是个聪明点的聊天框但当你发现它已经能看懂一份工具清单、自己决定调用哪个函数、把返回值消化成下一步动作的时候你心里那个这玩意儿能帮我省一个人的念头就开始压不住了。这篇文章我想用我做过的几个实际项目来聊聊为什么说能干活和会聊天是两码事怎么让千问这样的模型真正接进你的业务流程里以及这一路上我踩过的那些坑。如果你正准备把一个只会聊天的机器人升级成能办事的助手这篇文章应该能帮你省下几周的摸索时间。1. 从对话到干活大模型价值的临界点1.1 对话只是入口执行才是价值先别急着聊技术我们算一笔账。一个纯对话机器人它的价值上限大概是节省一点客服人力或者做个有趣的产品Demo。但一个能执行任务的Agent它的价值空间会大得多——它可以替你盯监控数据、自动生成报表、帮你把客户的消息分类并回复、甚至在无人值守的时候完成一整套操作。这两者的差距本质上不是工程复杂度的问题而是产品定位的分水岭。我做个项目时印象特别深。甲方最初的需求是做个AI客服我说没问题。后来聊深了才发现他们真正的痛点根本不是回答客户问题而是每天有几百条售后消息需要分类、查订单、填工单、发通知——四步操作全是重复劳动。这哪是对话需求这分明是自动化需求。等我把方案改成AI工具调用的Agent架构时对方的眼神都亮了。那一刻我确定了纯对话是玩具能干活才是工具。千问这个系列模型之所以在这个时间点值得关注不光是它的语言理解能力又强了而是它在要不要调用工具、调哪个工具、参数怎么传这件事上的判断越来越稳。这件事看着简单但如果你在去年试过让某个大模型去调API大概率会有种教它用工具比自己做还累的挫败感。模型不是乱调就是不调参数传得七零八落。而现在的千问在这块的表现基本到了可以放手让它跑一跑的程度。1.2 会聊天和能干活的底层差别大多数人以为干活就是聊天的延伸——你能理解了难道还不能执行吗实际上这是两条完全不同的技术路线。聊天模型的核心目标函数是生成一个合理的回复它只需要对语言负责。但干活的模型需要的是生成一个正确的动作序列它得对结果负责。语言通顺不等于步骤正确这是本质区别。举个例子用户说帮我查一下昨天的销售额顺便把异常波动的数据标出来聊天模型会回复你一段话好的昨天的销售额是X异常波动的数据有Y但如果这个模型要干活它得经历这些步骤识别出这是一个多步骤任务拆成查数据和做分析两件事。知道查数据对应的是哪个数据接口该传什么时间参数。把接口返回的原始数据消化掉挑出异常波动的部分。生成一份标记好的结果用约定的格式返回给上层系统。这个过程里模型每一次思考都不是为了生成漂亮的文字而是为了驱动一次真实世界的状态变化。所以你会发现能干活的好模型和能聊天的好模型评价标准完全不同。后者看流畅度前者看重成功率、参数准确率、错误恢复能力。千问系列近几次更新里我对工具调用的成功率提升感知特别明显这也是我决定围绕它做深度集成的原因。1.3 为什么干活比对话更容易商业化纯对话产品很难收费这是行业里的公开秘密。用户用完就走觉得你不过是个高级点的搜索引擎。但干活不同它是可以量化价值的。一个Agent跑通了一条原本需要人力操作五分钟的流程省下的就是真金白银。我在和几个团队聊的时候发现一个规律凡是说得清这个Agent每天帮我处理多少单、省多少时间的项目推进得都特别快凡是还停留在我们想做个智能客服的项目基本都卡在需求评审阶段。因为前者是明确的降本增效后者是模糊的锦上添花。千问开始具备稳定的干活能力之后最直接的影响是以前大模型只能当个前端花瓶的项目现在可以把模型塞进业务流程的核心环节里了。这对做ToB解决方案的团队来说是个实打实的机会窗口。2. 核心机制拆解让千问学会调用工具2.1 Function Calling模型与外部世界的接口要让大模型干活第一步不是写业务代码是让模型具备调用外部工具的能力。这个能力在技术上有专门的称谓Function Calling也就是函数调用。打个比方模型本来是一个只有嘴巴的专家它再聪明也碰不到现实世界的任何东西。Function Calling就是给它装上手和脚让它能通过一套标准协议去操作你提供的工具。具体原理是这样的你在请求模型的时候除了给它用户的问题还额外给一份工具清单——每个工具包含名字、功能描述、参数定义。模型看完用户的问题再比对这些工具描述会决定这一步要不要调用工具、调用哪个、参数填什么。关键是模型返回的内容不再是一段人话而是一个结构化的调用指令你的代码拿到这个指令后去真正执行再把执行结果交还给模型让它继续下一步处理。我最初上手的时候以为这个功能很复杂其实千问的API已经把这层封装得很好了。你只需要在请求里加上工具定义模型就会在你需要的时候稳定返回工具调用请求。下面是一个最简化的示例能帮你理解它的工作方式from openai import OpenAI client OpenAI( api_keyyour-api-key, base_urlhttps://dashscope.aliyuncs.com/compatible-mode/v1 ) tools [ { type: function, function: { name: get_sales_data, description: 获取指定日期的销售额数据, parameters: { type: object, properties: { date: { type: string, description: 日期格式YYYY-MM-DD } }, required: [date] } } } ] response client.chat.completions.create( modelqwen-plus, messages[ {role: user, content: 帮我查一下昨天2025-01-15的销售额} ], toolstools ) # 如果模型决定调用工具它会在这里返回工具调用请求 print(response.choices[0].message.tool_calls)你看模型不会直接回答昨天的销售额是多少这种它根本不知道的事而是返回一个类似我需要调用get_sales_data这个函数参数date为2025-01-15的结构化指令。你的代码拿到这个指令后去数据库查询把结果再塞回对话上下文里模型就能基于真实数据继续做分析了。这就是一次完整的干活闭环。2.2 让模型准确选择工具的三个关键设计一开始我以为只要把工具定义传给了模型它就会准确调用。实践之后才发现模型选错工具、传错参数的频率直接决定了整个系统的体验。后来我总结出三个关键设计缺一个都不行。第一是工具名字和描述要像写给外行人看一样清晰。很多人写工具描述的时候想当然觉得参数名叫start_time大家都能懂。但模型是真不知道你的业务里start_time到底是订单创建时间还是支付时间的。你得把描述写成这样查询订单的起始时间格式为时间戳单位毫秒注意是订单支付完成的时间而非创建时间。别怕啰嗦描述越精准模型的判断越准。第二是参数定义要尽量用枚举值约束。比如有个字段表示订单状态你可以直接限定它只能传pending、completed、cancelled三个值。这样模型不会自作聪明传一个已完成或者end之类的值进来。代码里限制死取值范围比事后清洗有效得多。第三是工具数量要克制。这是我踩过最大的坑。有一天我兴奋地给Agent接入了二十多个工具心想这下它什么都能干了。结果模型开始频繁地在两个功能相似的函数之间反复横跳像一个选择困难症患者。后来我做了几层合并把工具收敛到八个左右准确率一下就上来了。工具太多的时候模型的信息检索负担太重。能用三个工具完成的事情不要拆成十个。2.3 千问在这块的优势和它的性格说句公道话市面上主流大模型我都试过Function Calling相关能力千问在我这边的实测感受排在第一梯队。最明显的一点是它对中文业务场景的理解有天然优势。比如你在工具描述里写查询最近三天的销量注意排除退款订单一些国外模型会在退款这个业务语义上绕弯子而千问基本能直接抓住重点。另外千问的参数提取能力强得有点超出预期。我给它一段用户原话里面混杂着时间、地点、人物、金额这些信息它能比较准确地抽取并映射到工具参数里。有一次用户说帮我把上周三到周五的所有异常订单重新跑一遍分析重点看退款率超过20%的店铺模型能准确解析出时间范围、操作对象、过滤条件一次性把参数填齐。这种把口语翻译成结构化参数的能力就是Agent体验好不好的分水岭。不过它也有自己的性格就是偶尔会过度自信。举个例子如果工具返回的数据有空值或者格式不对它会倾向于脑补一个合理的值而不是如实告诉你数据有问题。所以我在系统设计里加了一层校验逻辑比模型的判断优先发现参数不合预期就打回重来不能让它自作主张。3. 实操过程搭一个能真正干活的千问Agent3.1 环境准备与基础接入好了理论说完了咱们直接上手。我假设你已经有一个千问的API Key如果没有的话先去对应平台申请一个步骤很简单拿到Key就能开始。我的建议是直接用OpenAI兼容模式接入这样做的好处有两个一是市面上大部分开源框架都兼容OpenAI的客户端协议你可以直接复用二是将来想切换到其他模型代码改动成本很小。下面这个代码片段可以验证你的环境是否通pip install openai然后跑一个最基础的测试确认模型能正常响应from openai import OpenAI client OpenAI( api_keyyour-api-key, base_urlhttps://dashscope.aliyuncs.com/compatible-mode/v1 ) response client.chat.completions.create( modelqwen-plus, messages[ {role: system, content: 你是一个智能助手能调用工具完成任务。}, {role: user, content: 你好介绍一下你能做什么} ] ) print(response.choices[0].message.content)这里有一点要特别提醒base_url必须填对很多新手在这里卡了半天搞不清为什么一直报连接错误。如果是用国内的服务region-endpoint和compatible-mode两个地址的含义不同建议直接照官方文档用dashscope.aliyuncs.com/compatible-mode/v1这个兼容地址省心。3.2 设计工具函数从业务逻辑到模型可识别的Schema现在进入核心环节设计工具函数。好的工具设计是Agent能用起来的前提这一步做不好后面全白搭。我的习惯是先列出业务上需要的能力清单再逐条映射成工具定义。拿我最近做的一个售后工单自动处理Agent来举例。这个Agent要做的事情是收到客户反馈后自动判断问题类型、查询对应订单、检查售后政策、生成处理建议。我给它设计了四个工具工具名功能说明关键参数查询订单信息根据订单号查询订单详情含商品、金额、状态订单号(order_id)检查售后政策查询商品对应的退换货政策商品类目(category)、购买时长(days_since_purchase)查询客户历史工单查看该客户之前有没有类似的工单记录客户ID(customer_id)创建处理工单生成一条处理建议并写入工单系统订单号、处理类型、说明文本每个工具在代码里都是一个普通函数但在传给模型时需要转换成Schema格式。下面这个是查询订单信息的Schema示例tools [ { type: function, function: { name: query_order, description: 根据订单号查询订单详情包括商品信息、订单金额、物流状态、售后状态等, parameters: { type: object, properties: { order_id: { type: string, description: 订单号例如SO20250115001 } }, required: [order_id] } } }, # 其他工具定义省略 ]写到这里你会发现这个工具定义本质上就是一份给模型看的说明书。模型不执行这些函数执行它们的是我们的代码模型只负责根据用户需求选择正确的函数和参数。所以描述写得越具体模型选择就越准确。这里有个小技巧描述里带上业务关键词比如查询订单详情和根据订单号查询订单详情包括商品信息、订单金额、物流状态、售后状态后者明显更准确因为模型能从中理解这个工具返回什么数据、适合处理什么问题。3.3 多轮工具调用与上下文管理一个任务往往不是调一次工具就能完成的。比如用户问帮我查一下订单SO20250115001的售后进度如果还没处理完就催一下Agent需要先查询订单状态再判断是否需要进入催办流程可能还需要调用创建工单或者发通知的工具。这就是多轮工具调用。实现多轮调用的逻辑其实不复杂是一个循环把用户问题发给模型带上所有工具定义。如果模型返回了工具调用请求就执行对应的函数。把函数执行结果以工具消息的形式返回给模型。重复第2步直到模型不再请求调用工具返回最终答案。在代码层面关键点在于每次调用都要把之前的消息历史完整保留。我的做法是把每一轮的User消息、Assistant消息、Tool消息全部追加到一个列表里完整的上下文信息都保留下来。这里有一个特别容易踩的坑千问或者其他大模型对上下文长度有限制如果你把整个对话历史无脑塞进去很快会撞上长度上限。我的方案是做摘要压缩——当历史消息超过一定轮数时把前面的对话交给模型生成一个摘要用摘要替换旧消息。下面展示一下循环调用的核心逻辑def run_agent(user_input, tools, tool_functions): messages [{role: system, content: 你是一个售后工单处理助理。}] messages.append({role: user, content: user_input}) for _ in range(5): # 设置最大迭代次数防止死循环 response client.chat.completions.create( modelqwen-plus, messagesmessages, toolstools ) message response.choices[0].message if message.tool_calls: # 模型要求调用工具 messages.append(message) for tool_call in message.tool_calls: func_name tool_call.function.name func_args json.loads(tool_call.function.arguments) func tool_functions[func_name] result func(**func_args) messages.append({ role: tool, tool_call_id: tool_call.id, content: json.dumps(result, ensure_asciiFalse) }) else: # 模型不再调用工具返回最终回答 return message.content return 处理超时请稍后重试注意我设了一个最大迭代次数这里是5这是非常重要的一步。如果不加这个限制一旦模型陷入循环调工具的困境你的请求就会一直空转既浪费token又拖慢响应。实战中我会根据任务复杂度把上限设成5到10之间基本够用了。3.4 一个完整的业务流程案例这里我用一个模拟场景串一遍整个流程。假设我们接到一个用户消息我上周买的东西坏了订单号是SO20250110088想退货帮我看下能不能退。第一步模型看到订单号和退货两个关键信息决定调用query_order工具参数是{order_id: SO20250110088}。第二步你的代码执行query_order函数从数据库查出这笔订单的信息返回给模型订单状态已签收商品类目数码配件购买时间2025-01-10当前时间2025-01-15。第三步模型看到已签收、购买时间为5天前判断需要进一步确认退货政策。它继续调用check_return_policy工具参数是{category: 数码配件, days_since_purchase: 5}。第四步工具返回数码配件支持7天无理由退货用户购买5天在政策范围内。第五步模型综合所有信息决定调用create_ticket工具生成一条退货处理工单并返回给用户一段话您的订单支持退货我已为您生成退货工单工单编号TK20250115023请在24小时内寄回商品。整个过程中模型的角色更像是一个调度员它每一步都在思考现在我需要什么信息、哪个工具能提供这个信息、下一步应该做什么。这就是干活和聊天的区别——聊天只需要最后一句话漂亮干活看重的是每一步都准确。4. 常见问题与排查技巧实录4.1 模型死活不调用工具怎么办这是最常见的问题。你明明把工具定义传过去了模型却像没看见一样直接凭自己的知识硬答。排查思路按顺序来第一检查工具定义的数量和复杂度。如果工具特别多光看描述就有十几个模型可能会不知所措。我的经验是超过十个工具时要考虑合并同类的。第二检查消息格式。有些接口要求工具消息必须配套对应的工具调用ID如果ID对不上模型在后续轮次可能就无法正确理解工具上下文。我的做法是把每轮调用的tool_call_id存好确保一条不缺。第三给系统提示词加个强制引导。我在System Prompt里加了一句当需要查询实时数据或执行操作时必须调用提供的工具不要直接回答。这句简单的约束能把工具调用率提升好几个百分点。第四如果以上都排查了还是不行注意是不是某些特殊请求里带了奇怪的思考链干扰了模型。这时候可以简化用户输入去掉多余修饰词再试一次。4.2 参数传递出错模型总是不按套路传参另一个高频问题是模型生成的工具参数格式不对比如缺字段、多字段、类型不匹配。这个问题的成因通常在Schema定义上。我排查逻辑是先看参数的required字段是否把所有必要的字段都列上了。列上之后模型就应该稳定输出这些参数。再检查description里有没有说清楚参数值的格式。比如时间戳你需要写清楚是毫秒级Unix时间戳还是字符串YYYY-MM-DD不然模型隔三差五传出个别的格式。如果模型确实频繁传错特定参数还有一个笨但有效的办法在函数内部做兜底转换。例如不管模型传的是2025年1月15日还是2025-01-15还是2025/1/15函数内部统一解析为标准日期。工具自己皮实一点整体可靠性就上去了。4.3 多轮任务中模型失忆或者上下文混乱Agent干起活来一跑就是好几轮上下文越长出问题的概率越高。我遇到过模型的后续轮次把之前的工具执行结果理解错了或者是把A订单的信息安到B订单上。这个问题的根源在于上下文里的信息太多了模型抓不住重点。我的解决方案是在每次工具调用结果的文本里做高亮处理——不是真的高亮而是在返回内容里把关键信息提取出来用规整的格式重写一遍。比如原始工具返回的是一个大JSON我不会直接把整个JSON喂给模型而是写个小函数把关键字段抽出来整理成订单号xxx状态已签收金额xx元这样简洁明了的文本。模型处理这种结构化文本时上下文理解会清晰很多。4.4 常用排查手段汇总现象可能原因解决方案模型不调用工具工具描述不清/工具过多/提示词缺少引导优化描述、合并工具、在System Prompt中加强制性语句参数传错格式Schema定义不完整/描述不具体完善required字段、细化description、在函数内部做兜底转换多轮信息混乱上下文过长/关键信息被淹没精简工具返回内容、按关键字段提取、用规整文本重写任务中途报错工具执行异常/网络超时增加异常捕获、设置重试机制、模拟数据兜底返回效果不稳定模型参数配置不合适把temperature调到0.3以下必要时固定随机种子5. 实操心得让Agent从能用到好用的三个进阶技巧5.1 每次调用都要自证清白记录决策轨迹Agent跑起来之后最大的痛苦是出了问题不知道怎么排查。它自己调了什么工具、为什么调、返回了什么如果这些没有记录你只能看着最终结果发呆。我的做法是给Agent增加完整的日志系统每一步操作都记录模型的原始输出包括思考过程里的工具调用指令工具的名称、参数、返回值从调用到返回的耗时有了这些记录调试体验会完全不一样。有一次我花了半天时间定位一个问题Agent在处理用户订单时把一个老订单当成了当前订单。如果有调用轨迹一眼就能看到模型传入的日期参数有问题而不是在几万行日志里大海捞针。这个习惯一定从一开始就养成。5.2 给Agent配备防呆机制确认-重试-兜底让模型干活永远不能100%信任它。我的工程准则是高风险操作必须加确认环节低置信度的结果必须留在系统边界内校验。具体来说如果Agent要执行的是不可逆操作比如删除数据、扣款、提交订单我坚持让它在执行前输出一个操作确认步骤由用户或者上级系统做二次确认。虽然这会让流程多一步但换来的稳定性是值得的。另外每次工具调用都应该包一层try...except接口超时了就自动重试一次重试失败就返回一个明确的错误信息给模型让它决定怎么处理。5.3 用测试剧本替代手工试错最后一个小建议不要靠手工聊天来测试Agent的好坏。应该准备一套固定的测试剧本——比如正常路径、参数缺失路径、工具报错路径、上下文超长路径每次代码有改动就自动化跑一遍这些剧本对比结果是否回归。我的测试剧本大概有二十多个场景每次跑完能发现不少问题。这个投入非常值得因为Agent的调用链复杂手工测试往往只能覆盖到自己想得到的场景而测试剧本能帮你覆盖到系统设计的全部边界。这一两年做Agent相关项目我最大的体会是大模型的能力天花板已经足够高了真正的瓶颈往往在工程基础设施——工具的稳定性、上下文的管理、异常的处理。谁能把这些工程细节做好谁就能把大模型的能力转化成实际的业务价值。从会聊天到能干活看起来只是产品形态的一小步实际上是整个技术栈适配的一大步。千问这一步迈得颇为扎实而作为开发者我们要做的就是把油门踩下去在模型和业务之间建起一座可靠的桥。别等机会窗口关闭了再动手现在正是把模型从聊天框里解放出来、让它真正干活的好时候。
📝

华诺云谱内容团队

资深建站顾问 · 行业研究员

10年+企业数字化服务经验,专注智能建站、SEO优化与品牌营销,持续输出建站技巧、行业洞察与营销干货,已帮助5000+企业实现数字化增长。

你可能需要的服务

订阅华诺云谱资讯周报

每周一封,精选建站技巧、SEO与营销干货,直达邮箱。已有 8,000+ 企业主订阅,助你少走弯路。

↑