Kumiko 将 AGENTS.md、功能索引、组件导出索引及示例源码和测试随 npm 包交付,并用 CI 检查文档漂移。作者以陌生智能体完成四项任务检验效果,同时提醒有限测试不能证明普遍可靠性。
Kumiko 是我们基于 Bun、Hono 和 Postgres 构建的 TypeScript 框架,用于开发多租户应用。现在,越来越多基于它的代码由 coding Agent 编写,但直到上周,这些 Agent 能看到的框架信息还很少。在应用中工作的 Agent,只能看到应用本身及其 node_modules。我们发布的框架包里只有 dist、README 和许可证。文档站虽然存在,却不在 Agent 在应用中可见的范围内;写给 Agent 的说明则放在我们自己工作区的 skills 中,因此只有我们自己的会话读得到。
于是,我们把这些说明移进了包里,然后验证:一个从未看过框架源码的 Agent,能否仅凭包里的内容完成开发。
从 0.364.0 开始,@cosmicdrift/kumiko-framework 包含了一份 AGENTS.md,目前约 350 行。它解释了框架的模型,包括 feature、entity、handler、screen、event 和租户作用域;以“绝对不要这样做,要那样做”的形式列出规则;还提供了一张“需要 X,就用 Y”的对照表。旁边的 agents/ 目录由构建过程根据源码生成,包含:根据 feature manifest 生成的全部内置 feature 索引、全部 UI widget 导出的索引,以及每个经过测试的示例应用各自对应的文件,其中收录了 README、源码和测试。当生成文件与代码不再一致时,一项防漂移检查会让 CI 失败。
通过 create-kumiko-app 创建的新应用,会附带一份简短的 AGENTS.md,供其他 Agent 阅读,同时还会生成一份 CLAUDE.md,用来导入包里的文件:
@node_modules/@cosmicdrift/kumiko-framework/AGENTS.md
这些文档始终与安装的版本一致,而且可以离线使用。启动错误和 guard 消息也会携带 docsUrl,指向对应错误码的文档页面。
我们编写了四项真实应用中可能遇到的任务:
每项任务都有自己全新生成的项目骨架,通过 npm 安装到所有工作区之外的目录中。在工作区内,bun 会解析到工作区链接,而不是 npm tarball,这样 Agent 读到的就会是我们的源码,而不是发布的包。如果某个目录的上级目录中存在 package.json 或 bun.lock,准备脚本就会拒绝运行。
每个项目都分配了一个全新的 Agent(Sonnet),prompt 中包含环境、任务描述和规则:只能在项目内工作,只能读取项目及其 node_modules,不得使用 skills、记忆或网页搜索。任务结束时,它必须在 15 行内报告自己构建了什么、读了哪些文档,以及还缺少什么。
我们没有根据这份报告评分。一个脚本会把 Agent 从未见过的隐藏验收测试复制到项目中,然后运行 typecheck、lint、单元测试、启动验证、schema 验证、集成测试(包括 Agent 自己写的测试和隐藏测试),以及我们的 guard 检查。脚本还会扫描 Agent 的 diff,检查是否出现 any、原生 SQL 写入、内联样式,以及一个我们不允许在集成测试中使用的测试辅助工具。
每个项目都通过了全部八项检查,包括隐藏验收测试。任务 2 的集成测试共运行了 13 项:8 项由 Agent 编写,另外 5 项来自验收测试文件。
不过,每项任务都暴露出了文档缺口:
在任务 1 中,启动错误发挥了作用。Agent 犯了错,错误信息指出了修复方法,它随后完成了修复。而在另外三项任务中,仅靠文档还不够,Agent 通过读取 node_modules 中的类型声明和编译后的 JavaScript 补上了缺失的信息。规则允许这样做,人类开发者也会这么做。但在这次 eval 中,我们仍然把它算作文档缺口,因为下一个 Agent 未必会去那里找。
任务描述明确规定了名称、payload 和 ID,好让隐藏测试能够找到要测试的对象。像“加个功能,让我看看还剩多少任务”这样的开放式需求,要难得多。
任务 3 通过了声明和数据层面的检查。但没有任何检查真正渲染 screen,所以我们不知道标题的 key 能否正确解析。
更大的问题在于 Agent 的运行方式。为了节省时间,我们从一个已有的 Claude Code 会话中启动了 subagent,而没有使用配置目录为空的干净 CLI 环境。subagent 会继承会话中的用户指令和工作区指令,而我们的指令恰好规定了审查时检查的那些规则:禁止使用 any,使用真实的测试技术栈而不是 test dispatcher,不得直接写入 event 表。我们的结果表中有一列记录规则违规情况,四次运行的这一列都为空。但这完全无法说明,包里的文档是否教会了 Agent 这些规则。只有“Agent 读了哪些文档”和“遗漏了什么”这两列,才单独衡量了包内文档的效果。
我们检查了运行记录中是否出现项目之外的路径。四个 Agent 都没有访问我们的源码目录或配置目录,也没有使用 skill 或网页搜索。
全新项目骨架存在一些我们从未注意到的问题,因为我们自己从来不会从全新的项目骨架开始开发。在 Agent 能够开始工作之前,准备脚本不得不先修补其中一些问题,包括版本固定、.gitignore、Biome 配置,以及 .env 中随机生成的 master key 和管理员密码。这些修补都不会帮助 Agent 完成任务:
升级框架不会重写你的 bin/main.ts。如果你使用 0.364.0 或更早版本生成过应用,并以生产模式启动过它,请修改该管理员的密码,并手动添加这个环境变量。
我们自己的一个示例也教错了做法。custom-handlers 示例中的 counter:active 查询,先通过 list 加载一页数据,再用 JavaScript 过滤。这样永远只能看到第一页,而 Agent 需要计数时,很可能就会照抄这个示例。现在,它会在查询中进行过滤,并请求 totalCount。
在编写错误说明页面时,我们还发现,每个 docsUrl 都会返回 404。框架生成的是 /errors/<code>,而文档站提供的是 /en/errors/<code>/。现在,网站已经添加重定向;如果某个错误码没有对应页面,一项检查会让文档构建失败。
这次 eval 原本要验证的那轮运行,仍然没有完成:每个项目配一个 Agent,在项目目录中启动,并使用空的 Claude Code 配置目录,确保我们的设置不会泄漏进去。eval/agent-docs/ 中的 README 描述了这个流程。只有那次运行,才能告诉我们:AGENTS.md 中的规则,能否传达给一个只能接触到发布包的 Agent。
任务、隐藏验收测试和运行器,都放在公开仓库的 eval/agent-docs/ 目录下。
你会如何给这样的 eval 评分?即使任务通过了,我们仍会把“不得不读取编译后的代码”算作文档缺口。我们不确定这条界线划得对不对,也不确定是否应该因此判定任务失败。
如需采取进一步措施,可以考虑屏蔽此人和/或举报滥用行为。