ARTICLE DETAIL

资讯详情

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

从Function Calling到Agent技能体系:大模型应用开发实战指南

从Function Calling到Agent技能体系:大模型应用开发实战指南 做AI应用开发这段时间我越来越明确一个感受想让大模型真正干成一件事光给它一堆工具函数是不够的你得给它一套能被它理解和编排的技能体系。这个体会来自我们内部一个代号叫agent-skills的项目——把Agent的能力从散乱的函数列表重构成模块化的技能包。很多人问我这跟普通的Function Calling有什么区别这次就当作一次复盘把设计思路、踩坑记录和可复用的实现细节都摊开来聊一聊。这个项目解决的问题很具体当你的Agent需要处理查询订单、分析文件、生成报表、调用内部API等几十种不同能力时怎么让模型在复杂的上下文中依然能准确选对工具、执行对步骤。如果你正在做LLM应用开发或者你的团队已经遇到工具函数超过二十个后准确率直线下滑的问题这篇内容值得你看完。1. 为什么要做技能体系先摆出三个真实痛点1.1 工具函数堆成山模型还是不开窍最早我们做Agent用的就是标准的工具调用方式。系统提示词里塞下几十个函数定义每个函数带参数、带注释自我感觉配置得很完整。结果实测一跑问题立刻暴露用户问帮我查一下昨天订单的物流状态模型居然先去调用了一个搜索天气的工具绕了一圈才反应过来要找订单接口。不是模型变笨了而是工具列表这种形态本身有缺陷。LLM在生成一次调用的上下文中需要在有限注意力窗口里消化所有函数签名。当工具数量超过一定阈值模型对不同工具细节的区分度会明显下降。更麻烦的是工具函数是平铺的彼此之间没有场景属性模型只知道有这些函数却不知道什么场景下该优先用哪个。1.2 单一提示词的维护是一场灾难第二个痛点来得更快——维护。我们最初把所有工具说明、使用规则、输出要求全部塞进一个提示词模板里。产品经理提了个小需求订单查询要增加一个是否支持拆单的判断。就这么一个字段我们改了工具描述、改了输出解析逻辑、改了报错提示还要重新跑一遍回归测试生怕影响了另外几个工具的调用效果。单一提示词的本质问题在于高耦合。工具A的输出格式调整可能导致工具B的触发逻辑受影响给工具C增加一个描述也许会把工具D的优先级挤下去。改一次提示词就像推倒一块多米诺骨牌后面跟着一堆不确定性。做Agent应用的人应该都有这种体验提示词越改越长但模型的确定性越来越差。1.3 换个场景等于重写一遍最让我们下决心重构的是复用这个需求。当时团队同时推进好几个Agent项目一个是客服助手一个是内部数据分析助手一个是知识库问答助手。三个项目底层都有查询订单检索文档生成报表这些能力但代码完全是三套。每次新项目启动都得把之前的工具函数、提示词逻辑搬一遍再按新场景调整。费时费力不说风格还不统一。所以agent-skills的核心理念就定了把Agent的能力拆成独立的、可描述、可注册、可复用的技能模块。每个技能做好自己的事情并且用模型能看懂的方式告诉它我是什么、什么时候用我、怎么用我。这套思路后来被验证是有效的下面拆开讲。2. Agent Skills的设计要点技能不是工具是有灵魂的模块2.1 技能模块的六个组成部分我们在项目里给技能下的定义是一个自包含的能力单元包含六个部分组成部分作用说明技能标识唯一命名供调度和索引采用动词对象命名法比如query_order场景描述说明适用与不适用的场景告诉模型什么时候该用、什么时候别用输入参数结构化Schema定义输入包含字段类型、必填与否、含义说明执行逻辑具体完成任务的内部实现可以是API调用、代码执行、内部模型推理输出协议统一输出格式及成功/失败判定让模型能稳定解析执行结果示例演示提供1-2个调用样例帮助模型理解典型使用方式印象最深的是场景描述这部分。过去函数注释里只写查询订单状态但技能描述里我们会写当用户询问物流进度、包裹签收、发货时间时使用当用户只是询问退款规则时不要使用应转用query_refund_policy。这种正反结合的描述方式对模型选择技能的准确率提升非常明显。2.2 技能描述的艺术让大模型一眼看懂什么时候该用你技能描述写得好不好直接决定模型能不能在正确的时候激活正确技能。我们在反复调试中总结了一套描述模板第一句说明技能能力边界用一句话讲清楚这个技能负责什么第二句列典型触发场景把用户可能的说法整合进去第三句给出阻断条件明确哪些情况不要调用本技能。三段式结构信息密度高、便于模型快速匹配。比如query_order技能描述不是查询订单状态而是查询订单全生命周期状态包括待付款、已发货、已签收、退货中。当用户提供订单号或希望了解包裹位置、物流轨迹时使用。如果用户确认要申请退款优先使用apply_refund技能。这种写法等于给模型划了一条明确的分界线它就不再需要摸索。2.3 技能分类与依赖关系技能一多就需要分层。我们把技能分成三类基础技能原子操作不依赖其他技能比如search_knowledge、calculate。这类技能求稳输入输出要尽可能简单。组合技能编排多个基础技能来完成任务比如生成销售周报会先查询数据、再分析趋势、最后生成文档。决策技能本身不直接执行操作而是根据上下文决定分配哪个技能通常用于复杂任务的路由。依赖关系我们用一张技能依赖表管理。每个技能启动前检查前置技能是否可用避免在运行时才发现某个依赖没有被注册。这个设计在后续扩展多Agent时尤其重要技能之间的依赖清晰了任务编排就容易做。3. 从零搭建一套agent-skills我的实操过程3.1 定义技能Schema与注册表我们先定了一套统一的技能Schema所有技能都按这个结构声明。以query_order为例完整定义是这样的{ skill: query_order, description: 查询订单全生命周期状态包括待付款、已发货、已签收、退货中。当用户提供订单号或希望了解包裹位置、物流轨迹时使用。若用户申请退款请使用apply_refund。, input_schema: { type: object, properties: { order_id: { type: string, description: 订单号通常为12位数字或字母组合 }, include_tracking: { type: boolean, description: 是否需要返回物流轨迹明细默认false, default: false } }, required: [order_id] }, output_schema: { type: object, properties: { status: { type: string }, tracking_info: { type: array, items: { type: string } }, estimated_delivery: { type: string } } }, execution: { type: api_call, endpoint: https://api.example.com/v1/orders/{order_id}, method: GET, timeout_ms: 3000, retry: 1 }, success_condition: response.code 200 and response.data.status ! null, error_handling: { ORDER_NOT_FOUND: 请用户核对订单号是否正确, TIMEOUT: 请用户稍后重试 }, examples: [ { user_input: 我的订单231231231212到哪了, expected_skill: query_order } ] }这个Schema看起来字段多但每个字段都有实际用途。比如success_condition专门处理接口返回200但数据不对的情况error_handling是给模型兜底的让它在技能失败时知道该回复什么内容而不是自己编一个答案。所有技能定义统一存在一个注册表里启动时加载到内存。注册表承担两个职责一是给模型提供技能列表二是供调度器做能力查询。这样技能的新增和下线都是配置级别的事情不需要改主流程代码。3.2 调度器与LLM的配合方式拿到技能注册表后核心问题变成了模型怎么知道该调哪个技能我们的做法是做了一道技能召回加一次LLM决策的二级调度。第一级是召回基于用户请求和技能描述进行相似度匹配。这一步不是用复杂模型而是把技能描述和用户输入分别做向量化用余弦相似度召回Top K个候选技能。K一般设在5到10之间这样既保证候选覆盖又不会给模型太多干扰项。第二级是决策把候选技能的关键信息作为提示词上下文交给LLM由它选出最终要调用的技能并填充参数。这一步利用模型的语义理解能力做精准路由。相比直接把几十个技能全塞给模型两级调度大幅降低了上下文长度也让每一步决策目标更聚焦。两级调度还有个附赠的好处可以做限流和安全检查。在技能执行前调度器会检查参数格式、校验必填字段、判断是否有权限。有什么不合理的地方在调用外部系统之前就拦住了比让模型自己完成后端报错再处理要体面得多。3.3 一个完整技能的落地示例我拿生成日报这样一个组合技能来说说落地过程。这个技能需要先查询业务数据再调用数据分析脚本最后生成文本摘要。第一步定义依赖generate_daily_report声明依赖query_metrics和summarize_data。注册表在加载时会先检查这两个基础技能是否存在不存在则启动失败并给出提示。第二步定义执行编排我们用了一个简单的DAG执行器技能声明一个steps数组每个step指定要调用的子技能和参数来源。比如step1的output作为step2的inputstep2的output再作为step3的input。这样技能内部的流程可以写死外部只需要触发一个大技能模型不需要关心内部顺序。第三步处理预期的边界情况日报数据为空怎么办、指标接口超时怎么办、生成摘要时遇到超长文本怎么办。这些都要写进技能的错误处理分支里。我们发现把错误处理放在技能内部而不是让模型临场发挥稳定性会好很多。3.4 技能编排从单步骤到多步骤任务单技能能力再强也只解决一个问题。真实用户的一句话往往包含多个诉求比如帮我查一下上周的销售数据然后对比前一周写个总结。这在Agent里就是一个多步骤任务query_metrics查上周数据query_metrics再查前周数据summarize_data生成对比总结。我们在实践里采用计划-执行-验证的模式。模型先输出一个执行计划列出需要的技能序列和依赖关系调度器校验计划中的技能是否存在、参数是否齐备接着逐个执行技能并收集结果最后交给模型汇总生成最终答案。这套模式的一个关键心得是不要把技能编排的决策权完全交给模型也不要完全写死。折中的办法是给一些高频任务预定义好技能模板模型只需要填充参数遇到模板之外的组合需求再走自由的计划生成路径。这样既保证了常见场景的速度和稳定性又保留了处理复杂任务时的灵活性。4. 调试、优化与排查实录4.1 技能召回不出来问题出在哪最常遇到的问题是用户明明在问订单物流query_order的召回排名却很低。一开始我们怀疑是向量检索的问题排查后发现根因往往在技能描述的用词和用户口语表达差异太大。用户说的是我的快递到哪了东西发了没包裹到哪一站了而技能描述里写的是查询订单全生命周期状态这种偏正式的表述。语义距离被拉远了。后来我们把描述改成了贴近用户口语的说法并扩充了典型表达句式。说实话把用户真实语料里出现的高频问法直接揉进技能描述中是性价比最高的优化手段。注意技能描述不是写给人看的产品文档而是写给模型看的检索索引。用语越贴近真实用户表达召回效果越好。4.2 技能调用成功率上不去数据层面怎么调还有一次某个技能在召回环节没问题但实际调用成功率一直卡在85%左右。查日志发现大量失败是因为参数解析错误。比如用户说查一下231231和232323这两个订单模型只提取了一个订单号另一个被丢掉了。这个问题要从两个方向解决。一是调整输入Schema把订单号字段增加description说明支持给单个订单号如果用户提供多个订单号拆分后多次调用本技能。二是调整示例在examples里增加一个多个订单号的样本模型看到示例后提取参数的完整度明显提升。说到底LLM是个靠模式匹配运作的系统。你在Schema和示例里给出的模式越清晰它还原正确参数的概率就越高。4.3 上下文爆炸给技能输出设瘦身规则随着技能数量增加新的问题出现了每个技能执行后返回的结果都被原封不动地塞回对话上下文。一次复杂任务调了六个技能每个技能返回两千字上下文瞬间膨胀。模型后面的推理质量随之下降。我们给每个技能的output_schema加了max_output字段同时规定技能返回的结果必须精简只保留对下一步决策有用的信息。比如查询订单物流轨迹明细默认不返回除非用户明确要求查看物流轨迹。这样上下文的使用效率高了模型专注度也上来了。另外我们还做了执行结果缓存。同一个用户短时间内重复触发同一个技能直接走缓存不重新请求外部API既省时又省成本。缓存键设计成用户ID 技能名 参数哈希避免缓存不同用户之间的敏感数据这个细节在安全合规上格外重要。4.4 技能冲突的优先级处理当技能越来越多会不可避免地出现两个技能描述相似的情况。我们的经验是解决冲突不靠描述写得多么巨大而是靠显式的优先级声明。比如search_product和recommend_product一个偏精确匹配一个偏推荐联想。如果用户说帮我找找有没有红色的羽绒服两个技能看起来都沾边。这种情况下我们会在高优先级技能的描述里明确写上如果用户提到颜色、款式、尺码等具体属性优先使用本技能不要使用recommend_product。显式的排他式描述比模糊的相关时可用可靠得多。这里有个小习惯定期检查技能注册表里所有描述找出也适用于这类模糊字段逐条收紧。技能描述是越精确越省心。5. 技能体系的扩展思路与评估5.1 什么时候不该上技能体系这套方案不是银弹。如果你的Agent只需要调用两三个固定的工具用户问题也很固定直接写死业务逻辑比引入技能体系省事得多。技能体系的收益在能力数量多、场景变化快、需要频繁扩展时才能真正体现。我个人的判断标准是工具函数超过十个或者同样的能力需要在多个Agent间复用或者产品需求一个月内变过三次满足任意一条就值得引入技能体系。否则简单方案反而是最优解。5.2 从单Agent到多Agent技能共享与隔离技能体系天然适合多Agent场景。我们把技能注册表做了一个命名空间层公共技能放在global命名空间所有Agent可见部门专用技能放在team命名空间只有对应Agent能访问。比如query_order对所有客服类Agent开放内部财务专用的报表技能只对财务助手开放。这个设计解决了权限隔离和高频技能复用两个问题。新Agent接入时只需要声明自己要挂载哪些技能而不需要重新实现一遍能力。围绕统一技能注册表团队的合作模式也从各自开发一套工具函数变成共建一套企业技能库越积越厚。5.3 技能质量怎么度量三个指标最后说说评估。没有量化指标技能体系很容易变成一团混乱。我们日常关注三个指标技能召回率在测试集里正确的技能是否出现在召回Top K中目标一般设在95%以上。技能选择准确率模型最终选中的技能是否正确这个指标反映描述质量和提示词效果目标建议设定在90%以上。技能执行成功率选中技能后是否成功拿到预期结果。低于80%就要回头查参数解析、外部接口和错误处理分支。我们还建了一个小的回归测试集每次修改技能描述或调整Schema都会自动跑一遍确保改动某个技能不会影响其他技能。这件事看起来繁琐但维护久了会发现它才是整个Agent质量的生命线。最后分享一个我个人的维护技巧每个季度做一次技能去重和描述审计。把所有技能的调用日志拉出来凡是超过三个月没有被成功触发的技能重新审视是场景已经停了还是描述有问题导致模型找不到。前者直接下线后者重写描述。这个习惯让我维护的这套技能体系始终保持在干净、高效的状态不会膨胀成没人说得清楚的遗产代码。Agent的能力边界说到底取决于你给了它什么技能、以及这些技能描述得是否清楚。把技能体系当成一套需要持续维护的产品来做你的Agent才会越用越顺手。
返回列表