主规格位于 openspec/specs/<capability>/spec.md,描述系统当前已经同意并生效的行为。它与 change 下的 delta spec 是两种不同时间视角:delta 回答“这次准备改变什么”,main spec 回答“系统现在是什么样”。
📷 [图片 token=MiMVboo3zoF6t6xGhoJcel3unZb(未能下载,见飞书原文)]
主规格为什么是权威基准
OncallAgent 的代码量和能力边界较多,包括用户认证与 tenant 隔离、流式聊天、Prompt/Skill/记忆配置、知识文档索引、Milvus 与 BM25L 混合检索、用户级 MCP、CLS 日志访问、Alertmanager 告警和 LangGraph AIOps 诊断。只读代码很难快速判断哪些行为是有意设计,哪些只是实现细节。
主规格把已经生效的行为按 capability 固化下来。开始新 change 时,Codex 必须先读相关主规格,避免重复实现已有能力、破坏权限边界,或者把历史行为误判为偶然代码。
📷 [图片 token=FQcAbolpLo4e0fxuzfocrSqonpe(未能下载,见飞书原文)]
主规格的典型结构
# qwen-openai-provider Specification
## Purpose
## Requirements
### Requirement: ...
#### Scenario: ...
- **WHEN** ...
- **THEN** ...
Purpose 用一段话说明 capability 的长期职责。它不描述某次工单,而是解释该领域为什么存在。例如 qwen-openai-provider 的 Purpose 是通过 OpenAI-compatible 协议定义后端模型提供商合约,同时保持业务代码与供应商无关。
Requirements 收纳当前有效的全部行为约束。每个 Requirement 下包含一个或多个 Scenario。随着 change 归档,新的行为加入、既有行为修改、废弃行为删除,但文件始终试图描述当前状态。
📷 [图片 token=Fa2TbS65poxsTrx7wiHcLsHCnDf(未能下载,见飞书原文)]
sync 不是复制粘贴
OncallAgent 的 openspec-sync-specs 是 Agent 驱动的智能合并:
ADDED 在不存在时新增;若同名 requirement 已存在,则按修改处理,避免重复。
MODIFIED 只应用 delta 提到的变化,保留主规格中未被触及的其他描述和 Scenario。例如只新增一个越权场景时,不需要把正常场景全部复制到 delta。
REMOVED 删除整个 requirement;RENAMED 按 FROM/TO 修改名称。新 capability 不存在时,sync 会创建对应目录与主 spec,并补充 Purpose。
这个过程应保持幂等:同一个 delta 重复同步,不应不断追加重复 requirement。归档前需要比较 delta 与 main spec,明确说明将新增、修改、删除或重命名什么。
📷 [图片 token=SciCbXxU1owDYwxebPAcd7jOnAe(未能下载,见飞书原文)]
主案例怎样进入主规格
limit-qwen-embedding-batch-size 的 delta 在 qwen-openai-provider 下 ADDED 了 Qwen embedding batch compatibility。归档后,同名 requirement 和三个 Scenario 已出现在:
openspec/specs/qwen-openai-provider/spec.md
它与原有的模型配置、ChatOpenAI Provider、可替换抽象、readiness、Embedding 原始输入和显式维度等 requirement 共存。主规格没有变成“批量限制工单说明”,而是把新约束纳入 Qwen Provider 的完整当前契约。
📷 [图片 token=EMgybYzLlogx9ZxI6HOcAxNAnFd(未能下载,见飞书原文)]
这也说明主规格与 archive 分工不同。想知道“系统现在是否要求每批最多 10 条”,查 main spec;想知道“为什么在 2026-07-11 引入这个约束、当时有哪些取舍和任务”,查 archive change。
📷 [图片 token=XoPNb2umdoba7axEF2QcuSLZn6c(未能下载,见飞书原文)]
主规格、代码和测试怎样互证
主规格不是代码的自动镜像,也不能单独证明实现正确。主案例有三类证据:
**规格证据。**主 spec 明确每批上限、顺序完整性和大文档 succeeded。
实现证据。apps/backend/src/super_ai/llm/provider.py 定义 QWEN_EMBEDDING_BATCH_SIZE = 10,构造 OpenAIEmbeddings 时传入 chunk_size。
**测试证据。**Provider 单测用 11 条输入验证调用被拆成 10+1 且向量顺序不变;索引回归测试生成 11 个 chunk,验证任务 succeeded、11 个 chunk 全部写入。不同证据共同覆盖 Requirement,而不是只靠文档自证。
📷 [图片 token=OkolbCLFIoFtATx74aEc32gmnVe(未能下载,见飞书原文)]
为什么不能直接编辑主规格完成新需求
直接改 main spec 会丢失“变化从哪里来”的上下文:没有 proposal 解释动机,没有 design 记录取舍,没有 tasks 追踪交付,也没有独立 delta 便于审查。多人并行时,多个需求还会直接争抢同一主文件。
先在 change 中写 delta,可以隔离并行工作;实现和验证完成后再 sync,主规格才接收已经交付的变化。这个设计类似数据库迁移与当前 schema 的关系:迁移记录演进过程,当前 schema 描述最终状态。
📷 [图片 token=VBPObV2HFoNyavx0ejQcLmcQnUh(未能下载,见飞书原文)]
常见错误
**把 main spec 当产品愿望清单。**主规格只能声明已经生效的行为,未实现规划应留在 active change。
**归档了 change,却没同步主规格。**这会造成 archive 说功能已完成,而 main spec 不知道该行为。OncallAgent 的 wiki-sync 默认会阻断这种不一致。
**只看主规格,不看实现和测试。**规格是意图基准,不是运行证据。verify 必须建立文档、代码、测试之间的对应关系。
**在归档文件上继续开发。**后续变化应创建新的 delta,再合入主规格,不能修改旧 change 改写历史。
📷 [图片 token=HaRdbS7Zqo7d6gxy2f5crggUnfT(未能下载,见飞书原文)]
面试表达
主规格保存当前事实,delta 保存一次变化。我们不直接在主 spec 上堆需求,而是在独立 change 中完成 proposal、delta、design、tasks 和实现验证,再智能合并。这样主规格适合新任务读取,archive 适合追溯决策,二者分别解决“现在是什么”和“为什么变成这样”。
📷 [图片 token=H13pbSz5Lo0DQmxxeEzcBTx6n7d(未能下载,见飞书原文)]