主题: 应用部分1. 从遗留系统恢复规格
难度等级: 中级
预计学习时间: 6-8小时(理论+实践)
前置要求: 熟悉第一卷第13部分(现有项目的宪法恢复)
了解Kubernetes、Grafana、PagerDuty基础知识
有markdown和JSON工作经验
理解Git仓库结构
熟悉Given/When/Then格式的用户故事
学习目标: 区分需求(SDD合同)和背景上下文(memory bank),在制品盘存时应用严格分离
为一项生产需求创建genealogy.md,包含至少两个evidence_ref、置信水平和一个开放问题
将恢复的陈述转换为Given/When/Then格式并确定相应的JSON Schema字段
使用Qwen Code的提取模式(非生成模式)获取带有强制来源和反例的claims
通过阈值和SLA的来源引用评估需求就绪状态
概述: 本章专注于从遗留制品的混乱中恢复工程适用的规格:非结构化日志、Slack讨论串、仪表板截图和缺少正式SDD的事后分析。在SRE团队离职后,自动事故管理项目中只剩下碎片,需要从中提取可验证的需求,而非一组看似合理的猜测。学习焦点很窄:一个claim,两个来源,一个开放问题。完整的生产跟踪(规范化器、历史重放、文件仲裁)推迟到第8部分。主要结果——一份已填写的genealogy.md,将已批准需求与假设和背景上下文分离。
关键概念: Genealogy.md: 需求的溯源登记簿,将陈述与来源(日志、Slack、指标、事后分析)、置信水平(uncertainty)和开放问题关联。与显示文件变更者和变更时间的git log不同,genealogy.md展示需求本身的来源及其置信度。必填字段:claim、evidence_ref(至少两个)、status(approved/needs_clarity/rejected)、uncertainty、open_questions。
Memory bank: 独立的基础设施上下文层:有助于解释事实但本身不是合同的所有信息。集群拓扑、团队列表、历史协议、API限制、常用通信渠道、操作术语。将memory bank与需求混合是危险的:SDD中会出现虚假规则如「canary始终不可升级」,而实际上这只是测试namespace的上下文。
Evidence ref: 证据标注——指向源制品中具体位置的引用。格式:source:path#location。示例:grafana:NR-2026-05-17-01、postmortem:node-not-ready-2026-05、slack/thread_11#msg_7。没有evidence_ref,陈述就只是作者的意见,而非可验证的需求。
Claim(候选陈述): 已恢复的规则,尚未批准为合同。好的claim包含:具体阈值(>=3 NodeNotReady)、时间窗口(10分钟内)、条件(针对一个node)、相关性(伴随5xx增长)、evidence_ref、反例(counterexample)、缺失上下文(missing_context)、置信水平(confidence)。
Given/when/then: 行为需求描述格式。Given——系统初始状态。When——触发事件。Then——带有具体数字、状态和SLA的预期结果。示例:Given集群处于活跃值班且10分钟内>=3个NodeNotReady;When事件与部署相关;Then创建P1,8分钟内初步响应,15分钟后升级到NOC,10分钟内连续2次OK后关闭。
Json schema合同: 机器可读的需求表示,包含必填字段、允许值(enum)、数值边界(minimum/maximum)。双重记录(Given/When/Then + JSON Schema)弥合了「人类可理解」与「机器可验证」之间的差距。可验证字段:rule_id、severity(enum P0-P3)、sla_minutes、conditions(event_code、count、window_minutes、namespace_rule)。
时间线规范化: 将来源统一到同一时区(UTC),删除重复项,提取事件代码,用统一的事故标识符关联记录。框架:ts → source → event_code → actor → affected_scope → evidence_ref。没有规范化,恢复就变成了对记忆的争论,而非对系统行为的重建。
文件仲裁(完整跟踪): 由三个角色检查争议需求的流程:验证者(数字和状态的一致性)、实施者(当前管道中的可行性)、安全(安全操作边界,critical_risk时有否决权)。协调者维护日志,不投票。详细内容见第8部分。对于学习最小值——仅供参考。
Qwen code作为提取器: 模型不是作为业务逻辑的作者,而是作为从规范化数据中提取claims的中间人。好的提示要求:重复规则、支持来源、反例、置信水平。禁止无证据引用的陈述。模式:无头Plan Mode(--approval-mode plan)。
Uncertainty(置信水平): low——陈述已被独立来源确认,经历了重放或仲裁。medium——有矛盾、缺失上下文、一个来源。high——基于间接数据的假设。争议性事实不伪装成approved:标记为needs_clarity并附上验证计划。
练习题: 名称: 为node_not_ready创建genealogy.md
问题: 使用课程材料中的学习摘录,为node_not_ready事故创建一个genealogy.md文件,包含一条记录。已知:10分钟内三个NodeNotReady事件的grafana日志、PagerDuty上P1升级记录、事后分析中关于两个稳定窗口前不自动解决的决定,以及关于canary namespace的开放问题。任务:制定claim,添加两个evidence_ref,注明uncertainty和open_questions,分离memory bank。
解决方案: 1. 将模板book2/examples/templates/genealogy.md复制到工作目录。2. 记录claim:「10分钟内一个node>=3个NodeNotReady时创建P1,关闭需要10分钟内连续2次OK」。3. 添加evidence_ref:['grafana:NR-2026-05-17-01', 'postmortem:node-not-ready-2026-05']。4. 注明uncertainty:medium(由于canary的开放问题)。5. 添加open_questions:['canary namespace是否排除P1,还是仅降低置信度?']。6. 将以下内容放入memory bank:cluster=prod-k8s、node=worker-07、owner=platform_oncall(上下文,非合同)。7. 设置status:needs_clarity。8. 检查:陈述不能读作「作者的意见」——阈值由grafana引用保护,关闭条件由事后分析保护。
难度: beginner
名称: 将claim转换为Given/When/Then和JSON Schema
问题: 取上一练习的陈述,以双重记录形式表述:行为故事(Given/When/Then)和最小JSON Schema。确定模式中检查阈值、severity和关闭条件的三个字段。将canary的例外作为独立的namespace_rule条件表述。
解决方案: Given/When/Then: Given集群处于活跃值班且10分钟内为一个节点记录>=3个NodeNotReady;When事件不在canary namespace或canary中伴随相关的5xx增长;Then创建severity=P1的事故,8分钟内初步响应,15分钟后升级到NOC,10分钟内连续2次OK后关闭。JSON Schema: { '$id': 'urn:spec:node-not-ready:v1', 'type': 'object', 'required': ['rule_id','severity','sla_minutes','conditions'], 'properties': { 'rule_id': {'type':'string'}, 'severity': {'type':'string','enum':['P0','P1','P2','P3']}, 'sla_minutes': {'type':'integer','minimum':1,'maximum':120}, 'conditions': {'type':'object','required':['event_code','count','window_minutes','namespace_rule'], 'properties': {'count':{'type':'integer','minimum':3}, 'window_minutes':{'type':'integer','minimum':1}, 'namespace_rule':{'type':'string','enum':['standard','canary']}}}}} 三个可验证字段:count(阈值>=3)、severity(enum P1)、conditions含window_minutes和namespace_rule(通过连续OK的关闭条件——完整跟踪中通过auto_resolve_window和正则表达式)。
难度: intermediate
名称: 在真实片段中分离需求和memory bank
问题: 给定来自Slack讨论串和事后分析的片段:「值班员瓦夏凌晨3点发现prod-k8s中的worker-07再次NotReady,在#sre-alerts频道发帖,15分钟后叫来了NOC的娜斯佳,他们决定不自动关闭,因为上次auto-resolve导致事故复发。发现2:47在canary有计划部署。旧服务叫node-health-check,现在叫k8s-node-monitor」。分离为需求(SDD)和memory bank。解释为什么每个元素属于各自的层。
解决方案: SDD需求:「10分钟内>=3个NodeNotReady → P1」(从重复模式恢复)、「关闭需要10分钟内连续2次OK」(事后分析:拒绝auto-resolve)、「15分钟后升级到NOC」(事件时间链)。Memory bank:「值班员瓦夏」(谁值班——上下文,非合同)、频道「#sre-alerts」(常用通信渠道)、prod-k8s中的「worker-07」(具体拓扑,会变化)、「node-health-check」的旧名称(历史术语)、2:47在canary的「计划部署」(解释的上下文,但本身不是规则——unless作为namespace_rule条件表述)。论证:人员和节点名称会变化,通信渠道是操作习惯,而阈值和SLA是可验证的系统行为。
难度: intermediate
名称: 评估claim就绪状态
问题: 给定三个针对high_memory_usage的claims。A:「10分钟内appointments-api的memory_percent >= 90%时创建P1」——两个evidence_ref(grafana、事后分析),但两个稳定窗口的auto-resolve禁令未确认。B:「5分钟内memory_percent >= 85%时创建P0」——一个evidence_ref(Slack),无事后分析,与相邻集群矛盾。C:「10分钟内memory_percent >= 90%时创建P1,未经两个稳定5分钟窗口不允许auto-resolve」——三个evidence_ref,已在历史数据上通过重放,但服务所有者已离职。确定每个的status和uncertainty,论证。
解决方案: A:status needs_clarity,uncertainty medium。阈值和severity由两个来源保护,但关闭条件是开放问题。不能让部分确认的需求转为approved。计划:联系服务所有者或找额外的事后分析。B:status rejected(或保持假设),uncertainty high。一个来源,与相邻集群矛盾,阈值85%未保护——像是聊天中的猜测。未经重新验证不能接受。C:status approved或needs_clarity取决于流程。技术上三个来源和重放给出low uncertainty,但服务所有者缺席对未来修订是风险。决定:保留approved并注明「最后确认来自离职所有者,架构变更时需要重新验证」或转为needs_clarity直到指定新所有者。教训:即使强证据也需要活的修订机制。
难度: advanced
案例研究: 名称: AgentClinic-production中SRE团队离职后SLA升级的恢复
场景: 学习模型AgentClinic-production部署在Kubernetes中。SRE团队离职后留下碎片:47页非结构化日志、11条相关Slack消息、Grafana仪表板截图、缺少正式SDD的事后分析。三方检查电路接收来自Grafana和PagerDuty的webhooks。需要恢复一个需求:NodeNotReady何时成为P1,何时不能自动关闭。
挑战: 1. 分散的来源没有统一格式:指标日志、PagerDuty中的升级、非正式事后分析。2. 操作上下文(值班人员姓名、集群拓扑、旧服务名称)与业务规则混合的风险。3. 关于canary namespace的隐含规则:它是否排除P1,还是仅降低置信度?4. 缺少正式SDD——替代方案「一组看似合理的猜测」不可接受。5. 可验证性的必要性:恢复的规格应成为可执行制品。
解决方案: 1. 在盘存阶段已应用严格的「需求vs memory bank」过滤器。值班人员姓名、Slack频道、worker-07拓扑——移入memory bank。2. 创建规范化时间链:1248个NodeNotReady事件 → 按10分钟窗口分组 → 63个警报 → 提取8个已关闭事故。3. 发现NodeNotReady急剧增长与部署重合,且有两种行为分支:标准P1和带有放宽阈值的canary路径。4. 形成带有两个evidence_ref的claim:grafana:NR-2026-05-17-01和postmortem:node-not-ready-2026-05。5. 关于canary的开放问题已记录,未伪装成已批准规则。6. 双重记录:Given/When/Then + JSON Schema含severity、sla_minutes、conditions字段。7. 对于学习最小值——不带完整仲裁的genealogy.md;对于生产跟踪——预留过渡到第8部分的文件仲裁。
结果: 恢复了一个可验证的需求:「10分钟内一个节点>=3个NodeNotReady时创建P1,8分钟内初步响应,15分钟后升级到NOC,10分钟内连续2次OK后关闭」。Canary namespace的条件作为独立规则namespace_rule提出,而非注释。Genealogy.md允许审计每个要点的来源。规格已准备好在CI中进行验证和历史数据重放。
经验教训: 证据比自信的表述更重要:没有evidence_ref的漂亮规则仍然是假设
需求和memory bank的分离应在盘存阶段进行,而非最后——否则虚假规则会渗入合同
例外(canary、计划部署)不是噪音,而是隐藏条件规格的指示器;不能删除
开放问题应明确标记,不能伪装成approved——这是防止做出无根据决策的保护
双重记录Given/When/Then + JSON Schema弥合了人类可读描述与机器验证之间的差距
即使强证据也需要活的修订机制以应对团队或架构变更
相关概念: genealogy.md
memory bank
eqidence_ref
Given/When/Then
JSON Schema合同
时间线规范化
uncertainty
名称: 监控迁移项目中memory bank和需求混合的错误
场景: 平台团队从自建监控迁移到Prometheus+Grafana。在恢复警报规格时,工程师在SDD中加入了短语:「severity=P1的警报发送到#critical-alerts频道,提名platform团队的oncall」。
挑战: 6个月后,platform团队更名为platform-infra,频道改名,值班轮换转入PagerDuty。SDD不得不重写,尽管业务逻辑(何时创建P1)未变。问题:操作上下文被编码为合同需求。
解决方案: 回顾分析显示正确分离应为:需求——「10分钟内>=3个NodeNotReady时创建P1,按SLA 15分钟后进行升级」。Memory bank——「#critical-alerts频道、platform团队、当前瓦夏oncall」。用genealogy.md重写的SDD将稳定行为与可变上下文分离。JSON Schema包含severity和sla_minutes,但不包含channel_name或team_name。
结果: 新结构在三次通信重组中存活,合同不变。Memory bank更新是分钟级操作,不需要仲裁。教训得到证实:「合同vs上下文」过滤器对长期规格至关重要。
经验教训: 团队、频道、人员名称——memory bank,无论它们在编写时看起来多么「明显」
如果阅读SDD时产生「但如果团队改名怎么办?」的问题——这是层混合的信号
JSON Schema作为审计工具:如果字段没有enum或数值边界,它可能属于memory bank
相关概念: memory bank
合同vs上下文过滤器
JSON Schema合同
genealogy.md
学习建议: 从最小学习场景开始:一个claim,两个来源,一个开放问题。不要试图立即覆盖所有47页日志——那是完整生产跟踪
在应用到真实材料之前,先在学习摘录(4行)上练习。结构比数量重要
视觉风格:纸上画两列——左侧「需求(可验证)」,右侧「memory bank(上下文)」。逐条审查源制品片段,决定它属于哪一列
听觉风格:大声说出claims并问「系统应该有什么变化?」如果答案是「没什么,这只是信息」——这是memory bank
动觉风格:物理地将片段卡片在「SDD」和「memory bank」两堆之间移动,讨论每次移动
用「我能为此写自动化测试吗?」测试每个claim。如果不能——可能这不是需求,或需求不够具体
用genealogy.md作为「可疑性检查表」:如果记录没有两个evidence_ref——自动成为假设,无论作者多么自信
在Plan Mode的学习数据上练习Qwen Code,但始终用单独的解析器验证输出JSON——模型生成会话报告,不是严格的claims.json
创建「反例」作为必填字段:好练习——在源数据中找与claim矛盾的案例,并解释原因(例外?数据错误?假设错误?)
24小时后用「陌生眼光」审查genealogy.md:能否不回忆上下文就理解为什么阈值是3而非2或4?
附加资源: Genealogy.md模板:book2/examples/templates/genealogy.md — 溯源起点模板
第一卷学习项目:AgentClinic(TypeScript、Hono、服务端JSX、SQLite、Vitest)——从tech-stack.md和QWEN.md理解memory bank的支撑
Github spec kit: https://github.com/github/spec-kit — 「规格作为可执行制品」框架
第一卷第13部分:从现有项目恢复宪法——预备支撑
第二卷第8部分:part-08-multiagent-tribunal.md — 文件仲裁、验证者/实施者/安全角色详情
审判示例:examples/tribunal/ — 可运行的学习模拟
附录a:appendix-a-bridges-to-book.md — 与第一卷的连接
教材仓库:book2/examples/ — Python标准库可运行脚本
摘要: 从遗留系统恢复规格是一项工程技巧,将制品混乱(日志、聊天、事后分析)转化为可验证合同。关键原则:严格分离需求和memory bank、时间线规范化、通过带强制evidence_ref的Qwen Code进行提取(非生成)、双重记录Given/When/Then + JSON Schema、明确标记uncertainty和open_questions。学习最小值——一份已填写带保护陈述、两个来源和一个开放问题的genealogy.md。完整生产跟踪增加规范化器、历史重放和文件仲裁(第8部分)。主要结果:规格成为可验证、可质疑、可历史数据重放、可数月后审计的证据链。