资讯动态

Agent Skills实战:从Function Calling到多平台智能体应用

发布时间:2026/9/20 6:21:42 来源:尧图企业网站定制
做AI应用开发这行最容易被问到一个问题你做的那个智能体到底能不能像人一样用工具我最近花了几周时间把一个内部工具项目重构成基于Agent Skills的多平台应用从开发机上的命令行一路铺到Web端、IM机器人和企业知识库。整套实战到现在已完整收尾没有留任何隐藏内容所有章节都能直接照做。做完这轮改造我对Agent Skills的理解比之前任何一次技术调研都深它不是简单地把Function Calling包装一下而是真正把“能力”做成了可复用、可发现、可编排的标准化模块。这篇内容适合正在做Agent应用、被多平台重复开发折磨的开发者也适合手里有API、工具或内部系统想沉淀成一套技能给大模型统一调用的技术团队。我会把项目从设计思路、技能注册与调用管线到多平台落地的每一个步骤以及调试和上线后踩过的坑全部拆开来讲。1. 做多平台Agent为什么最后选了Skills这条路1.1 从一次失败的“万能Agent”说起先说反面教材。项目初期我图省事把所有工具都塞进一个大Prompt里搞了个“万能Agent”。当时想得很好反正大模型会自己根据对话内容选择合适的工具。结果一上线就翻车。第一工具一多模型就懵。一开始挂5个工具时效果还行挂到15个以后模型经常选错工具或者明明应该调A工具它偏要自己编一个结果出来。第二所有逻辑耦合在同一个System Prompt里改一个工具的描述往往会影响另一个工具的触发率。第三平台之间无法复用。我把这堆工具在Web端调通了到了IM机器人场景对话格式、上下文长度、权限校验全不一样等于从头再来一遍。这个阶段最大的教训是靠一个巨大的Prompt去管理多个能力本质上是在用文本格式维护一套分布式系统根本维护不住。能力必须拆分、封装、独立演进这就是我转向Agent Skills的原因。1.2 Agent Skills和Function Calling到底差在哪现在市面上提到工具调用很多人第一反应是Function Calling。但如果你把Agent Skills理解成“更工程化的Function Calling”会错过它最有价值的部分。我列一个对照表方便你判断自己该用哪种维度Function CallingAgent Skills粒度单个函数/API调用一个完整的能力闭环可包含多个函数和内部逻辑复用性通常是代码内直连换个平台要重写独立声明、独立打包同一份技能可挂载到多个平台发现机制由开发者在代码里写死函数列表通过技能描述做语义匹配Agent按需发现编排能力模型一次只能选一个函数支持多技能组合、串行/并行编排运维方式改动函数要重新发布整个应用技能可独立版本化、灰度发布、单独回滚关键差异在于“封装层级”。Function Calling解决的是“模型怎么调用一个函数”Agent Skills解决的是“一组能力如何被模型按需组织并执行”。后者天然更适合多平台你只需要把技能本身实现一次平台侧通过适配器对接即可。注意如果你的应用只有两三个固定接口且永远不会换平台用Function Calling完全够。Agent Skills的工程成本更高不要为了追概念而过度设计。1.3 多平台部署的收益与成本账做多平台之前一定要先算账。收益很清晰一次开发多处复用各平台只保留适配层和权限逻辑新平台接入时间从“两周”压缩到“两天”。成本也同样真实技能运行时要统一不同平台的执行环境不一样所以技能必须跑在独立进程或容器里靠标准协议通信。上下文分配要重新设计IM机器人上下文有限Web端可以传长文本同一套技能在不同平台的上下文预算完全不同。权限模型要收敛企业微信/OA里的操作审批和Web端的用户登录态不是一回事技能层不能感知这些只能由平台适配层统一注入身份信息。我的建议是不要为了“多平台”而多平台。先有一个被验证过的核心技能再复制到第二个平台。两次复制的经验足够你提炼出通用抽象三次以上才值得做完整的技能注册中心。2. 核心技术点拆解技能注册与调用管线2.1 技能声明文件就是Agent的“产品说明书”Agent Skills里最重要的一环不是实现代码而是那份技能声明文件。模型全靠它来理解“这个技能是干什么的、什么时候该调用、参数怎么填”。以我自己定义的一个天气查询技能为例name: weather_query version: 1.0.0 description: 查询指定城市的实时天气信息支持中文城市名或经纬度。 当用户询问天气、气温、降雨、风力、是否需要带伞时使用该技能。 input: - name: city type: string required: true description: 城市名称如北京、上海 - name: unit type: string default: celsius enum: [celsius, fahrenheit] output: type: object schema: temperature: number humidity: number wind_direction: string suggestion: string # 针对出门是否需要带伞给出建议 execution: runtime: python3.11 entry: run.py timeout: 10s注意description的写法我故意写上了“何时使用”的触发条件。这是因为模型判断是否调用技能主要看当前用户问题和技能描述之间的语义相关度。如果你只写“查询天气”用户问“今天出门要不要带伞”模型很可能不触发这个技能因为它觉得“带伞”不是“查询天气”。但如果你把“是否需要带伞”写进触发场景命中率会明显提升。执行层我用的是Python3.11独立运行时入口是run.py超时10秒。这里的超时一定要写否则一个卡死的技能会把整个Agent流程拖垮。2.2 技能发现与路由Agent怎么知道该用哪个技能技能多了以后你不可能把所有技能的完整声明都塞进模型上下文否则Token会爆掉。所以需要一个技能路由层我采用的是“语义召回 模型裁决”的两阶段方案。第一阶段把用户当前问题和所有技能的描述做向量化用余弦相似度召回Top-K个候选技能。这里有几个参数需要调Top-K我设置在3到5之间。太小容易漏召太大容易把不相关技能送入上下文干扰模型判断。相似度阈值建议0.35起步。这个值靠经验调不同领域差异很大。低于阈值时宁可让Agent说“我不会”也不要硬调用错误技能。第二阶段把召回结果连同技能输入参数Schema送给模型让模型决定最终调用哪个技能、填什么参数。这里有一个细节模型在“工具选择”和“参数生成”两个环节的准确率差异很大。如果第一阶段召回准确第二阶段模型基本能在九成以上案例里选对如果第一阶段召回就错了模型怎么选都是错的。所以我花在“技能描述如何写”上的时间比花在提示词上的时间多得多。# 技能路由的伪代码流程 query 上海明天会不会下雨 candidates vector_search(query, skills, top_k5) prompt build_router_prompt(query, candidates) skill_name, params llm_choose(prompt) result await execute_skill(skill_name, params)2.3 上下文和Token预算最容易失控的地方多平台Agent最大的隐性成本是Token消耗。我实测过一个典型任务系统提示词约800 Token三个候选技能声明约360 Token用户输入约300 Token历史对话保留最近10轮约2000 Token模型输出预留1000 Token总计接近4500 Token。如果上下文窗口是8K尚有余量但如果你图省事把20个技能声明一次性全塞进去光技能描述就2400 Token再加历史对话很快触顶。触顶后的后果很直接模型开始“忘记”部分技能或者截断历史上下文导致多轮对话中的用户意图丢失。所以我做了三个策略技能描述字数预算单个技能描述控制在200字以内包括触发场景、输入、输出关键字段。历史消息摘要化超过5轮对话把早期消息压缩成摘要而不是原文保留。结果再压缩技能返回的结果如果太长先做字段裁剪只保留模型真正需要的核心字段。实操心得技能返回的结果不要一股脑全塞给模型。比如查询数据库返回了50条记录模型根本记不住你应该先让技能做一次聚合或摘要返回5条以内的精华结果。这比加大模型窗口便宜得多效果也好得多。3. 多平台落地实录从CLI到办公协同3.1 阶段一本地开发环境下跑通技能调试闭环我建议所有技能先在本地命令行环境下跑通不要一上来就接平台。我在项目里的做法是搭了一个极简的“技能运行时”目录结构skills/ weather_query/ skill.yaml run.py requirements.txt report_generator/ skill.yaml run.py registry/ index.yaml # 技能注册表记录所有技能的名称、版本、入口 runtime/ worker.py # 负责加载技能、执行技能、返回标准化结果 router.py # 语义召回 模型裁决本地调试阶段的核心工具是一个“MockLLM”模块。它的作用是绕过大模型直接指定要调用哪个技能、传什么参数。这么做的好处是先验证技能本身的正确性。很多项目一开始就接大模型结果技能代码有Bug和模型选错工具混在一起排查时根本无法定位。先把两边分开验证再合到一起看协同效果。我实测本地跑通一个技能大概需要半天时间主要是打磨输入参数和返回格式的边界情况。跑通后我习惯写几条golden测试用例比如“明天上海会下雨吗”、“北京现在多少度”把它们固定成自动化测试后续改代码随时回归。3.2 阶段二暴露成HTTP服务接入Web端和IM机器人本地跑通之后第二步是把技能服务化。我这里采用的是“适配器模式”技能层完全不知道自己在被哪个平台调用平台侧通过适配器把各自的消息格式转换成统一协议。统一协议的核心就是一个JSON-RPC风格的接口{ skill: weather_query, params: { city: 上海, unit: celsius }, request_id: abc123 }Web端接入相对简单我用FastAPI包了一层HTTP服务把上面的协议包在/v1/execute接口里配合API Key鉴权和每秒请求数限流。Web场景下上下文比较充裕所以完整传历史对话没问题。IM机器人的差异点在于消息是异步的、碎片化的而且不同IM的接口差异很大。用户可能发一条“查下天气”然后过几分钟又补一句“顺便看看明天”。这时候Agent必须支持跨轮状态保存。我在适配器层做了一个轻量级会话状态管理把当前对话涉及的技能参数暂存在Redis里等下一次用户消息到达时合并参数。# IM适配器的关键逻辑状态合并 session redis.get(session_id) if session and session.get(skill) weather_query: merged_params merge(session[params], current_utterance)这个设计让IM场景的体验明显提升用户不需要一次性把所有参数说完Agent可以像人一样“猜”到用户是在追加信息。3.3 阶段三嵌入企业知识库与协作流程第三个阶段我把技能接入了企业知识库和内部协作流程这是Agent Skills最能体现价值的地方。以“周报自动生成”为例这不是一个单一技能而是三个技能的编排组合获取工作数据技能从项目管理工具拉取本周完成的任务、耗时、阻塞项数据分析技能计算完成率、对比上周数据、提取阻塞原因周报生成技能把分析结果按公司模板生成结构化周报我定义了一个简单的编排描述让Agent按依赖关系执行{ steps: [ {id: fetch, skill: project_data_fetch, params: {week_offset: 0}}, {id: analyze, skill: data_analyzer, params: {input: ${fetch.output}}}, {id: report, skill: report_generator, params: {input: ${analyze.output}}} ] }这里有一个关键实践技能编排不要完全交给模型自由发挥。在关键业务流程上我会预定义DAG模型只能决定参数不能决定调用顺序。否则模型今天输出一个A-B-C明天可能就变成B-A-C输出结果不可控。让模型在固定编排里填充参数稳定性会高很多。知识库场景则正好相反因为用户问题不可预知必须完全靠Agent自主决定调用哪个技能。我这里给知识库配了“文档检索、摘要生成、引用溯源”三个技能Agent根据问题内容动态组合简单问题只调检索复杂问题走“检索摘要溯源”。4. 调试、评测与上线之后的那些坑4.1 技能“偶尔失灵”的本质原因上线后最头疼的是“这次能用下次不能用”的间歇性问题。我把线上抓到的故障案例归类后发现原因基本集中在四类技能描述写得不到位触发了语义边界模糊。典型例子是用户问“上海适合穿什么衣服”天气技能没被召回因为描述里没写“穿衣建议”这类词的触发场景。参数枚举不严。接口明明只支持固定几个城市模型传了个“Shanghai”进去技能层没做归一化直接报错。上下文被截断。多轮对话一长早期用户提到的关键约束被截掉模型后续动作偏离目标。技能内部异常没有回传。技能进程崩溃了但给模型返回的是空字符串模型误以为“没有查询结果”于是编了一个答案出来。排查这类问题时我给技能的每次调用都打上结构化日志记录召回分数、最终命中的技能、模型生成的参数、技能执行耗时和返回码。有了这些信息定位问题基本就是把日志拉出来看一眼的事。4.2 性能与延迟编排不是串行把多个技能串行执行是最直观的思路但性能会让你怀疑人生。我最早做周报生成时三步编排串行执行总耗时稳定在6秒左右用户体感很差。后来我改成并行优化三段流水线里“取数”和“历史数据对比”两个技能没有依赖关系同时跑整体耗时压到3.5秒。这里有一个需要权衡的点并行调用会同时消耗多个资源而且如果两个技能都返回大量结果上下文会被快速占满。我的经验是无依赖的技能先并行有依赖的技能严格串行每个技能单独设超时10秒是最常见的值超过直接标记失败并让模型感知到“该技能超时”而不是让模型空转等待。4.3 多平台版本管理与灰度发布多平台上线后版本管理成了大问题。最开始我直接原地更新技能结果Web端已经用上了新版本IM机器人还在跑旧版逻辑两边结果不一致用户反馈对不上。后来我引入了语义化版本和灰度机制技能版本号用major.minor.patch表示破坏性变更必须升major。每个平台适配器里声明自己依赖的技能版本范围例如1.0.0 2.0.0。平台接入时指定版本灰度发布时先让5%的流量走新版本观察异常率再逐步扩量。这套机制看起来简单但能挡住大部分“改挂了全平台”的事故。尤其是企业内部场景一个技能可能被多个流程引用没有版本隔离改一个细节可能引发连锁故障。注意技能上下兼容不是理所当然的。新增一个必填参数、改输出字段名、调整返回数据格式都属于破坏性变更必须升major版本。不要觉得“就改个参数名内部用无所谓”在多平台环境下这种改动一定会坑到你。5. 技能设计的原则与沉淀5.1 小而专优于大而全做技能设计最大的诱惑是想做一个“万能技能”一个技能能查天气、能算汇率、能写周报。我试过结果模型经常把参数张冠李戴明明在问汇率它给技能传了一个城市名。后来我把技能拆小每个只解决一类问题准确率明显回升。技能粒度怎么把握我总结出一个标准如果一个技能输入参数里有超过3个彼此无关的选项就说明它应该拆分了。比如“查询工具”这个技能输入里有城市id、股票代码、订单号这就是三个技能混在一起模型绝对会选错。5.2 描述文本是隐性产品文档很多开发者把技能描述当摆设随便写两句话。但我实测下来描述质量直接影响召回和模型决策质量。我后来给每个技能的description建立了模板该技能可以{核心能力}。当用户需要{场景1}、{场景2}、{场景3}时使用该技能。 输入参数{参数名}表示{含义}常见的取值有{枚举值}。 输出结果包括{字段1}、{字段2}。如果{异常情况}返回{占位符}。这段模板最重要的部分是“触发场景”清单。它不是给代码看的是给模型看的“使用说明书”一定要把用户可能用到的口语化表达写进去。比如“天气”技能不仅写“天气”还要写“下雨、带伞、气温、冷暖、大风”。5.3 评测集要伴随技能一起生长技能上线不是终点而是评测的开始。我在项目里维护了一套黄金评测集初始约50条典型问答覆盖每个技能的常见问题、边界问题和异常输入。每次修改技能描述、升级技能版本、优化路由参数都要先跑一遍这套评测集对比改前改后的调用准确率。评测集必须是动态的。线上用户在IM里问了一个新说法如果Agent答错了我会把这个问题加入评测集作为下一次迭代的回归用例。几个月下来这套评测集已经从50条涨到400条但它带来的稳定性回报非常明显我可以放心地改代码、调Prompt而不担心改坏某个隐性场景。最后分享一点体会项目做完回头看最大的心得是Agent Skills真正解决的不是“模型会不会调用工具”而是“能力如何被组织、被管理、被复用”。它把分散在各种平台和脚本里的功能变成了有名字、有描述、有版本、可发现、可组合的标准单元。这个思路放在任何规模的项目里都成立即使你暂时只接一个平台也值得用技能化的方式组织代码。对我来说这套项目的价值不只是跑通了几个场景而是把一套可复用的方法论沉淀了下来后续再接新平台工作量确实从“周”降到了“天”。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价