| name | prism-wiki-depth |
| description | Use when writing or auditing core/ layer documentation to ensure knowledge depth. Covers design motivation extraction, constraint documentation, failure mode analysis, cross-module contracts, and change sensitivity. Complements prism-wiki-standard. |
| version | 3.0.0 |
| author | Wang |
| license | MIT |
| platforms | ["windows"] |
| metadata | {"hermes":{"tags":["wiki","knowledge-base","depth","design-intent","constraints","failure-modes"]}} |
| layer | skills |
深度知识提取规范
本 skill 是 prism-wiki-standard 的互补层。standard 负责骨架(格式、类型、链接),本 skill 负责血肉(设计意图、约束、故障模式、跨模块协议)。
适用范围:仅 core/ 层 API 文档(type: api)和 dev/ 层编码指南。ref/、docs/、client/ 不适用。
前置条件:必须先按 prism-wiki-standard 完成骨架(frontmatter、段落结构、函数签名表)。本 skill 不替代 standard,而是在已有骨架上注入深度。
核心理念
代码文档的深度不是行数,而是回答了几个读者无法从源码直接得到答案的问题。
好的深度文档回答五类问题:
| # | 问题 | 源码能回答吗 | 文档必须回答 |
|---|
| 1 | 为什么这么设计? | 不能 | 设计动机 |
| 2 | 有什么隐藏约束? | 部分(散在注释里) | 显式列出 |
| 3 | 出错了会怎样? | 部分(散在 trace 里) | 故障模式 |
| 4 | 和其他模块有什么隐式协议? | 不能 | 跨模块契约 |
| 5 | 改了这个会影响谁? | 不能 | 变更敏感度 |
一、设计动机提取
什么是设计动机
设计动机解释为什么选择这个实现方式,而不是其他可行方案。它回答"作者面对什么问题时做了这个选择"。
提取方法
- 读头文件的
@details 和 @note:这是作者自己写的意图
- 读实现文件中的 WHY 注释:搜索关键词
防止、避免、确保、否则、必须、限制、复用、解耦
- 问自己:如果删掉这段代码,什么会坏?怎么坏?
写入位置
在 API 文档的「概述」段落末尾,加一个 ## 设计决策 子段落(S 档必写,A 档可选,B 档不要写)。
格式
## 设计决策
### 为什么 X 使用 Y 模式?
[一句话描述问题]
[两到三句话解释选择原因和替代方案的取舍]
**后果**: [这个选择带来的限制或好处]
示例模式
以下是三类常见的设计决策模式,提取时对号入座:
模式 1:性能关键路径上的非显而易见选择
当一个看似多余的类型包装或间接层出现在热路径上时,通常是零分配或避免悬挂指针。
### 为什么会话计数器用 shared_ptr<atomic<T>> 包装?
会话关闭回调在对象析构后才执行。如果回调捕获裸指针,
所属模块部分析构后回调访问的是悬挂指针。
通过 shared_ptr 包装计数器,回调只捕获计数器本身,
独立于所属模块生命周期。
**后果**: 控制块生命周期可能长于所属模块,但开销可忽略
(每个模块仅一个计数器)。
模式 2:看似多余但实际有保护作用的代码
当出现 std::move 后对空容器操作、冗余的状态检查、双重锁等,通常是防范重入或迭代器失效。
### 为什么 close() 用 std::move(map) 而不是直接迭代?
子对象的 close() 会回调本对象的 remove 方法,后者执行
map.erase()。如果迭代的是 map 本身,erase 导致迭代器失效。
先 std::move 把 map 移到局部变量,原 map 变空。
后续 remove 方法对空 map 的 erase 是空操作。
**后果**: close() 只能调用一次。由原子标志的幂等性保证。
模式 3:类型选择中的权衡
当选择了看起来"不对"的类型(如 string_view 而非 string、span 而非 vector),通常是零拷贝或避免分配。
### 为什么路由查找使用透明哈希?
每次请求的路由查找需要用 `string_view`(来自协议解析的零拷贝视图)
查 `unordered_map<string, endpoint>`。普通查找会构造临时 string,
触发堆分配。
使用 `is_transparent` 透明查找,`string_view` 直接查表,热路径零分配。
**后果**: 哈希函数和相等比较必须保证跨类型一致性
(统一用 `hash<string_view>` 计算哈希值)。
提取清单
在写「设计决策」时,逐条检查:
二、约束文档化
什么是约束
约束是使用此代码时必须遵守的规则。违反约束不会产生编译错误,但会在运行时导致未定义行为、数据竞争、资源泄漏或安全漏洞。
约束分类
| 类型 | 说明 | 严重度 |
|---|
| 生命周期约束 | A 必须比 B 活得长 | 违反→悬挂指针/崩溃 |
| 调用顺序约束 | 必须先调 X 再调 Y | 违反→未初始化/数据丢失 |
| 线程安全约束 | 必须在特定上下文调用 | 违反→数据竞争 |
| 状态前置条件 | 调用时对象必须处于某状态 | 违反→逻辑错误 |
| 资源上限约束 | 数据大小/数量有硬上限 | 违反→溢出/OOM |
写入位置
在 API 文档中加 ## 约束 段落(S 档必写,A 档必写,B 档写重要的)。
放在「依赖关系」之后、「函数签名表」之前。
格式
## 约束
### [约束名称]
**类型**: 生命周期 / 调用顺序 / 线程安全 / 状态前置 / 资源上限
**规则**: [一句话描述约束]
**违反后果**: [具体会出什么问题]
**源码依据**: [文件:行号]
示例模式
模式 1:状态前置 — 某方法仅在特定状态下安全
### rewind 只能在纯读取阶段使用
**类型**: 状态前置
**规则**: rewind() 仅在未发生写入时有效。
认证阶段是纯读取,安全 rewind。一旦开始写入,状态不可恢复。
**违反后果**: rewind 后读取到不一致的状态,
可能导致协议解析错误或安全漏洞。
**源码依据**: snapshot.hpp 布尔标志字段
模式 2:调用顺序 — 初始化依赖
### start() 必须在事件循环运行前调用
**类型**: 调用顺序
**规则**: start() 必须在事件循环 run() 之前调用,
否则内部清理协程不会启动。
**违反后果**: 过期资源永远不会被清理,容器无限增长。
**源码依据**: pool.hpp start() 方法注释
模式 3:线程安全 — 非线程安全但有例外
### 工作线程不可跨线程共享
**类型**: 线程安全
**规则**: 所有成员访问必须在所属线程内进行。
只有 dispatch() 和 snapshot() 是线程安全的
(内部使用 post 跨线程分发)。
**违反后果**: 数据竞争,未定义行为。
模式 4:资源管理 — RAII 依赖
### 连接包装器必须显式析构或 reset
**类型**: 资源管理
**规则**: 包装器必须在底层资源不再需要前析构或
显式 reset()/release()。否则资源不会被归还池中。
**违反后果**: 资源泄漏。池中可用资源逐渐耗尽。
提取清单
三、故障模式分析
什么是故障模式
故障模式描述代码在异常条件下怎么失败、失败时什么表现、如何恢复。它不是"函数返回错误码"的罗列,而是端到端的故障场景。
故障场景 vs 错误码
错误码(bad):函数返回 timeout
故障场景(good):上游服务器无响应时,连接池等待超时后返回 timeout。
连接未归还池中。调用方会尝试下一个端点或返回错误。
最终会话关闭,客户端收到连接重置。
写入位置
S 档文档加 ## 故障场景 段落,放在「调用链」之后。
A 档文档只在关键路径(网络 I/O、加密、状态机)写故障场景。
B 档不写。
格式
## 故障场景
### [场景名称]
**触发条件**: [什么情况会导致这个故障]
**传播路径**: [错误从哪里产生,经过哪些层,最终到哪]
**外部表现**: [客户端/运维看到什么现象]
**恢复机制**: [系统如何自动恢复,或需要人工干预]
**日志关键字**: [grep 什么字符串可以在日志中定位此故障]
示例模式
模式 1:级联失败 — 从底层到顶层的完整传播
### 所有候选地址连接失败
**触发条件**: 目标域名的所有解析结果都无法建立连接。
常见原因:目标宕机、防火墙阻断、本地网络中断。
**传播路径**: racer 每个候选连接超时或拒绝 →
所有 pending 递减到 0 且无 winner → 返回错误码 →
上层重试(默认 N 次)→ 全部失败 → 转发层返回错误 →
会话关闭 → 客户端收到 RST。
**外部表现**: 客户端连接超时。
**恢复机制**: 自动。下次请求重新触发解析和竞速。
如果是临时网络故障,新请求可能成功。
**日志关键字**: 模块名 + 错误码
模式 2:资源耗尽 — 自限流与自动恢复
### accept() 因文件描述符耗尽而失败
**触发条件**: 系统文件描述符达到上限。
**传播路径**: accept() 返回错误 →
指数退避(10ms → 20ms → 40ms → ... → 上限 1s) →
持续重试直到有 fd 释放 → 恢复 accept。
**外部表现**: 新连接间歇性无法建立。已建立的连接不受影响。
**恢复机制**: 自动。指数退避避免 CPU 空转。
当旧连接关闭释放 fd 后,accept 自动恢复。
**日志关键字**: accept + error
模式 3:安全检测 — 主动防御
### 密钥交换输出全零(低阶点攻击)
**触发条件**: 攻击者构造特殊的公钥,使得密钥交换输出全零。
**传播路径**: 密钥交换成功返回 → 全零检查捕获 →
日志警告 → 返回错误码 → 握手失败 →
回退到伪装目标或关闭连接。
**外部表现**: 连接被拒绝,日志出现警告信息。
**恢复机制**: 自动。该连接被拒绝,不影响其他连接。
攻击者无法获得任何有用的认证信息。
提取清单
四、跨模块契约
什么是跨模块契约
两个模块之间的隐式协议 — 不在类型系统或接口签名中体现,
但违反了就会出问题的约定。
常见契约类型
| 契约类型 | 说明 |
|---|
| 初始化顺序 | A 必须在 B 之前初始化 |
| 生命周期绑定 | A 持有 B 的引用,B 必须比 A 活得长 |
| 回调重入 | A 调用 B,B 的回调又会调用 A(需要防范重入) |
| 数据格式约定 | A 产生的数据 B 消费,格式必须匹配 |
| 线程归属 | A 的实例只被特定线程使用,B 必须遵守 |
写入位置
在「依赖关系」表格中增加一行契约说明。S 档必写,A 档重要契约必写。
格式
在依赖关系表格后加:
### 跨模块契约
| 模块 A | 模块 B | 契约内容 |
|--------|--------|---------|
| 上层 | 下层 | 上层负责 X;下层假设 Y;违反后 Z |
示例模式
模式 1:回调重入契约
### 跨模块契约
| 模块 A | 模块 B | 契约内容 |
|--------|--------|---------|
| 子对象 | 容器 | 子对象::close() 内部调用容器::remove()。这是重入调用:容器::close() → 子对象::close() → 容器::remove()。容器用 std::move(map) 在迭代前清空,确保 remove() 的 erase 操作安全(对空 map 的 erase 是空操作) |
模式 2:初始化顺序契约
### 跨模块契约
| 模块 A | 模块 B | 契约内容 |
|--------|--------|---------|
| 注册模块 | 使用模块 | register() 必须在使用模块首次使用前调用(启动流程保证)。注册顺序决定默认优先级 |
模式 3:副作用契约
### 跨模块契约
| 模块 A | 模块 B | 契约内容 |
|--------|--------|---------|
| 健康检查 | 连接池 | check() 临时设置 socket 为非阻塞模式,检查后恢复原始状态。如果恢复失败,后续 async 操作可能阻塞在非预期模式 |
提取清单
五、变更敏感度
什么是变更敏感度
描述修改此模块时,哪些改动会影响其他模块,以及其他模块的哪些改动会破坏此模块。
写入位置
S 档文档末尾加 ## 变更敏感度 段落。A 档不写。
格式
## 变更敏感度
### 对外影响(改这里会影响谁)
| 变更 | 影响范围 | 影响 |
|------|---------|------|
| 修改 close() 的幂等性假设 | 所有子对象 | 它们的 close() 依赖 remove() 对空容器安全 |
| 修改内部数据结构 | 所有子类 | 子类直接操作该 buffer |
### 对内影响(改别人会影响这里)
| 上游变更 | 本模块受影响 | 需要检查 |
|---------|------------|---------|
| 子对象增加新的关闭路径 | remove() 被新路径调用 | 确认容器状态一致 |
| 依赖层新增装饰器 | cancel()/close() 穿透 | 确认新装饰器正确转发 |
提取方法
- 找
friend 和继承关系:基类改了,所有子类都受影响
- 找回调/观察者模式:回调签名改了,所有注册者都受影响
- 找协议常量:magic number、版本号改了,所有对端都受影响
- 找 shared_ptr 共享状态:共享对象的结构改了,所有持有者都受影响
六、代码片段使用规则
prism-wiki-standard 的 Pitfall #15 说"不要贴源码片段"。
这个规则过于绝对。以下定义什么时候该贴、什么时候不该贴。
应该贴代码(知识提炼后的片段)
-
展示调用模式:读者无法仅从函数签名理解的组合使用方式
// 好:展示了 shared_ptr<atomic> 的使用模式
auto counter = metrics.session_counter(); // shared_ptr 拷贝
auto on_closed = [counter]() noexcept {
counter->fetch_sub(1U, std::memory_order_relaxed); // 安全
};
-
展示关键算法:不可从函数名直接推断的逻辑
// 好:EMA 平滑算法的具体参数
smoothed = (smoothed * 7 + effective) / 8;
-
展示错误处理路径:多步恢复逻辑
// 好:fd 转移后的错误恢复
auto native_handle = sock.release();
migrated.assign(protocol, native_handle, ec);
if (ec || !migrated.is_open())
{
closesocket(native_handle);
}
不应该贴代码(直接搬运)
- Doxygen 已有的内容:函数签名、参数列表、返回值类型 — 这些已经在 wiki 的函数签名表里了
- getter/setter 实现:显而易见的代码
- 整个类定义:用成员变量表代替
- 重载的每个版本:选一个有代表性的,其余用文字说明差异
判断标准
问自己:如果删掉这段代码,只保留文字描述,读者能否理解?
- 能 → 不要贴代码,用文字
- 不能 → 贴代码,因为代码本身就是知识的载体
七、深度审计检查
在完成 prism-wiki-standard 的骨架审计后,运行以下深度审计。
以下脚本需替换路径为实际的知识库目录和源码目录。
设计动机审计
for f in $(find $WIKI_DIR -name '*.md' -not -path '*/.git/*' -not -path '*/.obsidian/*'); do
lines=$(wc -l < "$f")
[ "$lines" -gt 150 ] && ! grep -q '设计决策\|为什么' "$f" && echo "MISSING_MOTIVE($lines): $f"
done
约束审计
for f in $(find $WIKI_DIR -name '*.md' -not -path '*/.git/*' -not -path '*/.obsidian/*'); do
lines=$(wc -l < "$f")
[ "$lines" -gt 80 ] || continue
src=$(head -15 "$f" | grep -m1 '^source:' | sed 's/^source: *"*//;s/"*$//')
[ -z "$src" ] && continue
hpp="${SRC_DIR}/${src}"
[ ! -f "$hpp" ] && continue
warnings=$(grep -c '@warning' "$hpp" 2>/dev/null || echo 0)
has_constraints=$(grep -c '约束' "$f" 2>/dev/null || echo 0)
[ "$warnings" -gt 0 ] && [ "$has_constraints" -eq 0 ] && echo "MISSING_CONSTRAINT(warnings=$warnings): $f"
done
故障模式审计
for f in $(find $WIKI_DIR -name '*.md' -not -path '*/.git/*' -not -path '*/.obsidian/*'); do
lines=$(wc -l < "$f")
[ "$lines" -gt 150 ] || continue
has_io=$(grep -cl 'async_read\|async_write\|co_await\|socket\|tls\|ssl\|encrypt\|decrypt' "$f" 2>/dev/null)
[ -n "$has_io" ] && ! grep -q '故障场景\|故障模式' "$f" && echo "MISSING_FAILURE($lines): $f"
done
代码搬运检测
for f in $(find $WIKI_DIR -name '*.md' -not -path '*/.git/*' -not -path '*/.obsidian/*'); do
total=$(wc -l < "$f")
[ "$total" -lt 30 ] && continue
code_lines=$(awk '/^```/{flag=1-flag;next} flag{count++}END{print count+0}' "$f")
ratio=$((code_lines * 100 / total))
[ "$ratio" -gt 40 ] && echo "CODE_HEAVY(${ratio}%): $f"
done
八、写入流程(与 prism-wiki-standard 协作)
新建 S 档文档
- 按
prism-wiki-standard 完成骨架(frontmatter → 概述 → 命名空间 → 依赖关系 → 函数签名表 → 函数分节)
- 本 skill 接管:在概述中提取设计动机,加「设计决策」
- 本 skill 接管:在依赖关系后加「约束」
- 本 skill 接管:在调用链后加「故障场景」
- 本 skill 接管:在依赖关系表格后加「跨模块契约」
- 本 skill 接管:在文档末尾加「变更敏感度」
- 回到
prism-wiki-standard:更新 index.md,记 log.md
刷新已有文档
- 必须重读头文件和实现文件:不是在旧文档上修补
- 对比旧文档与新源码,标记哪些「设计决策」和「约束」仍然有效
- 删除过时的深度内容,补充新增的
- 运行深度审计检查
优先级排序
按以下顺序注入深度,而非一次全写:
- 约束 — 最实用,读者最需要,违反后果最严重
- 设计决策 — 理解系统的关键,但阅读频率较低
- 跨模块契约 — 修改代码时必须知道
- 故障场景 — 排障时需要
- 变更敏感度 — 重构时需要
九、质量指标
深度文档的质量不用行数衡量,用信息密度衡量:
| 指标 | 计算方法 | 目标 |
|---|
| 约束覆盖率 | 有约束段落的 S/A 档文档数 / 总 S/A 档文档数 | ≥ 80% |
| 设计动机覆盖率 | 有「设计决策」的 S 档文档数 / 总 S 档文档数 | ≥ 60% |
| 故障场景覆盖率 | 有「故障场景」的网络/加密相关 S 档文档数 / 总相关文档数 | ≥ 50% |
| 代码搬运率 | 代码行 / 总行数 | ≤ 30% |
| 重复信息率 | 与 Doxygen 注释重复的描述 / 总描述 | ≤ 20% |
重复信息率是关键指标。如果 wiki 的某段话和头文件 Doxygen 注释说的是同一件事,至少有一方是多余的。要么 wiki 说得比 Doxygen 更深(补充 Why、后果、场景),要么删掉 wiki 搬运的那段。
十、源码沉默时的处理策略
问题
上文的提取方法依赖源码中有 WHY 注释(@note、@warning、"防止"、"避免"等关键词)。但大量设计决策没有任何注释:
- hardcoded 常量的具体数值(为什么是 6 而不是 8?)
- 阈值参数的选取依据(为什么是 90%/80%?)
- 数据结构大小限制(为什么帧头是 N 字节?)
- 缓冲区大小的选取(为什么是 65536?)
三级处理策略
对每个设计决策,按以下顺序判断:
级别 1:源码有 WHY 注释 → 提取并扩展
这是最简单的情况。直接提取,按「设计决策」格式写。
验证:标注 **源码依据**: file:line,读者可以跳过去确认。
级别 2:源码有 WHAT 但没有 WHY → 从上下文推断,标注置信度
从代码的上下文推断设计意图。但必须标注推断的置信度,让读者知道这不是源码明确说的。
推断方法:
- 看调用方:谁调用这个函数?调用方的注释是否暗示了设计意图?
- 看边界条件:hardcoded 常量通常与某个协议规范或系统限制相关 — 去找原始规范
- 看替代方案:如果不用这个值/方法,最自然的替代是什么?为什么替代方案不可行?
- 看历史:
git log -p -- file 看这个值是何时引入的,commit message 可能解释原因
格式:
### 为什么 max_racing 是 N?
[从上下文推断的原因]
**置信度**: 推断(源码只写了 WHAT,未说明 WHY 的具体依据)
**替代方案**: [其他可行值及为什么不选]
**源码依据**: file:line
关键要素:
- 置信度标注:
推断 或 高确信 或 推测
- 替代方案:说明为什么不用其他值
- 源码依据:指向原始 WHAT 注释
级别 3:既没有 WHY 也没有 WHAT → 不写设计决策,标注"未记录"
不要编造动机。如果无法从上下文合理推断,写:
## 设计决策
> 此模块的设计决策未在源码注释中记录。以下列出已知的非显而易见的实现选择,
> 等待作者确认或补充。
### [某某设计选择]?
[已知事实]
**状态**: 未确认
**源码依据**: file:line
绝对禁止
- 禁止编造理由:如果不知道为什么,写"未记录"比编造一个看似合理的理由好 100 倍
- 禁止把 WHAT 当 WHY:"
max_hdr_size = 65536 限制了大小" — 这是 WHAT,不是 WHY。WHY 是"为什么选 65536 而不是其他值"
- 禁止猜测作者意图:如果注释没说,你不知道作者当时在想什么。只能从代码事实和外部规范推断
十一、伪深度防御
什么是伪深度
伪深度是看似有设计洞察,实际上要么是编造的,要么是错误归因,要么是显而易见的废话。这是 AI 写文档时最容易犯的错误。
伪深度模式识别
| 伪深度模式 | 示例 | 问题 |
|---|
| 显而易见包装 | "为什么用 unordered_map?因为 O(1) 查找比 O(log n) 快" | 废话,任何 C++ 开发者都知道 |
| 编造理由 | "为什么用 HMAC-SHA1?因为 SHA-256 在当时还没有广泛部署" | 事实错误(SHA-256 早已普及) |
| 错误归因 | "为什么用 write_channel?为了避免单个慢速消费者阻塞整个会话" | 过度泛化。实际只阻塞帧循环,其他流不受影响 |
| 替代方案稻草人 | "为什么不使用互斥锁?因为互斥锁会阻塞事件循环线程" | 正确但无信息量 — 协程项目里禁止 mutex 是基本原则,不是设计决策 |
| 空泛后果 | "后果: 需要注意内存管理" | 没有具体说明什么后果 |
验证流程
写完每条设计决策后,执行以下验证:
第一步:删除测试
删掉你写的"为什么"解释,只保留事实描述。问自己:读者能否从事实描述中推出你的解释?
- 能推出 → 你的解释是显而易见的废话,重写或删除
- 推不出 → 你的解释有价值,保留
第二步:源码确认
你写的每条断言,能否在头文件或实现文件中找到对应的注释/代码支撑?
- 能找到 → 标注
**源码依据**: file:line
- 找不到 → 回到「十、源码沉默时的处理策略」,标注置信度或标为"未记录"
第三步:精确性检查
你写的描述是否比源码注释更精确?检查有没有过度泛化:
源码注释: "write_channel 解耦帧循环与 target 写入,避免慢速 target 阻塞帧循环"
伪深度: "避免单个慢速 target 阻塞整个会话"
正确深度: "避免慢速 target 阻塞帧循环(frame loop)。其他已建立的流
不受影响,因为它们的数据在各自的缓冲区中排队"
差异:伪深度用"整个会话"泛化了"帧循环",导致读者对影响范围产生错误理解。
质量自检清单
每条设计决策写完后,逐条打分:
5 项全部通过才能保留。任何一项不通过,重写或删除。
十二、完整 S 档深度文档结构模板
以下是 S 档深度文档的完整结构模板,展示所有深度段落如何在一个文档中衔接。
---
layer: core
source: [源文件路径]
title: [模块标题]
created: [日期]
updated: [日期]
tags: [标签列表]
---
# [模块标题]
> 源码: [头文件] | 实现: [实现文件]
> 模块: [[父模块链接]]
## 概述
[2-3 句功能描述。不是搬运 Doxygen,而是解释这个模块在整个系统中
扮演什么角色、解决什么问题。]
## 命名空间
[命名空间声明]
## 依赖关系
| 依赖方向 | 模块 | 说明 |
|----------|------|------|
| 依赖 | [[模块A]] | 原因 |
| 被调用 | [[模块B]] | 谁在什么场景调用 |
## 设计决策
### 为什么 [非显而易见的设计选择]?
[问题描述]
[选择原因和替代方案取舍]
**后果**: [具体限制或好处]
**置信度**: [有源码依据则省略,推断则标注]
**源码依据**: [file:line]
[重复此模式,每条设计决策独立成段]
## 约束
### [约束名称]
**类型**: 生命周期 / 调用顺序 / 线程安全 / 状态前置 / 资源上限
**规则**: [一句话描述]
**违反后果**: [具体会出什么问题]
**源码依据**: [file:line]
[重复此模式]
## 函数签名表
| 函数 | 签名 | 说明 |
|------|------|------|
| ... | ... | ... |
## 函数: [函数名]
> 源码: [头文件] | 实现: [实现文件:行号]
[函数详细文档,包括参数、返回值、流程]
## 故障场景
### [场景名称]
**触发条件**: [什么情况导致]
**传播路径**: [错误从哪产生,经过哪些层,到哪]
**外部表现**: [客户端/运维看到什么]
**恢复机制**: [自动/人工,怎么恢复]
**日志关键字**: [grep 什么字符串定位]
[重复此模式]
## 跨模块契约
| 模块 A | 模块 B | 契约内容 |
|--------|--------|---------|
| ... | ... | ... |
## 变更敏感度
### 对外影响(改这里会影响谁)
| 变更 | 影响范围 | 影响 |
|------|---------|------|
| ... | ... | ... |
### 对内影响(改别人会影响这里)
| 上游变更 | 本模块受影响 | 需要检查 |
|---------|------------|---------|
| ... | ... | ... |
## 相关文档
- [[相关模块A]]
- [[相关模块B]]
结构要点
- 概述不重复 Doxygen:概述回答"这个模块在系统中的角色",Doxygen 回答"这个函数做什么"
- 设计决策在约束之前:先理解为什么这么设计,再理解使用限制
- 故障场景在函数文档之后:需要先理解正常流程,再看异常流程
- 跨模块契约和变更敏感度在最后:这是维护者视角的内容
- 信息不重复:概述中提到的东西,设计决策不重写;约束中提到的限制,故障场景只引用不重述
十三、模块深度优先级排序
知识库的深度注入应按模块重要性排序,而非按文件名字母序。
排序维度
每个模块按三个维度打分,计算优先级:
| 维度 | 评分标准 |
|---|
| 使用频率 | 核心路径上的模块 = 3,多数请求经过 = 2,少数场景使用 = 1 |
| 安全/正确性敏感度 | 涉及密码学/协议解析/资源管理 = 3,业务逻辑 = 2,辅助工具 = 1 |
| 修改频率 | 经常修改 = 3,偶尔修改 = 2,几乎不改 = 1 |
优先级 = 使用频率 × 安全敏感度 × 修改频率(范围 1-27)
分级标准
| 优先级分数 | 等级 | 深度要求 |
|---|
| 18-27 | P0 必须有深度 | 全部 5 个深度段落 |
| 10-17 | P1 应该有深度 | 约束 + 设计决策 + 故障场景 |
| 5-9 | P2 可以有深度 | 约束 + 设计决策 |
| 1-4 | P3 不需要深度 | 只需骨架,强行加深度反而制造噪音 |
执行建议
按 P0 → P1 → P2 → P3 顺序注入深度。每个优先级内,按以下顺序:
- 约束(最实用)
- 设计决策(理解关键)
- 故障场景(排障用)
- 跨模块契约(重构用)
- 变更敏感度(维护用)
每批 3-5 个文件,写完后运行深度审计确认质量。