news 2026/10/21 4:01:36

大模型安全调用业务技能:agent-skills技能层设计与实践

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
大模型安全调用业务技能:agent-skills技能层设计与实践

去年下半年,我把大部分开发时间都投进了一个叫 agent-skills 的代码库。名字起得很简单,做的事情却不小:让大模型不再停留在“会聊天”,而是能安全、稳定地调用一堆真实业务技能完成任务。无论订单查询、退款计算还是排班调整,每个能力都被封装成独立的技能模块,智能体在回答问题时按需选择、调用并执行。这篇文章就是把我在这个项目里踩过的坑、沉淀下来的设计和实践心得完整梳理一遍。如果你正在做 AI 应用或智能体开发,或者正准备在业务系统上接入大模型能力,这篇内容应该能给你一套可以参照的落地路径。

1. 项目起源:给智能体“装手”之前,我踩过哪些坑

1.1 第一版教训:API 调用写对了,业务却总是答非所问

最早我们团队接了一个售后客服场景:用户问订单什么时候到、退款到没到账、发票怎么开。当时的做法非常朴素,把全部 API 说明塞进系统提示词,让模型自己判断该调哪个接口。结果上线第一周就出了不少问题。

最常见的翻车场景是参数混淆。比如用户说“我要查一下上个星期的退货单”,模型能识别出需要调用查询接口,却把order_id和return_id搞混,查出来一个不存在的记录,最后回复用户“未查询到相关信息”。更气人的是,这种错误不是偶发,同一个问题换几个说法就可能触发一次。原因在于提示词里的 API 描述只是“给模型看的一段文本”,它没有强约束,也没有反馈闭环。模型本质上是在靠着模糊语义猜测接口用法,猜错了它自己也不知道。

还有一类问题是模型过于自信地补全参数。用户只提供了手机号,模型却“贴心”地编造了一个订单号填进参数里,接口调用失败后又换一个编号继续试。这些错误在日志里看起来都很零散,但统计下来非常可观——我们当时粗算了一下,大约有 30% 的 API 调用错误属于参数错误或选错接口。

1.2 agent-skills 的定位:在模型和 API 之间加一层可治理的机制

踩了这些坑之后,我意识到问题不是模型能力不够,而是我们缺了一层“技能管理层”。这层东西要能做到三件事:

  • 把每个 API 封装成一个有完整契约的技能,包括名称、功能描述、输入输出格式、出错时的标准化反馈,而不是把接口说明扔给模型去自由发挥。
  • 在调用前后加校验和兜底逻辑,参数不齐就补、格式不对就拦、调用失败就换方案,避免模型在错误路径上一次又一次地空转。
  • 让所有技能可测试、可灰度、可回滚,不能像改提示词那样靠“感觉”上线。

agent-skills 这个名字里的“skills”指的就是这一层能力单元。每个技能不是一个简单的函数封装,而是一个包含文档定义、输入 Schema、执行函数、测试用例和安全策略的四位一体模块。它解决的核心问题是:当模型和真实系统对话时,怎样保证这个过程可控、可观测、可纠错。

2. 技能模块的核心设计:从原生函数到可被模型理解的描述

2.1 一份技能清单应该包含哪些字段

我见过很多初版设计,技能就是一个函数名加一句注释,然后靠提示词把函数列表扔给模型。前期没问题,技能一多就乱了。我们后来把每个技能的数据结构固定成下面这样:

@dataclass class SkillSpec: name: str # 技能唯一标识,如 "query_order" description: str # 给模型看的用法说明 input_schema: dict # JSON Schema,描述每个参数 handler: Callable # 实际执行业务逻辑的函数 version: str = "1.0.0" # 技能版本 namespace: str = "common" # 所属领域/命名空间 tags: list[str] = [] # 检索标签 examples: list[dict] = [] # 典型调用示例 security: dict = {} # 权限、限流、审计配置

这里最容易被忽略的是description的写法。很多人写“查询订单信息”,这种描述是给程序员看的,模型看了并不清楚“什么时候该用”。后来我们总结出一个写法原则:描述要写“在什么情况下用这个技能”,而不是“这个技能是什么”。对比一下:

  • 差:查询订单信息。
  • 好:当用户询问订单状态、物流进度、发货时间、预计送达时间等与订单履约进度相关的问题时使用。若用户提供订单号则直接传入 order_id,否则用手机号或用户 ID 查询。

这种描述把触发场景、参数来源和边界都讲清楚了,模型选技能的准确率明显提升。

examples字段也很重要。模型对抽象描述的理解不稳定,但给它一两个具体的输入输出例子,它就能很快对齐。我们的每个技能至少配两组示例,一组是标准调用,一组是边界情况(比如查不到记录时怎么返回)。

2.2 输入参数 Schema:决定模型会不会用错的菜谱

输入 Schema 是技能契约里最关键的一环。我们使用 JSON Schema 定义,因为主流模型厂商的工具调用格式基本都兼容它。一个典型的订单查询技能长这样:

{ "name": "query_order", "description": "当用户咨询订单状态、物流进度、发货/送达时间时使用", "parameters": { "type": "object", "properties": { "order_id": { "type": "string", "description": "订单号,格式通常为 16 位数字。若用户只提供了手机号,该字段可以不传" }, "mobile": { "type": "string", "description": "用户下单手机号,用于通过手机号反查订单,格式为 11 位手机号" }, "page": { "type": "integer", "description": "页码,默认 1", "minimum": 1 } }, "required": [], "oneOf": [ {"required": ["order_id"]}, {"required": ["mobile"]} ] } }

这里用oneOf约束“订单号和手机号至少传一个”,是一个很实用的技巧。模型有时候会贪多,两个参数都编一个出来,加上这种约束后它能明白“只需满足其中一条路径”。

写参数描述时,不要用缩写,不要把内部字段名直接暴露给模型。我们曾经有个技能叫getOrderByUID,参数叫u,模型永远猜不出u是什么。改名为query_order、参数改为user_id后,准确率一下子飙升。还有枚举值一定要列全,不列全模型就会开始幻觉出一些不存在的取值。

2.3 结果返回结构的标准化

技能执行后的返回结果同样需要统一。我们定了一套结构化返回格式:

{ "status": "success" | "failed" | "retryable", "data": { ... }, # 业务数据(成功时) "error_code": "ORDER_NOT_FOUND", # 错误码 "user_message": "没有查询到该订单,请确认订单号是否正确" # 可直接回复用户的话 }

user_message是很多人会漏掉的设计。当技能内部已经知道“查不到”时,它可以直接生成一句用户能看懂的话。模型拿到这句话,就不需要再编理由了。retryable状态则告诉模型:这次失败是临时的,你可以换个参数再试,而不是鲁莽地不断重试。这套结构让下游的 Agent 行为可预测了很多。

3. 运行时调度与异常恢复:模型说错话时,系统怎么兜底

3.1 function calling 链路中常见的三类失败

技能定义好了,真正的挑战在运行时。模型调用工具这个过程,看起来是一个标准的 function calling,但实测中最常翻车的是三类:

第一类是参数幻觉。模型会生成一个看起来合理但实际不存在的参数值,比如用户说“查一下我上个月的订单”,模型直接编一个order_id: "202502001",但这个号码根本没存在过。第二类是参数缺漏。模型识别出要调用某个技能,却漏掉了必填参数,导致下游接口 400。第三类是死循环重试。技能抛错后,模型不换策略,而是用同样的参数再调一遍,连续失败四五次后才放弃。

这三类问题不是靠调提示词能根治的,必须在技能调用层加硬校验。

3.2 参数校验与自动修复

我们实现了一个前置校验器,核心逻辑是:模型生成工具调用参数后,先不直接执行,而是先跑一遍 JSON Schema 校验。如果校验失败,系统做两件事。

首先尝试自动修复:根据缺失参数的名称,从全局上下文里找候选值。比如模型漏了order_id,但对话历史里用户提到过“订单号 2025020112345678”,那我们就把它补进去。这种“上下文补参”的成功率不错,比让模型自己发现缺参再补要快得多。

如果无法自动修复,就把校验错误返回给模型,并附上明确的补充指引。我们的返回消息大概是:

调用技能 query_order 时缺少参数 mobile。用户最近一条消息中的手机号是 13800138000,是否使用该值?请重新生成完整的调用参数。

这里有一个关键点:返回给模型的错误信息必须指出“缺了什么、应该怎么补”,而不是简单抛一个validation error。模型对模糊报错的处理能力很差。

3.3 三级降级链路:深度技能、轻量技能、固定话术

即使参数校验通过了,技能执行时仍可能失败,比如下游系统超时、数据源返回异常。我们设计了三级降级策略,用一张决策表来管理:

级别触发条件处理方式示例
L1 深度技能主技能调用成功返回结构化数据给模型调订单中心查询订单详情
L2 轻量技能主技能失败但存在替代数据源调用简化版接口,只返回关键字段查缓存/只读库,仅返回状态和日志时间
L3 固定兜底连续失败或数据不一致返回确定性话术,停止工具调用“受系统影响暂时无法查询,请稍后重试”

降级不是无限尝试的代名词。我们在技能调用层加了一个熔断器:同一个技能在 30 秒内连续失败 3 次,就自动切换为 L3;在 5 分钟内错误率达到 50%,则直接停止该技能的自动调用,避免模型在故障系统上反复空转浪费时间和 token。

这套机制上线后效果立竿见影。之前那种“模型在同一接口上死磕五次”的情况基本消失了,平均单轮对话的工具调用次数从 2.7 次降到了 1.4 次,响应时间也快了很多。

4. 技能仓库的组织方式:权限、版本、冲突与冷热分层

4.1 命名空间划分:别把技能全部平铺在一起

技能数量到 30 个以上的时候,模型在选择技能时已经会出现“选择困难”。把所有技能平铺在一个大列表里,不仅增加模型的理解负担,还容易让它在语义相近的技能之间犹豫不决。

我们最终按业务域划分命名空间,每个命名空间是一个独立子模块:

agent-skills/ ├── order/ # 订单域 │ ├── query_order.py │ ├── cancel_order.py │ └── change_address.py ├── payment/ # 支付域 │ ├── query_refund.py │ └── apply_refund.py ├── aftersales/ # 售后域 │ ├── create_return.py │ └── track_return.py └── common/ # 通用域 ├── transfer_human.py └── faq_search.py

这听起来像最简单的代码组织,但在 Agent 场景里有额外意义:模型在收到用户请求后,会先经过一层“意图路由”,预测它属于哪个命名空间,然后只把该命名空间下的技能列表注入提示词。这样上下文窗口里技能数量少、语义相近、模型选择压力小。

4.2 版本管理与灰度:改一个技能不拖垮整个对话

技能本质上和一段线上代码没有区别,改坏了会影响真实用户体验。最初我们很随意,直接改代码上线,结果某个同事把“查询订单”的技能改成了默认只返回最近一单,导致用户查历史订单时永远返回错误结果。好在那次只是测试环境。

之后我们建立了技能版本管理机制。每个技能可以同时存在多个版本,线上流量按比例分配:

版本功能变化灰度比例回滚策略
v1.0.0基线版本20%全量回退
v1.1.0新增物流轨迹字段30%失败率超过 8% 自动回退
v1.2.0优化描述文案50%参数解析错误率超过 5% 自动回退

灰度期间我们重点看两个指标:技能调用成功率和用户侧满意度。一旦发现某项指标下降,就把流量切回稳定版本,整个操作改配置即可,不需要重新发布代码。

4.3 冷热技能分离:技能太多时先检索再注入

当技能总数超过 60 个以后,光靠命名空间还是不够。十几个技能虽然不多,但如果每个都带长长的 JSON Schema,上下文窗口会被迅速吃掉。我们的做法是把技能分为冷热两档,热技能(最近 7 天调用次数高的)直接随提示词注入,冷技能则通过一个检索层按需加载。

检索层的实现不算复杂:把每个技能的name + description + tags + examples拼成一段文本,做 embedding,然后和用户当前问题计算相似度,召回 top-5 候选技能。同时保留一组规则映射(比如用户消息里包含“退货”就强制召回create_return)作为兜底。这套“向量召回 + 规则强制”双通道,保证了技能数量再大,单次对话真正暴露给模型的最多也就十来个。

5. 真实效果复盘:用数据说话

5.1 离线评测集:让每个技能改动有据可依

技能层面的改动如果没有评测集,回归成本非常高。我们做了一个固定的离线评测集,包含 100 条典型用户问题和 50 条边界问题,覆盖订单查询、退款申请、发票、地址修改等主要场景。每跑一次版本,都把这些问题重新过一遍,然后记录三个指标:

  • 技能选择准确率:模型是否选中了正确的技能。
  • 参数准确率:生成参数是否能被 Schema 校验通过。
  • 任务完成率:最终是否给出了用户可以接受的答复。

下表是我们某一次从“纯提示词方案”切换到 agent-skills 框架后的对比数据,虽然不是业界标准,但能看出量级差异:

指标纯提示词方案agent-skills 方案
技能选择准确率71%92%
参数准确率64%89%
任务完成率58%84%
平均工具调用次数/轮2.71.4
单轮平均耗时3.2s2.1s

这里要说明,这套评测集的问题难度是固定的,数字只能反映我们业务场景的相对变化,不能外推成通用标准。但趋势是确定的:当模型不再“自由猜测接口”后,准确率的提升非常直接。

5.2 两个印象最深的调试案例

第一件是金额精度问题。某个退款相关技能返回浮点数金额,模型在回复用户时偶尔会写成 9.999999 这类值,虽然罕见,但发生在退款金额上会出大事。查了半天发现是技能内部把以分为单位的整数转成了 float 再返回,丢失了精度。修复很简单,全部统一成“以分为单位的整数”作为传输格式,再由前端做格式化。这个教训让我们规定:涉及金额的技能一律用整数分存储和传输,不允许出现浮点数。

第二件是技能误匹配问题。用户说“我不想用这个订单了,帮我取消”,我们的意图路由把“取消”匹配到了create_return(创建退货单),导致系统问用户要退货原因,用户很恼火。后来我们在两个技能的 description 里明确做了“区分提示”,并且在examples中加入相似问法的反例,这类误判率才降下来。给技能写清楚“和哪些技能容易混淆、边界在哪”,比单纯把描述写长更有用。

5.3 线上日志里的意外收获

接入 agent-skills 后,我们发现一个很意外的收益:排查问题变得容易多了。以前模型直接调用 API,出错后只有一行请求日志,很难定位是模型理解错了还是接口参数错了。现在每个技能调用都有完整的“意图→候选技能→选中技能→参数校验→执行→结果返回”链路日志,复盘一个问题只需要几分钟。这也是技能化设计带来的隐性价值——可观测性天然提升了。

6. 后续规划与给同行的一些建议

6.1 正在探索:让技能自己“长出来”

agent-skills 目前还是“人写技能、模型调用”的模式,我在想的是能不能让流程更加自动化。比如给定一个新的 API 文档,系统自动生成技能描述、输入 Schema 和基础测试用例,再通过离线评测来自动验证。这个方向我在一个小范围实验里跑通了:把某新接口的 OpenAPI 规范喂给一个专用生成流程,产出的技能描述质量已经接近人工写的 80%,后期只需要调整边界描述。

另一个方向是技能的评估自动化。每个技能升级后,自动用线上样本生成变体问题(改写法、换数字、加口语词),然后跑一遍评测,和旧版本对比。这套流程如果打通,技能迭代就能像 CI/CD 一样发布。

6.2 什么情况下别急着上技能框架

一个很常见的问题:是不是所有 Agent 项目都需要一套 agent-skills 这样重的技能框架?我的答案是看规模。如果整个应用只需要调用三五个核心接口,且这些接口非常稳定、参数简单,那你直接在提示词里写清楚也够用,强行上一套框架反而增加维护成本。

建议的触发条件是:一个智能体需要管理超过十个业务技能,或者接口参数经常变、出错影响大,或者你希望在多个项目里复用技能能力。满足任何一条,就值得拆出独立的技能层。另外还有一个心理上的判断标准——如果你已经出现过“这次改动不知道会不会影响上一个场景”的担忧,那说明技能之间已经出现耦合,该上框架了。

我在整理这些内容时,翻到了自己最初给 agent-skills 记录的 TODO,第一条写着“把技能定义做规范”。现在回头看,这一步确实是最关键的。如果你也正在搭自己的智能体,我建议你不要急着写调用代码,先花一个下午把技能接口、返回结构和测试样例定下来。这个习惯帮我省掉了无数次的返工和线上翻车。实际上,后面我们新同学上手项目时,首先看的也是技能定义文档,而不是业务代码——技能层已经成为了整个系统最稳定的锚点。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/21 4:01:36

STM32 Boost 数控电源实战:从硬件拓扑到 PID 闭环与可编程输出

简介:这份资源面向嵌入式开发学习者与电源设计爱好者,提供一套基于STM32的Boost数控电源完整工程,帮助理解升压变换器与数字闭环控制的实际落地方式。压缩包共510个文件,约32.82MB,以96个h头文件、70个c源文件为核心&a…

作者头像 李华
网站建设 2026/10/21 4:01:36

跨境人力资源服务如何用AI破局:从合规到薪酬的落地实践

做跨境人力资源服务这些年,我最大的感受是:出海企业很少在业务规划上出问题,反而是在“人”这件事上反复踩坑。试用期怎么设才不违反当地劳动法,13薪到底算不算固定成本,个税社保按什么基数缴,员工手册直接…

作者头像 李华
网站建设 2026/10/21 3:59:29

智能驾驶决策规划十年:从规则脚本到端到端融合

如果让我用一个词总结智能驾驶决策规划这十年,我会选“溶解”。2015年的时候,决策规划还是一个边界清晰的专业方向,做决策的是状态机,做规划的是采样和优化,接口定义得很明白,分工也很传统。但到了2025年&a…

作者头像 李华
网站建设 2026/10/21 3:59:20

如何5分钟用Transformers加载Ling-3.0-flash-base-30T:快速开始完全教程

大模型基础模型预训练 【免费下载链接】Ling-3.0-flash-base-30T 项目地址: https://ai.gitcode.com/hf_mirrors/inclusionAI/Ling-3.0-flash-base-30T 点击查看 免费下载 想用 Transformers 加载 Ling-3.0-flash-base-30T?本教程带你 5 分钟完成。Ling…

作者头像 李华
网站建设 2026/10/21 3:59:00

AI编程助手如何实现自主循环?Claude Code插件Ralph Wiggum实践解析

最近半年我一直在折腾各种 AI 编程工具,日常大量重复性的“搬砖活”大多交给了 Claude Code 这类终端里的 AI 编程助手。但用得越多,越发现一个问题:它更像一个“有问必答”的顾问,而不是一个能“自己待着干一晚上活”的执行者。大…

作者头像 李华
网站建设 2026/10/21 3:58:46

虚拟机密码恢复全攻略:从Linux单用户模式到Windows SAM文件实战

虚拟机忘记密码这种事,做过运维或者搞过实验环境的人多少都遇到过几次。尤其是手头一堆测试用的虚机,隔几个月再开机,密码早不知道扔哪去了。网上搜“虚拟机密码破解”,出来的教程五花八门,但大部分只告诉你按几个键、…

作者头像 李华