jianwei
jianwei
发布于 2026-01-26 / 9 阅读
0
0

Agent skills 在开发中的实践:一次构建,处处复用

1. 核心定义:Skills 是什么?

一句话概括:

Skills 是一种模块化的能力扩展机制,也是一种简洁、开源的格式。它将特定的工作流程、知识和最佳实践封装成可复用的“技能包”,旨在为智能体赋予特定的专业能力。

  • 封装性: 将复杂的指令、脚本和资源打包。

  • 目的: 解决智能体在执行实际工作时缺失特定上下文的问题,提升任务执行的准确性与效率。

  • 标准化

  • 2025年12月18日:Agent Skills 已成为跨平台可移植的开放标准,多个平台已官宣接入。

在公司内部,Coco、Aime、字节云 AI PaaS 等平台已上线支持Agent Skills。

2. 物理架构:Skills 长什么样?

在 Agent (如 Claude / Coco) 的架构中,Skills 是一个打包好的能力文件夹

  • 存放位置: 通常位于项目根目录下的 .coco/skills 文件夹内(支持 Git 版本控制)。

  • 内部结构: 一个标准的 Skill 文件夹包含以下核心组件:

一个简单的skill可以只有一个SKILL.md文件,一个更完整的 Skill 目录树示例如下:

.
└── .coco/
    └── skills/
        └── skill-name/
            ├── SKILL.md          # 核心入口(必需)
            ├── reference.md      # 详细参考文档(可选)
            ├── examples.md       # 完整的请求与响应示例(可选)
            └── scripts/
                └── lint.py       # 用于自动检查的辅助脚本(可选)

3. 运作机制:Skills 如何被调用?

Skills 并非“常驻内存”,而是采用动态加载机制,确保高效的资源利用。

  1. 按需触发 (On-demand): Agent 不会一直“背诵”这些知识。

  2. 自主决策 (AI Autonomy):

    1. 当模型接收到你的请求时,它会扫描现有 Skills 的 Description (功能描述)

    2. 如果请求与某个 Skill 匹配(例如你问“帮我分析这个 Excel”),Agent 会自动决定调用该 Skill。

  • 渐进式披露:为了保持 SKILL.md 的简洁并高效利用上下文,可以将详细的文档、示例或脚本拆分到独立的文件中。在 SKILL.md 中,可以在需要的地方引用这些文件,Agent 会在需要时加载它们。这种“渐进式披露”的设计,让 Agent 只在必要时动态加载相关资源。

💡 核心逻辑: 无需每次重复提供指令,Agent 根据 Skill 定义的流程,更精准、可靠地完成任务。


4. 为什么需要 Skills?(核心价值)

Skills 解决了通用大模型在特定场景下“不够专业”或“缺乏上下文”的痛点。

对不同角色的价值

  • 对于 开发者

    • 一次构建,处处运行: 开发一次能力,可部署至多款支持 Skills 标准的智能体产品中复用。

  • 对于 兼容型智能体

    • 即插即用: 原生支持后,终端用户无需复杂配置,即可让 Agent 获得新能力。

  • 对于 团队

    • 知识沉淀: 将组织内部的知识封装为可移植、可版本控制(Git)的标准化包。

    • 协作同步: 团队成员可以轻松共享特定的项目知识与工作流。

🚀 Skills 能实现什么?

  • 领域专业能力: 将行业专家的知识封装为可复用的指令。

  • 全新功能拓展: 让 Agent 具备原本没有的能力(如特定格式的文件处理)。

  • 标准化工作流: 将多步骤的复杂任务转化为一致性强、可审计的标准流程。

  • 跨平台互通: 同一 Skill 可在所有支持该标准的 Agent 间通用。如果你有具体的 Skill 案例(比如一段 Python 脚本或一个工作流描述),我可以帮你尝试写一个 SKILL.md 的草稿模板。

5. Skills、MCP、Prompt、SubAgent在Coding Agent中的区别

Prompt (提示词)

Skills (技能)

MCP (Model Context Protocol)

SubAgent (子智能体)

定义

给 LLM 的自然语言指令,定义目标和角色。

封装好的、可复用的特定任务流程或知识包。

连接 AI 与外部数据/工具的通用开放协议标准。

拥有独立上下文和系统提示词的“分包商”智能体。

核心作用

“大脑”的念头:触发思考,设定当下的上下文。

“肌肉记忆”:教 Agent 如何熟练地完成某类特定任务(如“写单元测试”)。

“神经接口”:标准化的插座,让 Agent 能“插上”并使用数据库、GitHub、本地文件等。

“专家顾问”:处理主 Agent 无法专注处理的复杂、独立子任务。

表现形式

文本字符串(System/User Prompt)。

通常是 Markdown/Text 文件(包含最佳实践、Prompt 模板),有时包含脚本。

JSON-RPC 协议、Client-Server 架构(代码层面)。

独立的 Agent 实例(有自己的循环、Memory)。

主要解决

意图理解与短期行为指导。

任务执行的质量与标准化(避免每次都重写 Prompt)。

工具连接的互通性(解决每个工具都要写专用 Adapter 的问题)。

复杂任务的拆解与上下文隔离(防止主上下文超长)。

比喻

工作指令:“请帮我修车”。

维修手册:具体的“更换轮胎步骤指南”。

万能扳手接口:标准化的接口,能接各种型号的钻头(工具)。

外包修车工:专门负责修引擎的专家,修完只告诉你结果。

文档知识库与Skills的区别:

  • 因为 Skills 的元信息的存在,在Agent初始化时,agent此时就已经知道它有哪些“技能”,在对话中根据用户意图自已决定什么时候使用这个“技能”,同时能更精准规范地执行流程。

  • 而知识库存放在项目内,只有等待agent去检索召回或者是人为添加进上下文,缺点是召回不稳定,同时上下文容易过长。

6. 场景实践

1. 多语言文案拉取场景

固定流程:

  1. 运行 ./Scripts/I18nSync 同步脚本获取最新文案

  2. 在Development_Pods/SAAdaptor/CN/Resources/zh/iOS.json找到本次需要新增的key

  3. 在文件LocalizationKey.swift中增加对应的key

  4. 在对应业务场景使用与替换文案key

流程非常固定,转化为模型可使用的skill:

创建SKILL.md,其内容中除了name和description,其他部分只有在模型判断需要使用该skill时才会加载进模型的上下文。

---
name: I18n_Sync

description: 自动同步最新的多语言文案(i18n keys)并添加到项目中。关键词:多语言、i18n、localization、文案 key、多语言 key
---

## 流程
### 1. 执行同步脚本
首先运行 i18n 同步脚本获取最新文案,如果失败直接停止并输出原因:
```bash
swift ./Scripts/I18nSync.swift
```

### 2. 检查更新的文案
查看 `/Development_Pods/SAAdaptor/CN/Resources/zh/iOS.json` 文件的 diff,确认有新的文案 key 被拉取:

```bash
git diff /Development_Pods/SAAdaptor/CN/Resources/zh/iOS.json
```

这会显示类似以下的新增文案:
```json
"parallel_selfie_loading" : "加载中",
"parallel_selfie_loadingtitle" : "加载失败",
```

### 3. 解析用户需要的文案
找到用户提供的需要新增的 key ,随后将其添加到LocalizationKey.swift,即使该文件已有对应同样中文的key,上一步拉取到了新的也需要添加新的key。

### 4. 添加到 LocalizationKey.swift
在 `/Development_Pods/SAInfra/Sources/Localization/LocalizationKey.swift` 文件中添加新的 localization key。

**格式规则:**
```swift
public static let {key_name} = "{key_name}".localized
```

**插入位置:**
- 找到合适的位置(建议按字母顺序或功能模块分组)
- 在现有的 `public static let` 声明中添加新行

**示例:**
```swift
public static let parallel_selfie_loading = "parallel_selfie_loading".localized
public static let parallel_selfie_loadingtitle = "parallel_selfie_loadingtitle".localized
```

### 5. 验证
确认以下内容:
- [ ] iOS.json 文件中新增了对应的 key 和中文文案
- [ ] LocalizationKey.swift 中正确添加了对应的key
- [ ] 格式符合现有代码规范
- [ ] 没有重复的 key

## Notes
- 确保在项目根目录执行命令
- 添加 key 时保持代码格式一致性
- 建议按功能模块组织新增的 key

## 涉及文件

- `./Scripts/I18nSync.swift` - 同步脚本
- `/Development_Pods/SAAdaptor/CN/Resources/zh/iOS.json` - 中文文案文件
- `/Development_Pods/SAInfra/Sources/Localization/LocalizationKey.swift` - Key 定义文件

效率提升:

原来的手动操作

  1. 运行 ./Scripts/I18nSync 同步脚本获取最新文案

  2. 在Development_Pods/SAAdaptor/CN/Resources/zh/iOS.json找到本次需要新增的key:“评论了你的剧情故事:%s”、“加载中”...

  3. 在文件LocalizationKey.swift中对应的功能区域增加对应的key public static let comic_exam_publish_title: String = "comic_exam_publish_title".localized

  4. 在对应业务场景使用与替换文案key

➡️

一句话指令

拉取文案key:“评论了你的剧情故事:%s”、“加载中”,替换到当前页面

6.2 通用模块的业务迭代

类似输入框工具栏面板、增加一种状态提示条,这种通用的迭代场景,每位同学都有可能会在这里增加新item,但并不是每个人都对这部分代码熟悉,我认为这种场景非常适合使用Skill来封装类似【增加一个新item】的开发流程,从而达到:

编写一份skill,项目所有成员可以不需要找人沟通、不需要看文档、不需要理解、也不需要写代码,一句话完成开发。

增加输入框面板item

飞书文档 - 图片.png
---
name: add-input-tool-item
description:  MessageInputToolBar (输入框工具栏/加号面板) 新增功能按钮 (PlayToolItem) 的开发指南。当需要在消息输入工具栏(输入框工具栏/加号面板)中增加新项时,使用此技能。
---

## 流程

### 1. 定义 Action 类型与埋点
*   **文件**: `SAGameMiddleware/Sources/InputView/ToolBar/MessageInputToolBarAction.swift`,enum PlayToolType定义在story.thrift文件中
*   **操作**:
    1.  在 `MessageInputToolBarActionType` 枚举中新增 `case <NewItem>`。
    2.  在 `trackClickName` 计算属性的 switch 语句中,为 `.<NewItem>` 添加对应的埋点字符串(例如 `"<tracking_name>"`)。

### 2. 映射服务端类型与红点逻辑
*   **文件**: `SAGameMiddleware/Sources/InputView/ToolBar/MessageInputToolBarItem.swift`
*   **操作**:
    1.  在 `extension MessageInputToolBarActionType` 的 `from(playToolType:)` 方法中,将服务端下发的 `PlayToolType.<NewItem>` 映射到客户端的 `.<NewItem>`。
    2.  在 `extension MessageInputToolBarItem` 的 `shouldShowRed(_:storyId:)` 方法中,新增 `case .<NewItem>` 分支。
        *   *逻辑*: 定义红点展示规则(例如:查询 `AdvancedPlayStateManager`、特定 RedDotService,或直接返回 `.none`)。默认定义为展示新功能引导红点,即此处使用以下逻辑:
           if toolBarItem.needGuide && !AdvancedPlayStateManager.shared.hasPlayed(for: toolBarItem.playToolKey, version: toolBarItem.playToolVersion) {
                return .onlyDot
            } else {
                return .none
            }

### 3. 视图层事件分发
*   **文件**: `SAGameMiddleware/Sources/InputView/InputViews/MessageInputView+Attachment.swift`
*   **操作**:
    1.  在 `handleToolAction(_:)` 方法的 switch 语句中添加 `case .<NewItem>`。
    2.  *逻辑*:
        *   返回 `false` (推荐): 如果业务逻辑需要在 VC/Controller 层处理(如弹窗、跳转)。
        *   返回 `true`: 如果逻辑仅在 InputView 内部处理(如打开相册选择器)。

### 4. 业务层点击事件响应 (Delegate 实现)
*   **涉及文件** (根据业务场景选择):
    *   `BotPlayViewController.swift` (Bot 玩法)
    *   `MainFeedContainerViewController.swift` (feed流)
    *   `StoryPlayViewController.swift` (story场景)
*   **操作**:
    1.  找到实现 `FullScreenMessageInputMenuDelegate` 协议的 `fullScreenInputView(_:didSelectMenu:)` 方法。
    2.  在 switch 语句中新增 `case .<NewItem>:`。
    3.  实现具体的点击跳转或业务逻辑(例如 `Router.shared.open(...)` 或 `Handler.open(...)`)。

### 5. 配置与过滤
*   **文件**: `SAGameMiddleware/Sources/Setting/ABTest/Values/ABTestValue+AttachmentMessage.swift`
*   **注意**: `getToolBarItems` 确保服务端下发的配置能正确通过过滤逻辑

这里的测试在IDL新增了一种目前不存在的音乐类型的工具栏item,同时mock了服务端下发的数据,端上的开发都由Agent一次指令运行得到:

image-nAtc.png
image-WVSi.png

执行效果:

飞书文档 - 图片 (1).png


评论