Files
prop-ai-hr/docs/data-quality-audit.md
T

496 lines
69 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# prop-ai-hr 数据质量与脏数据治理专项审计
> 实施状态(2026-08-02):本报告正文保留改造前的只读审计基线。当前工作树已在本地完成 P0 主门禁,并落地 P1 的版本化规范化、独立隐私脱敏派生与复扫、结构感知切片、SimHash 近重复、异步语义重复候选、同名版本冲突、确定性 claim 级冲突证据、带管理端人工审核的版本化术语表,以及 P2 的索引死信健康度、真实召回证据、答案争议回到具体 chunk/asset 的人工 `KEEP/WITHDRAW` 闭环、持久化质量告警、Actuator 健康项和可失败的黄金检索评测门禁。`dq-gate-v3` 还会以软 reason code 标记页码、重复页眉页脚、领域无关、案例上下文缺失和孤立片段,不改写原文、不自动拒绝或批准。历史附件可通过只读预览和带 manifest 的 CLI 按游标恢复纳管;任何历史数据都不能自动批准。AI 整理案例会永久保存 synthetic、模型/生成器、prompt 版本、原始音频和人工审核 provenance。100–300 条真实业务黄金集标注、复杂适用条件阈值标定、生产告警平台接入和全量历史重清洗尚未完成。该状态不代表生产部署或业务资料已获批准。
>
> 快照说明:正文中的“本地 MinIO 为空、Qdrant collection 不存在”等数据是第一阶段只读审计时的基线,不是 2026-08-01 本地 E2E 完成后的运行状态;最新实施证据见第 19 节。
> 审计日期:2026-08-01
> 审计方式:代码、SQL 迁移、测试及本地运行数据的只读审计
> 审计结论适用范围:当前工作区(包括审计时已经存在、尚未提交的引用定位器改动)与本机开发数据快照;不等同于生产数据全量结论
> 第一阶段变更边界:只生成本报告,未修改业务代码、配置、数据库、对象存储或向量索引;后续本地实施状态见第 19 节
## 1. 执行摘要
当前系统已经具备若干有价值的局部控制:知识查询以服务端计算“租户 + 调用应用 + 主体授权”的空间交集;文件接入有大小/解析失败处理和文件、文本哈希精确去重;正式制度查询有来源权威性、版本、审批、生效和失效日期门禁;个人知识有私有对象存储、所有权校验、精确哈希去重、发布审核和确定性脱敏;陪练场景有内容版本、哈希和高风险双人审核;考试发布和显式记忆也有人工或用户确认。
但这些控制没有形成统一的数据质量生命周期。普通企业资料的主路径实际是:上传或导入后,只要解析出非空文本,就直接切片写入 `aihr_knowledge_fragment`,随后向量化并可被普通检索使用。该路径没有通用的隐私检查、领域验证、上下文完整性检查、近重复/语义重复、质量原因码、隔离区或人工批准门禁。结论是:**当前存在 `RAW/PARSED -> INDEXED` 直接路径,目标生命周期并未全局落地。**
最高风险还包括:个人经验经一次发布审核后直接写入企业知识表;模型整理或兜底总结可直接把案例标成“已入库”;考试生成从租户全部片段取材,未复用文档批准/有效期门禁;向量删除失败仅尽力忽略,没有持久化重试;查询审计没有记录具体片段修订,错误答案无法从 `request_id` 稳定反查污染版本。普通企业 RAG 还把片段原文直接拼进提示词,缺少个人问答路径已有的“来源是不可信数据、不得执行其中指令”边界。
本地只读快照进一步证明风险已经落地,而不只是理论可能:1,046 个附件全部处于“已解析”,8,607 个片段中检测到 1,897 个全局精确重复余量、1,178 个疑似页码/分页噪声命中、19 个含 UTF-8 替换字符的片段、6 个找不到附件的片段;1,207 个上传任务中 94 个失败,失败率 7.79%。正式来源治理仅覆盖 10 个附件,约占附件数 0.96%。本地 Qdrant collection 不存在,本地两个 MinIO bucket 均无对象,因此本机数据库中的原文可恢复性和向量一致性无法成立。一次真实移动端请假/考勤问题还召回了招聘、垃圾站保洁、防汛等无关引用,属于已观察到的检索相关性失败。
建议按最小增量方式修复,不整体重写现有知识平台:保留 `sys_oss`、`aihr_knowledge_*` 和现有权限模型,新增不可变原始资产、版本、质量评估、问题明细、审核决策、片段修订、血缘和索引 outbox 侧表。生产检索只允许读取 `PUBLISHED` 且版本有效的片段修订;普通上传、个人发布、案例和 AI 生成内容统一先进入隔离/待审区。P0 应优先关闭新污染入口、建立可靠撤回与片段级审计,再迁移历史数据。
## 2. 审计方法与边界
### 2.1 审计方法
1. 由 HTTP Controller 反向跟踪上传、导入、检索、案例、考试、陪练、个人知识、记忆和反馈到 Service、SQL 与存储对象。
2. 检查解析器、文本规范化、切片、哈希去重、embedding、Qdrant payload/filter、删除与重建代码。
3. 检查知识、个人知识、案例、陪练、考试、记忆、反馈、组织快照相关建表和增量迁移。
4. 检查现有单元测试中对租户、所有权、审批、去重、正式制度和失败路径的断言。
5. 对本机 MySQL、Qdrant 和 MinIO 做只读计数、孤儿关系、重复、字符异常及运行状态检查;未读取或披露业务正文、姓名、手机号和密钥。
### 2.2 证据口径与限制
- 文件行号指向审计时工作区,不保证后续代码变更后仍保持相同行号。
- 本地数据库来自当前开发环境,只说明本地快照;不能外推为生产全量数据质量。
- 本地 MinIO 无对象、Qdrant collection 不存在,因此未能验证远端原文件完整性、生产向量数量和线上删除一致性。
- “有”表示所审计主路径存在明确、不可绕过的控制;“部分”表示只覆盖特定格式、来源、意图或入口;“无”表示未找到足够代码证据。
- 当前未发现独立训练流水线。本文所称“训练集/评测集泄漏”同时覆盖陪练、考试、评分、案例等可被误作训练或评测语料的业务数据。
## 3. 当前数据血缘图
```mermaid
flowchart LR
A["原始来源<br/>企业文件/ZIP/本地目录/API<br/>个人文本/文件/URL<br/>案例录音/用户对话/组织 API"]
B["接入与暂存<br/>Controller + UploadQueue<br/>sys_oss / staging_path / personal MinIO"]
C["解析<br/>Tika / OCR / 视频抽帧 / ASR / URL fetch"]
D["规范化与分类<br/>换行+trim / LLM或规则分类<br/>文件与文本哈希精确去重"]
E["切片<br/>空段落 + 字符硬切 + overlap"]
F["事实存储<br/>aihr_knowledge_attach<br/>aihr_knowledge_fragment<br/>个人/案例/陪练/考试/记忆表"]
G["向量化<br/>embedding provider<br/>Qdrant aihr_knowledge"]
H["检索<br/>MySQL FULLTEXT/LIKE + Qdrant<br/>tenant + app + principal space"]
I["模型生成<br/>RAG 答案/案例总结/考试题/评分/记忆草稿"]
J["用户交互<br/>问师傅/案例/陪练/考试/反馈"]
K["回流<br/>query log/answer feedback/review<br/>practice session/calibration/memory"]
A --> B --> C --> D --> E --> F --> G --> H --> I --> J --> K
K -. "当前无统一批准后回流" .-> F
C -. "非空即可" .-> E
F -. "普通资料无文档批准过滤" .-> H
```
### 3.1 企业知识主链路
| 节点 | 实际入口、函数与存储 | 证据 |
|---|---|---|
| 接入 | `POST /api/knowledge/doc/upload-async`;`AihrSopController.uploadAsync` 调用 `AihrUploadQueueService.enqueue`。另有同步 `/doc/upload`、本地目录 `/doc/import-local` 和 `/doc/import-local-task` 运维入口 | `backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/controller/AihrSopController.java:73`、`:188`、`:206`、`:212` |
| 队列 | `AihrUploadQueueService.processItem` 从 `aihr_knowledge_upload_item.staging_path` 取文件,转入 `processStagedDocument`,结果仅以片段数是否大于 0 判定完成 | `backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/service/AihrUploadQueueService.java:350`、`:388`、`:396` |
| 解析 | 普通文档走 `TikaKnowledgeDocumentParser`;图片走视觉 OCR,视频走关键帧/转写相关分支。Tika 输出全文、metadata 和按行段;嵌入文档明确不解析 | `backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/knowledge/parse/TikaKnowledgeDocumentParser.java:117`、`:132`、`:166`;`backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/service/AihrSopSeedService.java:4022`、`:4050`、`:4092` |
| 清洗 | `normalizeExtractedText` 只统一换行并 `trim`;没有通用页眉页脚、页码、水印、控制字符、PII 或领域规则 | `backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/service/AihrSopSeedService.java:4226` |
| 分类/去重 | 计算文件 SHA-256、MD5、文本 SHA-256;通过 `duplicateHit` 复用精确重复附件;分类 insight 可来自模型或兜底 | `backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/service/AihrSopSeedService.java:1262`、`:1263`、`:1727`、`:1755` |
| 切片 | `split` 按空段落组块,超长内容按字符硬切,并加 overlap;片段无独立修订号/规则版本 | `backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/service/AihrSopSeedService.java:4240`、`:4252`、`:4264` |
| 原文/附件 | 原文件通常写 `sys_oss`,文档成员写 `aihr_knowledge_attach`;其 `status=2` 仅表示解析完成,不是质量批准 | `backend/script/sql/aihr_knowledge_mysql8.sql:34`、`:43`;`backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/service/AihrSopSeedService.java:1279`、`:1282` |
| 片段 | 删除同文档旧片段后,直接插入 `aihr_knowledge_fragment`,随后标记附件已解析 | `backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/service/AihrSopSeedService.java:1296`、`:1299`、`:1308` |
| 定位 | 当前工作区新增 `aihr_knowledge_fragment_locator`;企业导入仍丢弃解析器 segments,定位回填主要以切片序号模拟段落,不能证明 PDF 页/PPT 页/Excel 行 | `backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/service/AihrSopSeedService.java:4091`、`:4095`;`backend/script/sql/aihr_knowledge_mysql8.sql:95` |
| 向量化 | 每个片段 embedding 后 upsert Qdrant,payload 为 `tenant_id/knowledge_id/category/doc_id/idx/embedding_model` | `backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/service/AihrSopSeedService.java:2269`、`:3737`、`:3757` |
| 检索 | `/api/knowledge/search` 进入统一查询;MySQL FULLTEXT、LIKE、向量结果都以租户和授权空间为核心条件 | `backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/controller/AihrSopController.java:105`;`backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/service/AihrSopSeedService.java:3052`、`:3103`、`:3164` |
| 权限 | 服务端计算应用空间、主体授权空间、请求空间的交集;Qdrant 过滤包含租户和空间 ID | `backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/knowledge/service/AihrKnowledgeAccessService.java:23`、`:57`、`:66`;`backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/service/AihrSopSeedService.java:3945` |
| 生成 | 检索片段拼入企业 RAG prompt,模型生成答案;引用再从片段表 hydrate | `backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/service/AihrSopSeedService.java:3331`;`backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/knowledge/service/AihrKnowledgeQueryService.java:648` |
| 回流 | 写 `aihr_knowledge_query_log`、`aihr_sop_answer_review`、`aihr_knowledge_answer_feedback`;差评只屏蔽相同规范化问题与片段组合 | `backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/knowledge/service/AihrKnowledgeQueryAuditService.java:23`;`backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/service/AihrSopSeedService.java:2975` |
### 3.2 特殊来源与生成链路
| 数据流 | 当前实际路径 | 是否进入企业可信区 |
|---|---|---|
| 正式制度 | 普通知识接入后,另由 `aihr_knowledge_source_governance` 登记 `APPROVED`、版本、生效/失效日期;只有识别为正式制度意图时附加过滤 | 特殊意图受控;普通检索仍可能命中相同片段。证据:`backend/script/sql/update/aihr_20260730_formal_knowledge_source_governance_mysql8.sql:5`;`backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/knowledge/service/AihrKnowledgeQueryService.java:662` |
| 个人知识 | `POST /api/aihr/personal-assistant/items/text|file|url` -> 私有 item/fragment;HR/超级管理员审核发布 -> 脱敏后的 fragment 直接复制到企业知识表 | 是,审批后直接写企业表,但绕过通用附件/版本/质量/重新解析链。证据:`backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/personal/controller/PersonalAssistantController.java:89`、`:94`、`:103`;`backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/personal/service/PersonalPublishService.java:107`、`:232` |
| 案例录音 | `/api/knowledge/case/upload` -> OSS + ASR -> `aihr_case_record`;`organize` 用模型或本地兜底总结;`curate` 只进入候选池;`review` 完成业务审核;`publish` 在来源授权、脱敏、适用岗位、版本和负责人齐全后才标记 `knowledge_status=PUBLISHED` | 员工列表/详情只读取 `knowledge_status=PUBLISHED`,审核通过不会自动进入正式知识库。迁移:`backend/script/sql/update/aihr_20260802_case_experience_governance_mysql8.sql`;服务:`backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/service/AihrCaseService.java` |
| AI 考试题 | `AihrExamService.generateDraft` 从租户片段抽取 grounding,模型/规则生成草稿;主管创建并发布考试 | 不直接自动发布,但 grounding 未要求来源批准、附件有效或授权空间。证据:`backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/learning/AihrExamService.java:185`、`:482`、`:541` |
| 陪练 | 用户回答、音频、LLM/seed 评分写 session/assignment;可选 calibration 保存人工校准 | 不写企业知识表,但属于运行/评测数据,当前缺少统一 synthetic、置信度、模型版本和数据用途隔离。证据:`backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/service/AihrPracticeSeedService.java:1513`;`backend/script/sql/aihr_practice_mysql8.sql:27`、`:168` |
| 陪练场景 | 场景有草稿、待业务审核、已发布、已下线;高风险需不同用户双审并校验内容版本/哈希 | 局部强控制。证据:`backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/service/AihrPracticeSeedService.java:599`、`:675`、`:696`;`backend/script/sql/aihr_practice_mysql8.sql:184` |
| 显式记忆 | 模型/规则先给候选项,用户确认后进入 PRIVATE;COMPANY 捕获保持 `PENDING`;召回走独立记忆表 | 当前未直接写企业知识片段,控制较好。证据:`backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/memory/AihrMemoryService.java:517`、`:859`、`:900` |
| 组织 API 同步 | `/api/aihr/org/sync-changes` 写组织快照;要求显式 dry-run,重复成员和异常数据可 fail closed | 影响身份/授权而非知识正文;属于重要旁路数据。证据:`backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/controller/AihrOrgSyncController.java:24`、`:39`;`backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/service/AihrOrgSyncService.java:208`、`:301` |
## 4. 当前数据对象和存储结构
| 数据域 | 主要对象/表 | 当前角色与关键缺口 |
|---|---|---|
| 原文件 | `sys_oss`、MinIO `ruoyi` | 保存对象与扩展元数据;没有独立不可变业务资产版本、采集来源可信等级和保留策略。`sys_oss.ext1` 中有文件/文本哈希和自动分类元数据,但更新失败被忽略:`AihrSopSeedService.java:1813-1826`。 |
| 企业空间 | `aihr_knowledge_info`、`aihr_knowledge_category`、`aihr_knowledge_acl` | 空间状态为 `DRAFT/ACTIVE/DISABLED`,是空间级开关,不是文档/片段质量状态:`backend/script/sql/aihr_knowledge_mysql8.sql:4`、`:11`。 |
| 企业附件 | `aihr_knowledge_attach` | 只有解析状态 `0/1/2/3`;`2=已解析` 被界面称作“可引用资料”,不代表质量批准:`backend/script/sql/aihr_knowledge_mysql8.sql:34`、`:43`;`AihrSopSeedService.java:1830-1843`。 |
| 企业片段 | `aihr_knowledge_fragment`、`aihr_knowledge_fragment_locator` | 正文、embedding、doc/idx 和定位;无来源版本、质量状态、原因码、规则版本、有效期、synthetic/runtime 标记和修订历史:`backend/script/sql/aihr_knowledge_mysql8.sql:73`、`:95`。 |
| 正式来源 | `aihr_knowledge_source_governance` | 有来源 ID、附件、版本、权威类型、生命周期、生效/失效、内容哈希;只覆盖正式制度专用路径:`backend/script/sql/update/aihr_20260730_formal_knowledge_source_governance_mysql8.sql:5-30`。 |
| 应用与授权 | `aihr_knowledge_app`、`aihr_knowledge_app_space`、`aihr_knowledge_space_grant` | 支持租户、应用、主体授权交集;这是访问控制,不替代内容质量审批:`backend/script/sql/aihr_knowledge_mysql8.sql:249`、`:266`、`:288`。 |
| 处理任务 | `aihr_knowledge_upload_item`、`aihr_knowledge_import_task` | 记录等待/处理中/完成/失败和错误文本;没有阶段级质量结果、reason code 列表、检测器版本和人工处置:`backend/script/sql/aihr_knowledge_mysql8.sql:149`、`:228`。 |
| 查询与反馈 | `aihr_knowledge_query_log`、`aihr_sop_answer_review`、`aihr_knowledge_answer_feedback`、`aihr_knowledge_gap` | 有请求、状态、来源类型、提示词版本和特定 query-fragment 差评;缺精确使用片段修订、排名、模型/embedding 版本、答案 hash 及处置闭环:`backend/script/sql/aihr_knowledge_mysql8.sql:300`;`backend/script/sql/aihr_practice_mysql8.sql:283`、`:296`、`:318`。 |
| 个人知识 | `aihr_personal_item`、`aihr_personal_fragment`、OCR job/page、cleanup job、publish request | 所有权、私有存储、精确 hash、处理和发布状态较完整;但批准发布时仍复制进通用企业片段,丢失完整个人来源版本链:`backend/script/sql/aihr_personal_knowledge_mysql8.sql:35`、`:64`、`:132`、`:190`。 |
| 案例 | `aihr_case_record` | 原转写、AI 总结、主管点评和状态同表;状态主要是流程展示,不足以区分 raw/trusted/synthetic:`backend/script/sql/aihr_practice_mysql8.sql:4-24`。 |
| 陪练与评分 | `aihr_practice_session`、`assignment`、`calibration`、`scenario`、`rubric` | 保存回答、AI 分数、规则兜底、人审校准及内容版本;运行数据和评测用途没有统一数据资产标记:`backend/script/sql/aihr_practice_mysql8.sql:27`、`:137`、`:168`、`:184`。 |
| 考试 | `aihr_onboard_exam*` | 草稿/发布、题目正确答案、员工答案和得分;AI 题来源未绑定具体已批准 chunk revision:`backend/script/sql/update/aihr_20260717_learning_closure_mysql8.sql:102`、`:161`、`:189`。 |
| 记忆 | `aihr_service_memory*`、assistant capture | 候选、确认、拒绝、过期和公司 pending 独立于知识片段,边界相对清晰:`backend/script/sql/aihr_service_memory_mysql8.sql:14`。 |
## 5. 信任边界分析
| 内容类型 | 是否存在直接进入可信/生产使用区的路径 | 现有控制 | 结论与证据 |
|---|---|---|---|
| 未审核上传文件 | 是 | 角色限制、非空、解析状态、精确重复 | 非空即写片段并 embedding,无通用审批:`AihrSopController.java:73-84`;`AihrSopSeedService.java:1243-1308`。P0。 |
| OCR 结果 | 是 | 图片/视频无结果可待处理;个人 PDF 有分页失败状态 | 企业 OCR 有文本即可进入同一片段链,未保留置信度和逐页证据:`AihrSopSeedService.java:673`、`:4050`。P0。 |
| 录音转写 | 部分是 | 案例上传需登录/项目权限 | 转写进入案例,整理/入库无前置质量审核:`AihrCaseController.java:53-74`;`AihrCaseService.java:96-124`。P1。 |
| 员工个人经验 | 是 | 个人区隔离;发布需 HR/超级管理员审核和确定性脱敏 | 一次批准后直接复制到企业知识表,不走版本/质量链:`PersonalPublishService.java:107-159`、`:232-264`。P0。 |
| 信息不完整案例 | 是 | 可由主管事后点评 | `curate` 不检查必填证据、上下文、结论依据,直接标已入库:`AihrCaseService.java:107-124`。P1。 |
| 用户回答 | 否(企业知识),是(运行/评分表) | 身份、会话和项目范围;部分人工校准 | 没有发现自动写入知识片段的路径;但评分/未来训练用途无统一 consent/purpose/split 标记:`aihr_practice_mysql8.sql:27-75`、`:168-182`。P1。 |
| 模型回答 | 否(企业知识),是(日志/评审) | 有引用和问答评审表 | 未发现自动提升为企业知识;审计却不能稳定记录用过的 chunk revision:`AihrKnowledgeQueryAuditService.java:23-37`。P1。 |
| AI 生成案例 | 是(案例库) | 操作者需有案例管理权限 | 模型失败还会用本地兜底,随后可直接已入库;没有 synthetic 标记和批准门:`AihrCaseService.java:96-124`。P0/P1 边界,按 P1 排期但必须与 P0 门禁一起封堵。 |
| AI 生成标准答案/考试题 | 否(自动发布),但来源可污染 | 生成草稿后由主管创建/发布,试卷总分等有校验 | grounding 从全部租户片段读取,未过滤批准、空间和附件有效性:`AihrExamService.java:482-568`;发布控制:`:185-251`。P1。 |
| AI 生成评分 | 是(业务结果) | `score_mode`、rubric snapshot、可选 calibration | LLM 失败退 seed,结果直接用于 session/assignment;人审不是强制,缺模型/置信度/异常 reason:`AihrPracticeSeedService.java:1513-1539`;`aihr_practice_mysql8.sql:157-177`。P1。 |
| 旧版本制度 | 是,普通检索可命中 | 正式制度意图有版本、生效/失效门禁 | 门禁只在 `formalPolicyOnly` 分支,普通检索不要求治理行:`AihrKnowledgeQueryService.java:648-713`。P0。 |
| 来源不明数据 | 是 | 文件名、OSS、remark、部分 hash | 普通片段不要求来源所有者、权威性、版本、采集时间和适用范围。P0。 |
| 跨租户数据 | 主查询未发现直接路径;索引漂移仍有残余风险 | SQL tenant 条件、空间交集、Qdrant tenant filter | 服务端隔离较强:`AihrKnowledgeAccessService.java:23-104`;`AihrSopSeedService.java:3945-3956`。但向量删除失败无可靠补偿,必须做一致性审计。P1。 |
## 6. 脏数据类型覆盖矩阵
| 脏数据类型 | 是否已处理 | 处理位置/方式 | 可绕过路径 | 风险 | 代码证据 |
|---|---|---|---|---|---|
| 空内容 | 部分 | Tika 空文本抛错;企业保存拒绝 blank;图片/视频无视觉结果保持待处理 | 仅检查整体非空,极短、只有页眉页脚或无语义文本仍可入库 | 中 | `TikaKnowledgeDocumentParser.java:117-124`;`AihrSopSeedService.java:1250-1256` |
| 乱码和解析失败 | 部分 | 解析异常、大小限制、任务失败/重试 | 未检测替换字符、控制字符、异常编码比例;本地有 19 个含 `U+FFFD` 片段 | 高 | `TikaKnowledgeDocumentParser.java:105-123`;`AihrUploadQueueService.java:410-417` |
| 文档缺页 | 无(通用) | 个人 PDF OCR 有 page/job 状态和 partial | 企业 Tika 丢失真实分页结构,也没有声明页数与解析页数比对 | 高 | `TikaKnowledgeDocumentParser.java:128-143`;`PersonalPdfOcrService.java:436-444` |
| 精确重复 | 部分 | 企业文件/文本 SHA-256;个人内容 hash | 片段级重复、不同包装/页眉差异、跨空间重复仍存在;本地全局重复余量 1,897 | 中 | `AihrSopSeedService.java:1262-1267`、`:1755-1788`;`PersonalIngestionService.java:171-203` |
| 近重复 | 无 | 未发现 MinHash/SimHash/edit distance 规则 | 改空格、页眉、格式或少量措辞即可绕过精确 hash | 高 | `AihrSopSeedService.java:1262-1267` |
| 语义重复 | 无 | 未发现聚类/相似度阻断或审核队列 | 同义改写、不同专家复述都能并存并进入召回 | 高 | `AihrSopSeedService.java:1296-1306` |
| 来源不明 | 部分 | OSS、文件名、docId、remark;正式制度有 source registry | 普通资料、个人发布生成的企业片段不要求权威来源和稳定版本 | 严重 | `aihr_knowledge_mysql8.sql:34-92`;`PersonalPublishService.java:232-254` |
| 领域无关 | 无(通用) | LLM/规则自动分类只决定空间/标签 | 分类结果不形成阻断或 reason code;已观察到跨主题错误召回 | 严重 | `AihrSopSeedService.java:1263-1269`、`:1296-1308` |
| 上下文不完整 | 无 | 切片带 overlap;回答提示要求依据片段 | 字符硬切、表格/页码结构丢失,无法验证片段是否自洽 | 高 | `AihrSopSeedService.java:4240-4269`;`aihr_20260725_sop_answer_partial_evidence_prompt_mysql8.sql:7-8` |
| 结论无依据 | 部分 | RAG prompt 要求只基于片段;正式制度再做相关证据判断 | 原片段自身可能无来源/不可信;案例总结、考试题和评分可由模型/兜底直接产生 | 严重 | `AihrKnowledgeQueryService.java:704-710`;`AihrCaseService.java:96-124` |
| 错误标签 | 无(通用) | insight 保存 `classifiedBy/reason/tags` | 没有确定性校验、人工确认状态和标签版本;分类失败不阻断入库 | 高 | `AihrSopSeedService.java:1264-1269`、`:1818-1824` |
| 制度过期 | 部分 | 正式制度意图检查 `APPROVED`、生效/失效和版本 | 普通知识查询、考试 grounding 不执行该条件 | 严重 | `AihrKnowledgeQueryService.java:662-680`;`AihrExamService.java:482-538` |
| 多版本冲突 | 部分 | 正式制度 `source_id/source_version` 唯一登记;陪练场景有版本/hash | 普通资料无 supersedes/有效期;新上传不会自动下线旧版 | 严重 | `aihr_20260730_formal_knowledge_source_governance_mysql8.sql:8-27`;`aihr_knowledge_mysql8.sql:34-92` |
| 多专家意见冲突 | 无 | 未发现 claim/观点级冲突模型和仲裁状态 | 多份专家经验可同时发布/召回,无法表达适用条件和异议 | 高 | `PersonalPublishService.java:232-264` |
| 未脱敏个人信息 | 部分 | 个人发布使用确定性 sanitizer;工作投稿另有隐私警告 | 普通企业上传/OCR/ASR 无通用 PII 扫描;上传角色可信不能代替内容脱敏 | 严重 | `PersonalPublishService.java:139-151`;`PersonalPromptSanitizer.java:10-62` |
| 跨租户数据 | 有(检索主链)/部分(索引一致性) | SQL tenant、应用/主体/请求空间交集、Qdrant tenant payload/filter | 删除失败、历史错误 payload 或旁路直写需靠对账发现;当前无 outbox/reconciler | 高 | `AihrKnowledgeAccessService.java:23-104`;`AihrSopSeedService.java:3737-3748`、`:3945-3956` |
| 提示词注入 | 部分 | 个人 QA 明确把来源当不可信并 XML 隔离 | 企业 RAG 将片段原文直接拼入 prompt,未找到等价指令隔离 | 严重 | `PersonalAnswerService.java:249-274`;`AihrSopSeedService.java:3331-3381` |
| AI 合成数据污染 | 部分 | 考试先草稿后人工发布;记忆需确认 | 案例自动“已入库”;个人发布/企业片段无统一 `is_synthetic/generator`;LLM 分类无版本化门禁 | 严重 | `AihrCaseService.java:96-124`;`AihrExamService.java:185-251` |
| 运行日志回流污染 | 部分 | 当前未发现 query log、agent run 自动写入企业片段;记忆独立且需确认 | 缺统一 data purpose 和 lineage,未来离线导出/训练难以防误用;案例和陪练运行表仍可被当语料 | 高 | `aihr_knowledge_mysql8.sql:300-372`;`AihrMemoryService.java:859-942` |
| 训练集和评测集泄漏 | 无 | 未发现正式 split registry、样本 hash、时间切分或用途限制 | 考题、参考答案、员工答案、AI 评分、案例同处业务库,离线抽取无法证明隔离 | 高 | `aihr_20260717_learning_closure_mysql8.sql:151-200`;`aihr_practice_mysql8.sql:137-182` |
## 7. P0、P1、P2 问题清单
### P0:必须先阻止新增污染或越权可信化
**P0-1 普通资料非空即可进入生产片段和索引。** 解析、切片、写 MySQL、embedding 在同一保存流程连续执行,缺少 `PRIVACY_CHECKED -> DOMAIN_VALIDATED -> QUALITY_EVALUATED -> APPROVED` 门禁。证据:`AihrUploadQueueService.java:350-417`;`AihrSopSeedService.java:1243-1314`、`:2269-2312`。影响所有普通文件、OCR/视频文本、同步上传和运维导入。
**P0-2 生产检索不要求文档/片段批准与有效版本。** 普通 FULLTEXT、LIKE、向量 hydrate 仅约束租户/空间等条件;只有正式制度意图附加 governance 条件。证据:`AihrSopSeedService.java:2949-2972`、`:3052-3200`;`AihrKnowledgeQueryService.java:648-713`。旧制度、低质量和待处置片段可被正常问答使用。
**P0-3 个人发布可直接污染企业可信表。** 审核与脱敏值得保留,但发布器直接新建企业空间并写 `aihr_knowledge_fragment`,没有附件、不可变来源版本、质量评估、重切片或索引生命周期。证据:`PersonalPublishService.java:107-159`、`:232-264`。
**P0-4 撤回与向量删除不是可靠事务。** 文档解绑先删 MySQL,Qdrant 删除异常被捕获并忽略;没有 durable outbox、重试、死信和对账修复。证据:`AihrSopSeedService.java:842-881`、`:3763-3780`。结果可能是 SQL 已撤回但向量仍残留,且无法证明最终一致。
**P0-5 来源、版本和可追溯性不足。** 普通片段没有稳定 source/version、有效期、适用范围、规则版本和修订;当前 locator 也不能恢复真实 PDF 页/PPT 页/Excel 行。证据:`aihr_knowledge_mysql8.sql:34-119`;`TikaKnowledgeDocumentParser.java:117-143`;`AihrSopSeedService.java:4091-4122`。
**P0-6 企业 RAG 缺少来源指令隔离。** 个人 QA 明确声明来源不可信并转义,企业 RAG 没有等价边界,恶意文档可尝试覆盖系统指令。证据:`PersonalAnswerService.java:249-274`;`AihrSopSeedService.java:3331-3381`。
### P1:P0 门禁建立后立即治理
**P1-1 清洗和结构恢复过弱。** 目前主要是换行/trim 与字符切片,不能处理页眉页脚、目录、水印、表格、OCR 置信度、控制字符和段落重复。本地 1,178 个疑似页码噪声、19 个替换字符、195 个小于 40 字片段构成直接证据。代码:`AihrSopSeedService.java:4226-4269`。
**P1-2 只有精确文档去重,没有近重复、语义重复和冲突检测。** 本地 8,607 个片段中,全局精确重复余量 1,897,同空间 560,同文档 422。代码:`AihrSopSeedService.java:1262-1267`、`:1755-1788`。
**P1-3 AI/兜底案例可直接标“已入库”。** 主管 review 是事后评论,不是发布前审核;缺 synthetic、generator、grounding 和证据完整性。证据:`AihrCaseService.java:96-138`、`:299-333`。
**P1-4 AI 考试题的 grounding 绕过知识批准和授权范围。** 草稿发布有人审,但底层素材来自租户全部片段,可能吸收过期、跨岗位或未批准内容。证据:`AihrExamService.java:482-568`。
**P1-5 AI 评分被作为业务结果,校准非强制。** LLM 与 seed fallback 都生成实际分数,需记录模型、prompt/rubric、置信度、fallback reason 并对高风险结果抽审/强审。证据:`AihrPracticeSeedService.java:1513-1539`;`aihr_practice_mysql8.sql:168-182`。
**P1-6 查询审计无法反查确切污染修订。** 日志只存问题 hash、空间、来源类型、状态、时延和 prompt version,不存 fragment revision IDs、排名、分数、答案 hash、生成模型。证据:`AihrKnowledgeQueryAuditService.java:23-37`;`aihr_knowledge_mysql8.sql:300-317`。
**P1-7 反馈没有形成源数据隔离闭环。** downvote 只屏蔽“相同规范化问题 + fragment”组合,不会降低全局质量状态、隔离来源或触发再审。证据:`AihrSopSeedService.java:2975-3010`;`aihr_practice_mysql8.sql:318-337`。
**P1-8 数据质量监控不足。** 处理概览只有完成、处理中、失败、片段和 embedding 数,并把解析成功称为“可引用资料”;没有重复率、乱码率、PII、过期、隔离、索引漂移、审批时长和质量告警。证据:`AihrSopSeedService.java:1830-1873`。
### P2:治理能力完善与运营优化
**P2-1 缺 claim/观点级冲突模型。** 制度冲突、专家意见冲突只能靠文档级人工发现,无法让检索优先选择当前有效结论。
**P2-2 缺统一数据用途和同意记录。** 用户回答、对话、音频、评分、日志虽然没有自动进入企业知识,但没有统一的 `purpose/consent/retention/training_eligible` 控制,未来离线使用容易污染训练和评测。
**P2-3 缺独立基准数据集与回归门禁。** 现有测试覆盖若干权限和业务状态,但未发现跨格式脏数据 corpus、检索黄金集、跨租户 canary、污染注入和 train/eval 泄漏检测套件。
**P2-4 局部状态语义不统一。** 附件“已解析”、案例“已入库”、个人 item “READY”、空间“ACTIVE”、场景“已发布”分别由业务模块定义,无法回答一个资产是否已清洗、已批准、已索引、已失效。
## 8. 重点架构问题逐项结论
| # | 问题 | 结论 |
|---|---|---|
| 1 | `RAW -> INDEXED` 直接路径 | **存在。** 普通资料非空后写片段并 embedding:`AihrSopSeedService.java:1243-1308`。 |
| 2 | `MODEL_OUTPUT -> TRUSTED_KNOWLEDGE` 直接路径 | **存在于案例;个人发布存在人工批准后直写。** 案例模型/兜底总结可直接已入库:`AihrCaseService.java:96-124`。模型回答和显式记忆未发现直接写企业片段。 |
| 3 | raw/trusted/synthetic/runtime 混表或混 collection | **存在混合。** `aihr_knowledge_fragment` 没有 trust/origin 字段;个人发布片段与上传片段混用;案例 raw transcript、AI summary、review 同表。Qdrant 只有默认 collection。 |
| 4 | 单一 `is_clean`/综合分,缺原因码 | **不是“只有 is_clean”,而是通用质量状态和原因码整体缺失。** 上传 error 和反馈 reason 只覆盖局部故障/回答反馈。 |
| 5 | 无来源、版本、范围、有效期 | **普通资料存在。** 正式制度和陪练场景是局部例外。 |
| 6 | 发布后不能撤回或不能同步清索引 | **可发起解绑,但不能保证同步清除。** Qdrant 删除失败被忽略,无持久重试:`AihrSopSeedService.java:3763-3780`。 |
| 7 | 清洗覆盖原始内容 | **原文件通常保存在 OSS,未发现清洗回写覆盖对象。** 但本地 MinIO 为空,且缺不可变业务版本约束,不能证明所有历史原文可恢复。 |
| 8 | LLM 是唯一质量判断者 | **部分存在。** 自动分类/案例总结缺人工质量门;考试发布和高风险场景有人审。 |
| 9 | LLM 自行删除、批准或发布 | **未发现 LLM 直接调用删除/批准 API。** 但服务代码会把 LLM/兜底案例结果直接置为已入库,效果等价于缺前置人审。 |
| 10 | 清洗规则修改后无法识别旧规则版本 | **存在。** 片段无 parser/normalizer/chunker/rule version。locator 仅有自身 `locator_version`。 |
| 11 | 错误回答无法反查具体污染 chunk | **存在。** 引用响应含 fragment ID,但持久 query log 不保存片段修订集合:`AihrKnowledgeQueryAuditService.java:23-37`。 |
| 12 | 多租户只在前端/Prompt | **否。** SQL、应用/主体授权交集和 Qdrant filter 均在服务端;残余风险是索引漂移与旁路数据而非纯前端隔离。 |
## 9. 建议的数据状态机
目标状态机是数据资产的统一治理主状态,不替换上传任务、OCR job 等技术状态。
```mermaid
stateDiagram-v2
[*] --> RAW
RAW --> PARSED: parser 成功,保留原文与解析产物
RAW --> QUARANTINED: 空/损坏/恶意文件
PARSED --> NORMALIZED: 确定性清洗完成
NORMALIZED --> CLASSIFIED: 来源/领域/范围分类
CLASSIFIED --> DEDUPLICATED: 精确/近似/语义去重
DEDUPLICATED --> PRIVACY_CHECKED: PII 与秘密检查
PRIVACY_CHECKED --> DOMAIN_VALIDATED: 领域和适用范围验证
DOMAIN_VALIDATED --> QUALITY_EVALUATED: 结构/依据/时效/冲突评分
QUALITY_EVALUATED --> QUARANTINED: 阻断 reason code
QUALITY_EVALUATED --> REVIEW_PENDING: 需人工判断
QUALITY_EVALUATED --> APPROVED: 低风险自动规则通过
REVIEW_PENDING --> APPROVED: 授权审核人批准
REVIEW_PENDING --> QUARANTINED: 驳回/待补充
APPROVED --> PUBLISHED: outbox 索引成功并校验
PUBLISHED --> DEPRECATED: 撤回/过期/被新版本替代
QUARANTINED --> PARSED: 修复原始来源后新建版本
DEPRECATED --> REVIEW_PENDING: 新修订重新走完整检查
```
强制不变量:
1. 原始对象和原始 hash 不可覆盖;重传、重解析、重新清洗都创建新 `data_version`。
2. `PUBLISHED` 的前置条件是 `APPROVED`、无阻断 reason、来源可追溯、租户和适用范围完整、未过期、索引 upsert 成功。
3. 生产 FULLTEXT、LIKE、向量检索和所有下游 grounding 只读取 `PUBLISHED` 的 `chunk_revision`。
4. `SYNTHETIC`、`RUNTIME`、`QUARANTINE` 永不进入生产 collection;AI 生成内容默认 `REVIEW_PENDING`。
5. `DEPRECATED` 立即从检索可见集合移除,并通过 outbox 幂等删除向量;物理保留历史用于审计。
6. 状态转换由确定性规则和授权人完成;LLM 只能提出标签/风险建议,不能执行批准、发布、删除。
## 10. 建议的数据表和字段
优先采用旁路增量表,避免直接重构现有所有业务表。
| 建议表 | 关键字段 | 作用 |
|---|---|---|
| `aihr_data_asset` | `id, tenant_id, asset_type, source_type, source_owner, source_system, source_uri_redacted, raw_oss_id, raw_sha256, captured_at, retention_class, consent_scope, created_by` | 一份不可变原始资产的稳定身份;`raw_sha256` + 租户唯一,禁止 update 原始对象。 |
| `aihr_data_version` | `id, asset_id, version_no, parent_version_id, source_version, authority_type, applicability_json, effective_at, expires_at, parser_version, normalizer_version, classifier_version, dedupe_version, privacy_rule_version, quality_rule_version, lifecycle_status, origin_type, synthetic, generator_model, prompt_version, superseded_by` | 每次解析/清洗/规则升级产生版本,承载完整生命周期。 |
| `aihr_quality_assessment` | `id, version_id, stage, detector_code, detector_version, score, decision, assessed_at, run_id, metrics_json` | 一次检测运行;允许多个维度,不以一个 `quality_score` 代替原因。 |
| `aihr_quality_issue` | `id, assessment_id, version_id, chunk_revision_id, reason_code, severity, blocking, location_json, evidence_hash, status, resolved_by, resolved_at` | 结构化问题明细和定位;敏感 evidence 只存 hash/脱敏摘要。 |
| `aihr_review_decision` | `id, version_id, decision, reviewer_user_id, reviewer_role, comment, content_hash, decided_at, previous_decision_id` | 人工批准/驳回/撤回,绑定当时内容 hash,内容变化后自动失效。 |
| `aihr_chunk_revision` | `id, version_id, tenant_id, knowledge_id, doc_id, chunk_key, revision_no, content, content_sha256, locator_json, parent_chunk_id, lifecycle_status, effective_at, expires_at, created_at` | 生产片段的不可变修订;现有 fragment ID 可作为迁移映射,不再原地覆盖。 |
| `aihr_lineage_edge` | `from_type, from_id, to_type, to_id, relation, pipeline_run_id, created_at` | 表达 `raw -> parsed -> normalized -> chunk -> embedding -> answer/exam/case/score` 血缘。 |
| `aihr_index_outbox` | `id, tenant_id, chunk_revision_id, collection, operation, generation, payload_sha256, status, attempts, next_retry_at, last_error_code, created_at, completed_at` | MySQL 事务内登记 UPSERT/DELETE,worker 幂等执行、重试、死信和对账。 |
| `aihr_query_evidence` | `request_id, rank, chunk_revision_id, retrieval_mode, retrieval_score, rerank_score, payload_generation, cited, created_at` | 从错误答案反查精确污染修订和召回排名。 |
| `aihr_dataset_membership` | `dataset_id, version_id/chunk_revision_id, split, purpose, snapshot_at, content_sha256, approval_id` | 管理 TRAIN/VALIDATION/TEST/GOLDEN,防止内容 hash 跨 split 和时间穿越。 |
字段约束建议:所有业务主键关联同时携带/校验 `tenant_id`;`lifecycle_status` 用 CHECK 或受控枚举;`PUBLISHED` 需要唯一有效版本;`effective_at < expires_at`;`synthetic=true` 必须有 generator;`origin_type` 至少区分 `ENTERPRISE_SOURCE/PERSONAL_EXPERIENCE/SYNTHETIC/RUNTIME/EXTERNAL_API`。
## 11. 建议的 reason code
reason code 必须一对多记录,包含 `severity`、`blocking`、检测器版本和证据位置。首批至少支持:
| 类别 | reason code |
|---|---|
| 解析完整性 | `PARSE_EMPTY`, `PARSE_FAILED`, `PARSE_TRUNCATED`, `PAGE_COUNT_MISMATCH`, `EMBEDDED_CONTENT_SKIPPED` |
| 编码/OCR/ASR | `ENCODING_REPLACEMENT_CHAR`, `UNICODE_CONTROL_CHAR`, `OCR_LOW_CONFIDENCE`, `OCR_PARTIAL`, `ASR_LOW_CONFIDENCE`, `ASR_SPEAKER_UNCERTAIN` |
| 清洗与结构 | `HEADER_FOOTER_NOISE`, `PAGINATION_NOISE`, `TOC_NOISE`, `WATERMARK_NOISE`, `TABLE_STRUCTURE_LOST`, `CHUNK_TOO_SHORT`, `CHUNK_CONTEXT_BROKEN` |
| 重复 | `EXACT_DUPLICATE`, `NEAR_DUPLICATE`, `SEMANTIC_DUPLICATE` |
| 来源与版本 | `SOURCE_UNKNOWN`, `SOURCE_HASH_MISMATCH`, `SOURCE_VERSION_MISSING`, `SOURCE_UNAVAILABLE`, `SUPERSEDED_VERSION` |
| 业务质量 | `DOMAIN_IRRELEVANT`, `CONTEXT_INCOMPLETE`, `UNSUPPORTED_CLAIM`, `LABEL_CONFLICT`, `APPLICABILITY_MISSING` |
| 时效与冲突 | `POLICY_EXPIRED`, `VERSION_CONFLICT`, `EXPERT_CONFLICT` |
| 安全与隔离 | `PII_DETECTED`, `SECRET_DETECTED`, `CROSS_TENANT_REFERENCE`, `PROMPT_INJECTION_SUSPECTED` |
| 来源污染 | `SYNTHETIC_UNDECLARED`, `RUNTIME_DATA_CONTAMINATION`, `TRAIN_EVAL_LEAKAGE` |
| 索引一致性 | `INDEX_UPSERT_FAILED`, `INDEX_DELETE_FAILED`, `INDEX_DRIFT`, `INDEX_PAYLOAD_INVALID` |
默认阻断发布的 reason:所有 parse failure、page mismatch、source unknown/version missing、PII/secret、cross-tenant、prompt injection、undeclared synthetic、runtime contamination、train/eval leakage、expired policy、unsupported claim 和 index drift。`CHUNK_TOO_SHORT` 等可配置为告警,但不得被单一总分抵消严重问题。
## 12. 数据隔离和向量索引方案
1. MySQL 事实源分层:raw/parsed/quality/review/chunk revision 分表;现有 `aihr_knowledge_fragment` 在过渡期只作为兼容发布视图或发布投影,不再接受任意入口直写。
2. 逻辑 collection 至少分 `aihr_knowledge_prod`、`aihr_knowledge_quarantine`、`aihr_knowledge_synthetic`、`aihr_knowledge_runtime`。最小 P0 可先只建立 prod,其他状态完全不入向量库;不要用 payload flag 作为唯一隔离。
3. prod payload 必须包含 `tenant_id, knowledge_id, asset_id, data_version_id, chunk_revision_id, lifecycle_status, source_version, effective_at, expires_at, applicability_hash, index_generation, embedding_model`。
4. Qdrant 查询仍保留 tenant + allowed knowledge IDs filter,同时增加 `lifecycle_status=PUBLISHED` 和 generation;从 MySQL hydrate 时再次校验发布/有效期,防止 stale vector 绕过。
5. 所有 upsert/delete 先与状态变更同事务写 `aihr_index_outbox`;worker 使用确定性 point ID(基于 chunk revision)和 generation 幂等处理。失败指数退避,超过阈值进入 dead letter 并告警。
6. 定时 reconciler 对比 MySQL `PUBLISHED` 修订与 Qdrant point:多余点发 DELETE,缺失/哈希不一致发 UPSERT;输出 `INDEX_DRIFT`,不得静默修复后不留审计。
7. 撤回顺序:MySQL 先将版本标为 `DEPRECATED` 使 hydrate 立即不可见,再提交 DELETE outbox;物理删除原文件需满足保留期和引用计数,不与检索撤回绑定。
## 13. 历史数据迁移与重新清洗方案
1. **冻结新增污染**:先上线检索发布门和新导入 `REVIEW_PENDING` 默认值;在此之前不要批量重建向量,否则会扩大污染面。
2. **只读盘点**:按租户导出附件、片段、OSS、正式治理、空间授权和 Qdrant manifest,只记录 ID/hash/计数,不导出敏感正文到报告。
3. **建立资产映射**:以 `tenant_id + oss_id/file hash/doc_id` 回填 `aihr_data_asset/version`。能找到原文件的版本标 `RAW`;找不到原文件的 6 个孤儿片段及其他不可恢复项先 `QUARANTINED/SOURCE_UNAVAILABLE`,不得假定可信。
4. **保留旧数据**:原 `aihr_knowledge_fragment` 和 `sys_oss` 只读保留;新 parser/normalizer/chunker 输出新 version/chunk revision,不 UPDATE 覆盖旧正文。
5. **分批重处理**:优先正式制度、安全/消防/财务/人事等高风险空间,再处理高频引用和普通空间。每批记录 pipeline/rule versions、输入/输出 hash 和问题明细。
6. **去重与版本归并**:先精确 hash,再近重复,最后语义候选;自动检测只生成 duplicate group,版本冲突和专家冲突由业务审核人决定 authoritative/superseded。
7. **隐私与领域门禁**:确定性 PII/秘密/注入规则先跑,LLM 仅作为补充风险信号;命中阻断项进入隔离。
8. **人工审核**:给审核人显示原始文件、解析差异、清洗差异、重复组、适用范围、有效期和 reason codes;批准绑定 content hash。
9. **双写验证**:候选 prod collection 建新 generation,以 shadow query 与黄金集比较;不切换用户流量。
10. **原子切换与回滚**:达到验收标准后切换 active generation;旧 generation 保留约定窗口,可即时切回。切换后再按 outbox 清理旧点。
本地快照迁移基线:附件 1,046、片段 8,607、locator 0、上传任务 1,207(完成 1,113、失败 94)、embedded 3,893。重清洗后应分别对账,不得把“有 embedding”当作“已批准”。
## 14. 自动检测与人工审核边界
| 能力 | 自动化可决定 | 必须人工决定 |
|---|---|---|
| 技术完整性 | 文件 hash、MIME、大小、解析异常、页数比、乱码、OCR/ASR 置信度阈值 | 低置信度页面是否可由原件补正 |
| 清洗 | 确定性移除已验证的页眉页脚/页码副本,并保留 diff | 表格语义重构、歧义段落合并、事实改写 |
| 去重 | 精确重复可自动归组;近/语义重复生成候选 | 哪个版本权威、是否合并、是否存在适用范围差异 |
| 隐私 | 规则/NER 检测和默认阻断;自动遮罩仅生成候选版本 | 高风险 PII、商业秘密是否允许发布及适用范围 |
| 领域与依据 | 分类器/LLM 给相关度、claim-evidence 候选和 reason | 边界业务、制度解释、冲突结论、例外条款 |
| 发布 | 系统验证前置条件、角色、双审、hash 未变化 | 授权审核人批准;高风险制度至少双人或法务/业务责任人审核 |
| AI 内容 | 强制标 synthetic、绑定模型/prompt/grounding | 案例、标准答案、题目、评分用于正式业务前的责任人确认 |
| 删除/撤回 | 系统执行幂等状态变更、outbox 和对账 | 业务撤回决定;LLM 无权批准、发布或删除 |
LLM 输出永远是检测信号或草稿,不是唯一审批证据。任何自动低风险批准都必须基于可解释、版本化的确定性规则,并允许抽样复核和一键撤回。
## 15. 测试数据集设计
建立不含真实个人信息的版本化测试 corpus,每条样本有 `sample_id`、租户、来源类型、期望状态、期望 reason codes、期望 locator、期望可检索范围和 content hash。
1. 格式集:原生/扫描 PDF、缺页 PDF、含附件 PDF、DOCX 页眉页脚/批注、PPTX 多页、XLSX 多 sheet/合并单元格、TXT/CSV 编码变体、图片、长短音频、视频关键帧。
2. 噪声集:页码、水印、目录、重复页、零宽字符、`U+FFFD`、乱码、空文档、超长段、极短段、表格错序。
3. 重复集:字节相同、文本相同格式不同、页眉不同、少量改写、语义同义、同文档重复段、多版本制度。
4. 信任集:未知来源、过期制度、冲突制度、专家冲突、缺少适用范围、无依据结论、AI 合成未标记。
5. 安全集:合成手机号/证件/住址/邮箱、内部密钥样式、跨租户 canary、文档内提示词注入和工具调用诱导。
6. 检索黄金集:物业高频问题、无答案问题、正式制度问题、跨岗位/项目问题;每题标 allowed/forbidden chunk revisions 和最低 nDCG/Recall。
7. 回流集:用户回答、模型回答、差评、日志、记忆草稿、已确认 private/company pending,验证均不进入 prod knowledge。
8. 数据集隔离集:相同/近似 content hash 跨 train/validation/test,验证构建任务 fail closed。
## 16. 单元测试、集成测试和 E2E 测试方案
### 16.1 单元测试
- parser:格式、字符编码、页数、embedded exclusion、超限、空内容和 locator 准确性。
- normalizer:保留原文、确定性 diff、页眉页脚/页码规则、Unicode 控制字符和规则版本。
- dedupe:精确、近似、语义阈值边界;同内容不同适用范围不能自动合并。
- privacy/security:合成 PII、秘密、prompt injection;阻断 reason 不能被质量总分覆盖。
- state machine:非法跳转(尤其 `RAW/PARSED -> PUBLISHED`)必须失败;审批必须绑定当前 content hash。
- retrieval predicate:所有检索实现和 exam/case grounding 只能访问 `PUBLISHED`、未过期、授权范围内的 revision。
- outbox:幂等 upsert/delete、重试、generation、死信和并发状态转换。
### 16.2 集成测试
- MySQL + MinIO + Qdrant:上传后未批准时 MySQL/Qdrant 均不可检索;批准后只出现目标 revision;撤回后 SQL 立即不可见、Qdrant 最终删除。
- 模拟 Qdrant 500/超时:状态不谎报成功,outbox 可重试,reconciler 能发现并修复 drift。
- 多租户:同 docId、同 fragment text、同 point ID 场景下,用 tenant canary 验证 SQL、Qdrant、hydrate、下载全链路隔离。
- 规则升级:同一 raw 产生新 version,旧 PUBLISHED 继续可回滚;切换后查询审计只记录新 revision。
- 个人发布、案例、考试:统一进入 `REVIEW_PENDING`,不得直接写 prod;grounding 只能来自批准版本。
- 反馈闭环:达到阈值生成 quality issue/再审,不直接由模型或单个用户删除内容。
### 16.3 E2E 测试
1. 管理员上传一份包含 PII、过期版本和注入文本的文件,确认隔离页显示 reason codes、原文/清洗 diff,用户端搜不到。
2. 审核通过干净新版本,用户端在授权租户/岗位/项目能检索并看到正确页/段定位;其他租户不可见。
3. 撤回文档,在 MySQL 可见性切断后立即查询无结果;等待 outbox 后 Qdrant point 为 0,并保留完整审计。
4. 用真实业务黄金问题在 `390x844` 移动端跑问师傅主路径,验证引用相关、可打开原文定位、无无关主题结果。
5. 生成案例/考试题/评分,验证 synthetic/model/prompt/grounding 元数据存在,未人审内容不进入正式案例/试卷/生产统计。
现有测试应保留并扩展,例如正式制度过滤 `AihrSopSeedServiceTest.java:505`、个人精确去重 `PersonalIngestionServiceTest.java:134-182`、个人发布审核 `PersonalPublishServiceTest.java:58-64`、跨身份 fail closed `OrgSnapshotEnterpriseKnowledgeAccessPolicyTest.java:94-108`。它们证明局部控制,但不是全局 DQ 门禁测试。
## 17. 可量化验收标准
| 类别 | P0 上线门槛 | 稳态目标 |
|---|---|---|
| 生命周期 | 100% 新资产不能从 `RAW/PARSED` 直接到 prod;100% prod chunk revision 有批准记录 | 非法状态跳转测试 100% 拦截 |
| 血缘 | 100% prod chunk 可追溯到 tenant、raw asset/hash、version、规则版本、审核人 | 任一 `request_id` 5 分钟内反查全部 chunk revisions、排名和答案 hash |
| 来源/版本 | 高风险制度 100% 有 source/version/effective/expiry/authority | 过期或被替代版本召回率 0 |
| 隐私 | 阻断测试集 PII/secret 召回率 0;人工批准例外有审计 | PII 检测 recall >= 99%,precision >= 95%(在标注集) |
| 重复 | 新数据精确重复自动发现率 100% | 近重复 recall >= 95%;prod 同版本精确重复余量 0 |
| 解析 | 空/失败/缺页/替换字符阻断准确率 100% | `U+FFFD` prod 片段 0;page count mismatch prod 0 |
| 检索质量 | 跨租户 canary 泄漏 0;非 PUBLISHED 命中 0 | 黄金集 Recall@5 >= 90%,nDCG@5 >= 0.85;无关引用率 <= 5% |
| 索引一致性 | DELETE/UPSERT 失败均有 outbox 与告警;不静默丢失 | MySQL-Qdrant drift <= 0.1%,P0 delete 99% 在 5 分钟内收敛,最长 30 分钟 |
| 人工审核 | 高风险内容 100% 人审;高风险制度/场景双审身份不同 | 审核决定 100% 绑定 content hash,变更后旧批准自动失效 |
| AI 内容 | AI 案例/题目/答案/评分 100% 标 synthetic、model、prompt、grounding | 未批准 synthetic 进入 prod 数量 0 |
| 回流/数据集 | runtime/user interaction 自动进入 prod 数量 0 | train/eval content hash 交集 0;每次构建生成可审计 manifest |
| 可恢复性 | 100% 新资产原始对象存在且 hash 校验通过 | 原文抽检 100% 可恢复;不存在无来源 prod orphan |
上线前还应以本地当前值建立下降基线:上传失败率 7.79%、全局重复余量 1,897、页码类噪声 1,178、替换字符片段 19、孤儿片段 6。不能通过删除统计口径来“达标”,必须保留迁移前后 manifest 和 reason 分布。
## 18. 最小修改文件清单、实施风险与回滚
### 18.1 最小 P0 修改文件清单(审计建议;当前本地实施映射见第 19 节)
| 范围 | 最小改动 |
|---|---|
| SQL 迁移 | 新增一个 `backend/script/sql/update/aihr_<date>_data_quality_lifecycle_mysql8.sql`,创建 asset/version/assessment/issue/review/chunk revision/lineage/index outbox/query evidence 表和必要索引;不破坏现有表。 |
| 上传编排 | `AihrUploadQueueService.java`:完成条件改为“解析产物已进入治理状态”,不再以 fragment_count > 0 代表可引用。 |
| 解析/保存 | `AihrSopSeedService.java`:保存 immutable raw/version;停止普通入口直写生产片段/直接 embedding;发布、撤回只生成 outbox。 |
| 文档解析 | `ParsedDocument.java`、`TikaKnowledgeDocumentParser.java`:保留真实结构/页数/segment locator 和 parser version;显式记录 embedded skipped、truncated 等 reason。 |
| 查询 | `AihrKnowledgeQueryService.java` 与 `AihrSopSeedService.java`:所有 FULLTEXT/LIKE/vector/hydrate 统一要求 PUBLISHED revision 与有效期;企业 prompt 增加不可信来源隔离。 |
| 审计 | `AihrKnowledgeQueryAuditService.java`:新增 query evidence 批量写入、答案 hash、生成/检索模型版本。 |
| 向量 | 可在现有 service 内先加 `AihrIndexOutboxService/Worker/Reconciler`,后续再拆模块;所有 point 使用 revision/generation。 |
| 特殊入口 | `PersonalPublishService.java`、`AihrCaseService.java`、`AihrExamService.java`:改为创建待审版本,复用同一发布门和批准 grounding。 |
| API/管理端 | `AihrSopController.java` 及管理端资料处理页:增加质量详情、reason、审核、撤回/重处理;不恢复目录导入 UI。 |
| 测试 | 扩展 knowledge/personal/case/exam tests;新增 lifecycle、quality detector、outbox、reconciliation、cross-tenant E2E 和 corpus fixtures。 |
### 18.2 实施风险
- 加发布过滤后,历史片段默认都不满足 `PUBLISHED`,直接启用会造成检索结果骤降。必须先 inventory/backfill,并按风险分空间灰度。
- 重解析会改变 chunk ID、引用和排序;需要 `legacy_fragment_id -> chunk_revision_id` 映射与引用兼容期。
- 新旧 collection 双写会增加 embedding 成本和资源;必须按 hash 跳过未变化版本,并设置批次限流。
- 自动 PII/领域规则存在误报;默认隔离虽安全,但可能形成审核积压,需要容量、SLA 和批量归组能力。
- MinIO 原文件缺失的历史资料无法可靠重建定位与质量证据;不得从现有片段反向伪造“原始版本”。
- 对案例、考试和个人发布统一加门禁会改变运营流程,应在 API 中显式返回 `REVIEW_PENDING`,不能继续显示“已入库/已发布”。
### 18.3 回滚方案
1. 数据库采用纯新增迁移;旧表和原始对象不删除、不覆盖。应用回滚时可忽略新表。
2. 新检索以 feature flag/active generation 切换;回滚只切回旧 generation,不重新导入或覆盖数据。
3. 新旧索引并存一个约定窗口;任何质量或召回回归先停新发布 worker,再切回旧读路径。
4. outbox 操作幂等;回滚应用版本不能删除未完成事件。恢复后由兼容 worker 继续消费或人工批准重放。
5. 历史 migration batch 有 manifest、输入/输出 hash、状态和 rollback generation;只撤销“可见性/索引指针”,不物理删除审计链。
6. P0 安全不变量不得作为普通回滚项:跨租户过滤、非 PUBLISHED 隔离、PII/secret 阻断、撤回后 hydrate 校验必须保持 fail closed。
---
**最终判断:** 当前系统不是“完全没有治理”,而是存在多个质量不同、彼此不统一的局部生命周期。正式制度、个人知识、陪练场景、考试和记忆分别实现了部分正确控制,但企业通用知识主链仍把“解析成功”近似等同于“可引用”。在完成 P0 之前,不应把现有 `aihr_knowledge_fragment` 或默认 Qdrant collection 定义为经过审核的可信知识区,也不应将其直接作为训练集或评测集来源。
## 19. 审计后本地实施映射(2026-08-02)
本节只说明审计后工作树的本地实现,不改写前述审计时证据,也不代表生产部署。
| 审计问题 | 当前本地措施 | 状态 |
|---|---|---|
| `RAW -> INDEXED` | 上传、个人投稿和 AI 整理内容先创建不可变 asset/version/chunk revision;只有人工批准后创建生产 fragment 与 UPSERT outbox | 已实现并有门禁测试 |
| `MODEL_OUTPUT -> TRUSTED` | synthetic/用户交互默认不可信;模型不能自行批准、删除或发布 | 已实现 |
| 软问题可绕过 | 发布后端接受 reason code 后重新统计全部 `OPEN SOFT`,仍有遗漏即拒绝 | 已实现并有回归测试 |
| 清洗覆盖原文 | NFKC/控制字符/空白规范化只生成 `normalize-v2` 新版本;保存 extractor/cleaner/chunker 版本 | 已实现 |
| 规范化正文携带个人信息进入下游 | `privacy-redaction-v1` 生成独立 `redacted_content/redacted_source_name` 并保存派生 hash、隐私状态、规则版本和分类计数;解析/规范化正文只作受控审计证据,chunk、语义分析、模型调用、发布和引用标题只消费 `CLEAN/REDACTED` 派生内容;复扫残留以 `PII_DETECTED` 阻断 | 已实现;管理端提供脱敏预览,规则升级需生成新派生版本并重新审核 |
| 同名上传覆盖来源 | 取消附件文件名唯一约束;每次接入创建独立 attachment/doc/asset,精确重复只复用只读 OSS 对象 | 已实现并有迁移契约测试 |
| 结构与重复 | 标题、段落、Q&A、SOP 步骤切片;保存结构画像和 64 位 SimHash;异步 embedding 仅生成语义候选,本地降级显式标记 | 已实现;真实业务阈值待标定 |
| 旧制度冲突 | 同租户同名已发布资料内容不同产生 `VERSION_CONFLICT`;规范性知识必须显式 `supersedesAssetId`,同事务撤回旧资产 | 已实现并有回归测试 |
| 制度/专家 claim 冲突 | 确定性提取正反约束和数值 claim,只与同租户已发布 claim 比较;人工逐条确认、误报或解决,未裁决阻断发布 | 已实现基础规则;复杂条件与多专家关系待扩充 |
| 撤回后向量残留 | MySQL 立即切断生产可见性,DELETE outbox 最多重试 8 次后进入 `DEAD_LETTER`;提供 `index-health` | 已实现;生产告警接入待做 |
| 知识空间解绑绕过撤回 | 已批准或已发布资产解绑前必须由当前人工操作人走统一 `WITHDRAW`;OSS 孤儿清理同时检查附件与治理资产引用,核验失败时保留原件 | 已实现并有回归测试 |
| 错误答案无法定位污染 chunk | query evidence 保存实际 rank/score/channel/used;差评带 requestId 关联 fragment、chunk revision、asset/version | 已实现 |
| 用户反馈自动影响可信数据 | 反馈只创建 `DOWNSTREAM_ANSWER_DISPUTED`;人工 `KEEP` 保留,`WITHDRAW` 调统一撤回 | 已实现并有回归测试 |
| 缺测试基线 | 增加 PII、secret、提示词注入、synthetic、无来源历史数据黄金夹具及生命周期/证据/反馈单测 | 已实现首批;真实业务标注集待扩充 |
| 历史片段伪造来源 | `/quality/legacy/stage` 不复制旧 fragment 作为事实;存在同租户 OSS 原件时下载到受限临时文件并重新解析,不存在或不可读时创建 `QUARANTINED / SOURCE_UNAVAILABLE` 版本 | 已实现并有游标、隔离测试;全量迁移待人工分批执行 |
| Agent 反馈缺少真实检索血缘 | Agent run 只保存内部 `KNOWLEDGE:<requestId>` 引用,不保存问题/答案/附件;反馈在同租户内解析到真实 query evidence 和 fragment | 已实现并通过本地真实链路验证 |
| 缺生产质量告警 | `aihr_quality_alert` 保存稳定告警码、严重级别、证据、次数和打开/解决时间;定时扫描覆盖索引漂移、死信、血缘、开放问题、过期资料和查询证据,Actuator 暴露 `aihrKnowledgeQuality` | 已在本地实现并验证迁移幂等;外部告警平台接入待生产运维配置 |
| 缺可重复检索质量评测 | `scripts/evaluate-knowledge-quality.mjs` 计算 Recall@K、MRR、nDCG@K、跨租户禁止片段泄漏率和无证据率,不记录令牌或问题正文 | 工具和示例契约已实现;真实业务黄金集仍需专家标注后才能形成验收结论 |
| 上传失败时绕过生命周期 | 上传、批量上传和媒体重处理不再回退为直接写 `aihr_knowledge_fragment`/embedding/Qdrant;生命周期服务不可用时返回 503 | 已实现;必须保持 fail-closed |
| 只在初次检索检查发布状态 | 检索引用水合、引用详情、相邻片段和原件下载均独立复核当前版本、`PUBLISHED`、`HUMAN_VERIFIED`、`production/ACTIVE` 和有效期 | 已实现并有回归测试 |
| 历史向量与生产向量混算 | 新向量 payload 固定带 `dataset_code=production`、`PUBLISHED`、`HUMAN_VERIFIED`;查询、重建和健康计数只处理这些点,未纳管片段单列 `UNGOVERNED_LEGACY_FRAGMENTS` | 已实现;旧点暂不物理删除,待历史纳管完成后按 manifest 处置 |
| 页码、页眉和上下文噪声只定义未检测 | `dq-gate-v3` 以版本化确定性规则生成 `PAGINATION_NOISE/HEADER_FOOTER_NOISE/DOMAIN_IRRELEVANT/CONTEXT_INCOMPLETE/CHUNK_CONTEXT_BROKEN` 软问题 | 已实现并有门禁测试;阈值仍需真实语料标定,算法不自动清洗原文 |
| 历史纳管缺少断点恢复 | `scripts/stage-legacy-knowledge.mjs` 默认 dry-run;带 token 时先调用只读 `/legacy/preview`,显式 `--execute` 才分批写入;manifest 不保存正文/token,失败 ID 不推进恢复游标 | 已实现并有 Java/Node 测试;尚未对 1,046 份附件执行 |
| 评测不能作为发布门禁 | 评测契约区分 answerable/no-answer,统计并可分别设置 Recall/MRR/nDCG、禁止来源泄漏、错误来源和无答案误召回阈值;任一阈值失败退出码为 2 | 已实现并有 Node 测试;示例仅为模板,不能冒充人工黄金集 |
| 业务黑话缺少治理 | 新增不可变版本术语表和管理端入口,支持草稿、批准、驳回、新修订与停用;只有同租户人工 `ACTIVE` 版本按项目/角色扩展召回查询,definition 不作为事实注入 Prompt | 已实现服务端 API、管理端、检索接入和迁移;词条内容尚待业务审核录入 |
| AI 案例审核后丢失合成身份 | 案例整理记录 synthetic、generator/model、prompt version、grounding OSS、摘要 hash、reviewer/time;“已入库”仍是人工批准的合成案例 | 已实现并有迁移与单测,不等同于权威知识 |
本地真实链路已经验证:一次 Agent 查询能够从 `aihr_agent_run.result_ref` 反查知识请求、召回证据和具体 fragment,并将“未解决”反馈落为 `DOWNSTREAM_ANSWER_DISPUTED`;一次已发布资产撤回后,MySQL 生产 fragment 与 locator 被移除,数据集成员停用,DELETE outbox 成功,Qdrant 对应点为 0,同时 asset/version/chunk revision/原件审计链保留。隐私链路另以访谈类测试资料验证:原始/规范化正文保留审计内容,脱敏正文、派生 chunk、摘要、标签、归类理由和响应 snippet 不再包含已识别姓名或手机号,审核页只显示脱敏预览;`PII_REDACTED` 作为处理证据保留,残留 PII 才触发硬门禁。最后一次快照保留 2 个 `REVIEW_PENDING / UNTRUSTED` 测试资产,生产 fragment、生产成员、outbox 与 Qdrant 生产点均为 0。2026-08-02 收尾时本地后端未运行,因此这些是最后已记录快照,不是实时状态;未重置数据库,也未覆盖从 YCWY 同步的资料。
当前仍不得宣称完成的事项:历史 1,046 份附件未完成逐份来源/版本复核;缺失 MinIO 原件的历史片段无法恢复真实来源;业务术语尚未由责任人录入并审核;语义阈值、噪声阈值与复杂 claim 适用条件尚未用真实业务黄金集标定;生产 Qdrant 一致性、外部告警送达、黄金集 Recall/nDCG、无答案误召回与误隔离率尚未按真实标注集验收。
## 20. 综合改进方案与实施顺序
### 20.1 对其它 Agent 建议的取舍
| 建议 | 结论 | 在本项目中的正确用法 |
|---|---|---|
| OCR 除噪、术语表、结构化 Q&A/SOP | 采用 | 作为 `PARSED -> NORMALIZED/CLASSIFIED` 的候选产物,保留原文、规则版本和差异;仍需质量门禁与人工批准。 |
| 用 AI 把乱码整理成 JSON 后入向量库 | 不能直接采用 | AI 只能生成 `synthetic_unverified` 候选和清洗建议;不得覆盖原件,也不得自动进入生产索引。 |
| 固定使用 500–800 字、重叠 100 字 | 不作为全局规则 | 先按标题、段落、Q&A、SOP 步骤和页码结构切片,再用黄金集标定不同文档类型的长度和重叠。 |
| 标题、岗位、场景 metadata filter | 采用 | metadata 必须由服务端从已审核资产生成,并与租户、空间、项目、岗位、有效期过滤同时执行,不能信任客户端标签。 |
| HyDE | 暂不进入 P0 | 它可能提高召回,也可能放大查询假设和错误制度;只能在人工黄金集上与基线做 A/B,对无答案率和错误来源率设置回退门槛。 |
| Prompt 强制引用、资料不足时拒答 | 采用但不视为安全边界 | 服务端先完成发布状态与权限过滤并记录召回证据;Prompt 只约束表达,不能替代数据准入、权限或撤回。 |
| 覆盖旧文件做“一劳永逸”清洗 | 拒绝 | 原始对象不可变;新清洗结果生成新版本,批准后通过 `supersedesAssetId` 失效旧版本。 |
### 20.2 最合适的落地顺序
1. **P0 新污染止血(本地代码已完成,尚未生产发布)**:保持所有接入 fail-closed;生产检索、引用详情和下载只认当前人工批准版本;向量写入、查询、删除和计数只认生产标签;撤回先切断 MySQL 可见性,再由 outbox 删除向量。
2. **P0 生产前准备**:在结构副本按顺序执行 `aihr_20260801` 至 `aihr_20260814` 迁移并做幂等验证;备份生产数据库;部署后先验证空候选不可检索、隐私状态未完成或复扫残留 PII 的候选不可发布、批准后可检索、撤回后不可检索和跨租户负例。未通过时只回滚应用可见性,不删除原件和审计表。
3. **P0 历史资料分批纳管**:按租户和 `attachmentId` 游标盘点 1,046 份附件,以 MinIO 原件为唯一重解析依据;建议每批 20–50 份。每批执行“重解析 -> 自动检测 -> 人工审核 -> 小范围发布 -> 标准题回归”,禁止自动批准或把 8,607 个旧片段反向伪造成原始来源。
4. **P1 内容质量提升**:先处理现行制度和高频 SOP,再处理专家访谈、案例和 OCR 材料;建立术语表、适用范围、有效期、版本替代、结构化步骤和正反例标签。AI 只辅助提取、纠错候选和冲突发现,最终事实与适用范围由人确认。
5. **P1 检索标定**:业务专家标注 100–300 条黄金问题,覆盖有答案、无答案、旧制度、冲突制度、多租户、岗位/项目差异和 OCR 噪声。先调结构切片、metadata filter、混合召回和 rerank,再评估 HyDE;没有 Recall/nDCG、错误来源率和无证据率数据,不宣称“更准确”。
6. **P2 质量闭环**:把错误回答通过 `requestId -> evidence -> fragment -> version -> asset -> source` 定位到污染源;人工决定 `KEEP/WITHDRAW`,随后重建受影响生产点并运行回归集。告警平台接入 `DEAD_LETTER`、索引漂移、发布血缘缺口、过期资料和跨租户 canary。
### 20.3 决策原则
最优先的不是让模型“更会猜”,而是让系统能够证明每条生产内容为何可信、适用于谁、何时有效、由谁批准,并能在发现问题后立即停止检索和追溯原件。在此基础上再做 OCR 除噪、结构化、检索参数和 Prompt 优化,收益才可测量且不会形成新的自污染。