04 · 知识存储 · 落地方案
04 · 知识存储 · 落地方案
对应蓝图 §5。本文解决:审核通过的知识如何结构化存储、切分、索引?
状态:草案(S1.5 / S1.6 / S2.3 / S3.4 / S3.5 / S3.6 / S4.1 待确认,其中 S1.6 须在 P0 启动前决策且不可逆)
前置依赖:03-知识审核(知识条目已审核通过,
status: active)关联文档:02-知识验证(CI 校验 frontmatter)· 05-知识检索与引用(索引的消费方)· 06-知识维护与治理(
flags/ Sunset 字段的写入方)· 09-度量评价与落地路径(重建时长作为容灾指标)
1. 概述
知识存储解决:审核通过的知识如何结构化存储、切分、索引?
知识库建设为多层知识控制系统,向量库只作为可重建的派生索引——删除索引不应导致知识丢失,索引损坏后应能从权威源(Git 仓库)重建。
2. 多层知识控制模型
派生索引原则:L4 层(向量索引、全文索引)均可从 L0 层(Git 仓库)完全重建。备份策略:索引不备份,只重建。
3. 元数据 Schema
3.1 P0 最小 frontmatter(7 字段)
---
status: active # draft | active | deprecated | revoked
source_uri: gitlab://ocp/payment/src/main/java/com/ocp/pay/RefundService.java
source_revision: 8f31c92a4b
owner_group: ocp/payment-team
classification: internal # public | internal | confidential | restricted
knowledge_type: Contract # Concept | Decision | Policy | Procedure | Contract | FAQ | IncidentLesson
title: "退款接口的幂等键约束"
---knowledge_type 为何是必填而非派生:分级复核周期(本文 §3.5)、漂移语义过滤(06 §2.2)、主张抽取范围(02 §4.4)都以它为输入。留到后期采集会让这三项机制在 P0 全部退化为无差别处理,而回填需 Owner 逐条判断——采集成本随时间单调上升。
3.2 自动派生字段
| 派生字段 | 来源与规则 |
|---|---|
knowledge_id | 文件路径 docs/knowledge/{module}/{topic}.md → KB-{MODULE}-{TOPIC},一经分配不再变更 |
content_hash | 对 git blob 计算:git show {source_revision}:{path} | sha256sum(不对工作区文件计算) |
revision_id | {source_revision} + {content_hash[:12]},用于索引版本对齐 |
effective_from | git log 中该文件的首次提交时间 |
effective_to | active 期间恒为 null;仅在转入 deprecated / revoked 时写入 |
review_due_at | effective_from + 该知识类型的复核周期(分级) |
steward | frontmatter 显式声明;未声明时回落 CODEOWNERS 派生,但必须写回固化 |
flags / flagged_at | CI 写入的异常标记,人工不得手改 |
3.3 content_hash 必须基于 git blob
对工作区文件直接计算 SHA-256 会因换行符双轨(CRLF vs LF)和格式化器(Prettier)产生全量误报。hash 输入必须统一为规范化后的仓库字节:
# 入库时记录
git show {source_revision}:{path} | sha256sum
# 漂移检测时比对(同一条命令,天然免疫 CRLF 与编辑器差异)3.4 effective_to 不能取「末次提交时间」
一条 active 知识没有失效时间。若把 effective_to 派生为「git log 末次提交时间」,则每条知识在被派生的那一瞬间就已落在有效区间之外,导致版本裁决规则与派生规则互相击穿。
| status | effective_to | 裁决含义 |
|---|---|---|
draft | null | 未生效,不参与裁决 |
active | null | 开区间,持续有效 |
deprecated | 进入 deprecated 的时间 | 已过 sunset 起点,仍可召回但需告警 |
revoked | 撤销时间 | 硬失效,任何情况下不得进入召回集 |
3.5 复核周期按风险分级
| 知识类型 / 风险 | 复核周期 | 超期后果 |
|---|---|---|
安全、合规、资金、权限(restricted) | 30 天 | verification_overdue + 高优先级告警 |
| Contract(接口、表结构、配置) | 60 天 | verification_overdue + 召回时附过时警告 |
| Procedure、Policy | 90 天 | verification_overdue |
| Concept、Decision、FAQ | 180 天 | 仅在检索命中时提示 Owner |
| 命中语义相关变更 | 立即(不等周期) | drift_detected |
3.6 关键决策点
| # | 决策 | 已确认 | 说明 |
|---|---|---|---|
| S1.1 | P0 frontmatter 字段数 | ✅ 7 字段 | 与蓝图 §5.2 一致(新增 knowledge_type,理由见 §3.1) |
| S1.2 | content_hash 基于 git blob | ✅ | 免疫 CRLF 与格式化器差异 |
| S1.3 | effective_to 规则 | ✅ active 期间为 null | 避免与裁决规则互相击穿 |
| S1.4 | 复核周期分级 | ✅ 30/60/90/180 天 | 初始建议值,须按实际校准 |
| S1.5 | flags 字段写入权限 | 待确认 | 选项:A) 仅 CI 可写,人工不可手改;B) CI 和 Owner 均可写。推荐 A + 写回 Git frontmatter:若不写回 Git,flags/flagged_at 只存在于元数据库,则 09 §2.6.5「元数据库可从 Git 重建」不成立(治理过程状态会全部丢失)。「人工不得手改」可通过 CODEOWNERS 限制 + CI 校验该字段 diff 实现,不依赖存储位置 |
| S1.6 | 上下文前缀是否持久化 | 待确认 | P0 启动前必须决策,不可逆。前缀是 chunk 的确定性函数,若不持久化(随 chunk_hash 写入 Git sidecar 或元数据库),则每次容灾全量重建都要重跑「chunk 总数 × 1 次 LLM 调用」,09 §2.6.5「索引不备份只重建」在大型档(百万级向量)不成立。详见 09 §2.6.6 |
3.7 字段字典(跨文档字段的唯一登记处)
本文是元数据 Schema 的唯一权威。05 / 06 在各自文档中使用了若干本文原先未登记的字段——字段定义分散在使用方会造成 Schema 双头:两个文档各自演进后必然分叉,索引 Pipeline 与 CI 校验也无法据本文生成完整校验规则。下列字段统一在此登记。
登记规则:任何文档新增 frontmatter 或索引字段,必须先在本表登记(字段名 / 类型 / 取值域 / 写入方 / 引入阶段),再在使用方文档中引用。未登记的字段 CI 校验按 Error 处理。
3.7.1 治理与生命周期字段(写入方:06 / CI)
| 字段 | 类型 | 取值域 | 写入方 | 引入阶段 | 用途 |
|---|---|---|---|---|---|
applies_to | object | {system, service, env, version} | 作者 + CI 校验 | P0(可选)→ P1(Contract 类必填) | 06 §2.1 版本裁决第 1 条的判定输入 |
review_state | enum | pending | approved | rejected | 03 审核流程 | P0 | 06 §2.1 裁决第 2 条 |
effective_at | ISO 8601 | — | CI 派生 | P0 | 06 §2.1 裁决第 3、4 条 |
expires_at | ISO 8601 | null | — | CI 派生 | P0 | 06 §2.1 裁决第 4 条;active 期间为 null(同 §3.4 的 effective_to 规则) |
source_type | enum | T1 | T2 | T3 | T4 | T5 | 作者 + Steward 复核 | P0 | 06 §2.1 裁决第 6 条的权威源等级 |
supersedes | list[knowledge_id] | — | 作者 | P1 | 06 §2.1 裁决第 5 条(反向引用查替代关系) |
flags | list[enum] | drift_detected | verification_overdue | owner_missing | steward_missing | conflicted | 仅 CI(S1.5) | P0 | 06 §2.3;独立于 status,可叠加 |
flagged_at | ISO 8601 | — | 仅 CI | P0 | 06 §2.3;与 flags 同生命周期 |
auto_rebase_to | commit SHA | — | 仅 CI(漂移检测) | P1 | 06 §2.2 无关变更时静默推进基线。统计「年更新次数」时必须按 author_email 过滤掉该字段产生的 bot 提交(06 §3.5) |
deprecated_at | ISO 8601 | — | Owner | P1 | 06 §2.5 Sunset |
replacement_api | URI | — | Owner | P1 | 06 §2.5 Sunset |
migration_guide | URI | — | Owner | P1 | 06 §2.5 Sunset |
sunset_at | ISO 8601 | — | Owner | P1 | 06 §2.5;到期处置见 06 M5.4 |
known_consumers | list[string] | — | CODEOWNERS 反查(P1)→ 关系图谱派生(P3) | P1 | 06 §2.5;删除前须确认清零 |
3.7.2 检索与引用字段(写入方:05 / 索引 Pipeline)
| 字段 | 类型 | 取值域 | 写入方 | 引入阶段 | 用途 |
|---|---|---|---|---|---|
section_anchor | string | 条目标题文本 | 索引 Pipeline | P0 | 05 §7.1 chunk_ref 的三段之一 |
min_classification | enum | 同 classification | 索引 Pipeline 派生 | P0 | 05 §10.1 MCP search_knowledge 参数,只能收紧不能放宽 |
acl_deny | list[group] | — | Steward | P1 | 05 §9.1 Chunk 级 ACL 的显式拒绝项 |
inherits_from | list[knowledge_id] | — | 索引 Pipeline 派生 | P1 | 05 §9.1;OWASP 第 4 类威胁(权限继承)的强制载体 |
index_version | string | — | 索引 Pipeline | P0 | 07 §3.4 门禁执行时锁定索引版本,避免评测期间索引变动 |
latency_breakdown | object | 各环节耗时 | 检索服务 | P1 | 05 §5 延迟预算的实测数据来源 |
degradation_flags | list[enum] | rerank_skipped | vector_channel_down | bm25_channel_down | rate_limited | 检索服务 | P1 | 本文新增。05 §5 允许「超时则跳过 rerank」、09 §2.6.4 允许通道降级;若不记录,审计日志无法区分「知识本身质量差」与「本次降级导致质量差」,07 §2.1 的四层归因会把降级误判为 Retrieval Failure |
stale_warning | bool | — | 检索服务 | P1 | 05 §8「引用与当前版本不一致 → 警告」的载体 |
retrieval_degraded | bool | — | 检索服务 | P1 | 05 §8 拒答与降级行为的审计标记 |
3.7.3 字段字典的三项约束
expires_at与effective_to不是两个字段。本文 §3.2 用effective_to,06 §2.1 用expires_at,语义完全相同(有效区间右端点)。统一为effective_to,06 侧的expires_at视为别名,CI 校验时归一化——否则同一条知识会出现两个右端点,裁决第 4 条无法执行。applies_to在 P0 为可选,但 Contract 类在 P1 起必填。06 §2.1 裁决第 1 条(适用范围匹配)是七条中的第一条,缺applies_to时该条恒为「无法判定」,裁决直接落到第 7 条conflicted——即没有applies_to的版本裁决机制是不成立的。flags的写入方唯一性是 S1.5 的实质内容,与存储位置(Git frontmatter vs 元数据库)无关。无论 S1.5 选 A 还是 B,都必须由 CODEOWNERS + CI diff 校验保证人工不可手改。
4. 目录结构
docs/knowledge/
├── {module}/ # 按业务模块组织
│ ├── {topic}.md # 知识条目
│ └── ...
├── _drafts/ # 摄入链路自动生成的草稿(01 §5),确认后移入 {module}/
├── _index.yaml # 知识目录(CI 生成,禁止手工编辑)
└── _glossary.yaml # 受控术语表4.1 _index.yaml 路由地图
version: 1
generated_at: "2026-10-21T10:00:00Z"
domains:
- domain: payment
canonical_name: 支付域
owner_group: ocp/payment-team
classification: internal
entry_points:
- "退款、支付、对账、幂等"
services: [payment-core, payment-gateway]
knowledge_count: 42
modules:
- module: refund
path: docs/knowledge/payment/refund/
topics: [幂等, 状态机, 超时补偿]为什么禁止手工编辑:手工维护的目录与真实条目必然漂移,L0 层一旦给出错误的域归属,Agent 会在错误的域里检索并自信地拒答。
4.2 关键决策点
| # | 决策 | 已确认 | 说明 |
|---|---|---|---|
| S2.1 | 目录结构 | ✅ docs/knowledge/{module}/{topic}.md | 与蓝图 §5.3 一致 |
| S2.2 | _index.yaml 生成方式 | ✅ CI 自动生成 | 禁止手工维护 |
| S2.3 | _glossary.yaml 维护方式 | 待确认 | 选项:A) 手工维护;B) CI 半自动生成(从知识条目中抽取术语) |
5. 索引 Pipeline
5.1 Chunk 切分规则
- 按语义边界切分,不得纯定长:
H2 > H3 > 段落 > 句子 - 单个 H2 章节超出 token 上限时才在段落边界二次切分
- 表格与代码块作为不可分割单元(超长表格按行组切分并重复表头)
- 初始参数:300-500 tokens / 重叠 50 tokens(必须按文档类型分别实测)
5.2 上下文前缀(关键技术)
在把 chunk 送去向量化之前,先拼接一段说明「这个 chunk 在整篇文档中处于什么位置」的前缀:
原始 chunk:
「该接口已废弃,请改用 v2。」
加前缀后送 Embedding:
「本片段来自《支付域 - 退款接口契约》,
描述 refund/v1 接口的生命周期状态。
该接口已废弃,请改用 v2。」前缀同时进入 Embedding 与 BM25 两个通道。若只对向量通道加前缀,BM25 通道仍检索脱离上下文的裸 chunk,混合检索的收益会被抵消一半。
5.3 每个索引 Chunk 的元数据
# frontmatter 携带(P0 最小集)
source_uri:
source_revision:
owner_group:
classification:
status:
title:
# 索引时自动附加
knowledge_id:
content_hash:
chunk_hash: # 内容寻址
context_prefix: # 上下文前缀
index_build_id:
embedding_model_version:
acl_groups: # 派生 chunk 继承源条目权限5.4 Embedding 模型选型要点
| 考量 | 要求 | 说明 |
|---|---|---|
| 中英混合 | 必须 | 企业知识普遍中英混杂 |
| 代码语义 | 建议 | 知识条目含代码引用 |
| 最大输入长度 | ≥ chunk 上限 + 前缀长度 | 前缀会挤占有效长度 |
| 维度 | 与向量库成本权衡 | 维度越高成本越高 |
| 版本可锁定 | 必须 | 模型升级会改变向量空间 |
不得直接使用未评估的模型:必须用评测集实测后再定。不接受榜单排名作为选型依据。
5.5 向量存储选型
| 方案 | 适用场景 | 主要代价 |
|---|---|---|
| Elasticsearch / OpenSearch | 已有 ES 集群,BM25 与向量同库 | 向量检索性能弱于专用库 |
| Milvus | 大规模(千万级)、独立扩缩容 | 多一套存储运维 |
| Qdrant | 中等规模、重视过滤性能 | 生态较新 |
| pgvector | 已有 PostgreSQL、向量量级不大 | 大规模下性能受限 |
选型的决定性因素是过滤性能:检索前按 classification 与权限过滤是每次查询都带的高选择性过滤条件。必须选用支持 filtered ANN(pre-filtering)的方案。
5.6 模型版本迁移:双索引蓝绿切换
5.7 关键决策点
| # | 决策 | 已确认 | 说明 |
|---|---|---|---|
| S3.1 | Chunk 切分策略 | ✅ 按语义边界,非定长 | 表格和代码块不可分割 |
| S3.2 | 上下文前缀 | ✅ 必须做 | 同时进 Embedding 和 BM25 |
| S3.3 | Chunk 大小初始值 | ✅ 300-500 tokens / 重叠 50 | 须实测校准 |
| S3.4 | 向量存储选型 | 待确认 | 选项:A) ES 同库;B) Milvus;C) Qdrant;D) pgvector |
| S3.5 | Embedding 模型 | 待确认 | 须用评测集实测后选定 |
| S3.6 | 索引构建触发方式 | 待确认 | 选项:A) MR 合并后自动触发;B) 定时批量构建 |
6. 实施步骤
步骤编号使用 ST 前缀,与决策编号 S1.x–S3.x 区分。原文两处都用 S,导致「S4 的前置条件是 S3.4 决策已确认」这类表述中,S3.4 既可能被读成「步骤 S3 与 S4」也可能被读成「决策 S3.4(向量存储选型)」——实际指后者,但读者无从判定。
| 步骤 | 内容 | 产出物 | 前置条件 | 优先级(09 §3) |
|---|---|---|---|---|
| ST1 | 创建目录结构 | docs/knowledge/ + _drafts/ | 无 | P0 |
| ST2 | 实现 _index.yaml CI 生成脚本 | scripts/generate-index.py | ST1 完成 | P0 |
| ST3 | 实现 Chunk 切分 + 上下文前缀 | scripts/chunk-split.py | ST1 完成;S1.6 已决策 | P0(切分)/ P1(前缀增强) |
| ST4 | 选型并部署向量存储 | 向量库实例 | 决策 S3.4 已确认 | P0 |
| ST5 | 实现索引 Pipeline | 切分 → 前缀 → Embedding → 写入 | ST3、ST4 完成 | P0 |
| ST6 | 实现双索引蓝绿切换 | 切换脚本 | ST5 完成 | P2 |
| ST7 | 端到端验证 | MR 合并 → 知识可检索 | ST1–ST6 完成 | P0(最小链路)/ P2(完整) |
_drafts/未在 §4 目录结构中登记,但 ST1 要求创建它,01 §5 的草稿流程也依赖它。§4 的目录树须补_drafts/,否则 CI 校验会把它当作未登记路径。
7. 度量
| 指标 | 计算方式 | 目标 | 性质 |
|---|---|---|---|
| 索引延迟 | MR 合并到 Chunk 可检索的时间 | ≤ 5 分钟 | 阈值待实测 |
| 索引完整性 | 索引 Chunk 对应的条目数 / active 条目数 | 100% | 机制保证 |
| 索引重建时间 | 从权威源完整重建全部索引的时间 | 按规模档位分级:小型 ≤ 10 分钟 / 中型 ≤ 30 分钟 / 大型 ≤ 2 小时(初始建议值) | 阈值待实测。原「≤ 30 分钟」是与规模无关的常数,而 09 §2.6.1 明确规模分三档(千级 / 万级 / 百万级以上向量);百万级 chunk 在 1800 秒内跑完要求 ≥ 555 chunk/秒的持续 Embedding 吞吐,对千级与百万级同时适用在物理上不成立。本指标同时是容灾指标(09 §2.6.5「索引不备份只重建」的成立前提),不达标即触发索引备份的受控回退 |
| 前缀覆盖率 | 已生成上下文前缀的 chunk 数 / 总 chunk 数 | 100% | 机制保证 |
| 模型版本一致率 | 索引中 embedding_model_version 唯一的比例 | 100%(非迁移期) | 机制保证 |
8. 风险与依赖
| 风险 | 影响 | 缓解措施 |
|---|---|---|
| 向量存储选型不当 | 检索性能或成本不可控 | 先用小规模数据压测,再决定生产选型 |
| Embedding 模型不适配 | 中文/代码语义召回差 | 用评测集实测后再定,不照搬榜单 |
| 索引重建时间过长 | 容灾能力不足 | 优化 Pipeline 并行度,必要时引入备份 |
| 上下文前缀 LLM 成本过高 | 索引构建成本超出预算 | 用 Prompt Caching 缓存文档级上下文 |
9. 待确认决策汇总
| 编号 | 决策 | 选项 | 推荐 |
|---|---|---|---|
| S1.5 | flags 写入权限与存储位置 | A) 仅 CI 可写 + 写回 Git frontmatter;B) CI + Owner 可写;C) 仅 CI 可写 + 只存元数据库 | A(C 会使 09 §2.6.5「元数据库治理状态可从 Git 重建」不成立) |
| S1.6 | 上下文前缀是否持久化 | A) 持久化(Git sidecar 或元数据库);B) 每次重建重算 | A。P0 启动前必须决策,不可逆;B 会使大型档的「索引不备份只重建」不成立(09 §2.6.5) |
| S2.3 | glossary 维护方式 | A) 手工;B) 半自动 | A(P0)→ B(P2) |
| S3.4 | 向量存储选型 | A) ES;B) Milvus;C) Qdrant;D) pgvector | 按团队现有基础设施选择 |
| S3.5 | Embedding 模型 | 须实测 | — |
| S3.6 | 索引构建触发方式 | A) MR 后自动;B) 定时批量 | A |
| S4.1 | _drafts/ 是否纳入 §4 目录结构登记 | A) 纳入;B) 视为临时路径不登记 | A(ST1 与 01 §5 均依赖该路径存在) |