作者在多仓库SDK开发中发现,AI Agent需要包作者显式提供机器可读的接口说明文档,才能准确理解和使用SDK。
我同时开发几个紧密相关的代码仓库。
有些是可复用的 SDK,用于声明式 schema、基础设施、有状态工作流和其他领域抽象。另一些则是消费这些 SDK 的应用程序。
开发循环不断跨越包边界。
SDK A ──────┐
│
SDK B ──────┼──▶ application
│ │
SDK C ──────┘ │
▲ │
└──── feedback ───┘
我之前已经写过为什么我认为这不需要 monorepo,以及为什么我更倾向于让仓库本身携带当前的真相来源:
AI Agents Don't Need a Monorepo. They Need a Readable Codebase
The Repo Is the Context: Why Agents Don't Need History
这里不再重复这些论点。
这篇文章从更后面的一层开始。
随着这些 SDK 变得越来越"智能体感知",每个包都开始需要告诉编码智能体该如何使用它。
我已经在使用项目本地的入口,如 .claude/、.codex/、AGENTS.md 和包级别的 skill。它们很有用。显式的项目本地上下文是有效的。
维护是其中别扭的部分。
当一个 SDK 发生变化时,我会告诉智能体去更新消费仓库中相应的说明、规则或 skill。
但是在跨多个包和仓库重复这样做之后,我注意到一件事:
我反复发出的更新指令悄然成了一个未成文的协议。
哪些文件需要改动?
哪个来源是规范的?
什么应该被复制?
什么只应该被引用?
什么属于包,什么属于消费仓库?
不同的编码智能体 harness 如何接收相同的包知识而不产生独立副本?
我最初以为需要一个更好的同步器。
我现在认为问题在更高一层。
包已经有了面向程序的接口。它们越来越需要面向编码智能体的接口。
我一直把这个接口的协议称为 AIC — Agent Index Convention。
它仍是我自己开发环境中的一个草案。具体的机制会发生变化。
但它描述的边界要稳定得多。
缺失的包接口
包已经知道如何向程序介绍自己:
package name
version
API
types
schemas
config
CLI
编码智能体需要另一组事实:
当前手册在哪里?
哪些规则重要?
哪些 skill 可用?
哪些文件是生成的?
哪些命令是安全的?
哪些主机特定配置适用?
我把这看作是包的智能体面向接口。
问题出现在多个包在同一主机仓库中暴露这个接口时。
没有共享的约定,每个包倾向于独立解决这个问题。
package A ──▶ AGENTS.md
package B ──▶ CLAUDE.md
package C ──▶ .claude/skills/
package D ──▶ .codex/...
每个集成单独看都可以完全合理。
组合引入的是另一类问题。
这看起来不再像文档管理。
它开始像包组合。
AIC 分离了三个角色:
Provider package
│
│ declares agent-facing assets
▼
Host repository
│
│ exposes them through actual harness loading paths
▼
Coding agent
Provider 是提供智能体面向上下文的包。
Host 是消费该包的仓库。
provider 将其智能体面向的源随包一起发布:
provider-package/
├── agent-index.json
├── AGENTS.md
└── skills/
一个最小化清单可能长这样:
{
"schema": "agent-index/v1",
"package": "@scope/schema-sdk",
"version": "0.14.0",
"summary": "Declarative schema toolkit",
"manual": "AGENTS.md",
"skills": [
{
"name": "schema-design",
"src": "skills/schema-design"
}
],
"instanceConfig": {
"source": "declarative",
"readFrom": "schema.config.json",
"format": "json",
"fields": ["runtime", "validation"]
}
}
agent-index.json 不是另一本手册。
identity
version
canonical manual
discoverable assets
host-resolved facts
对我来说有用的改变是,维护契约变成了声明式的。
不再反复告诉智能体:
Update the Claude and Codex instructions
to match the latest SDK behavior.
它可以检查一个更接近这样的结构:
provider
├── canonical manual
├── skills
├── resolved host config
└── target loading semantics
更新不再取决于那天我对维护任务的描述有多好。
注入、引用或物化
这个区分在实际使用中可能是我觉得最有用的部分。
并非所有智能体面向的资产都应该以相同方式跨越包边界。
┌── Index ─────▶ inject
Provider package ─┼── Manual ─────▶ reference
└── Skill ─────▶ materialize
我最初想用一个机制处理所有三个。
实际的加载语义使那个抽象变得错误。
引用可以保持版本绑定的内容
将手册复制到 host 会在 host 中产生两个独立变化的真相。
installed package v0.14
copied manual v0.13
我在其他地方已经遇到了足够多的包版本偏斜问题,不想在智能体上下文层重现同一类 bug。
这里比普通的过时文档更糟糕。
被智能体消费的一条过时指令,如果该智能体可以编辑代码和运行命令,就是可执行的错误信息。
智能体可能根本没有产生幻觉。
它可能完全按照错误的版本行事。
所以规范手册保留在已安装的包中。
host 存储一个指针,而不是另一个副本。
在实践中,这是我目前更信任的更耐久的 AIC 决策之一:升级包不需要另一个手册副本来以某种方式保持同步。
物化发现需要的内容
Skill 则不同。
如果一个 harness 仅通过扫描特定目录来发现 skill,那么提到它的包路径并不等同于将它放到 harness 查找的位置。
该资产需要物理存在。
所以我使用的规则是:
当读取访问足够时引用。当发现需要存在时物化。
这比"复制一切"或"永不复制"都更有用。
上下文放置应该遵循实际的加载语义,而不是美学的统一抽象。
在我一直在测试的 AIC 决策中,这种不对称是我目前最信任的决策之一。
一个主机索引,多个 provider
每个 provider 为主机索引贡献一个小的命名空间块。
### Agent index: `@scope/schema-sdk` v0.14.0
- Manual: read `node_modules/@scope/schema-sdk/AGENTS.md`
- Skills: managed under the harness discovery path
- Host config: validation=strict
多个 provider 可以共存:
AGENTS.md
│
├── human-owned content
├── @scope/package-a
├── @scope/package-b
└── @scope/package-c
核心不变量是:
provider 拥有其命名空间,而不是 AGENTS.md。
更新包 B 可能只更新 B 的块。
rewrite human content
move package A
modify package C
regenerate the whole file
物化资产遵循相同规则:每个 provider 获取自己的碰撞安全命名空间。
这也是我能够测试而非仅描述的事情之一。
在我使用的实现中,外部 provider 块被保留而不是被规范化到当前 provider 的表示中。用相同输入重复同步被测试为无操作。Provider 也可以共享锁状态而不会一个 provider 扁平化另一个 provider 的私有字段。
这些细节是有意平淡的。
但它们是说"多个 provider 可以共存"和实际让它们共存之间的区别。
包不需要成对的集成。
包 A 不需要知道包 B 存在。
包 B 不需要包 C 的插件。
它们能组合是因为共享了一个所有权规则。
协调是昂贵的。命名空间是便宜的。
这就是 AIC 对我来说不再感觉像同步器的地方。
它开始感觉像一个包协议。
Harness 特定文件是派生表面
AIC 不是反对 .claude/、.codex/ 或其他 harness 特定位置的论点。
我使用它们因为它们有用。
问题是被独立维护。
┌── AGENTS.md
│
provider source ───────┼── Claude adapter
├── Codex adapter
├── skill discovery adapter
└── other thin adapters
manual
├── Claude copy
├── Codex copy
├── Cursor copy
└── another copy
Harness 特定文件是派生表面。
规范的包知识保持单一。
这也给了设计一个逃生舱口。
如果一个 harness 最终提供了更原生的机制来消费包所有的指令或技能,那么这个适配器应该消失。
包的边界没有必要消失。
适配器层是可替代的。边界声明不是。
这个区别很重要,因为 harness 的行为变化可能比包的契约更快。
同步应该是无聊的
AIC 目前的生命周期操作大致如下:
agent-index sync
agent-index check
agent-index remove
最重要的属性是幂等性。
相同的 provider
相同的版本
相同的 source
相同的管理主机状态
│
▼
no-op
用相同的输入运行两次同步不应该产生第二次写入、时间戳抖动或 Git diff。
对于物化的资产,AIC 追踪两边:
provider source ── hash ──▶ sourceHash
host copy ── hash ──▶ destHash
provider 升级了
host 副本在本地被编辑过
什么都没变
这些状态不应该都导致"再次复制"。
原始资产是一字节一字节物化的。出处信息放在旁边,而不是被注入到 skill frontmatter、脚本或模板中。
我不需要这里有什么复杂的协调逻辑。
我想要的是确定性的所有权和无聊的失败模式。
我关心确定性还有一个原因:
智能体也是维护者。
人类通常可以推断两种略有不同的布局代表大致相同的约定。
智能体更受益于:
一种格式
一种所有权规则
一种生命周期
一个事实来源
维护结构越容易机械地检查,下一个智能体会话需要从文本中重构的东西就越少。
确定性并不能消除漂移
这是实现使权衡更清晰的一个地方。
协议可以定义应该同步什么,但不能保证每次同步都立即发生。
我有过这样的情况:因为漏掉了一个手动步骤,包版本和其管理的智能体状态暂时分叉了。
这正是 AIC 旨在检测的那类问题。
这也表明声明协议并不会神奇地消除其维护成本。
目前的分离是刻意的:
常见路径保持廉价。
更强的路径保持显式。
我宁愿这样,也不愿把每次 CLI 启动都变成完整的文件系统完整性扫描。
生成的智能体状态应该进入 Git
我提交生成的索引、物化的技能和锁/出处状态。
- ### Agent index: `@scope/schema-sdk` v0.13.0
+ ### Agent index: `@scope/schema-sdk` v0.14.0
变化了智能体能发现什么。
改变了的物化技能可以改变智能体能做什么。
我希望这些变化与导致它们的依赖更新并列可见。
所以我把 AIC 输出更多地想象成:
生成不是问题。不可见的生成才是。
Preflight 修复;它不声明所有权
保持生成状态的新鲜自然会导致自动修复。
这是我变得刻意保守的地方。
我不想让依赖安装静默地重写主机拥有的文件。
我也不想在第一次执行 SDK CLI 时让它自行决定仓库是否选择了智能体集成。
所以 AIC 区分了 adoption 和 maintenance。
初次接触
└──▶ 警告 / 显式同步
现有 provider + 过时版本
└──▶ 修复自己的命名空间
管理内容在本地被编辑过
└──▶ 保留 + 警告
CI / 只读文件系统
└──▶ 不要变更
移除或禁用的 provider
└──▶ 保持移除状态
自动化可以维护已有的所有权。但不应该创造所有权。
Preflight 需要一个执行机会。
如果一个依赖升级了,但相关的 provider CLI 还没有运行,物化状态可能暂时保持过时。
我今天接受这个窗口期。
另一种做法是让包安装自动变更主机,我目前认为那是更糟糕的所有权边界。
需要更严格保证的团队可以在 CI 中显式运行 check 或在 CI 中运行。
所以 preflight 不能消除漂移。
它使常见的修复路径变得廉价,同时将完整的完整性验证保持为显式。
这也消除了我旧工作流程的另一部分。
我不再想要这样:
请在此 SDK 变更后更新智能体文件。
作为开发流程的一部分。
如果这种关系是结构性的,那么更新规则也应该是结构性的。
这在哪些地方实际上可行
有一个重要的限定条件。
我目前使用 AIC 的 provider 属于同一个开发环境。
我控制着协议的两端。
这给了我一些有用的东西:我可以测试独立版本化的包是否通过相同的规则实际组合。
它还不能证明无关的第三方包作者会采用它们。
所以今天我会把 AIC 描述为一个生态系统内的工作约定,而不是生态系统范围的标准。
这个区别很重要,因为协议的最终价值大部分来自网络效应。
这是我还没有证明的部分。
还有其他一些目前的假设。
引用的资产需要已安装的包
留在已安装包内部的手册只有在依赖实际可用时才有用。
在依赖安装前的全新克隆中,指针可能暂时指向虚无。
我目前接受这个,因为引用旨在描述已安装包的状态。
这意味着 AIC 不能替代依赖可用性。
AIC 不能发明通用发现
AIC 依赖于 harness 已经加载的入口点——例如 AGENTS.md、其中的 import 或 harness 特定的发现目录。
它不能从第一性原理解决"每个可能的编码智能体如何发现 AIC?"的问题。
我宁愿适应真实的加载行为,而不是引入另一个强制的引导机制。
物化的资产扩大了信任边界
一份引用的手册是信息。
一份物化的技能可以影响智能体实际做什么。
这使得第三方 provider 与我自己控制的包成为不同的信任问题。
内容哈希告诉我资产是否改变了。
它们不能回答谁应该被信任来提供该资产。
这是我想要在将 agent-index/v1 视为第三方生态系统契约之前使其更明确的领域之一。
我期望保留的部分
智能体 harness 发展很快。
对指令、技能或包拥有的智能体上下文的原生打包可能最终吸收 AIC 适配器今天做的一些事情。
如果发生这种情况,我不想为了自己的利益而维护当前的同步机制。
我期望保留的部分更小:
声明面向智能体的包边界
只拥有自己的命名空间
将规范知识保持与版本绑定
将引用与需要发现的物化分离
使派生状态可确定和可审查
尊重先前的主机所有权决策
适配器层是可替代的。
边界声明才是有趣的部分。
这也是我不想太早冻结 schema 的原因。
在要求无关 provider 采用它之前,我宁愿让这个约定在我已经运营的仓库中变得无聊:
更少的漂移事件
更少的手动修复指令
更少只存在于我脑中的约定
如果这继续有效,格式就有了背后的证据。
如果生态系统收敛到一个更好的原生机制,AIC 应该成为该机制的薄适配器,而不是与之竞争。
我目前认为属于结构性的部分:
包需要一个显式的面向智能体的接口
多个 provider 需要命名空间所有权
规范手册应该保持与其包的版本绑定
被引用和可发现的资产需要不同的持久化语义
harness 特定的表面应该是派生的,而不是独立的事实来源
同步应该是可确定和可审查的
维护结构应该易于智能体本身检查
自动修复应该尊重已有的所有权
我期望改变的部分:
确切的 agent-index.json schema
工作区和嵌套仓库的作用域
技能发现路径
第三方 provider 的完整性和信任规则
有一段时间,我以为我需要一个更好的方式来告诉编码智能体保持 .claude/、.codex/、手册和技能同步。
最终我意识到,重复的指令本身就是缺失的规范。
包已经知道如何向程序介绍自己。
AIC 是我尝试给它们一种小型、可组合的方式来向编码智能体介绍自己。