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

69 KiB
Raw Blame History

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. 当前数据血缘图

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
案例录音 /api/knowledge/case/upload -> OSS + ASR -> aihr_case_record;organize 用模型或本地兜底总结;curate 直接标“已入库”;主管 review 只是事后点评 是案例库可见数据,但没有前置发布审批。证据:backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/controller/AihrCaseController.java:53、:61、:69、:108;backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/service/AihrCaseService.java:96、:107、:127
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 等技术状态。

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 优化,收益才可测量且不会形成新的自污染。