微信扫码
添加专属顾问
我要投稿
深入解析大模型Agent Skill的底层实现机制,揭秘LLM HTTP交互流中的技术细节。核心内容: 1. Skill功能的本质:应用层抽象与协议层原语组合 2. 从Skill描述到系统提示的动态注入过程 3. 多轮工具调用循环的完整执行流程
前几天我在司内论坛的一个问答中提到了要写一个拉取网络文章文本的 skill,昨天我实现了之后也就开始好奇起 Skills 的底层原理了——相比起各种花里胡哨的 agrnt 应用,我一直以来都很对大模型在纯粹的 HTTP 交互层面是如何交互的更感兴趣。
我们知道,大模型只会对话,聊天,所谓的工具调用也只不过是特化的聊天功能而已。但是各种天花乱坠的 skills,是如何在底层的 HTTP 中,与大模型——这个只会嘴遁的工具交互的呢?
本文面向对 OpenAI 兼容协议 有了解的同学,如果不了解的话,可以参考协议文档,或者是我的 两篇老文章 https://cloud.tencent.com/developer/article/2518981 简单了解。
在 OpenAI 兼容协议中,根本不存在 "Skill" 这个字段或角色。 Skills 是一个纯粹的应用层抽象,它最终被 Cursor(或类似的 AI IDE)"编译"成三种协议原语的组合:
System/Developer Message — 把 Skill 的指令文本注入到 system prompt 中
Tools Definition — 把 Skill 需要用到的工具(如 Shell、Read 等)注册为 tools数组
Multi-turn Tool Calling Loop — LLM 根据注入的指令,自主决策发起 tool_calls,宿主执行后把结果喂回去
用一句话总结:Skill = 动态注入的 system prompt 片段 + 预定义的 tool schema + 多轮 tool calling 循环。这里我们用 Cursor 以及我实现的 skill mp-read 来举例,其他工具(OpenClaw)也是一样的——
在你打开 Cursor、还没说话的时候,Cursor 就已经扫描了 .cursor/skills/、.Agents/skills/ 等目录,收集了所有 Skill 的 name + description(来自 YAML frontmatter),然后把它们作为静态上下文塞进 system prompt。
以你的 mp-read 为例,SKILL.md 的 frontmatter 是:
name: mp-readdescription: >-Extract plain text from Tencent MP (mp.weixin.qq) articles using a headlessChrome browser. Use when the user wants to read, fetch, extract, summarize,or reference a MP article, or when a mp.weixin.qq URL appears in conversation.
这段信息会被注入成类似这样的 system prompt 片段(Cursor 真实行为的简化版):
<available_skills><agent_skill fullPath="/path/to/mp-read/SKILL.md">Extract plain text from Tencent MP (mp.weixin.qq) articles using a headlessChrome browser. Use when the user wants to read, fetch, extract, summarize,or reference a MP article, or when a mp.weixin.qq URL appears in conversation.</agent_skill></available_skills>
注意:此时 SKILL.md 的正文还没有被读取。这就是 Cursor 文档里说的 "Progressive Loading"——只先放名字和描述,不浪费 token。
假设用户发了一句:
帮我读一下这篇公众号文章:https://mp.weixin.qq.com/s/HHPK6QvclYaxlDg28elN8w
此时 Cursor 作为客户端,构造出的第一次 API 请求大致如下(简化但忠实于 OpenAI 协议):
{"model": "claude-4.6-opus","messages": [{"role": "system","content": "You are an AI coding assistant...\n\n<available_skills>\n<agent_skill fullPath=\"/Users/123456/.../mp-read/SKILL.md\">\n Extract plain text from Tencent MP (mp.weixin.qq) articles...\n</agent_skill>\n</available_skills>\n\nWhen a skill is relevant, read and follow it IMMEDIATELY as your first action..."},{"role": "user","content": "帮我读一下这篇公众号文章:https://mp.weixin.qq.com/s/HHPK6QvclYaxlDg28elN8w"}],"tools": [{"type": "function","function": {"name": "Read","description": "Reads a file from the local filesystem...","parameters": {"type": "object","properties": {"path": { "type": "string", "description": "The absolute path of the file to read." },"offset": { "type": "integer" },"limit": { "type": "integer" }},"required": ["path"]}}},{"type": "function","function": {"name": "Shell","description": "Executes a given command in a shell session...","parameters": {"type": "object","properties": {"command": { "type": "string" },"description": { "type": "string" },"working_directory": { "type": "string" },"block_until_ms": { "type": "number" }},"required": ["command"]}}}],"tool_choice": "auto"}
关键点:
tools 数组是 Cursor 预定义好的,不是 Skill 定义的。Skill 本身不声明工具——它只是告诉 LLM "你可以用 Read 来读文件,用 Shell 来执行命令"。
system prompt 里有 Skill 的描述摘要和一条关键指令:"When a skill is relevant, read and follow it IMMEDIATELY"
LLM 看到用户提到了 mp.weixin.qq,匹配到了 system prompt 中 mp-read skill 的描述,于是按照指令"先读 Skill 文件"。它返回的响应是:
{"choices": [{"message": {"role": "assistant","content": null,"tool_calls": [{"id": "call_abc123","type": "function","function": {"name": "Read","arguments": "{\"path\": \"/path/to/mp-read/SKILL.md\"}"}}]},"finish_reason": "tool_calls"}]}
这就是 Skill 的"加载"——本质上就是 LLM 自己发起了一次 Read tool call,读取 SKILL.md 文件。
Cursor 在本地执行 Read("/Users/123456/.../mp-read/SKILL.md"),拿到文件内容,然后构造下一轮请求:
{"messages": [{ "role": "system", "content": "(同上,省略)" },{ "role": "user", "content": "帮我读一下这篇这篇公众号文章:https://mp.weixin.qq.com/s/HHPK6QvclYaxlDg28elN8w" },{"role": "assistant","content": null,"tool_calls": [{"id": "call_abc123","type": "function","function": {"name": "Read","arguments": "{\"path\": \"/Users/123456/.../mp-read/SKILL.md\"}"}}]},{"role": "tool","tool_call_id": "call_abc123","content": "---\nname: mp-read\ndescription: >-\n Extract plain text from MP...\n---\n'\n```\n\n(...完整 SKILL.md 内容...)"}],"tools": [ "(同上,Shell、Read 等工具定义)" ]}
到这一步,SKILL.md 的全部内容已经通过 role: "tool" 消息进入了 LLM 的上下文窗口。 LLM 现在拥有了完整的"技能说明书"。
LLM 读完 SKILL.md 后,按照 "Prerequisites Check" 章节的指示(章节中要求用户必须实现准备好一个 cookie.txt 文件),先检查环境。它返回:
{"choices": [{"message": {"role": "assistant","content": null,"tool_calls": [{"id": "call_def456","type": "function","function": {"name": "Shell","arguments": "{\"command\": \"which mp-read\", \"description\": \"Check mp-read is installed\"}"}},{"id": "call_ghi789","type": "function","function": {"name": "Read","arguments": "{\"path\": \".../cookie.txt\"}"}}]},"finish_reason": "tool_calls"}]}
注意这里 一次返回了两个 tool_calls——这是 OpenAI 协议支持的并行 tool calling。LLM 判断这两个检查互相独立,所以同时发起。
Cursor 并行执行两个 tool call,然后把结果都塞进 messages:
{"messages": ["(...之前的所有消息...)",{"role": "assistant","tool_calls": [{ "id": "call_def456", "function": { "name": "Shell", "arguments": "..." } },{ "id": "call_ghi789", "function": { "name": "Read", "arguments": "..." } }]},{"role": "tool","tool_call_id": "call_def456","content": "/usr/local/go/bin/mp-read"},{"role": "tool","tool_call_id": "call_ghi789","content": "skey=xxxxxxx; p_skey=yyyyyyy; RIO_TCOA=zzzzzz..."}]}
前置检查通过,LLM 按照 SKILL.md 中的 Usage 章节,构造并发起实际的 mp-read 命令:
{"choices": [{"message": {"role": "assistant","content": null,"tool_calls": [{"id": "call_jkl012","type": "function","function": {"name": "Shell","arguments": "{\"command\": \"mp-read 2026/0316_mp_read/cookie.txt 'https://mp.weixin.qq.com/s/HHPK6QvclYaxlDg28elN8w'\", \"description\": \"Extract MP article text\", \"block_until_ms\": 120000}"}}]},"finish_reason": "tool_calls"}]}
注意 block_until_ms: 120000——这正是 SKILL.md 中明确要求的。LLM 从 Skill 文档中"学到"了这个参数应该设多大。
Shell 执行完毕,mp-read 的 stdout 输出(文章全文)通过 role: "tool" 消息回传给 LLM。LLM 最终生成一个 role: "assistant" 的纯文本响应,把文章内容整理后呈现给用户:
{"choices": [{"message": {"role": "assistant","content": "这篇公众号文章的内容如下:\n\n# 文章标题\n\n文章正文内容...","tool_calls": null},"finish_reason": "stop"}]}
把上面 7 步画成一张协议交互时序图:
Cursor (客户端) LLM (服务端)│ │┌─────────────────┤ ││ 启动时扫描 skill │ ││ 目录,提取 name │ ││ + description │ │└────────┬────────┘ ││ │Round 1 │ POST /chat/completions ││ system: "...skills摘要..." + tools: [Shell,Read] ││ user: "帮我读一下 https://mp.weixin.qq.com/s/HHPK6QvclYaxlDg28elN8w" ││ ─────────────────────────────────────────────────> ││ ││ assistant.tool_calls: Read(SKILL.md) ││ <───────────────────────────────────────────────── ││ │┌────────┤ 执行: Read(SKILL.md) │└────────┤ ││ │Round 2 │ tool: (SKILL.md 完整内容) ││ ─────────────────────────────────────────────────> ││ │ ← LLM 现在 "学会" 了这个 Skill│ assistant.tool_calls: ││ Shell("which mp-read") ││ Read(cookie.txt) ││ <───────────────────────────────────────────────── ││ │┌────────┤ 并行执行两个 tool call │└────────┤ ││ │Round 3 │ tool: "/usr/local/go/bin/mp-read" ││ tool: "skey=xxx;p_skey=yyy..." ││ ─────────────────────────────────────────────────> ││ ││ assistant.tool_calls: ││ Shell("mp-read cookie.txt 'URL'", ││ block_until_ms=120000) ││ <───────────────────────────────────────────────── ││ │┌────────┤ 执行: mp-read(耗时 ~30s) │└────────┤ ││ │Round 4 │ tool: (文章全文) ││ ─────────────────────────────────────────────────> ││ ││ assistant.content: "这篇文章的内容如下:..." ││ finish_reason: "stop" ││ <───────────────────────────────────────────────── │▼ ▼
messages[0].role = "system" 的文本中 |
|
tool_calls: [Read(SKILL.md)],结果通过 role: "tool" 回传 |
|
tool_calls |
|
核心洞察:Skill 在协议层面完全不存在。它是一种"给 LLM 写使用手册,让 LLM 通过已有工具自己照着做"的设计模式。 整个过程就是:
在 system prompt 里告诉 LLM "你有这些技能手册可以查"
LLM 通过 Read 工具自己去读手册
LLM 读完手册后,按手册说的步骤,通过 Shell/Read 等工具一步步执行
这种设计的精妙之处在于:它完全复用了 OpenAI 协议已有的 tool calling 机制,不需要任何协议扩展。 Skill 的全部"魔法"都发生在 system prompt 的措辞和 SKILL.md 文件的编写质量上——本质上是一种 prompt engineering + 文件系统的组合。
一个简简单单的对话功能,人类真的是能够玩出花来,语言不愧是高智慧的人类区别于动物的一个重要特征呀!
感谢你读到这里,不如关注一下?👇
53AI,企业落地大模型首选服务商
产品:场景落地咨询+大模型应用平台+行业解决方案
承诺:免费POC验证,效果达标后再合作。零风险落地应用大模型,已交付160+中大型企业
2026-05-27
Skill越详细Agent越傻!砍到40词一次选对
2026-05-27
1.1万字 Codex保姆级教程:小白从安装到跑通项目
2026-05-27
verify-data:一个端到端的数据验数 Agent Skill
2026-05-26
lark-cli:企业 SaaS 走向 AI 化时,值得对照的一份范本
2026-05-25
SkillRouter:从 8 万技能里找对那一个,这套方案只要 1.2B 参数
2026-05-25
让 Codex 设计前端不丑:Skill、参考站点与重开发流程
2026-05-25
Claude Code 这16个官方Skill,用了半年我总结出最值得装的7个
2026-05-25
最强Hermes Agent使用指南
2026-04-05
2026-03-05
2026-03-17
2026-03-04
2026-03-03
2026-03-03
2026-03-17
2026-03-26
2026-03-10
2026-03-05