Comet 实现原理深度解析:如何让 AI 编程工作流可恢复、可约束、可评测

Comet 实现原理深度解析:如何让 AI 编程工作流可恢复、可约束、可评测

**版本基线:**本文以 Comet 官方发布版 0.4.0-beta.4(2026-07-12)为准,对应源码提交 4dcf177。该版本仍带有 beta 标识,后续字段与接口可能变化。

项目地址: rpamis/comet。截至 2026 年 7 月 14 日,GitHub 页面显示约 2.3k Star、226 Fork。

**运行环境:**Node.js 20+、npm、Git。Comet 0.4 已改为纯 Node.js 运行时,不再要求 Bash、Git Bash 或 WSL。

Comet 的核心价值,不是把更多 Skill 串在一起,而是把容易漂移的自然语言流程,改造成可恢复、可约束、可审计、可评测的外部控制系统。

它不是新的代码生成模型,而是位于模型外部的工作流控制层。Comet 试图解决的也不是"模型会不会写代码",而是长任务进入真实工程后更难处理的四个问题:

  1. 任务中断后,怎样准确恢复?
  2. 模型怎样被限制在当前阶段?
  3. 设计、实现和验证怎样留下可复核证据?
  4. 一个 Skill 是否有效,怎样用实验判断?

要理解 Comet,先区分两个层次。第一层是单个用户意图的交付流程 ,第二层是承载这套流程的 Harness 如何持续改进

单个意图的交付流程

用户提出一个需求后,Comet 将它具体化为一个可追踪的 change,并管理它从需求澄清到最终归档的生命周期:

text 复制代码
用户意图 / change
  → Skill 路由:确定当前要做什么
  → Classic 工作流状态机:记录当前阶段
  → 阶段守卫:检查能否离开当前阶段
  → 工件交接:传递上下文和证据
  → 验证与归档:确认结果并沉淀规格

这里的 Classic 工作流,是 Comet 内置的规格开发工作流,按 Open、Design、Build、Verify、Archive 五个阶段交付一个 change。Classic 工作流状态机 则是它的阶段控制机制:记录当前阶段、限定合法迁移,并检查迁移所需的前置条件。本文后续统一使用这两个名称。

Harness 的改进流程

单次 change 完成后,Eval 系统还可以检验 Skill 或 Bundle 是否稳定有效,并将结果用于下一轮改进:

text 复制代码
Skill / Bundle
  → Eval 重复评测:检验流程是否稳定有效
  → 结果分析:定位规则、工具或评测问题
  → Skill 迭代:改进后重新评测

这两个闭环不能混为一条业务流程:前者交付用户意图,后者改进交付意图的系统。

本文将 Comet 概括为一个 Agent Skill Harness。这里的 Harness 不是新的模型,也不是单个 Skill,而是包在模型外部的一组运行机制:Skill 引导模型,状态机记录进度,Node.js 程序执行门禁,文件承载上下文,Eval 系统反馈质量。

本文先从使用方式入手,说明 Comet 如何组织一条完整流程;随后进入 0.4.0-beta.4 源码,依次分析路由、状态、守卫、交接、归档和评测。这样安排,是为了先建立"它要解决什么问题"的整体认识,再解释"它具体如何实现"。


1. 先会使用:用最短路径跑通 Comet

1.1 Comet 是什么,又不是什么

Comet 不是新的代码生成模型,而是模型外部的工作流控制层。

Comet 的核心能力可以分为三条主线:

  • Agent 指令 /comet :运行可恢复的 Classic 工作流,把 OpenSpec 与 Superpowers 串成 Open、Design、Build、Verify、Archive 五个阶段。
  • Agent 指令 /comet-any :组合任意 Skill,编排面向特定场景的新工作流,并将组合结果打包为可复用、可分发的 Bundle
  • CLI 命令 comet eval:使用 Rubric、Pass@k 等指标评测 Skill 或 Bundle 的执行质量。

三条主线分别对应固定流程、自由组合和质量反馈。接下来先完成安装初始化,再分别介绍 Classic 工作流/comet-anycomet eval 的使用方式。

1.2 安装与初始化

安装前确认环境:

bash 复制代码
node --version  # 要求 Node.js >= 20
npm --version
git --version

全局安装、初始化并检查环境:

bash 复制代码
npm install -g @rpamis/comet  # 全局安装 Comet CLI
cd your-project               # 进入目标项目
comet init                    # 初始化项目,并自动安装 OpenSpec、Superpowers 等依赖
comet doctor                  # 检查安装、Skill、脚本和状态
comet status                  # 查看活跃 change、任务进度和下一步建议

1.3 Classic 工作流:从需求到归档

/comet 运行固定的 Classic 工作流,将 OpenSpec 与 Superpowers 组织为五个阶段:

阶段 Agent 指令 主要职责 核心工件
Open /comet-open 探索需求,创建 change 和规格工件 proposal.mdtasks.md
Design /comet-design 比较方案,确认设计和验收场景 Design Doc、delta spec
Build /comet-build 制定计划,实现代码并完成测试、调试和评审 Implementation Plan、代码提交
Verify /comet-verify 核对任务、设计和规格,验证实现结果 Verification Report
Archive /comet-archive 合并规格变更,归档已完成的 change 主规格、归档 change

五个阶段是 Classic 工作流的能力全集,但任务不一定全部经过。主入口会根据变更范围,在三种工作流预设之间路由:

预设 适用任务 阶段路径 关键约束
full 新增能力,或涉及公共 API、Schema、跨模块设计 Open → Design → Build → Verify → Archive 完整执行设计、TDD 和评审相关检查
hotfix 修复范围明确、无需重新设计的缺陷 Open → Build → Verify → Archive 跳过 Design,但仍保留实现、验证和归档证据
tweak 不新增能力的小范围行为或配置调整 Open → Build → Verify → Archive 与 hotfix 同为轻量路径,仍受 Build、Verify Guard 约束

hotfixtweak 不是绕过流程,而是缩短流程。两者都从 Open 直接进入 Build;如果执行中发现涉及新能力、公共接口、Schema 或跨模块影响,preset-escalate 会将其升级为 full,再补齐 Design 阶段。主入口还可输出 resume 以恢复已有 change,或输出 ask_user 请求补充信息;这两项是路由结果,不是工作流预设。

假设需求是:

给现有系统增加站内通知中心,只支持站内信,不做短信和推送。

这个需求会新增能力,并可能跨越多个模块,因此应采用 full

text 复制代码
/comet 给现有系统增加站内通知中心,只支持站内信,不做短信和推送

Classic 状态机根据当前阶段、工件和运行证据计算下一步。full 完成 Open 的出口检查后进入 Design,再依次推进 Build、Verify 和 Archive;hotfixtweak 则在 Open 后直接进入 Build。用户也可以预先指定设计、工作区、执行模式和失败处理等默认策略,让流程连续运行;遇到关键决策点时,Agent 会暂停并请求确认。

任务在换会话、上下文压缩或 Agent 退出后中断,只需重新执行主入口:

text 复制代码
/comet

/comet 会读取仓库中持久化的 change、阶段、工件、运行证据和待处理动作,再判断应继续当前阶段、进入下一阶段,还是请求用户补充决策。用户不需要手动指定阶段,也不应直接修改 .comet.yaml 或工件来推动流程。所有状态变化都应由阶段指令、Guard 和状态转移函数完成。

1.4 /comet-any:创建并复用团队工作流

/comet-any 用于把团队规范生成可执行的 Skill Bundle。以"实现页面必须使用 elementui,完成后必须执行 whitebox-code-standard 审查"为例,负责人先输入:

text 复制代码
/comet-any
请创建一个团队开发工作流:
1. 保留 Open、Design、Build、Verify、Archive 五个阶段;
2. Build 阶段及其子代理必须使用 elementui Skill;
3. Build 完成后必须使用 whitebox-code-standard Skill 审查;
4. 生成后先评测,不要直接安装。

Agent 会先发现真实可用的 Skill,再展示工作流节点、Skill 绑定、Guard 和 Handoff。用户确认方案后,才生成 Bundle,并依次完成评测、审查和人工审批:

bash 复制代码
comet creator generate team-comet --json
comet eval ./generated-skill/comet/eval.yaml --quick --html
comet publish review team-comet --platform cursor --json
comet publish approve team-comet --reviewer team-lead --json

这里的 team-comet 是自定义的 Bundle 名称。生成的入口 Skill 名称通常也是 team-comet,安装到 Agent 平台后,用户通过 /team-comet 调用;它不是新的 comet CLI 子命令。/comet-any 负责创建工作流,/team-comet 负责使用工作流。

同一项目:提交 Git 后直接共享

如果所有人都基于同一个项目开发,负责人将 Bundle 分发到该项目,并把生成文件提交 Git:

bash 复制代码
comet publish distribute team-comet --platform cursor --scope project --preview --json
comet publish distribute team-comet --platform cursor --scope project --json

git add .cursor
git commit -m "chore: add team-comet workflow"
git push

团队成员拉取代码后,重新打开项目或创建新的 Agent 会话,即可使用:

text 复制代码
/team-comet 给订单系统增加退款审批功能

工作流定义由 Git 共享,任务状态仍保存在各自项目的工作区中,互不影响。中断后再次输入 /team-comet,入口 Skill 会读取当前项目状态并恢复流程。更新工作流时,由维护者重新生成、评测、审批并提交新版本。

跨项目、跨机器:单独维护工作流仓库

如果多个项目需要复用同一个工作流,建议建立独立的工作流仓库,例如:

text 复制代码
team-workflows/
└── team-comet/
    ├── skills/
    ├── reference/
    ├── scripts/
    ├── rules/
    └── comet/

负责人在该仓库中完成生成、评测和审批,然后提交 Git:

bash 复制代码
git add team-comet
git commit -m "feat: publish team-comet workflow"
git push

其他机器或项目使用时,先拉取这个仓库:

bash 复制代码
git clone <team-workflows-git-url>
cd team-workflows

然后基于该仓库的 Bundle 分发到当前机器的 Cursor。建议先预览,再正式安装:

bash 复制代码
comet publish distribute team-comet --project . --platform cursor --scope global --preview --json
comet publish distribute team-comet --project . --platform cursor --scope global --json

--scope global 适合让当前机器上的多个项目使用 /team-comet。如果只希望某个项目使用,应将分发结果同步到该项目的 .cursor 目录,并提交该项目 Git。无论采用哪种方式,目标项目都应安装 Comet 基础 Skill,并确保 elementuiwhitebox-code-standard 等依赖 Skill 可被发现。

因此,推荐的复用策略是:同项目直接提交 .cursor;跨项目建立工作流仓库;跨机器先拉取工作流仓库,再执行分发。工作流定义统一管理,项目中的 change、阶段和运行状态则分别保存。

1.5 comet eval:评测 Skill 与 Bundle

comet eval 使用 Rubric、Pass@k 等指标评测 Skill 或 Bundle 的执行质量。开发本地 Skill 时,可先运行快速评测并生成 HTML 报告:

bash 复制代码
comet eval ./my-skill --quick --html

/comet-any 生成 Bundle 后,可先检查评测清单能否被正确加载,再执行完整评测:

bash 复制代码
comet eval ./generated-skill/comet/eval.yaml --collect  # 预检评测用例
comet eval ./generated-skill/comet/eval.yaml --html     # 执行评测并生成报告

Comet 还提供 Dashboard,用于查看当前阶段、任务进度和归档历史:

bash 复制代码
comet dashboard

至此,Comet 的基本使用流程已经完整。下面从整体架构开始,分析它如何保存状态、恢复任务并约束 Agent 行为。


2. 整体架构:四层 Harness 分离概率与确定性

Comet 的架构原则,是让模型处理语义,让程序处理状态。

text 复制代码
┌──────────────────────────────────────────┐
│ 引导层:SKILL.md、Rules、Reference       │
├──────────────────────────────────────────┤
│ 控制层:State、Guard、Handoff、Archive    │
├──────────────────────────────────────────┤
│ 状态层:YAML、JSON、JSONL、Markdown       │
├──────────────────────────────────────────┤
│ 反馈层:Eval Harness、Rubric、Pass 指标    │
└──────────────────────────────────────────┘

2.1 引导层负责语义,不做最终裁决

assets/skills-zh/comet/SKILL.md 是主入口协议。它要求 Agent 先发现 active change,再构造 CometIntentFrame,最后交给 runtime scorer 路由。

主入口能识别 full、hotfix、tweak、resume 和 ask_user。Skill 擅长理解"这是修 Bug 还是新增能力",但它不是状态权威,因为自然语言可能被遗忘或误读。

所以源码把阶段推进交给 comet statecomet guard。Skill 只负责告诉 Agent 何时调用。

2.2 控制层负责确定性操作

经典流程的核心实现位于 domains/comet-classic/

源文件 作用
classic-state.ts 定义 Classic 状态与字段校验
classic-transitions.ts 定义合法事件和状态变化
classic-guard.ts 检查阶段退出条件
classic-hook-guard.ts 写文件前检查阶段权限
classic-handoff.ts 生成交接包与哈希
classic-archive.ts 调用 OpenSpec 并归档
classic-resume-probe.ts 判断是否自动恢复
classic-state-events.ts 追加状态迁移审计

这些模块不依赖模型"自觉"。检查失败会返回非零退出码,阶段不会推进。

2.3 状态层把记忆移出上下文

Comet 0.4 按职责拆分状态。下表中的"写入与维护方",是指负责读写该文件的组件或参与者,并非文件的所有权主体:

文件 写入与维护方 记录内容
.openspec.yaml OpenSpec change 的规格生命周期状态
.comet.yaml Comet 的 Classic 工作流状态机 当前处于 Open、Design、Build、Verify 或 Archive 阶段,以及构建、验证和归档状态
.comet/run-state.json Comet 运行引擎 当前执行到的步骤、迭代次数和待处理动作,用于中断后继续执行
.comet/state-events.jsonl Comet 状态迁移模块 每次状态变化的追加式事件记录,用于追踪和审计
Markdown 业务工件 人与 Agent 协作维护 proposal.mdtasks.md、Design Doc、Implementation Plan 和 Verification Report 等需求、设计、实施计划与验证证据

这里的 Classic 工作流状态机,就是前文介绍的 Classic 工作流的控制模块。Comet 运行引擎 是 Comet 内部负责逐步执行任务并记录运行进度的程序组件,不是另一套工作流。Comet 状态迁移模块 同样属于 Comet,它把状态变化写成只追加、不覆盖的事件日志。

这套拆分将人类可读的业务工件与机器运行状态分开,也将当前状态快照与历史迁移记录分开。

2.4 反馈层用实验替代感觉

Comet Eval 要回答的是:同一个 Skill 在标准任务上重复运行,是否稳定地产生预期结果?

CLI 定位 npm 包内的 eval/ Harness,再通过 uv run pytest 启动测试。源码见 app/commands/eval.ts 第 32---192 行

四层组合后,模型理解需求并生成代码;程序校验状态和证据;文件保存跨会话事实;评测判断 Skill 是否值得发布。

3. 主入口原理:/comet 如何决定"新建、恢复还是暂停"

**主入口不是别名,而是一个带安全默认值的决策器。**它先理解请求,再读取仓库事实,最后只输出当前证据允许的动作。

3.1 路由输入分为两类

路由必须同时回答"用户要做什么"和"仓库现在是什么状态"。前者来自自然语言,后者来自 active change、阶段、工件和运行状态。只看其中一类都会产生错误:可能把普通问答接到旧 change,也可能在已有流程上重复创建 change。

CometIntentFrame 把自然语言转换为固定字段,例如请求动作、候选预设、change ID,以及是否涉及新能力、公共 API、Schema 或跨模块影响。模型负责提取语义,runtime scorer 再将结果归一化为 fullhotfixtweakresumeask_user。这样,概率性的理解只发生在入口,后续控制使用结构化结果。

3.2 恢复探测遵循"证据不足就询问"

resolveCometResumeProbe() 先排除已在流程内、明确拒绝 Comet、没有 active change 等情况;当存在多个 change、工作区有未提交改动,或流程处于 plan-ready、verify fail 等决策点时,它不会猜测归属,而是返回 ask_user。只有 change 名称、继续意图和工作区状态共同支持时,才自动恢复。

这套顺序的原理是默认不越权 ,而不是记住一组关键词。来源见 classic-resume-probe.ts 第 303---379 行

3.3 下一步由仓库事实计算

comet status 汇总阶段、预设、任务勾选、验证结果、Engine 诊断和最近命令证据,再生成 nextCommand。因此它回答的不是"模型觉得下一步是什么",而是"当前事实满足哪条规则"。

text 复制代码
nextCommand = f(phase, workflow, artifacts, taskProgress, evidence)

所谓零上下文恢复,恢复的也是这些已持久化事实,而不是模型没有写下来的隐性思考。换会话后,入口重新加载 change、阶段、步骤、工件引用和 pending action,再从同一组输入得到下一步。源码见 app/commands/status.ts 第 42---173 行


4. Classic 工作流状态机原理:快照、运行状态与事件如何协同

**Classic 状态机不是一份状态文件,而是三种不同时间尺度的信息共同工作。**快照回答"现在处于什么阶段",运行状态回答"这一轮执行到哪一步",事件回答"状态为什么发生变化"。三者分工不同,不能相互替代。

先看一个最小模型:

text 复制代码
事件 event
   │ 经过合法性校验
   ▼
状态快照 snapshot ──决定下一步允许做什么
   │
   └──关联运行状态 run-state ──恢复当前动作和执行进度

每次快照变化,同时追加一条事件记录,用于审计和追溯

4.1 先分清三个概念

快照:流程当前的业务事实

.comet.yaml 是 Classic 工作流的状态快照。它记录 change 当前处于 Open、Design、Build、Verify 还是 Archive,以及预设、构建方式、隔离方式、验证结果和工件引用等业务字段。

ClassicState 用枚举约束 phasebuild_modeisolationverify_result 等字段,并校验工件路径与哈希格式。源码见 classic-state.ts 第 3---188 行

快照只说明"当前位置",不等于"阶段已经完成"。例如 phase: build 表示当前在 Build;它不能证明代码已构建。verify_result: pass 也必须来自合法迁移和对应证据,而不是 Agent 的口头声明。

运行状态:一次执行的过程进度

.comet/run-state.json运行状态。它记录当前执行到哪个步骤、执行了多少轮、使用了哪个 Skill 版本、引用了哪些工件、重试了几次,以及是否存在待处理动作。

它关注的是"这次运行做到了哪一步",而不是 change 的长期业务阶段。writeRunState() 采用临时文件加 rename 的方式写入,降低进程中断时留下损坏 JSON 的风险。源码见 domains/engine/state.ts 第 99---176 行.comet.yaml 只通过 run_id 关联对应运行状态。

事件:状态变化的事实记录

.comet/state-events.jsonl追加式事件日志 。每次状态迁移,appendClassicStateEvent() 都记录事件名称、来源、迁移前后的状态和字段差异。源码见 classic-state-events.ts 第 1---37 行

事件日志关注"发生过什么,以及为什么发生"。它不负责直接决定当前阶段,也不应被当作可随意修改的当前状态。快照适合快速决策,事件适合审计和追溯。

4.2 状态机真正控制的是"事件能否改变状态"

Agent 不能直接把 phaseopen 改成 build。它只能请求一个事件,例如完成 Open、跳过 Design、验证失败或验证通过;状态机再根据当前快照判断该事件是否合法。

CLASSIC_TRANSITION_TABLE 声明事件允许从哪些状态出发,applyClassicTransition() 统一计算新的快照。full 在 Open 后进入 Design,轻量预设可直接进入 Build;verify-fail 回到 Build;preset-escalate 可升级为 full。实现见 classic-transitions.ts 第 3---154 行

可以把一次迁移理解为一个受约束的函数:

text 复制代码
newSnapshot = transition(currentSnapshot, event)

如果当前阶段不允许该事件,函数就拒绝迁移;如果允许,程序才更新 .comet.yaml,并追加对应事件。这样,阶段推进不再依赖模型是否正确记住流程,而依赖程序是否接受这次状态变化。

4.3 三者如何协同:以一次 Handoff 为例

**Handoff 展示了状态机如何同时处理"意图、进度和结果"。**它会修改交接文件、上下文索引和 checkpoint,不能假设所有写入会一次完成。

一次典型执行可以拆成五步:

  1. 记录意图 :运行状态写入 pending action,说明准备生成哪一份 Handoff。
  2. 执行副作用:程序读取 proposal、Design、tasks 和规格文件,生成 Markdown、JSON 和哈希。
  3. 记录进度:运行状态写入已生成的文件、源工件哈希和 checkpoint。
  4. 确认结果:程序检查目标文件存在,且记录的哈希与当前内容一致。
  5. 完成动作 :确认成功后清除 pending action;若失败则保留它,等待恢复处理。

如果进程在第 2 步后退出,下一次运行不会从聊天记录猜测进度。它会读取 pending、目标文件和哈希,然后判断是继续生成、复用已完成结果,还是拒绝陈旧结果。

因此,可恢复性不是"保存了一个阶段字段",而是同时保存了操作意图、执行进度和结果证据。只有这三类信息齐全,程序才能区分"尚未开始""执行一半"和"已经完成"。

4.4 为什么既要快照,也要事件

只保存快照,系统能快速知道当前阶段,却无法解释状态来源。只保存事件,系统虽然保留了完整历史,但每次恢复都要重放事件,复杂度更高,也更容易受到旧数据异常影响。

Comet 采用两者并存的方式:.comet.yaml 保存当前可直接读取的快照,.comet/state-events.jsonl 保存不可覆盖的迁移历史。运行状态则补足"单次动作执行到哪里"的信息。

最终可以这样记忆:**YAML 管业务当前位置,JSON 管运行中断恢复,JSONL 管状态变化审计。**它们共同构成工作流的持久化控制面,而不是三份重复数据。


5. 守卫原理:在不同时间点阻止不同错误

**Comet 不是重复检查同一件事,而是在"决策、迁移、写入"三个时间点分别设防。**三层守卫的覆盖范围不同,组合后才形成闭环。

5.1 Skill 约束决策过程

Skill 说明当前阶段应该调用哪些能力、何时暂停询问、何种变化需要升级预设。它能理解语义,却仍依赖模型遵守,因此适合引导,不适合作为最终门禁。

5.2 Guard 控制阶段出口

classicGuardCommand() 先校验 change、目录和状态 Schema,再按当前阶段收集证据。任一条件失败,就返回 BLOCKED 和非零退出码;只有全部通过,--apply 才调用状态迁移。入口见 classic-guard.ts 第 698---753 行

各阶段检查的不是相同文件,而是对应阶段的完成定义:

出口 要证明的事实 主要证据
Open 需求已形成可执行 change proposal、design、tasks
Design 设计可被后续阶段准确接收 Design Doc、handoff、hash
Build 计划与实现确实完成 模式选择、任务勾选、构建结果
Verify 实现满足已声明标准 验证命令、报告、分支状态
Archive change 已形成闭环 归档状态、完整工件、完成任务

这套设计把"完成"从一句自然语言声明,转换为一组可复核谓词。以 Build 为例,Guard 不仅看任务是否勾选,还检查隔离方式、执行模式、TDD 与 Review 选择,以及构建证据。相关实现见 classic-guard.ts 第 527---558 行

如果无法从 package.jsonpom.xmlCargo.toml 推断构建命令,用户可执行自定义命令,再用 record-check 保存命令、工作目录、退出码和时间。Guard 检查记录,而不是相信 Agent 口头报告。实现见 classic-guard.ts 第 249---348 行

5.3 Hook 控制写入入口

Guard 只能阻止离开阶段,无法阻止 Agent 在 Open 阶段提前修改业务代码。PreToolUse Hook 因此在文件写入前运行:先将目标转换为仓库相对路径,再确定所属 active change,最后按阶段判断路径是否可写。

Open 和 Design 主要允许规格与设计工件;Build 才允许业务源码;Archive 只允许归档相关变更。多个 change 无法确定归属时,Hook 要求先选择 change。realpath 检查还用于阻止符号链接逃逸。实现见 classic-hook-guard.ts 第 382---437 行

5.4 三层守卫形成的保证不同

text 复制代码
用户请求
  → Skill:这个动作在语义上是否合适?
  → Hook:这个文件现在是否允许写?
  → Guard:现有证据是否足以离开阶段?
  → Transition:目标状态是否合法?

Skill 失败时,Hook 和 Guard 仍可限制副作用;没有 Hook 时,Guard 仍能阻止错误推进,却不能证明业务文件从未被提前修改。因此,未声明 supportsHooks 的 Cursor 等宿主,只能达到"阶段出口可控",不能达到"每次写入前可拦截"。平台能力定义见 platform/install/platforms.ts 第 8---68 行


6. 交接原理:把上下文变成可校验输入

**Handoff 的目标不是缩短一段对话,而是固定 Build 阶段实际依赖的规格版本。**如果新 Agent 重新总结聊天记录,设计约束可能遗漏,也无法判断原文后来是否变化。

6.1 交接包由源工件确定

Comet 固定读取 proposal、design、tasks 和各 capability 的 spec.md。程序按路径排序,计算每个文件的 SHA256,再对"路径 + 文件哈希"计算总 context_hash。同一组文件产生同一结果,任一文件变化都会形成新版本。源码见 classic-handoff.ts 第 66---91 行

Handoff 同时生成 Markdown 和 JSON。前者给人和 Agent 阅读,包含来源路径、行范围、哈希与内容;后者给程序索引和校验。两种格式共享同一组源文件,因此"可读上下文"和"机器证据"不会各自演化。

6.2 压缩按工件角色进行

普通模式下,compact 截取超长文件,full 嵌入全文,两者都保留路径与哈希。context_compression=beta 时,验收所需的 delta spec 保留原文,proposal、design、tasks 退化为哈希引用,OpenSpec 继续作为 canonical spec。实现见 classic-handoff.ts 第 100---232 行

这里的关键不是压缩算法,而是优先保留直接约束实现与验收的内容。支撑材料即使不内嵌,也能通过路径和哈希定位原文。

6.3 哈希与 checkpoint 保证可重复执行

生成前,Engine 写入 pending action 并进入 waiting;交接文件、context、artifacts 和 checkpoint 全部落盘后,才清除 pending。再次执行时,若源哈希和各项记录一致,程序复用已有结果;若源文件已变化,则拒绝陈旧交接。

因此,Handoff 同时解决两个问题:中断后能判断执行到了哪里,重复执行时不会悄悄生成不同上下文。SHA256 证明的是版本一致性,不证明规格内容正确。


7. 归档原理:把 change 纳入项目正式规格

**归档的本质,是把一次临时 change 转换为项目长期认可的结果。**它不只是移动目录,而是同时完成三件事:将增量规格合并进主规格、给设计和实施文档标记最终状态、关闭 Comet 中的工作流。

text 复制代码
归档前                              归档后
change 中的增量规格                  合并后的主规格
进行中的 Design Doc 和 Plan   →      已完成标记的文档
phase: archive                      archived: true

这三部分必须指向同一个完成结果。否则可能出现规格已经更新,但 Comet 仍显示未归档;或者状态已经关闭,主规格却没有合并成功。

7.1 先理解"临时变更"与"正式规格"

OpenSpec 在 change 目录中使用 delta spec 描述本次变化。ADDEDMODIFIEDREMOVEDRENAMED Requirements 表示某项要求应如何新增、修改、删除或重命名。

这些标记只适合描述"本次改了什么",不是项目长期规格的最终写法。归档时,OpenSpec 会把 delta 应用到主规格。合并后的主规格只保留当前有效的系统要求,成为后续 change 和 Agent 读取的正式依据。

因此,归档不是保存一份历史副本,而是完成一次知识转换:change 记录变化过程,主规格记录变化后的系统事实。

7.2 归档开始前必须满足三个条件

classicArchiveCommand() 不会收到命令就立即修改文件。它首先检查三个条件:

  1. 流程已到 Archive 阶段phase 必须是 archive,前面的阶段不能被跳过。
  2. 验证已经通过verify_result 必须是 pass,失败的实现不能进入正式规格。
  3. 用户已经确认archive_confirmation 必须是 confirmed,对话中的模糊同意不算机器可验证的授权。

目标目录还必须通过路径合法性检查。任何条件不满足,归档都会停止。用户可以先使用 --dry-run 查看将要合并的规格、标记的文档和更新的状态,再决定是否确认。

这些检查的目的,是把"可以归档"变成明确的机器条件。Skill 负责提醒用户确认,状态字段负责保证命令无法绕过确认。

7.3 归档按固定顺序更新三个对象

条件满足后,Comet 才开始执行归档。顺序如下:

  1. 调用 openspec archive <change> --yes,把 delta 合并到主规格。
  2. 检查合并后的主规格,确认没有遗留 delta 专用标题。
  3. 给 Design Doc 和 Implementation Plan 写入完成标记。
  4. 更新 artifacts 和 checkpoint,保存本次归档结果。
  5. 将 Classic 状态标记为 archived,追加状态事件。
  6. 所有步骤确认完成后,清除 pending action。

核心实现见 classic-archive.ts 第 185---365 行

这个顺序体现了一条关键原则:**先完成正式规格合并,再关闭 Comet 工作流。**如果 OpenSpec 合并失败,Comet 不会提前把 change 标记为已归档,从而避免状态与真实文件不一致。

7.4 pending 和 checkpoint 负责处理中断

归档会连续修改主规格、文档和 Comet 状态,这些操作无法在一个文件写入中同时完成。进程可能在任意两步之间退出,留下"完成了一部分"的状态。

Comet 因此在开始前写入 pending action,记录准备执行的归档动作;每完成关键步骤,再更新 checkpoint 和产物记录。全部完成后,pending 才会被清除。

恢复时,程序不依赖 Agent 猜测,而是检查三类事实:

  • pending 表明原计划执行什么;
  • checkpoint 表明流程推进到哪里;
  • 目标文件和状态表明哪些结果已经真实落盘。

程序据此判断应继续未完成步骤、接受已有结果,还是因结果冲突而停止。这里所谓可恢复,不是简单地"重新运行一次命令",而是先识别上次停在哪里,再避免重复或遗漏操作。

7.5 清洁检查保证主规格只表达当前事实

归档完成后,主规格中不应继续出现 ADDEDMODIFIEDREMOVEDRENAMED Requirements 等 delta 标题。否则后续 Agent 可能把"修改指令"误读为系统当前要求。

verifyMainSpecsClean() 会扫描合并结果,并在发现这些临时标记时阻止归档完成。通过检查后,长期知识由主规格、Design Doc、Implementation Plan、Verification Report 和状态事件共同保存。

最终,归档形成一个闭环:**验证结果证明实现可以接纳,用户确认授予归档权限,OpenSpec 更新正式规格,Comet 再关闭工作流并保留审计记录。**这才是"把临时 change 变成长期事实"的完整含义。


8. 跨平台原理:统一控制协议,按宿主能力降级

**Comet 复用的是同一套工作流协议,不是同一套安装文件。**不同 Agent 宿主的 Skill 目录、规则格式、配置入口和 Hook 能力不同,安装器必须把统一资源映射到平台能力。

8.1 PLATFORMS 是适配器注册表

每个平台条目声明项目级与全局 Skill 目录、历史目录、Rules 格式、OpenSpec 工具 ID 和 Hook 能力。comet init 根据注册表探测宿主,再生成 install、overwrite 或 skip 计划,最后安装 Skill、Rules、Hook 和工作目录。完整定义见 platform/install/platforms.ts 第 43---267 行

这种方式把"工作流定义"和"宿主落盘方式"分开。新增平台主要扩展适配数据与配置合并逻辑,不必复制状态机、Guard 或 Archive 实现。

8.2 能力缺失时只能降低保证等级

平台适配不应伪装不存在的能力。支持 PreToolUse Hook 的宿主可以在写入前调用 Hook Guard;未声明 supportsHooks 的宿主只能安装 Skill、Rules 和阶段 Guard。

所以"支持 33 个平台"表示工作流可以分发,不表示 33 个平台具有相同约束强度。使用者应分别确认:Skill 能否加载、阶段出口能否检查、文件写入能否提前拦截。

8.3 Node.js 统一的是运行基础

0.4 将核心 TypeScript 构建为 .mjs,统一路径、进程和文件 API,并通过 npm 一并分发 CLI、Skill 与 Eval Harness。这减少了 Bash、Git Bash 和 WSL 差异,也让状态机与 Guard 可以用 Vitest 测试。

但 Node.js 只统一 Comet 自身运行时。归档仍依赖 OpenSpec CLI,Eval 仍需 uv 与 Pytest。因此准确结论是"核心控制面无 Bash 依赖",而不是"系统没有外部依赖"。


9. Eval 原理:怎样判断一套 Skill 是否真的可靠

**Eval 解决的核心问题,是区分"偶然做对一次"和"能够稳定做对"。**大模型输出具有随机性。同一套 Skill 面对同一个任务,多次执行也可能得到不同结果,因此不能用一次成功证明它已经可靠。

假设一套代码审查 Skill 连续运行五次,只有两次发现了关键漏洞。它证明这套 Skill"有能力发现",却不能证明团队每次使用都能得到可靠审查。Eval 的作用,就是把这种波动变成可重复、可比较的数据。

text 复制代码
固定版本的 Skill 或 Bundle
          +
固定的评测任务和评分标准
          ↓
       重复执行
          ↓
逐次评分 → 汇总指标 → 失败归因 → 判断是否可发布

9.1 一次 Eval 固定三类输入

要让两轮结果可以比较,首先必须固定三类输入:

  1. 被测对象:要评测的 Skill 或 Bundle。
  2. 评测任务:要求 Agent 完成的标准化场景。
  3. 评分标准:判断结果是否合格的 Rubric。

Rubric 可以理解为一张评分表。它把"完成得好"拆成可检查的条件,例如是否修改了正确文件、是否补充测试、是否通过命令验证、是否违反既定约束。

评测清单 eval.yaml 负责描述要运行哪些任务及相关配置。它把测试对象和测试规则固定下来,避免不同轮次临时换题或改变标准。

这里的原则与软件测试相同:只有输入和判定标准一致,前后两次结果才有比较意义。

9.2 重复运行是为了测出随机波动

一次任务通过,只能说明这次运行成功。Eval 会让同一版本在相同任务上运行多次,再分别记录每次结果。

例如,某项任务运行五次:

text 复制代码
第 1 次:通过
第 2 次:失败
第 3 次:通过
第 4 次:失败
第 5 次:通过

这个结果表明 Skill 具备完成任务的能力,但稳定性不足。如果只展示第 1 次结果,就会掩盖后续失败;如果只展示平均分,又可能看不出结果在成功与失败之间大幅波动。

因此,Eval 不只关心"最高能做到什么",还关心"重复使用时有多稳定"。这两类问题需要不同指标回答。

9.3 不同指标回答不同问题

**Pass@k 关注多次尝试中能否至少成功一次。**它更适合衡量探索能力。例如生成多个候选方案,只要其中一个可用,就仍有实际价值。

**连续成功类指标关注每次执行是否都可靠。**它更接近自动化生产场景,因为无人值守流程不能依赖"失败后多试几次"。

weightedScore 用于汇总带不同权重的评分项,反映整体完成质量。instabilityGap 用于观察结果波动,帮助识别"平均成绩尚可,但每次表现差异很大"的情况。

这些指标不能互相替代:

  • Pass@k 高、稳定性低:偶尔能做对,适合人工挑选结果。
  • 整体得分高、波动大:能力较强,但行为不可预测。
  • 得分和稳定性都高:更适合进入自动化工作流。

所以 Eval 的结论不应只是一个总分,而应同时说明能力上限、平均质量和运行稳定性

9.4 comet eval 把评测过程固定下来

comet eval 负责把评测清单转换成一次可重复执行的测试过程。它解析 Skill 目录或 comet/eval.yaml,定位 Comet 包内的 Eval Harness,检查 uv,生成运行期 manifest,再通过 uv run pytest 执行任务并生成报告。

这里的 Eval Harness 是评测执行器。它负责加载任务、启动测试、收集结果和生成报告,并不等于被测试的 Skill。入口实现见 app/commands/eval.ts 第 32---192 行

整个过程可以分成四步:

  1. 发现:读取清单,找到被测对象和任务。
  2. 准备:检查依赖,生成本轮实际使用的 manifest。
  3. 执行:按配置重复运行任务,并用 Rubric 评分。
  4. 汇总:计算指标,记录失败,生成可查看的报告。

--collect 只执行发现和预检,不真正运行昂贵的 Agent 任务。它适合提前发现路径错误、任务缺失和依赖问题。

CLI 的价值不是让一次运行自动变得正确,而是保证每轮评测采用相同入口和记录方式,从而能够复现和比较。

9.5 评测结果必须绑定具体版本

Skill 修改后,旧评测结果不能继续证明新版本可靠。评分任务或 Rubric 发生变化后,旧结果同样不能与新结果直接混用。

Comet 为此给两类内容计算哈希:

  • draftHash 标识被测 Skill 或 Bundle 的版本;
  • evalManifestHash 标识本轮任务和评测配置的版本。

RepositoryEvalResult 同时保存这两个哈希,以及 tasks、treatments、指标、失败和报告。写入结果时,程序会重新比较当前哈希;如果内容已经变化,旧结果就不能登记为当前版本的证据。实现见 domains/bundle/eval.ts 第 354---405 行

这相当于给报告注明"测试了哪个版本、使用了哪套试题"。哈希不证明 Skill 一定正确,但能防止旧报告为新版本背书。

9.6 被测对象是整套控制机制

/comet-any 生成的 Bundle 不一定只有提示词。它还可能包含 Skills、Scripts、Rules、Hooks、References 和 Agents。这些部分会共同影响 Agent 的最终行为。

例如,Skill 可能正确要求"修改后必须运行测试",但 Hook 配置缺失,实际工作流仍可能跳过约束。只评审 SKILL.md,无法发现整套流程是否完整。

validateStableFactoryControlPlane() 因此会检查工作流协议、决策点、恢复资源和 manifest 声明。源码见 domains/bundle/eval.ts 第 44---152 行

所以 Comet Eval 的被测对象不是孤立提示词,而是提示词与脚本、规则、Hook 等机制共同形成的实际工作流

9.7 失败归因决定下一步改什么

评测失败不一定说明 Skill 有问题。失败可能来自以下几类原因:

  • 工作流问题:规则缺失、指令冲突或工具调用错误;
  • 评测问题:任务描述含糊,Rubric 无法正确判断;
  • 模型问题:模型能力不足,或单次输出发生随机偏差;
  • 环境问题:认证失败、网络异常、限流或依赖缺失。

如果把网络超时当成 Skill 缺陷,就会错误修改工作流;如果把真实业务失败归因于环境波动,又会放过不可靠版本。

因此,报告既要给出"通过率和稳定性如何",也要记录 failure attribution,即失败属于哪一类。指标决定版本是否变好,失败归因决定应该改 Skill、改评测任务、修复环境,还是重新执行。

最终,Eval 建立的是一条证据链:**固定被测版本,使用固定任务重复运行,按固定 Rubric 评分,再结合指标和失败原因判断是否可靠。**它不能证明未来永远不会失败,但比一次演示成功更接近真实工程中的可靠性。


10. Comet 能保证什么,不能保证什么

Comet 能保证工作流按既定规则推进,并保存可复核的过程证据;它不能保证需求、设计、代码和评测标准本身一定正确。

本章不是罗列 Comet 的产品缺陷,而是说明每种机制的保证范围。前文解释了这些机制如何工作,本章进一步回答:使用它们后,我们可以合理期待什么,又有哪些判断仍然必须由人完成。

Comet 机制 能保证什么 不能保证什么
状态机 阶段和迁移符合预设规则 需求方向正确
Guard 指定证据存在并满足检查条件 证据覆盖所有风险
Handoff 与哈希 使用的是指定版本的上下文 上下文内容正确
Hook 在宿主支持时拦截越权写入 所有平台约束能力相同
Eval 衡量给定任务上的重复表现 未来所有任务都不会失败

10.1 状态合法,为什么不代表需求正确

状态机检查的是流程是否合法。例如,Open 完成后才能进入 Design,Verify 通过后才能进入 Archive。它可以阻止跳过阶段或提交非法状态组合。

但状态机无法判断用户是否真正需要这个功能,也无法判断设计方向是否合理。一个理解错误的需求,同样可能拥有完整 proposal、Design Doc 和合法状态,并最终完成归档。

因此,两类判断必须分开:

  • 状态机判断"是否按规则执行";
  • 用户和工程人员判断"是否在做正确的事情"。

Open 与 Design 阶段保留人工确认,正是为了处理第二类问题。Comet 能防止流程失控,不能替代产品判断和架构决策。

10.2 验证通过,为什么不代表测试充分

Guard 能检查任务是否勾选、测试命令是否执行、退出码是否为 0,以及 Verification Report 是否存在。它可以阻止 Agent 在没有证据时直接宣称完成。

但这些检查只能证明"指定动作已经完成"。如果测试遗漏关键场景、断言本身写错,或者验证命令没有覆盖受影响模块,Guard 仍可能看到一个合法的成功结果。

因此,Guard 提供的保证是:**验证过程确实发生,并留下了符合规则的证据。**它不能保证测试设计已经覆盖所有风险,也不能替代代码评审和工程判断。

10.3 哈希一致,为什么不代表内容可信

Handoff 使用哈希确认 Build 读取的是哪一版规格,Eval 使用哈希确认报告对应哪一版 Skill 和评测清单。只要内容变化,哈希也会变化,从而阻止旧上下文或旧报告被悄悄复用。

但哈希只回答"是不是这一版",不回答"这一版写得是否正确"。错误规格、有偏的 Rubric 和不完整的任务清单,都可以拥有完全合法的哈希。

所以哈希提供的是版本一致性 ,不是内容正确性。内容质量仍要通过需求评审、设计评审和评测标准审查来保证。

10.4 平台支持,为什么不代表约束能力相同

Comet 可以把 Skill、Rules 和工作流命令分发到多个 Agent 宿主,但不同宿主提供的扩展能力并不相同。

支持 PreToolUse Hook 的宿主,可以在 Agent 写文件前调用 Hook Guard,提前阻止阶段外修改。不支持该 Hook 的宿主,仍可在阶段出口运行 Guard,却无法证明 Agent 从未提前修改源码。

因此,"支持 Comet"至少包含三个不同等级:

  1. 工作流可加载:宿主能够发现并运行 Skill。
  2. 阶段出口可控制:Guard 能阻止证据不足的阶段迁移。
  3. 每次写入前可拦截:Hook 能在文件修改发生前检查权限。

平台兼容只说明基本工作流可以运行,不代表所有平台都达到最高约束等级。使用者应根据宿主能力判断实际保证强度。

10.5 流程更完整,为什么不一定更适合

Comet 的每项保证都有成本。规格维护、人工确认、状态诊断、验证和重复评测都会增加执行时间、Token 消耗和协作负担。

跨模块、周期较长、需要审计的 change,失败代价较高,完整流程带来的收益通常大于成本。拼写修复或低风险配置调整若执行五个阶段,流程成本反而可能超过变更本身。

fullhotfixtweak 的区别,就是按风险选择约束强度。轻量预设可以省略 Design,但仍保留 Build、Verify 和 Archive 的核心证据。

所以正确原则不是"流程越多越可靠",而是:变更风险越高、失败代价越大,越需要更完整的约束和证据。

本章可以归结为一句话:**Comet 管理的是过程可靠性,不是结果必然正确。**它让错误更难被无证据地掩盖,让中断和状态变化更容易追溯,但最终的需求取舍、设计质量和风险判断仍属于人类工程责任。


11. 从 Comet 提炼出的五条 Agent 工程原则

原则一:提示词负责引导,程序负责约束

模型适合判断意图,不适合充当状态数据库。凡是"绝不能跳过"的规则,都应尽量落到退出码、Schema 或 Hook。

原则二:模型上下文不是可靠状态存储

跨会话事实必须进入文件、数据库或事件流。否则所谓恢复,只是在要求新模型重新猜测。

原则三:阶段出口必须检查可验证证据

"我完成了"不是证据。文件、哈希、测试输出、退出码、时间和分支状态,才是可以复核的输入。

原则四:长操作必须设计 pending 与幂等恢复

涉及多文件写入、外部命令和目录迁移时,应先记录意图,再执行副作用,最后写 checkpoint。重复调用应能识别已完成结果。

原则五:Skill 演进必须绑定版本并重复评测

一次成功无法证明稳定。评测结果还必须绑定 Skill 与 manifest 哈希,避免旧证据替新版本背书。


12. 结语:Comet 真正展示的是 Harness Engineering

回到开篇问题:Comet 如何让 AI 编程工作流可恢复、可约束、可评测?

答案不是一段超级提示词,而是一组职责清晰的外部机制:

  • Skill 把自然语言意图路由到正确流程;
  • Classic 工作流状态机记录阶段和用户选择;
  • Engine 保存步骤、pending action 和 checkpoint;
  • Guard 检查阶段出口的工件与命令证据;
  • Hook 在宿主支持时阻止越权写入;
  • Handoff 用源文件和 SHA256 保持上下文一致;
  • Archive 把临时 change 合并为长期规格;
  • Eval 用重复实验驱动 Skill 迭代。

因此,Comet 最值得研究的不是 OpenSpec 与 Superpowers 的组合方式,而是它怎样把概率模型包在确定性控制面里。

模型能力决定系统能做到多好,Harness 决定这种能力能否稳定进入工程现场。AI 编程下一阶段的竞争,不会只发生在模型参数上,还会发生在模型周围的状态、守卫、证据、恢复和评测系统上。


参考资料

  1. Comet 官方仓库
  2. Comet 0.4.0-beta.4 Release
  3. Comet 0.4.0-beta.4 README
  4. Classic 主 Skill
  5. Classic 状态定义
  6. Classic 状态迁移
  7. Classic 阶段守卫
  8. Classic 写入 Hook
  9. Classic Handoff
  10. Classic Archive
  11. Comet Eval CLI
  12. Bundle Eval 与证据绑定
相关推荐
武子康6 小时前
Hugging Face AI 驱动入侵真正暴露的是 Dataset Processing 信任边界(攻击链 + 三层隔离 + 行动清单)
人工智能·安全·llm
GuWenyue6 小时前
LLM工具调用慢到崩溃?1套Promise.all并行方案,Agent性能直接翻倍
人工智能
GuWenyue6 小时前
复刻Cursor编程Agent!4套文件命令工具+Promise.all并发,AI自动生成React项目
人工智能
技录局6 小时前
Superpowers 实现原理深度解析:如何把工程纪律变成 Agent 的默认行为
人工智能
z12345677796 小时前
小说大纲生成工具怎么选:把灵感、结构和日更节奏放到同一条写作线上
人工智能
Swift社区6 小时前
AI 为什么越来越像微服务?
人工智能·微服务·架构
xin(n_n)b6 小时前
科研小白的进阶笔记(一)
人工智能·笔记
神奇霸王龙6 小时前
Imagen 4 降价 18%: 生图价格战开场
图像处理·人工智能·gpt·ai·ai作画·imagen
明月光8186 小时前
MaaS 平台踩坑记:你记住的模型名,可能明天就没了
人工智能