尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

AI智能体升级实战:从规则匹配到Function Call准确率提升至86%

AI智能体升级实战:从规则匹配到Function Call准确率提升至86% 干过一段时间AI智能体开发的兄弟应该都有同感意图识别和指令解析这块最省事的上手方式永远是规则匹配但最让人头疼的维护工作也恰恰是规则匹配。我之前维护的那套商品推荐智能体从第一版上线开始就用的正则加关键词抽取上线两周效果还行越往后越吃力准确率常年卡在72%附近怎么都上不去。后来一咬牙做了次技术选型改造把核心链路从规则匹配切到Function Call函数调用准确率才从72%爬到86%以上整个系统的可维护性也跟着上来了。这篇文章就把这次升级的完整思路、踩坑过程和选型细节掏出来聊聊供准备做智能体落地或正在被规则匹配折磨的同学参考。1. 一个被规则匹配拖垮的AI智能体先说业务背景1.1 这套智能体到底在解决什么问题我负责的智能体是一款商品推荐助手用户可以通过自然语言咨询商品信息并获取推荐结果。典型询问包括“帮我找一下300到500块钱的机械键盘”“有没有适合跑步戴的蓝牙耳机”“上次推荐的那个鼠标还有货吗”等等。这类需求背后隐含两个核心能力第一是意图识别判断用户是想查商品、问库存还是比价第二是参数抽取从用户的话里把品类、价格区间、品牌、型号、排序方式等信息抠出来然后传给商品检索接口。第一版交互链路非常直接用户输入先进正则匹配命中哪个意图就进哪个分支同时用正则把参数逐个抽出来抽不出来的就追问用户。因为商品垂类相对固定第一版上线时只维护了十几个正则库存、品牌这类槽位各有三五个关键词表简单场景跑起来其实很顺。问题在于用户不会永远用你预设的句式说话特别是当业务方开始做活动运营用户问法变得五花八门之后规则匹配的短板就彻底暴露了。1.2 规则匹配的四个硬伤第一个硬伤是正则表达式的维护成本。新来一个用户说法比如“有没有比那个贵一点的黑轴键盘”你就得在原正则上继续加分支、加否定模式、加指代处理。我统计过半年时间规则文件从几百行涨到三千多行每次改规则都要重新跑全量回归改坏一个分支就可能带崩其他场景。第二个硬伤是泛化能力几乎为零。规则匹配本质上是“记忆见过的说法”不是“理解没见过的话”。同一个意思换个说法就识别失败这是常态。最典型的例子是用户说“两百以内的”“不要超过两百”“预算就二百”这三个表达需要写三条正则才能都覆盖漏一个就掉一个场景的准确率。第三个硬伤是没有上下文概念。多轮对话里用户经常会省略主语比如先问“有无线鼠标吗”再追问“罗技的呢”规则匹配处理这种指代全靠人工维护全局变量聊岔一句就串档。而用户实际使用智能体时多轮对话比例接近四成这个短板非常致命。第四个硬伤是组合爆炸。规则匹配适合处理“槽位少、句式固定”的场景但商品推荐这个场景里品类、价格、品牌、颜色、连接方式、适用场景每个维度都有几十种可能取值组合起来就是几千种说法。规则表因此急剧膨胀最终准确率上不去本质上是被这种组合表达撑爆的。1.3 什么时候规则匹配仍然够用这里我必须说句公道话规则匹配不是一无是处在很多偏固定的场景里它依然是最优选。比如客服工单的分类标签、设备指令解析、表单自动填充这类输入句式相对固定、槽位少、不依赖多轮上下文用规则匹配简单直接响应也快完全没有必要上模型。但如果你做的是开放式对话入口用户的表达五花八门且需要多轮信息补全那就得认真考虑从规则匹配迁移到更灵活的技术方案。我这次做选型时反复权衡的就是这个边界不是要全面替换而是要让规则匹配退到降级兜底的位置把主识别链路交给更聪明的方案。2. 技术选型推演为什么是Function Call而不是其他方案2.1 三条候选路线的对比当时摆在桌面上的候选方案有三条路第一条是在原有规则匹配基础上叠机器学习分类器用文本分类模型做意图识别参数抽取仍然靠规则第二条是让大语言模型直接输出JSON把意图和参数都放在一个结构化结果里第三条就是Function Call让模型选择一个预先定义好的函数并按照函数约束输出参数。三条路线我都找人做了小规模验证结论差异非常明显。路线一属于保守改良意图分类准确率确实能上来但参数抽取的瓶颈还在等于只修好了一半问题路线二看着灵活实际落地时缺少约束模型输出JSON的格式稳定性很难保证并且模型自由发挥的空间太大容易出现意图边界漂移路线三也就是Function Call本质上就是“让模型在给定选项里做选择而不是让它自己编答案”可控性和格式稳定性都好很多。我整理了三条路线的对比方案意图识别能力参数抽取能力格式稳定性多轮上下文支持开发成本规则分类器中上低仍需规则穷举高低中LLM直接输出JSON高高低依赖prompt约束中低Function Call高高高结构约束强高低2.2 Function Call到底是个什么东西Function Call这个概念现在被很多平台包装成“工具调用”本质其实并不复杂。它是让大语言模型在理解用户输入后从一个预先定义好的函数清单里选一个最匹配的函数并按照该函数的参数约束输出结构化的调用信息。它跟我们日常说的“模型调用外部工具”是两回事Function Call更准确地说是一种“结构化输出协议”模型并不真正执行函数它只是输出“我想调用哪个函数、参数值是多少”真正的执行动作由你程序端的调度器完成。我用一个生活化的类比解释一下规则匹配像一位只认固定路线的老出租车司机你说“去北京站”他懂你说“去火车站就是那个很多人的地方”他可能就懵了。Function Call更像一位用导航的司机你不一定非要说准确的路名他能根据你的描述判断目的地并且导航会告诉他必须带哪个参数比如目的地经纬度他只需要照着填。关键点在于不是让司机自由发挥解释“你要去哪”而是让他在标准化的导航表单里填值。在工程实现上Function Call会在解码阶段对输出做结构化约束保证生成的JSON符合函数定义的schema这就把“自然语言到结构化数据”这个最大的难点从提示工程问题变成了一个模型原生支持的标准化功能。2.3 最终选择Function Call的四个理由选型不是追逐热点而是要看四个维度可控性、上下文支持、多平台兼容性、落地成本。第一是可控性。Function Call的意图识别是封闭集合里的选择函数列表就是智能体能力的天花板。函数定义清楚之后模型不会被带偏到无关输出这点对线上稳定性特别重要。第二是多轮上下文支持。函数调用天然适合多轮对话中参数补全的场景。用户第一轮说“推荐个机械键盘”模型调用函数时发现“价格区间”没填可以在函数参数上标记为缺失然后由外层对话管理追问第二轮用户补充“300到400吧”模型能自动把新信息并进已有参数里。第三是多平台兼容性。当时调研的几家大模型平台都支持Function Call有的叫Tool Use有的叫Function Calling都是同一套思路标准化程度高。这意味着智能体不会被单一模型厂商绑定切换模型时函数定义基本可以平移。第四是落地成本低。不需要自己去训练语义理解模型也不需要对每条业务规则做适配只要把业务能力抽象成若干函数再把函数参数设计好剩下的交给模型理解即可。整个改造周期比我们预估的短很多这就是为什么我们最后果断选择了Function Call。3. 改造实战从规则匹配迁移到Function Call3.1 整体架构怎么变旧的架构是三条串行链路用户输入先进规则引擎判断意图然后槽位填充模块用正则抽取参数最后拼装查询条件去调商品服务。新架构把链路改成了四层第一层是入口模块负责把用户输入和最近的对话摘要组装成上下文第二层是大模型决策层让它根据函数列表决定调用哪个函数并填好参数第三层是调度校验层校验模型输出是否合法、参数是否齐全缺失则发起追问第四层才是具体的业务工具层真正执行商品检索和结果包装。这个改动看起来只是把“规则判断”换成了“模型判断”但内核完全变了。规则匹配时代意图和抽取逻辑混在一起写正则本质上是在写一堆判断逻辑的夹缝里抠参数。Function Call时代意图和参数抽取都由模型完成代码里只剩下对模型输出结果的接收、校验和执行整体逻辑清爽很多。改造时我特意留了一个兼容层模型如果超时或返回结果置信度过低就自动降级到老规则引擎。这个降级开关是灰度发布期间最重要的保险实际上也真的救了两次线上事故。3.2 函数定义Schema怎么设计函数定义是整个改造里最核心的环节它直接决定模型能不能正确理解你的业务。我拿商品推荐场景举例最核心的函数之一长这样{ name: search_product, description: 根据用户的商品需求和偏好搜索匹配的商品当用户想找商品、了解商品信息、比较商品时使用, parameters: { type: object, properties: { category: { type: string, enum: [键盘, 鼠标, 耳机, 显示器, 笔记本, 手机], description: 商品品类必须从枚举值中选择用户没有明确提到品类时可以为空 }, price_min: { type: number, description: 用户能接受的最低价格没有限制则为空 }, price_max: { type: number, description: 用户能接受的最高价格没有限制则为空 }, brand: { type: string, description: 用户指定的品牌名未指定则为空 }, features: { type: array, items: { type: string, enum: [无线, 机械, 静音, 背光, 便携, 降噪, 长续航] }, description: 用户要求的商品特性列表可以为空 }, sort: { type: string, enum: [默认, 价格升序, 价格降序, 销量优先], description: 排序方式用户未明确要求则为默认 } }, required: [] } }这个schema看起来不难但里面有大量细节是从线上失败案例里总结出来的。首先名称和描述要面向模型写不是面向程序员写。模型的语义理解能力完全依赖描述文字的清晰度模糊描述会导致模型在多个相关函数之间摇摆函数数量越多越明显。其次能用枚举就不要用自由文本。category和features我都设计成枚举值就是为了限制模型的自由发挥空间。比如用户说“想要个打字舒服的键盘”模型如果自由填写features就会出现“打字舒服”“手感好”“办公”“舒适”等无数种写法下游根本没法统一处理。给死枚举之后这类语义都会被收敛到“机械”或“静音”这两个已有特性上。再次能复用就不要重复。函数定义应该是一等公民字段尽量在多个函数间复用同一个格式不要给每个函数单独设计一套参数结构。这样做的好处是模型学习的成本低而且调用切换时不容易把参数搞混。3.3 参数缺失补全和多轮对话处理Function Call真正让我觉得值回票价的地方是多轮对话里的参数补全。规则匹配时代一个槽位一个追问逻辑全部要手写而且追问顺序还很难定。Function Call方案下模型的函数调用本身就是天然的意图表示。流程是这样用户第一轮说“帮我看看有什么键盘”模型得知category已填但price_max没填。代码侧拿到函数调用结果后发现必填参数缺失或者关键业务参数缺失就反问用户“你预算大概多少”。用户回答“500以内吧”下一轮调用时会把历史消息一起送进去模型看到新输入后自动补全price_max为500同时保留上一轮的category字段。这里有一个工程细节值得分享为了让多轮参数补全更稳定每次请求时不要只把当前这轮的用户话术发给模型最好把之前几轮的函数调用结果作为历史上下文也带上并在系统提示词里写明“已经获取的信息包括品类键盘需要补充价格上限”。很多Function Call实现把历史交互丢弃了导致第二轮模型忘了第一轮选定的品类这个问题在真实场景里非常常见。3.4 旧规则引擎的去留迁移到Function Call之后旧规则引擎并没有被直接删掉。我把它降级为一个兜底验证器职责有两块第一是在模型调用异常时作为降级通道保证智能体至少还能用规则匹配兜住一部分高频句式第二是做输出合法性检查模型返回的category如果不在枚举值里规则引擎可以秒级发现并拦截避免脏数据进到下游。灰度期间的策略是按流量比例缓慢放开从5%到50%再到全量全程用埋点数据对比新旧两个链路的准确率。直到全量跑了一周新链路的准确率稳定超过旧链路15个百分点以上我才把主链路的流量完全切过去。这段双轨运行的经验给我的感受是技术升级最怕的不是新技术不够强而是没有给自己留退路。持续可对比、可回滚才是工程化切系统的正确姿势。4. 准确率提升86%背后的调优过程4.1 评测集怎么建才能避免自欺欺人准确率提升这件事前提是评测标准要靠谱否则你优化了半天只是自我感觉良好。我搭建评测集的方式是直接从线上日志抽样涵盖了用户真实说法的分布而不是用一个理想化的手工构造数据集。评测集总量是2000条按来源分成三个难度层级。简单层是句式规范的直接表达比如“给我推荐一款500元以下的无线鼠标”这类只占30%。中等层是带一定口语化或者包含两个以上参数的表达比如“我想买个键盘最好是静音的预算就三四百”这类占40%。困难层则包含指代、省略、否定、模糊描述等复杂情况比如“有没有比刚才那个便宜点的、但是要无线的那种”这类占30%。我特别想强调的是评测集必须包含线上真实但“不标准”的说法而不要只保留你觉得模型应该答对的样例。很多人搭建评测集时习惯性过滤掉那些规则匹配答不对的样本结果评测数据上去了一看线上问题原样保留。准确率这东西测什么就优化什么样本分布失真会让整个调优过程失去方向。准确率的具体口径我定义为模型识别出的意图和所有关键业务参数与标注结果完全一致才算这一条正确。意图对了但参数错一个整条算错这个口径不可谓不严。最初基线测试里规则匹配在这个口径下的准确率是72%Function Call裸跑的准确率是79%经过后续几轮调优才达到86%以上。4.2 五个关键优化点分别贡献了多少从79%到86%这个过程中我记录了每一次优化动作和对应提升最终整理出五个关键优化点。第一是函数描述的重写。最初我把函数描述写得很技术化比如“查询商品资源的接口”模型对函数调用的触发时机把握得不准。改成行为导向的描述写明“用户想找商品、了解商品信息、比较商品时使用”之后意图识别误判率明显下降。这一个动作让准确率从79%提到了81%左右。第二是枚举约束收敛。一开始features字段是自由填空的模型能写出十几种同义表达下游匹配经常落空。改成枚举后准确率直接提升约2个百分点。这步带给我的启发是能用选项约束的地方就不要开放输入模型在选项之间做选择远比自由发挥稳定。第三是few-shot示例注入。在系统提示词里加入五组用户问题到函数调用的示例包括简单表达和复杂指代表达各几组效果提升到84%左右。模型的输入侧上下文本身就是一种可调的“软代码”few-shot写得到位很多意图瑕疵就自动被纠偏了。第四是temperature从0.7调到0.1。我对准确率敏感的决策式任务温度越低越稳定。低温带来的副作用是回答缺少多样性但对于商品推荐这种场景稳定远比多样重要。这个调整贡献了大约1个点的提升。第五是错误重试机制。当模型输出的JSON校验失败或者参数落在枚举之外时不要把错误直接抛给用户而是把校验报错信息作为提示词回传让模型自我纠正一次。这个机制又贡献了约1个点的准确率并且大幅降低了返回给用户端的报错率。4.3 为什么86%之后很难再往上走优化到86%之后我做过一段时间的继续冲刺发现再往上提升的边际成本变得极高。原因在于剩余错误样本集中在几个很困难的类型上比如用户表达里带有对比关系“A和B比哪个好”、推理需求“这个价位有没有配置更高一点的”或者指代链条拉得特别长的情况。这些本质上已经不只是工具调用的问题而是依赖模型本身的推理能力上限。这里我的心得是86%这个水平对我们这个场景是够用的因为剩下14%的错误中有超过一半会被多轮追问和降级策略接住真正流转到用户侧的不满远小于指标呈现的比例。做系统不是要把一个指标推到100%而是要把指标优化到让整体体验可接受的程度同时保留充足的兜底能力。5. 常见问题与排查技巧实录5.1 一次性说清经典问题排查思路整个改造过程中踩了不少坑我把最典型的几个问题整理成一张速查表新上手Function Call的兄弟可以直接对着查现象根因排查思路与解法模型频繁调用无关函数函数描述过于宽泛或函数间边界不清晰重写描述明确每个函数的适用场景必要时加few-shot示例做行为示范多个函数参数结构相似导致混用函数设计没有业务边界字段重叠度高函数收敛合并同类函数减少模型选择负担返回的JSON偶尔不是合法格式温度过高或模型本身输出随机性大降低temperature至0.1-0.2增加重试机制用校验逻辑兜住解析失败多轮对话中丢失已确认参数没把历史函数调用结果传给模型组装请求时沉淀累计参数并在系统提示词中注明已获取的参数列表枚举值外出现新词枚举表覆盖不全或用户用了同义词补充映射词典将同义词归一化到枚举值同时让校验层记录未识别词用于迭代参数缺失时模型自己编一个默认值没有在schema中指示可空或需要追问required设为空数组并在字段描述中写明“未提则为空不要猜测”线上延迟比规则匹配高不少大模型推理耗时高于正则匹配设置超时上限、引入流式输出或对简单短句走轻量模型快速通道这些坑很多都是一次性的但一旦踩中排查成本不低。我建议刚迁移的同学上线前先把这几个场景写进测试用例里比上线后再被用户反馈打脸要省事得多。5.2 函数粒度的坑拆得太细反而更差我最初设计函数时有个错误倾向为了让模型“更有能力”把所有业务能力拆得非常细拆出了“查价格”“查库存”“查品牌”“查销量”等十几个函数。结果模型经常在相似的函数之间犹豫不决甚至出现用户问库存模型却去调价格接口的诡异行为。后来我把函数粒度收敛到业务动作级别整个推荐助手只保留三个核心函数搜索商品、获取商品详情、获取商品库存。每个函数承载一个完整业务意图参数内部再做细分。准确率立刻回升模型的选择也稳定了。这个经验总结下来就是函数不是越细越好而是要和用户意图粒度对齐。一个函数对应一个完整业务目标参数是达成目标的变量这才是Function Call的正确设计姿势。5.3 监控链路怎么搭才不被动升级过程中我给智能体搭了三层监控确保一旦出问题能快速定位。第一层是函数调用监控统计每个函数的调用占比和调用失败率占比异常波动往往意味着意图识别偏移第二层是参数落库监控记录每个参数的填充率填充率长期偏低的参数说明用户很少表达需要考虑调整问法引导第三层是人工抽检复核每天随机抽100条线上会话人工判断函数调用是否合理。前两层都是自动化日志第三层则需要固定的人工投入但恰恰这层最不可省略因为模型输出的准确率需要通过人工标注来验证。很多智能体项目上线后效果退化根本原因就是缺少人工抽检环节模型行为漂移了好几天才发现而此时用户已经流失了一波。监控体系加上前文说的降级策略整套系统就不再是“模型猜对就工作模型猜错就发呆”的状态而是每个失败点都有应对路径。6. 一些关于智能体技术选型的个人体会这次从规则匹配到Function Call的升级给我最大的感触是技术选型不是找最强的方案而是找最适配当前业务约束的方案。规则匹配并不是因为它简单就该被鄙视Function Call也不是因为时髦就该被使用真正重要的是把业务需求量化成准确率、延迟、维护成本这些指标再进行决策。Function Call当前已经成为我搭建智能体的默认起点它很好地平衡了自然语言理解能力和程序可控性。但它也不是终点未来如果业务继续扩张需要智能体自主编排多个步骤完成复杂任务时可能还得引入更复杂的Agent规划机制。到那时候今天沉淀下来的函数定义和监控体系依然会是新架构的地基。最后给准备动手迁移的同学一个建议不要想着一次性推翻重来。先把核心链路切到Function Call保留旧规则引擎兜底然后建立好评测集和监控小流量灰度验证等新方案的收益数据跑出来之后再决定要不要全面切换。这样每一步都有数据支撑每一版都可回滚你做的就不是一次赌博而是一次经得起复盘的技术升级。
返回列表