# TruthLayer **Repository Path**: XingTuLink/truth-layer ## Basic Information - **Project Name**: TruthLayer - **Description**: 持续检查 AI 所依赖企业知识的 CLI 工具:从文档中抽取带原文证据的事实,自动检测冲突、过期与重复等知识漂移。 - **Primary Language**: Unknown - **License**: AGPL-3.0 - **Default Branch**: main - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-09-25 - **Last Updated**: 2026-10-24 ## Categories & Tags **Categories**: Uncategorized **Tags**: llm, RAG, knowledge-management, knowledge-drift, data-quality ## README # TruthLayer > 🌐 语言:**简体中文** · [English](./README.en.md) **持续检查 AI 所依赖的企业知识 —— CLI-first 的 Knowledge Drift Detector。** [![Python](https://img.shields.io/badge/python-3.11%2B-blue)](https://www.python.org/) [![License](https://img.shields.io/badge/license-AGPL--3.0-green)](./LICENSE) [![Tests](https://img.shields.io/badge/tests-434%20passed-success)](#测试) 企业制度、价格表、产品手册会持续修订,但 AI 助手引用的往往是旧版本:跨文档口径 冲突、已被新版本取代的政策、无人复核的过期数字、同一实体的多种异写……TruthLayer 把散落在文档里的知识抽取为带**原文证据**的结构化事实,并在每次扫描时确定性地检测 这些"知识漂移",在错误答案到达用户之前给出可追溯的告警。 - **CLI-first**:一条 `truthlayer scan` 跑完整条链路,可直接接入 CI(零服务依赖) - **Evidence First**:每条事实必须锚定文档原文引用,无证据不落库 - **LLM 只做候选,规则才裁决**:大模型负责抽取,冲突/过期判定全部由确定性规则完成 - **Embedding 只召回不裁决**:向量用于候选召回,最终结论不依赖向量相似度 - **本地可跑**:OpenAI 兼容协议,云端模型网关与本机 [Ollama](https://ollama.com/) 通用,支持零 API Key - **闭环处置**:人工决策(采纳/保留/误报)与忽略会被指纹记住,问题不会在下次扫描中重复打扰 - **可读报告 + CI 门禁**:自包含 HTML / 确定性 JSON 报告共用同一数据模型,`fail_on` 阈值直接映射退出码 --- ## 目录 - [它能检测什么](#它能检测什么) - [工作流程](#工作流程) - [快速开始](#快速开始) - [配置说明](#配置说明) - [CLI 命令](#cli-命令) - [测试](#测试) - [文档](#文档) - [路线图](#路线图) - [参与贡献](#参与贡献) - [许可证](#许可证) --- ## 它能检测什么 | 漂移类型 | 含义 | 典型场景 | |---|---|---| | `conflict` | 同一主体、同一谓词出现互相矛盾的有效值 | A 文档写座区制、B 文档写分区制 | | `confirmed_stale` | 事实已过期,或来源文档已被显式取代 | 2026 价格表声明取代 2025 版 | | `possibly_stale` | 事实长期未被复核(仅有年龄信号,警告级) | 定价类事实超过 90 天未见更新 | | `superseded` | 文档存在明确的新版本 | `产品价格表_2025.csv → _2026.csv` | | `duplicate` | 两个实体名称异写且共享鉴别性事实,疑似未归并 | "ACME CRM" 与 "ACME CRM Pro" | 每条检测结果都带有严重级别(warning / medium / high / critical)、置信度、 AI 影响等级,以及指向**新旧事实、来源文档、原文证据**的完整链接。 已报告过的问题通过确定性指纹跨扫描去重——不会每次扫描都重复打扰。 ## 工作流程 ```text 企业文档 (TXT/MD/CSV/PDF/DOCX/XLSX) │ ▼ 发现 → 解析 → 规范化 → 切块 │ ▼ LLM 候选抽取(实体 / 事实 / 关系) │ 结构化 schema + 确定性校验 ▼ Entity / Fact / Evidence 落库(每条事实必须挂原文证据) │ ├──▶ chunk / entity 向量化(pgvector,维度自由) ├──▶ 不可变快照 + 确定性 Knowledge Hash │ ▼ 四个确定性检测器 → 五类漂移 → 持久化 / 去重 / 摘要输出 ``` ## 快速开始 ### 环境要求 - Python 3.11+ - PostgreSQL 15+ 且安装 [pgvector](https://github.com/pgvector/pgvector) 扩展 - (可选)本机 [Ollama](https://ollama.com/) 拉取 `qwen2.5` 与 `bge-m3`,即可零 Key 完整运行; 也可使用任意 OpenAI 兼容的云端端点 ### 1. 安装 ```powershell git clone git@atomgit.com:XingTuLink/TruthLayer.git cd TruthLayer python -m venv .venv .\.venv\Scripts\python -m pip install -e ".[dev,parsers,openai,server]" ``` ### 2. 初始化数据库 ```powershell $env:TRUTHLAYER_DATABASE_URL="postgresql+psycopg://用户:密码@localhost:5432/truthlayer" .\.venv\Scripts\python scripts/dev_init_db.py # 幂等建库 .\.venv\Scripts\alembic upgrade head # 建表 + pgvector 扩展 ``` ### 3. 跑一遍内置演示知识库 仓库自带一家虚构公司 "ACME" 的知识库:休假/差旅制度两份、跨年价格表两份、 以及一份应被忽略的内部草稿。配置已指向本机 Ollama(无需 API Key): ```powershell # 仅做文档接入与配置校验 .\.venv\Scripts\truthlayer check .\examples\demo_kb # 完整扫描:抽取事实 → 向量 → 快照 → 漂移检测 .\.venv\Scripts\truthlayer scan .\examples\demo_kb ``` 扫描输出包含抽取统计、确定性知识哈希与漂移检测摘要。再次扫描内容不变时, 哈希保持一致、漂移全部显示为 already known(幂等)。 ### 4. 接入自己的知识库并启动控制台 控制台本身不绑定目录,工作区在**第一次扫描知识库目录时自动创建**——目录里的 `.truthlayer.yaml` 声明了工作区名称与源文档位置。因此首次部署固定两步: ```powershell # 第一步:扫描自己的知识库目录(目录内需含 .truthlayer.yaml) # 工作区此刻按配置名自动注册,文档入库 .\.venv\Scripts\truthlayer scan E:\我的知识库 # 第二步:首次构建前端(Node 20+;产物 ui/dist 不入库) cd ui; npm install; npm run build; cd .. # 第三步:启动 Web 控制台(默认 http://127.0.0.1:8765,可在漂移抽屉在线处置) .\.venv\Scripts\truthlayer serve --ui-dist .\ui\dist ``` 之后每次更新知识库,重跑同一条 `scan <目录>` 即可增量入库(按文件 hash 去重, 未变更文件跳过),控制台展示的就是该工作区的最新状态;可随时用 `--port` 改端口。控制台只展示**已经扫描过**的工作区,未扫描不会出现。 访问控制两种形态: - **本机回环**(`--host 127.0.0.1`,默认):免密,适合本机使用。 - **对外监听**:必须先设置 `TRUTHLAYER_UI_PASSWORD`,浏览器凭操作员密码登录 (HttpOnly Cookie,会话 12 小时);外部系统不登录,改用 `truthlayer token create <备注>` 签发 `tlv1_` 前缀的 Bearer 令牌。 控制台的漂移抽屉支持在线处置(采纳新值 / 保留旧值 / 人工裁定 / 标记误报 / 忽略):浏览器写请求自动携带 `X-TruthLayer-CSRF` 头,仍复用与 CLI 相同的 `ResolutionService`(one-shot、原因码与审计规则不变)。外部系统若要调用 处置接口,令牌需包含 `drift:write` scope。 ## 配置说明 在知识库根目录放置 `.truthlayer.yaml`(完整示例见 [examples/demo_kb/.truthlayer.yaml](./examples/demo_kb/.truthlayer.yaml)): ```yaml workspace: name: acme-demo sources: # 知识来源目录 + 权威度 + 来源类型 - path: ./docs/policies authority: 0.95 type: policy - path: ./docs/pricing authority: 0.9 type: pricing rules: stale_after_days: 365 # 一般事实未复核告警阈值 pricing_stale_days: 90 # 定价类事实更短的阈值 severity: # 按来源类型覆盖冲突严重级 pricing_change: high policy_change: critical ci: fail_on: critical # CI 失败阈值:none|critical|high|medium|warning version_mapping: # 显式版本链:只承认声明,绝不按文件名猜 - group: product_pricing files: - path: 产品价格表_2025.csv label: "2025" - path: 产品价格表_2026.csv label: "2026" supersedes: 产品价格表_2025.csv ignore: - "**/internal-test/**" extraction: # OpenAI 兼容端点(云端 / Ollama 通用) provider: openai_compatible model: qwen2.5:7b base_url: http://localhost:11434/v1 embedding: provider: openai_compatible model: bge-m3 base_url: http://localhost:11434/v1 dimensions: 1024 ``` **凭据规则**:API Key 只从环境变量读取(`TRUTHLAYER_LLM_API_KEY` / `TRUTHLAYER_EMBEDDING_API_KEY`,可用 `api_key_env` 改名),永远不写进配置文件; 本机回环端点无需 Key。 ## CLI 命令 | 命令 | 说明 | |---|---| | `truthlayer check ` | 校验配置并执行文档接入(解析/切块/版本链) | | `truthlayer scan ` | 完整扫描:接入 + 抽取 + 向量 + 快照 + 漂移检测 | | `truthlayer drift list [--status …] [--type …] [--workspace …]` | 列出漂移(默认只看 open) | | `truthlayer drift show ` | 单条详情:新旧事实、原文证据、AI 影响与建议操作 | | `truthlayer drift ignore [--reason …]` | 忽略(指纹记住,不再重复报出) | | `truthlayer resolve --decision …` | 人工处置,见下方决策说明 | | `truthlayer eval [--cases …]` | 跑 Golden QA 评测集并计算 Phase 0 Gate 指标(不连库、不调 LLM) | | `truthlayer serve [--host …] [--port …] [--ui-dist …]` | 启动 Web 控制台与 HTTP API(含在线处置);首次部署先 `scan` 注册工作区 | | `truthlayer token create/list/revoke` | 管理外部系统用的 API 令牌(`tlv1_` 前缀,Bearer) | | `truthlayer --version` | 版本信息 | 报告与 CI 门禁: ```powershell # scan 与 check 都可输出报告;check 不调用 LLM,渲染的是最近一次扫描的状态 truthlayer scan .\examples\demo_kb --html report.html --output report.json truthlayer check .\examples\demo_kb --html report.html # 处置一条冲突:采纳新事实,记录受控原因码与说明 truthlayer resolve --decision accept_newer ` --reason-code source_updated --reason "2026 新政策已发布" ``` 四种决策(Phase 0 仅支持 `scope=single`,单条处置): | decision | 含义 | authority_fact 默认 | |---|---|---| | `accept_newer` | 采纳新知识 | 新事实(可 `--authority-fact-id` 覆盖) | | `keep_old` | 保留旧知识 | 旧事实 | | `manual_override` | 人工裁定(新旧都不是最终答案) | 无 | | `false_positive` | 误报(如分级并存、抽取错误) | 无 | `--reason-code` 使用受控词表(`newer_version` / `source_updated` / `still_valid` / `lower_authority` / `multi_valued` / `extraction_error` / `detector_noise` / `duplicate_confirmed` / `manual` / `other`),自由说明写 `--reason`;处置人默认取系统用户名,可用 `--by` 指定。 退出码(稳定语义): - `0` 通过; - `1` 存在达到或超过 `ci.fail_on` 阈值的**开放**漂移(已忽略/已处置不阻断); - `2` 配置/数据库/解析等系统错误。 ## 评测与 Phase 0 Gate TruthLayer 用一套 **Golden QA 评测集**衡量"给定正确事实后,确定性检测器的裁决质量"。 每条用例是一个自包含场景(文档 / 实体 / 事实 + 规则阈值 + 期望漂移),评测时直接 映射为内存中的知识状态并喂给**真实检测器**——不连数据库、不调用 LM、毫秒级、结果完全 确定,因此任何人 clone 即可复跑,也作为回归测试在每次测试时强制执行: ```powershell # 终端查看指标;可同时导出确定性 JSON 与 Markdown .\.venv\Scripts\truthlayer eval --cases .\examples\qa_cases ` --output eval.json --markdown eval.md ``` 用例分三类:**Positive**(确有漂移,必须报)、**Negative**(正常知识,不得报)、 **Ambiguous Negative**(看似冲突,但因时间 / 范围 / 适用对象不同而不应报)。 Phase 0 Gate 阈值(严格不等号): | 指标 | 含义 | 阈值 | |---|---|---| | Precision | 报出的问题中确为漂移的比例 | > 80% | | Recall | 真实漂移被检出的比例 | > 70% | | F1 | Precision 与 Recall 的调和平均 | > 75% | | FPR | 良性(正常 / 疑似冲突)场景被误报的比例 | < 30% | 当前内置评测集位于 [examples/qa_cases](./examples/qa_cases)(59 条:正例 28 / 正常负例 17 / 模糊负例 14,覆盖全部五类型),在确定性检测核心上 **Precision / Recall / F1 均为 100%、FPR 为 0%,Gate 通过**;同时给出每个检测器的独立 P/R 与误报分类。 需要说明:该 Gate 衡量的是**确定性裁决层**(LLM 只负责候选抽取、向量只负责召回,最终 判定全部可回溯)。端到端的抽取质量由 Ollama 真实冒烟测试覆盖;真实世界的 **HCR (人工确认率)** 需在实际使用中持续采集,不与上述离线指标混用。新增检测器规则时,应先 往评测集补充业务语义驱动的正 / 负 / 模糊用例,再调整规则。 ### 端到端人工确认率(HCR)基线 离线 Gate 之外,我们还用一套 **24 份合成半真实文档**(含 8 个人为埋下的问题场景 G1–G8)跑 **真实 LM 抽取 + 检测**整条链路,再由人工逐条判定系统报告的漂移是否成立,以量化 LLM 抽取带来的端到端损耗。语料、埋点与可审计的标注/算分结果见 [examples/eval_corpus](./examples/eval_corpus)(该评测**非确定性、不是 CI 门禁**; 可用本机 Ollama 零 Key 跑,也可通过 `TRUTHLAYER_E2E_LLM_BASE_URL` / `TRUTHLAYER_E2E_LLM_MODEL` 指向任意 OpenAI 兼容在线端点,Key 只从 `TRUTHLAYER_LLM_API_KEY` 读取): | 运行(2026-10-06) | 模型(prompt) | 报告漂移 | HCR | actionable | 埋点召回 | chunks 失败(实体/事实) | |---|---|---|---|---|---|---| | run1 | qwen2.5:7b(v1) | 32 | **62.5%(20/32)** | 56.2% | **8/8** | 2(59/90) | | run3 | qwen2.5:7b(v3) | 17 | **100%(17/17)** | 94.1% | 6/8 | 1(70/99) | | run5 | deepseek-flash 在线(v3) | 39 | **89.7%(35/39)** | 84.6% | 7/8 | **0(84/171)** | | run6 | deepseek-flash 在线(v4) | 43 | **90.7%(39/43)** | 88.4% | **8/8** | **0(65/165)** | 四次运行的**确定性 Golden Gate 始终 P/R/F1=100%、FPR=0%**——只要抽取给出正确实体, 检测器零误报、零漏报;端到端差异全部来自 LLM 抽取的非确定性(实体数 59→84→65、事实数 90→171→165 的逐 run 波动即是直接证据)。 调优成效与残余短板: - **年龄误报已治理**:run1 的 possibly_stale HCR 仅 15.4%(纯年龄触发打到现行价目/长效 条款,且把"每年 6–8 月"季节窗误当失效日)。经版本链最新版/开放式年度费率两项确定性 抑制(R1/R2)+ prompt v3 日期口径收敛后,run3/run5/run6 的 possibly_stale HCR 均为 **100%**。 - **在线强模型救回两个抽取丢点**:deepseek-flash 0 chunk 失败,正确把高温津贴"最近一次 复核 2025-07-15"写入 `observed_at`(G6 浮现为 possibly_stale),并把"星云科技客服中心 / (高新分部)"建成两个实体(G8 浮现为 duplicate)。 - **R8 已在 v0.1.1 落地并验证**:run5 唯一漏报 **G4**(直营 800 / 经销 950 跨源冲突), 根因是抽取层把同名"数据迁移服务"在两份渠道表里标成**不同实体类型**(`service` vs `product`),实体解析按「工作区 + 规范名 + 类型」建键而碎裂成两个实体,冲突检测器按 「实体 + 谓词」分组便看不到这对矛盾;同一碎裂还让两表里**刻意同价**的企业培训 500 / 上门安装 200 触发 2 张 duplicate 误报。v0.1.1 用**受控类型等价类**(仅限可售品 product/service 等同义漂移、同名唯一时归并,跨语义大类绝不合并)修复:run6 中该服务并 为同一实体、800 vs 950 正确报 conflict,误报清零,**埋点召回首达 8/8**。归并的连带效应 是 3 张"渠道=直营/经销"冲突——渠道是同一服务可同时成立的**多值属性**,用语料配置 `multi_valued_predicates: [渠道]`(产品既有的 #19 机制)声明后即消除,**配置修正后 run6 为 40 报、HCR 97.5%、actionable 95%**。 - **残余限制(R6/R7/R9)**:run6 仅存 1 张抽取层偶发误报——把 Q2 促销窗 `valid_to` 错挂到 常规价"原价 199"(run2/run5 亦曾出现,记入抽取层 backlog)。异名实体的**过度合并**(R7) 与跨源强标识归一仍是长期课题,已由 duplicate 检测器"召回候选 → 确定性确认"兜底。 复跑:`scripts/e2e_eval.py` 产出 → `scripts/e2e_score.py --run --labels /labels.json` 按人工标注算分;已固化的 run1/run5/run6 标注与算分见 [examples/eval_corpus/results](./examples/eval_corpus/results)。 `eval` 退出码:`0` Gate 通过、`1` Gate 未达标、`2` 用例无法加载或产物无法写入。 ## 测试 ```powershell # 单元测试(无需数据库,确定性、毫秒级) .\.venv\Scripts\python -m pytest tests/unit # 全量测试(含 PostgreSQL 集成测试:自动使用一次性隔离库 _it, # 会话结束自动清理,不污染开发库) $env:TRUTHLAYER_DATABASE_URL="postgresql+psycopg://..." .\.venv\Scripts\python -m pytest ``` 当前测试套件 **434 个测试全部通过**:单元测试覆盖规范化/哈希/实体解析/证据校验/ 四个检测器的全部判定规则与负例、CI 阈值矩阵、报告 DTO 的确定性序列化与 HTML 转义、评测匹配 / 指标 / Gate 阈值边界,以及**零依赖的 Golden QA 回归集**(59 条用例、 三类齐全、Gate 必须保持通过);集成测试在真实 PostgreSQL 上覆盖五类型漂移的检出、落库字段、 跨扫描指纹去重、Resolution 全生命周期(处置/忽略/重复处置防护)、报告组装以及 drift/resolve CLI 命令,并覆盖 HTTP API 的三种调用者身份、令牌 scope/工作区绑定/吊销/过期、 分页筛选、错误信封与复核通道回填。另有 1 个 Ollama 真实冒烟测试默认跳过 (设置 `TRUTHLAYER_RUN_OLLAMA=1` 才运行)。 ## 文档 - 架构说明:[简体中文](./docs/architecture.md) · [English](./docs/architecture.en.md) —— 分层设计、数据模型、检测原理与关键设计决策 - 贡献指南:[简体中文](./CONTRIBUTING.md) · [English](./CONTRIBUTING.en.md) —— 开发环境、编码纪律、测试要求与 PR 流程 ## 路线图 TruthLayer 当前处于 **Phase 0(CLI 版本)**,按 6 个 Sprint 迭代,并已完成 M1 只读控制台与 M2 在线处置: - [x] Sprint 1–2:项目骨架、领域模型、6 种文档格式接入、版本链 - [x] Sprint 3:LLM 知识抽取、Evidence First、向量与不可变快照 - [x] Sprint 4:漂移检测引擎(四检测器 / 五类型、指纹去重) - [x] Sprint 5:漂移处置流程、`drift`/`resolve` CLI、CI fail_on、HTML/JSON 报告 - [x] Sprint 6:Golden QA 评测集(59 条 / 三类)、P/R/F1/FPR 指标与零依赖回归、`truthlayer eval` - [x] M1:只读 Web 控制台与 HTTP API(`serve`、`token`、密码会话 / Bearer 鉴权、复核通道历史) - [x] M2:在线处置(漂移抽屉内 resolve/ignore、`drift:write` scope、浏览器写请求 CSRF 校验) **确定性检测核心的 Phase 0 Gate 已通过**(Golden Set:Precision / Recall / F1 = 100%, FPR = 0%,且该 100/0 在多轮真实 LM 端到端运行中保持不变)。真实 LM 端到端 HCR 历经四跑 (qwen2.5:7b v1 62.5%/召回 8/8 → v3 100%/6/8 → 在线 deepseek-flash v3 89.7%/7/8 → v4 90.7%/8/8),确认端到端短板集中在 LLM 实体身份与日期抽取的非确定性,其中跨源实体 类型碎裂(R8)已在 v0.1.1 修复。 **首个版本 v0.1.0 已发布(CLI-first 实验版),质量打磨版 v0.1.1 随后跟进,属性语义解析与检测加固版 v0.2.0 已发布,只读控制台版 v0.3.0 与在线处置版 v0.4.0 已发布。** 当前版本号为 `0.4.0`。 ## 参与贡献 欢迎 Issue 与 PR!提交前请阅读[贡献指南](./CONTRIBUTING.md):开发环境搭建、 不可违反的设计红线(Evidence First、LLM 只做候选、Embedding 不裁决等)、 测试要求与提交规范。 ## 许可证 本项目基于 GNU Affero General Public License v3.0(AGPL-3.0) 开源,© 西安栈上月明软件科技有限公司。 请注意 AGPL 的关键约束:如果你修改了本项目并通过网络向用户提供服务(包括 SaaS / 托管形式),必须以相同协议向网络使用者公开修改后的完整源代码。 官方代码仓库: - AtomGit:`git@atomgit.com:XingTuLink/TruthLayer.git` - GitHub:`git@github.com:XingTuLink/TruthLayer.git` - Gitee:`https://gitee.com/XingTuLink/truth-layer`