☰
从LLM到Agent Skill:让模型从“会说话”到“会做事”的能力封装
2026/10/10 20:56:48 网站建设 项目流程

先聊个现象:很多团队用大模型做了不少Demo,聊天、问答、摘要都跑得通,但一到真实业务里就卡住了——模型什么都会说,但什么都不会做。它能告诉你应该查一下库存,但它不会真的去查;它能给出一个很“合理”的退款方案,但它不会真的把退款流程给执行了。

这就是 LLM 和 Agent 之间最核心的差距。LLM 是个聪明的“大脑”,但它没有“手”。而 Agent 要做的事情,恰恰是把这颗大脑接到真实的操作流程上,让它能调用工具、执行步骤、处理反馈、纠错重试。这一层能力拆到最后,就是今天想聊的主题——Agent Skill。

简单来讲,Agent Skill 就是把“让模型完成一件具体专业事”的方法封装成一个可复用的技能包。它不是一段 Prompt,也不是一个简单的函数接口,而是把指令、工具调用、参数校验、异常处理这些内容按标准结构打包在一起。这篇文章不打算讲某个特定框架的用法,而是想把我自己从模型调用走向 Agent 落地这条路上,关于底层逻辑的理解和实操经验梳理出来。适合正在做 AI 应用落地、被“模型很聪明但流程跑不通”困扰的开发者和产品技术人员参考。

1. 为什么转换思路:从 LLM 到 Agent 的必然演进

1.1 LLM 模型本身到底解决了什么,没解决什么

先把 LLM 的能力边界说清楚。大语言模型本质上是一个基于海量文本训练出来的概率推理引擎,它擅长的是“根据上下文预测最合适的下一个 Token”。因此,它天然擅长的事情包括:语义理解、总结归纳、代码生成、逻辑推理、知识问答。

但落地业务时,你很快会发现它有四个明显的短板。

第一,模型只能“说”不能“做”。它能生成一段调用接口的代码,但它自己不会真的发起网络请求。第二,模型的知识有截止时间,它不知道实时天气、当前库存、最新订单状态,这些信息必须从外部系统获取。第三,模型的上下文窗口再大也是有限的,塞入长文档、多次工具调用结果之后,很容易出现信息丢失或注意力漂移。第四,模型本身的输出存在不确定性,同一个输入可能返回不同结果,这在严谨的业务流程里是致命的。

举一个生活化的类比。LLM 就像一个特别懂行的顾问,坐在你面前,你问什么他都能答得很好,但他没有你公司的系统权限,也不负责实际执行。你让他“把这个客户的订单状态更新一下”,他能告诉你应该调哪个接口、用什么参数,但他不会动手。Agent 就是给这个顾问配上了“手”——让他能打开系统、读取数据、执行操作,并且在整个过程中自己判断下一步该做什么。

1.2 Agent 的核心循环:感知、决策、行动、反馈

真正理解 Agent,建议先忘掉那些花哨的概念,抓住一个最朴素的循环:感知 - 决策 - 行动 - 反馈。

感知是模型获取当前状态;决策是模型决定接下来做什么;行动是调用某个工具或接口去执行;反馈是看到执行结果后判断是否达成目标,然后决定继续、调整还是收尾。这个循环不断重复,直到任务完成,或者达到终止条件。

传统程序是我写死逻辑,你按步骤走;Agent 则是模型在运行时根据实际情况动态决定下一步怎么走。比如一个“自动整理报销单”的 Agent,它先感知到用户上传了一张发票图片,决策是调用 OCR 工具提取信息,看到提取结果之后再决策是否调用财务系统校验发票真伪,如果校验不通过则决策是让用户补充材料还是标记异常。每一步都是模型决定的,而不是开发者预先写死的 if-else。

这个循环带来一个非常重要的认知转变:Agent 的任务不是“执行路径”,而是“目标达成”。围绕目标去选择路径、动用工具、处理异常。这种设计让系统能够应对复杂多变的真实场景,但也正因为路径是不确定的,所以必须在这条链路上加一些确定性的约束——这就是 Skill 出现的直接原因。

1.3 Skill 在 Agent 体系里到底解决了什么痛点

一开始做 Agent 的时候,我犯过一个很常见的错:把所有指令和工具一股脑塞进一个超长 Prompt,让模型自己拼。结果就是上下文很快被撑爆、模型经常在两个工具之间犹豫不决、每次调试都要从头看一遍几百行的 Prompt,完全没法维护。

后来我意识到,问题不是模型不够聪明,而是我给模型的东西太“散”了。一个专业任务应该包含三类信息:什么时候做、怎么做、做完怎么验证。这三类信息如果没有结构化,模型就只能在混沌中猜。Skill 做的事情,就是把这些信息打包成一个边界清晰的“能力单元”。

一句话概括:Skill 是把“让模型完成一件可复用的专业事”所需的所有设计,按标准结构固化下来的产物。它既不是单纯的函数,也不是单纯的指令,而是介于“模型意图识别”和“底层工具执行”之间的一层能力封装。

它的价值主要体现在三个方面:一是复用,同一套能力可以在不同 Agent 中重复装配;二是隔离,一个 Skill 内部怎么实现不影响其他部分;三是可控,每一个 Skill 的触发条件、执行逻辑、失败处理都能被单独验证和测试。

2. Skill 到底是什么:与 Tool、Prompt、Function Call 的关系

2.1 先厘清几个特别容易混淆的概念

聊 Skill 之前,先把几个高频词汇摆在一起对比:Tool、Function Call、Prompt、Workflow、Agent Skill。它们经常被混着说,但实际是不同层面的东西。

概念本质回答的问题
LLM推理内核如何理解人与环境的状态
Prompt指令文本如何引导模型输出
Tool / Function具体执行能力如何完成一个动作
Function Call模型与工具间的接口协议模型决定去调哪个动作
Workflow固定流程编排多个动作的线性或分支组合
Agent Skill面向目标的能力单元什么场景下、以什么方式、达成什么目标

Tool 通常是一个外部函数的封装,比如“获取天气 API 的函数”“提交订单的接口”。Function Call 是让模型根据对话内容决定是否调用这些封装好的函数,并且把参数从自然语言中抽取出来。它是一个连接层,让模型能够使用工具。

Workflow 则是把多个步骤按预定顺序串起来,比如“先查库存,再计算价格,再生成订单”。Workflow 的好处是确定性强,每一步都明确;缺点是它没有办法应对计划之外的场景。一旦输入不符合预期,流程就断了。

Skill 恰好站在它们中间:它比 Function Call 更高层,因为它包含了任务判断、指令组织、参数抽取、结果验证等完整逻辑;它又比 Workflow 更灵活,因为 Skill 内部的路径可以由模型自行决策,并不要求每一步都走死。最直白的理解方式:Tool 是“手”,Skill 是“一套完整的操作方法”。

2.2 Skill 的典型构成五件套

一个标准、可复用的 Skill,我习惯把它拆成五个组成部分。

第一个是身份声明,包括名称和描述。这一段看似简单,实际直接决定模型能不能正确找到并触发这个 Skill。描述写得好,模型一眼就知道在什么场景下该用它;描述写得泛,模型就容易在该用的时候不用、不该用的时候乱用。

第二个是触发逻辑,设定这个 Skill 在什么条件下应该被唤起。可以是根据用户意图自动触发,也可以是显式指定的调用,还可以是某个状态条件下的自动执行。复杂的 Skill 还可以定义“当 A 条件满足时接管,当 B 条件出现时交还控制权”。

第三个是指令模板。这是给模型看的“操作说明”,内容通常包括:当前任务的背景、输入参数说明、处理步骤建议、输出格式要求、边界条件提醒。这里特别需要注意的是,指令模板不是让模型自由发挥的散文,而是把它约束在一个专业操作者的角色里。

第四个是可执行逻辑。真正动到外部系统的代码在这一层,比如调用 API、读写数据库、执行计算。这部分是确定性的,不应该依赖模型随机输出。

第五个是校验与反馈。执行完之后要检查结果是否合理,不合理的话是重试、换一种方式处理,还是直接宣告失败,这个兜底逻辑必须提前设计好。

这五部分缺一个都容易出现意想不到的问题。只给身份声明不写触发逻辑,模型会在一堆其他候选技能里迷路;写了指令没有校验,执行完得到错误数据,模型自己完全不知道。

2.3 从模型视角理解 Skill 的识别与触发

很多人容易忽略的是:模型本身并不“知道”自己有哪些 Skill。在 Agent 运行时中,所有可用 Skill 的描述会以一种结构化的方式进入模型的可视范围,模型要做的事情,是根据当前上下文从这些描述中选出最合适的一个或多个。

这个底层机制理解起来不复杂。你可以把它想象成在给一个新手同事一份“能力手册”,手册里都是简述性的条目:“这件事我能做”“那件事我能做”。真正遇到问题的时候,这个同事通过看手册找到对应条目,再打开条目背后的详细规程去执行。所以 Skill 的描述质量,直接决定了模型路由的准确率。

我自己在实践中有两个经验。第一个:描述里面一定要写清楚“什么时候不该用”。很多技能误触发,不是因为描述不够,而是因为边界没有写明。第二个:不要追求一句完美的描述,而是要通过测试不断调整。Skill 的路由效果,是要靠大量真实输入去检验的,单靠感觉写出来的描述,往往在第一次实际使用时就露馅。

3. Agent Skill 的底层逻辑与设计原则

3.1 技能粒度:太粗了失控,太细了爆炸

设计 Skill 时最需要拿捏的就是粒度。什么是好的粒度?我认为是“一个专职的人能独立负责的完整任务”。粒度过粗,比如把“处理所有业务”做成一个 Skill,里面的判断逻辑极其复杂,描述怎么写都覆盖不全所有场景,模型很容易在分支面前茫然。粒度过细,比如把“获取用户姓名”“获取用户手机号”“获取用户地址”各自做成独立 Skill,技能数量会迅速膨胀,模型的每一次路由都变成一场选择题考试,成本高不说,准确率还低。

我的经验是,粒度应该按照“事件边界”而不是“动作级别”来划分。比如“处理退货申请”是一个合理的 Skill,因为它包含查询订单、校验资格、计算退款金额、提交审批等多个动作,但目标只有一个。反过来,“调用退款接口”就不应该单独成为一个 Skill,因为它本身没有目标,只是整个退货流程里的一个环节。

过度拆分还有一个隐患:上下文开销。Agent 每轮决策都需要把候选 Skill 的描述注入上下文,技能数量越多,描述占用的 Token 就越多。等到候选技能多到几百个,光让模型选择技能这件事就会变得又贵又慢。

3.2 上下文传递:黑盒与白盒的平衡

Skill 在执行过程中会读入一些信息、产出一份结果。这中间就涉及上下文管理:Skill 内部的信息应该多大程度上暴露给 Agent 主流程,外部信息又应该多大程度地传入技能内部。

我见过两种极端做法。一种是把所有对话历史、所有工具结果全部塞给 Skill,让它自己慢慢处理。这样做的好处是不会缺信息,坏处是上下文很容易爆炸,而且大量无关信息反而会干扰模型的判断。另一种是把 Skill 完全黑盒化,只保留入口参数和出口结果。这样做隔离性很好,但某些情况下手里的信息根本不够用。

比较好的做法是按需注入 + 出口收敛。给 Skill 配置一个明确的输入接口:它需要哪几个字段、哪些外部系统可以访问、哪些历史记录可以读取,都在声明里锁定。执行完之后,它也只需要向主流程返回一个结构化的结果,而不是流式的 Token。

这个设计逻辑在一定程度上跟函数式编程很像:外部不关心函数内部怎么实现,只关心入参和返回值。Skill 的“参数传递”和“返回值定义”做得越干净,整个 Agent 系统就越容易维护。

3.3 可观测性与失败传播控制

Skill 内部是模型在驱动,模型就有概率犯错,这是无法百分之百消除的事实。所以我在搭建 Agent 系统时,一直把“可观测”和“失败控制”放在和“功能实现”同等重要的位置。

先说可观测性。每个 Skill 被触发时,至少要记录:为什么触发、输入参数是什么、模型生成了什么中间步骤、每一步消耗了多少 Token、最终结果是什么。这套日志不是为了事后给人看的,而是在开发调试阶段,唯一能帮助你判断“模型为什么会走错路”的依据。没有日志,遇到问题就是两眼一抹黑。

再说失败控制。Skill 内部通常需要加三层兜底。第一层是结果校验,比如工具返回为空,模型却生成了一段看起来很合理的内容,这里必须拦一下。第二层是重试机制,对于网络抖动、临时超时这一类问题,直接重试一两次往往就解决了。第三层是降级策略,当重试仍然失败时,是切换备用方案,还是把控制权还给主 Agent 说明失败原因,这个决策要提前想清楚。

还有一点是关于权限安全的,从设计上就应该让 Skill 遵循最小权限原则。一个查询库存的技能,不应该拥有修改订单的权限;一个负责生成文案的技能,不应该被允许调用删除接口。模型如果被提示词注入或误导,至少它手里能执行的动作范围是已经被锁死的。

4. 从零构建一个 Agent Skill:完整实操

4.1 明确场景与搭建准备

理论讲再多,不如动手写一个。我以“查询订单物流状态”这个 Skill 为例,完整展示构建过程。为什么选这个场景?因为它路径清晰:输入订单号,查数据库或接口,返回状态信息。同时又足够典型,涉及参数抽取、接口调用、结果格式化、异常处理这些常见环节。

环境方面不需要太复杂,我一般用 Python 实现核心逻辑,Agent 运行时层可以选用任何主流的 Agent 框架。目录结构建议这样组织:

agent_project/ skills/ order_tracking/ __init__.py skill.yaml executor.py agent.py

skill.yaml放技能的身份声明和指令模板,executor.py放真正执行外部调用的代码,agent.py是主入口,负责把 Skill 注册到 Agent 的运行时中。

4.2 编写 Skill 描述与指令模板

先看skill.yaml的关键部分。第一个重点是描述字段,这个直接决定模型会不会在需要的时候调用它:

name: order_tracking description: > 查询订单的物流状态和配送进度。适用于用户询问“我的订单到哪了”、“快递什么时候到”、 “帮我查一下物流”等场景。如果用户只是想了解退款流程或修改订单地址,不要使用本技能, 请转交相关技能处理。 parameters: order_id: type: string description: 订单编号,通常是字母和数字组成的字符串 required: true

这里有两个细节值得说一下。第一,描述里明确写出了适用的意图,以及哪些场景不适用。这个“排除描述”非常重要,能显著降低误触发率。第二,参数只定义了一个order_id,因为这是唯一的必需输入。如果让我再加一个可选参数“查询手机号”,我不会加,原因是这个 Skill 的闭环是通过订单号直接查接口,不需要额外验证,多加参数反而增加了模型抽取错误的风险。

再看指令模板。指令不要写成一大段散文,我习惯用结构化的分步说明:

你是物流查询助手。请根据用户提供的订单号,完成以下步骤: 1. 从用户对话中提取订单号,填入参数 order_id; 2. 调用 executor 中的 query_logistics(order_id) 获取物流信息; 3. 如果返回结果中包含异常状态码,如实反馈给用户; 4. 输出格式:当前状态 + 最新位置 + 预计送达时间。 约束:如果订单号格式不正确,先向用户确认,不要编造查询结果。

最后一句约束是我特别加上的。因为模型在没有查到信息时存在“幻觉补偿”倾向,它倾向于编造一个看似合理的快递状态来填补空白。明确的约束是第一步,硬性的结果校验是第二步。

4.3 实现执行逻辑与注册联调

再来看executor.py中的核心执行函数。这一段就是确定性的代码,不允许模型干预:

def query_logistics(order_id: str) -> dict: # 此处对接真实的物流查询接口 url = "https://api.example.com/logistics" params = {"order_id": order_id} resp = requests.get(url, params=params, timeout=5) data = resp.json() if data.get("status") != "ok": return { "ok": False, "reason": data.get("message", "unknown_error"), } latest = data["data"]["tracking"][-1] return { "ok": True, "current_status": data["data"]["status_text"], "latest_location": latest["location"], "estimated_time": data["data"].get("eta", "暂无"), }

这个函数本身很简单,但它体现了 Skill 内部逻辑的一个重要原则:把确定性的调用和不确定性的决策分离。模型可以决定“我需要查物流了”,但“怎么查、查到了怎么解析”这件事完全由代码控制。

最后到agent.py里把 Skill 注册进去:

from skills.order_tracking import order_tracking_skill agent.register_skill(order_tracking_skill)

注册完成之后,测试矩阵至少要覆盖四类情况:正常场景,比如收件人查自己订单的物流;缺参场景,用户只说了“查快递”但没给订单号,看模型会不会主动追问;异常场景,接口返回不存在或超时;边界场景,用户问的是退货进度,看模型会不会误调物流查询。

我第一次测试的时候,“查快递”这个模糊表达出现了两个问题:模型有时候追问订单号,有时候直接编一个单号去查接口。后来在指令模板里加了一句“如果没有明确的订单号,一律先向用户索要”,问题才解决。这些细节不经过真实验证,光靠设计是想不全的。

5. 实操中的常见问题与排查技巧

5.1 技能没有被触发,或者被误触发

这是所有问题里出现频率最高的。当一个实际场景发生,Agent 却没有调用应该用的 Skill,原因一般出在三个地方。

第一个原因是描述覆盖不足。模型没有从候选技能里选它,说明它没看出来该用这个技能。解决方法是把真实用户的表述采集下来,分析语义,反推描述里缺了哪些触发词和意图表达,然后补充进去。

第二个原因是描述边界不清。Skill A 和 Skill B 都写了“处理订单相关问题”,模型一看都符合,就只能凭感觉乱选。这种情况必须在描述中增加明确的差分条件,比如“仅当涉及物流信息时使用本技能”“仅当涉及退款时使用此技能”。

第三个原因是候选技能过多,导致注意力分散。当注册的技能超过一定数量,模型的选择准确率会明显下降。这个问题的解法不是去优化语言,而是换成更稳定的路由方案,比如先通过分类模型对用户意图做粗粒度分类,再路由到少量技能上。

5.2 工具返回为空,模型还在编造结果

这是让我印象最深的坑。有一次 Agent 调用订单接口返回了一个空列表,按理说应该告诉用户“没有查到信息”,但模型直接给出了一条“您的包裹正在派送中,预计今天送达”。

问题本质是模型没有依据,又倾向于提供一个和任务相关的回答。这时候单靠指令约束不够,必须要在执行逻辑上做硬校验。我后来的做法是:在executor里判断,如果返回结果为空,直接设置ok: False,并返回一个专用标记。然后在指令模板中约定:当看到ok: False时,如实反馈“暂未查询到物流信息”,不得补充任何额外细节。

这种“模型说人话、代码守底线”的组合,算是解决幻觉类问题最有效的实践路径。关键点是不要让模型有机会“自由发挥”在校验失败之后补全回答。

5.3 上下文开销越来越大

随着 Skill 越做越复杂,每次请求都给模型注入越来越多的指令文本,系统的 Token 消耗肉眼可见地往上涨。尤其是当候选技能多到几十个的时候,光描述就有好几千 Token。

我的经验是一个字:省。能不写进描述的绝不写,能短说的不长说。描述本身就是给模型看的索引,不是详细文档。另外一个技巧是按需加载:如果当前对话风格是“先确定意图,再调用能力”,那就先只注入一个精简的技能列表让模型做粗选,粗选命中了某个具体技能,再把这个技能的具体指令动态注入上下文。这样大部分请求都只需要一小段技能索引,成本明显更低。

5.4 权限边界与安全兜底

Skill 一旦开放出去,实际上是把原本属于系统操作者的部分控制权交给了模型。权限越大,风险越大。我在生产环境里会强制给每个技能设置三层约束。

第一层是参数白名单校验。模型抽取出来的参数,必须通过格式校验才能进入执行层;校验不通过直接拒绝,不让脏数据进到真实接口。第二层是目标白名单。一个技能只能调用自己声明过的外部系统,代码层面做限制,而不是靠提示词层面试图“嘱咐”模型不要用什么。第三层是审批闸门。某些高影响操作,比如退款、删除、发送消息,执行之前要求走一次人工确认流程,模型可以发起动作,但不能独立完成。

这三层思路是底线设计。Agent 应用越往核心业务走,越不能指望模型的判断力去保安全,只能在架构上把风险出口提前堵住。

6. 从 Skill 到能力生态:更上层的演进方向

6.1 把 Skill 当作资产来管理

单个 Skill 解决了具体问题,但当 Skill 数量增长到几十个、上百个时,就需要把它当成一等的资产来管理。意思是说,它需要版本号、负责人、更新记录、接口契约、评测标准。我见过乱掉的场景:老版本技能被更新后语义变化,多个 Agent 还在悄悄使用旧版接口,最后数据格式直接错乱。

所以哪怕团队只有两三个人,也建议从第一天就用简单的规范:每个 Skill 一个目录、一个描述文件、一个测试套件。目录结构上的整洁度,长远来看直接影响的是开发迭代的速度。

6.2 多 Agent 协作与技能编排

当业务变得更复杂,一个 Agent 带着一堆 Skill 也会碰到天花板。原因很简单:上下文容纳不下那么多技能的详细说明,单线程决策的延迟和出错率都会随着技能增多而上升。

一种常见的演进路径是主 Agent + 多个子 Agent。主 Agent 只负责理解用户需求、拆解任务,然后把子任务分发给不同的子 Agent。每个子 Agent 只携带自己领域内的少数几个 Skill。这样每个 Agent 的上下文压力都小,决策质量也更稳定。

这种模式下的关键技术点,是主 Agent 与子 Agent 之间的“任务交接协议”。子 Agent 完成任务之后返回的结果必须结构化,并且附带置信度说明;主 Agent 根据这些结果决定是要继续追问、直接答复,还是调用另一个子 Agent。这个协议就是 Skill 设计里“结果校验”思想在多 Agent 场景下的延伸。

6.3 对未来的一个务实判断

从 LLM 到 Agent Skill,本质上是一层一层的解耦:模型负责语义理解和任务规划,Skill 负责专业能力和边界约束,工具负责具体执行。现在的主流做法还是以个人实践为主,很多抽象和标准都在快速演进之中,今天的设计方式可能过半年就有更优解。

但有一点我觉得不会变:只要 Agent 还要走到真实业务里去干“具体的事”,那么“能力封装成技能、技能注册到运行时、运行时按意图路由、代码守住底线”这个链路就是绕不开的底子。把这个底子打扎实了,再多变化也能接得住。

我个人现在的实践习惯是:每做出一个新的 Agent 功能,都会多问自己一句——“如果这个能力以后还要在别的 Agent 里复用,它现在被封装好了吗?边界写清楚了吗?离开我会不会散架?”这个问题的答案,往往就是 Skill 设计做到位没有的直接衡量。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询