前言:为什么需要这篇文章

我最近完成了一项工作:为 某App IM 的未读数系统编写一份 AI Contextual Skill——一份结构化的知识文档,目标是让 AI 在面对未读数相关问题时,能够准确理解架构、定位代码、给出可靠回答。

这件事并不复杂,但做好并不容易。从初稿到最终达到生产级质量,经历了两轮结构优化、一次全量翻译、一次与实时代码的系统性比对验证。其间暴露的问题远比预想的多——方法签名写错、数据流描述偏差、Model 定义过时、信息大面积重复……每一个问题都可能导致 AI 生成错误代码或给出误导性建议。

这篇文章不是一份产品说明书,而是对整个过程的复盘和提炼。我试图回答一个问题:如何系统性地创建一份高质量的 AI Skill,并在后续的迭代中持续保持其可靠性?


一、先明确目标Skill的职责

Skill 到底是什么

Contextual Skill 不是代码注释的集合,也不是技术方案文档的翻版。它的本质是一份面向 AI 的结构化知识摘要,目的是让 AI 在接收到相关领域的问题时,能够迅速建立正确的上下文认知。

打个比方:如果把 AI 比作一个刚入职的工程师,Skill 就是那份让他在第一周就能理解系统全貌的内部 Wiki。它不需要事无巨细地讲解每个函数怎么实现,但需要让这位工程师知道——系统分几层、数据怎么流转、关键接口在哪里、改动某个功能时应该去看哪些文件。

几个常见的误区值得在一开始就澄清:

  • Skill 不是源码的副本。 把完整的函数实现搬进来,只会制造一个很快过时的快照。
  • Skill 不是实现教程。 详细的步骤讲解会随代码变更迅速失效,而且 AI 完全可以直接阅读源文件。
  • Skill 也不是随手记录。 碎片化的笔记缺乏结构,AI 很难从中提取有效信息。

真正好的 Skill,应该聚焦于那些相对稳定、偏宏观、能长期有效的知识:接口定义、组件职责、数据流逻辑、关键决策点。这些信息不会因为某次 bugfix 就失效,却能帮助 AI 在面对具体问题时快速定位到正确的代码区域。

在动手之前回答三个问题

开始写之前,有三件事必须想清楚。

第一,谁会用这份 Skill? 在我们的案例中,直接消费者是 AI 本身——它需要在处理 IM 相关代码问题时加载这份上下文。间接受益者是开发者,他们通过 AI 获得更精准的代码建议。还有一个容易被忽略的角色是未来的维护者——下一个需要更新这份 Skill 的人。不同角色对内容粒度的需求是不同的,但在我们的场景下,AI 的需求是第一优先级。

第二,这份 Skill 要解决什么类型的问题? 常见的用途大致可以分为四类:架构理解(系统长什么样)、决策支持(我该改哪里)、故障排查(问题出在哪)、扩展指南(如何加新功能)。在 IM 未读数这个案例中,主要诉求是架构理解和决策支持——开发者需要知道未读数系统有两条完全不同的数据路径(Non-Pagination 和 Pagination),修改时应该动哪条链路上的哪个组件。明确了用途,内容的取舍就有了依据。

第三,什么算"够好"? 这个标准必须在项目初期就定义。我的建议是从五个维度去考量:准确性(信息是否与代码一致)、完整性(关键概念是否覆盖到位)、易用性(能否快速找到需要的信息)、稳定性(代码变更后需要多大的更新工作量)、可维护性(信息是否重复散乱)。后面会详细展开这五个维度,这里先建立这个框架。

信息也有保质期

这是一个在初稿阶段很容易忽略、但在后续维护中会反复出现的问题:不同类型的信息,变化速度完全不同。

Protocol 定义、系统架构图、核心概念——这些信息以年为单位保持稳定。组件职责、数据流逻辑——这些以月为单位,偶尔随重构变化。具体的代码实现、某个方法的内部算法——以周甚至天为单位变化。

Skill 应当把精力集中在前两类信息上。对于那些可能频繁变化的内容,正确的做法不是详细讲解,而是给出索引——告诉 AI 去哪个文件的哪个位置查看最新实现。

在我们的案例中,初版 Skill 犯的一个典型错误就是把 v0_notifySectionsDMCount 方法的完整实现逻辑都写进了文档。这段代码逻辑一旦发生变更,文档就会和实际行为产生偏差。优化后,我们把它改为"计算逻辑位于 IMInboxDMSectionViewModel.v0_notifySectionsDMCount,详见源码"——既提供了足够的线索,又避免了过时风险。


二、把知识"装进"结构

原始文档为什么不能直接用

我们的输入是两份技术文档:inbox_filter_tab_unread.mdinbox_unread_count.md。它们是我引导AI在不同时期编写的上下文,存在典型的"原始文档"问题:信息按作者的写作思路而非使用者的查询需求组织;同一个概念在两份文档中各解释一遍、措辞不同;部分内容已经过时但没有标注。

把这些文档直接交给 AI 作为上下文,效果并不理想——AI 会看到冗余和矛盾的信息,输出质量无法保证。所以本质上,创建 Skill 是一个知识重组的过程:从按"来源"组织的原始文档,转化为按"语义"组织的结构化知识。

转化的三个步骤

第一步是提取。 逐页阅读原始文档,标注出所有需要保留的关键信息:核心概念(如两种计数模式、两条数据路径)、关键组件(如 IMUnreadCountDataSourceUnreadConversationInfoManager)、逻辑链路(SDK 回调如何一路传递到 TabBar Badge)、决策点(isInboxPaginationEnable 控制路径选择)、特殊规则(Muted 对话的排除逻辑)。同时明确标记那些不应该带入 Skill 的内容:过于具体的算法实现、已废弃的 API、纯粹的调试日志。

第二步是分类。 把提取出的信息按语义归类,而不是按来源文档归类。我们最终形成了六类信息:概念定义、技术架构、数据流程、接口定义、业务规则、文件索引。这个分类直接决定了 Skill 的章节结构。

第三步是关联。 建立信息之间的引用关系——每个组件都注明它在哪条数据路径上、实现了哪些 Protocol、位于哪个文件。每条规则都说明它在哪个环节生效。这些关联构成了一张知识网络,让 AI 可以从任何一个入口出发,顺藤摸瓜找到相关信息。

什么样的格式对 AI 最友好

不同类型的信息适合不同的表达方式。经过实践,以下几种格式被证明效果较好。

概念定义用表格。 简洁、可对比、无歧义。例如:

概念定义使用场景
Message Count所有未读消息的总数传统模式,用户看到消息数量
Conversation Count有未读消息的对话数新模式,用户看到对话数量

架构关系用 ASCII 流程图。 组件级即可,不要到方法级。每一步标注清楚角色和通信机制:

SDK Data (IESIMChatDataManager)
     ↓ SDK callback
IMChatDataController
     ↓ callback delegate
IMUnreadCountDataSource (storage)
     ↓ callback
IMInboxDMSectionViewModel (calculation)
     ↓ RAC binding
DirectMessageWidget → DirectMessageSharedInfo
     ↓ Publisher
DMInboxFilterWidget → DMInboxFilterSectionViewModel

决策逻辑用伪代码。 优先级关系在自然语言描述中容易含混,伪代码则一目了然:

if shouldCountUnreadByCell == true:
    return conversation_count
elif inboxFilterUnreadCountOpt_ABTest == 1:
    return message_count
else:
    return conversation_count

排除规则用层级树。 规则之间的组合关系和优先级,层级树比平铺列表更清晰。

主文档与参考文档的分层

一个 Skill 不应该是一个巨大的单文件。我们的实践表明,主文档 + 多份参考文档的分层结构最为有效。

主文档(SKILL.md)是导航中枢,篇幅控制在 400 行以内。它包含:快速导航表、核心概念的一句话定义、高层架构图(只展示主要组件)、关键 Flag 列表、文件映射(每个文件的职责一句话说明)。主文档的 90% 内容是索引和指向,只有不到 10% 是深入的讲解。

参考文档(references/*.md)是深度纵深,每份覆盖一个特定主题。在我们的案例中有四份:non-pagination-path.md(非分页路径的完整逻辑链)、pagination-path.md(分页路径 + 迁移指南)、filter-tab-badge.md(Filter Tab 的计算规则和排除规则)、data-models-and-interfaces.md(所有 Protocol 和 Model 的定义)。每份参考文档 200-400 行,包含逻辑链路说明、关键接口签名和调试指南。

这种分层的好处是:AI 在大多数场景下只需加载主文档就能回答问题;只有在需要深入某个主题时,才按需加载对应的参考文档。

单一信息源原则

这是贯穿整个 Skill 工程的核心原则,也是我们在优化阶段投入最大精力建立的机制。

原则很简单:每条信息只在一个地方被详细定义,其他所有提及它的地方只做简要引用并指向该定义。

实际操作时,我们对整个文档做了一次"信息审计",列出所有被重复定义的概念,然后为每类信息指定一个唯一的权威位置:

信息类型唯一权威位置
核心概念定义(计数模式、数据路径)SKILL.md 第 1 章
Protocol 和 Model 的完整定义references/data-models-and-interfaces.md
排除规则的详细逻辑references/filter-tab-badge.md
关键 Flag 和 Config 汇总SKILL.md 第 6 章
文件映射与职责SKILL.md 第 8 章

建立了这个原则之后,其他文档中再提到"排除规则"时,就不再展开讲解,而是写一句"详见 filter-tab-badge.md 排除规则章节"。这样做的直接收益是:当排除规则发生变化时,只需修改一个文件。如果没有这个原则,你需要找到所有散落各处的描述逐一修改——这在实践中很难做到不遗漏。


三、迭代优化——质量是打磨出来的

初稿一定有问题

这不是悲观,而是客观规律。初稿不可能一步到位,原因很简单:在你还没有写出完整的 Skill 之前,你无法看到全貌,也就无法发现结构层面的问题。

我们的初版 Skill 包含 7 个文件、约 2500 行,看起来相当完整。但审视之后,发现两个根本性问题:细节过多信息重复

细节过多

初版中,non-pagination-path.md 包含了 v0_notifySectionsDMCount 的完整实现代码,pagination-path.md 包含了 buildBizModel / updateBizModel 的详细逻辑,filter-tab-badge.md 包含了 getUnreadCount 的完整函数体。这些代码一旦变更,文档立刻过时——而在一个活跃迭代的项目中,这种变更随时可能发生。

更深层的问题在于:当 Skill 中包含详细的实现代码时,AI 会倾向于直接引用文档中的代码,而不是去查看最新的源文件。这等于给 AI 灌输了一份"冻结"的代码快照,反而降低了回答的准确性。

优化方向很明确:只保留关键逻辑框架,具体实现交给 AI 去源文件中现场查看。 Skill 中提及的代码,应当局限于关键的类名、方法名、接口签名——这些相对稳定的"锚点",而非具体的实现逻辑。

经过这轮精简,SKILL.md 从约 2500 行压缩到约 330 行。数据流的描述从"方法级"上升到"组件级",架构图从展示所有内部调用关系简化为只展示主要组件和通信机制。

信息重复

审计后发现,"两种计数模式"这个概念在 5 个不同位置被讲解;排除规则分散在 3 个文件中,措辞各不相同;关键 Flag 在 SKILL.md 和多个参考文档中各有一套独立描述。

信息重复带来的危害远比看上去严重。它不仅增加维护成本(改一处需要改多处),更危险的是:当不同位置的描述不可避免地产生差异时,AI 会看到矛盾的信息,输出质量将大幅下降。

解决方案就是前面提到的单一信息源原则。执行时的一个关键区分是:定义不应重复,但引用可以重复。 任何文档都可以说"排除规则详见 filter-tab-badge.md",这不算重复;但排除规则的具体条件和逻辑,只能在 filter-tab-badge.md 中出现一次。

第二轮优化:结构完善

第一轮解决了"多"和"重"的问题后,第二轮着眼于"准"和"清"。

架构图的改进。 初版的架构图没有标注通信机制。Non-Pagination 路径使用 KVO → RAC → Publisher 的多跳传播,Pagination 路径使用直接的 Observer 回调——这是两条路径在技术实现上最本质的差异,架构图必须体现出来。改进后的图在每一条连线上都标注了具体的通信方式。

快速导航的扩展。 初版的导航只覆盖了理论性问题("理解两种计数模式""查某个 Flag"),但开发者在实际工作中更常问的是操作性问题:"我要新增一个 filter 类型""我要修改某个 filter 的排除规则""我要从 Non-Pagination 迁移到 Pagination"。第二轮在导航表中增加了这些实战场景的入口。

文件映射的重新分类。 初版按功能模块分类,导致一些跨路径共用的组件(如 DMInboxFilterSectionViewModel)归类模糊,TabBarInboxItem 被放在"Config 与 Utils"里也不合理。改为按逻辑层分类:Non-Pagination 核心、Pagination 核心、Filter Tab 组件(两条路径共用)、TabBar 消费端(两条路径共用)、配置与工具类。

Protocol 表格的增强。 在 SKILL.md 的接口索引表中增加了"关键方法"列,让 AI 不必跳转到参考文档就能知道某个 Protocol 的核心 API 是什么。

验证:与代码对齐

这是整个流程中最容易被跳过、但价值最大的环节。

在完成两轮优化和英文翻译之后,我们对 Skill 中的所有关键技术声明进行了一次系统性的代码比对。方法是:逐个检查 Skill 中提到的类名、Protocol 定义、方法签名、数据流描述、文件路径,与仓库中的实际代码进行核实。我们发现了 5 个严重问题、2 个中等问题、3 个轻微问题

严重 问题示例:

  1. UnreadConversationInfoUpdateObserverProtocol 的方法名写错了。Skill 中写的是 unreadInfoDataSourceHasUpdated(_ dataSource: UnreadConversationInfoDataSource),实际代码是 unreadCountDataSourceHasUpdated(_ identifier: String)——方法名不同,参数类型也完全不同。如果 AI 按 Skill 中的定义生成代码,编译必然失败。

  2. setUpDataSources 的参数类型写错了。Skill 中写的是 [IMInboxFilterItemModelProtocol],实际是 [ChatListPageConfiguration]。这是一个完全不同的类型。

  3. Non-Pagination 路径的 RAC 绑定源描述有误。Skill 中写的是"RAC 绑定自 IMUnreadCountDataSource.unreadCountMap",实际上 DirectMessageWidget 绑定的是 IMInboxDMSectionViewModel.unreadCountMap——中间多了一层 ViewModel 的计算。这个错误会导致对整条数据流的理解产生偏差。

中等 问题示例:

  1. UnreadConversationInfo 被描述为一个 struct,实际是 NSObject 子类;属性名也不对,Skill 中写的是 messageCount / conversationCount,实际是 unmutedUnreadCount / mutedUnreadCount

  2. DMFilterGeneralModel 被标注了 @Published 的属性包装器,实际上它就是一个普通的数据模型类,Publisher 机制在 ViewModel 层。

轻微 问题示例:

  1. 四个文件的路径写错(ChatData/IMCore/ 实际是 ChatData/Manager/Inbox/Manager/ 实际是 Inbox/DataManager/),文档内部链接断裂(data-models.md 应为 data-models-and-interfaces.md)。

这些问题的共同特点是:它们看起来"差不多对",但在实际使用中会导致严重后果。一个写错的方法签名可能让 AI 生成无法编译的代码;一条描述偏差的数据流可能让 AI 把 bug 定位到错误的组件。

教训很明确:验证不是可选步骤,而是必须环节。 Skill 中的每一个技术声明都应该能在代码中找到对应的证据。


四、什么是"好 Skill"

经过这次完整的实践,我把"好 Skill"的标准凝练为五个维度。这不是理论推导,而是从实际踩坑中提炼出来的。

准确性

这是最基本的要求,也是最容易被忽视的。一个有 5% 错误率的 Skill 远比没有 Skill 更危险——因为它给出的错误信息带有"权威性",AI 和开发者都会倾向于信任它。

如何保证准确性?唯一可靠的手段是与代码比对验证。建议在每次大规模更新后都做一次系统性验证,生成验证报告,按严重程度分类,逐一修正。

完整性

完整性不等于事无巨细。它指的是:对于 Skill 声称覆盖的领域,关键的概念、组件、流程、接口是否都有涉及?有没有"只讲了 A 路径,忘了 B 路径"这种遗漏?

在我们的案例中,一个差点被遗漏的点是 Non-Pagination 路径中 enableDMMultiTabChatListRefactor 这个 Flag 控制的路由分叉——它决定了是按 filter identifier 路由还是按 DataManagerType 路由。这个信息在初版中只在参考文档中一笔带过,优化后被提升到了 SKILL.md 的 Flag 汇总章节。

易用性

判断标准很简单:当 AI 或开发者带着一个具体问题来查阅 Skill 时,能不能在两分钟内找到答案或者找到答案的入口?

快速导航表是实现易用性的关键机制。而且导航不能只覆盖理论性问题——"理解两种计数模式"这种导航当然需要,但开发者更常问的是"我要新增一个 filter 该改哪些文件"这种实操问题。后者直接决定了 Skill 在日常开发中的实际利用率。

稳定性

当代码发生变更时,Skill 需要多大的工作量来同步更新?如果一个 Skill 在每次代码提交后都需要大面积修改,那它的维护成本将很快超过收益。

保证稳定性的核心策略是前面反复强调的:聚焦于稳定的信息(接口、架构、概念),把易变信息(实现、算法、配置)替换为指向源代码的引用。

在我们的最终版 Skill 中,大约 85% 的内容属于"月级别不常变"的信息。即使 IM 未读数系统经历一次中等规模的重构,需要修改的内容也不会超过总量的 20%。

可维护性

可维护性和稳定性相关但不相同。稳定性关注的是"外部变化(代码)对 Skill 的冲击有多大",可维护性关注的是"Skill 本身的结构是否便于修改"。

单一信息源原则是可维护性的基石。在我们的案例中,建立这个原则之前,可维护性大概 5 分(满分 10);建立之后跃升到 9 分。这不是夸张——当你需要修改排除规则的描述时,只改一个文件和找遍五个文件的差距,就是 5 分和 9 分的差距。

一个简单的评估框架

如果需要量化,可以按以下权重进行加权评分:

总分 = 准确性 × 40% + 完整性 × 25% + 易用性 × 15% + 稳定性 × 10% + 可维护性 × 10%

准确性占比最高,因为一份不准确的 Skill 比没有 Skill 更糟。其次是完整性——残缺的知识同样会误导。易用性、稳定性、可维护性则关系到 Skill 的长期价值。

以我们案例中的实际演进为例:

版本准确性完整性易用性稳定性可维护性综合
初稿776656.5
第一轮优化后88.58.588.58.2
第二轮优化后899998.6
代码验证修正后9.5999.599.3

从 6.5 到 9.3,提升并非来自"写更多内容",而是来自"去掉不该有的、修正不对的、理清不清的"。


五、实操建议

工作流程

回顾整个过程,我把创建一份高质量 Skill 的工作拆解为五个阶段。

阶段一:规划(约 1-2 小时)。 确定目标用户、核心用途和成功标准。列举需要覆盖的关键概念、组件和流程。确定信息的分类方案。这个阶段的产出是一份内容大纲。不要跳过这个阶段——在我们的案例中,正是因为在动手前做了充分的规划(拆分策略、内容设计、章节大纲),后续的执行才相对顺畅。

阶段二:初稿(约 4-8 小时)。 按照大纲编写主文档和参考文档。这个阶段的目标是"先有",不必追求完美。初稿写完后回头审视,几乎一定会发现细节过多和信息重复的问题——这是正常的,留给下一阶段处理。

阶段三:优化(约 6-12 小时)。 这是最关键的阶段,建议分两到三轮执行。第一轮聚焦于精简——删除实现代码、简化架构图、把数据流从方法级提升到组件级。第二轮聚焦于去重——执行信息审计、建立单一信息源、把散落的定义统一为引用。第三轮聚焦于结构——完善快速导航、重新分类文件映射、在架构图上标注通信机制。

阶段四:验证(约 4-8 小时)。 逐一核实 Protocol 定义、方法签名、数据流描述、文件路径与实际代码的一致性。生成验证报告,按严重程度分类,逐一修正。这个阶段的投入回报率极高——在我们的案例中,仅这一步就发现并修正了 5 个可能导致 AI 生成错误代码的严重问题。

阶段五:持续维护。 建立定期同步机制。当代码发生重大变更(重构、新增功能、架构调整)时,触发对应的更新。单一信息源原则在此阶段发挥最大价值——需要修改时,你清楚知道该改哪个文件的哪个章节。

常见陷阱

陷阱表现避免方法
过度详细Skill 读起来像源码注释在规划阶段就约定"不包含实现代码"的红线
信息重复同一个概念在不同文档中措辞不同初稿完成后做一次信息审计,建立单一源
快速过时代码改了几行,Skill 就描述不准了用指向源代码的引用替代详细讲解
导航缺失开发者不知道从哪里开始查设计覆盖理论和实战两类场景的快速导航
跳过验证方法签名、参数类型写错却不自知把代码比对验证纳入标准流程

六、结语

写这篇文章的过程中,我一直在思考一个问题:创建 Skill 和写代码有什么共同点?

答案是:它们都不是一次性的工作,都需要迭代,都需要测试,都需要维护。 一份 Skill 和一段代码一样,初版一定有 bug(我们发现了 10 个),一定有冗余(行数从 2500 行砍到 330 行),一定有结构问题(文件映射需要重新分类)。区别只在于:代码有编译器和单元测试帮你发现问题,而 Skill 的质量保障手段还需要我们自己去建设。

如果只记住一条原则,我希望是这条:不要追求"全",要追求"准"和"稳"。 一份 330 行的、结构清晰、信息准确、指向明确的 Skill,远比一份 2500 行的、面面俱到但充满过时细节的文档更有价值。

80 分的准确加上清晰的结构,好过 99 分的详尽加上脆弱的稳定性。这是整个实践给我的最大启示。

本站提供的所有下载资源均来自互联网,仅提供学习交流使用,版权归原作者所有。如需商业使用,请联系原作者获得授权。 如您发现有涉嫌侵权的内容,请联系我们 邮箱:alixiixcom@163.com