--- layout: default title: "提取时的数据来源:源层" parent: "Chinese (Beta)" --- # 提取时的数据来源:源层 > **Beta Translation:** This document was translated via Machine Learning and as such may not be 100% accurate. All non-English languages are currently classified as Beta. ## 概述 本文档记录了关于提取时数据来源的笔记,用于未来的规范工作。提取时的数据来源记录了数据的“源层”,即数据最初来自哪里,以及它是如何提取和转换的。 这与查询时的数据来源(参见 `query-time-provenance.md`)不同,后者记录的是代理推理过程。 ## 问题陈述 ### 当前实现 <<<<<<< HEAD 目前,数据来源的工作方式如下: ======= 当前的数据来源工作方式如下: >>>>>>> 82edf2d (New md files from RunPod) 文档元数据以 RDF 三元组的形式存储在知识图谱中。 文档 ID 将元数据与文档关联起来,因此文档在图中显示为节点。 当从文档中提取出边(关系/事实)时,一个 `subjectOf` 关系将提取出的边链接回原始文档。 ### 当前方法的缺点 <<<<<<< HEAD 1. **重复加载元数据:** 文档元数据会被打包并重复加载,每次从该文档中提取一批三元组时都会重复。这既浪费又冗余,相同的元数据会作为“货物”随每次提取输出一起传输。 ======= 1. **重复加载元数据:** 文档元数据被打包并重复加载,每次从该文档中提取一批三元组时都会重复。这既浪费又冗余,相同的元数据会作为“货物”随每次提取输出一起传输。 >>>>>>> 82edf2d (New md files from RunPod) 2. **浅层数据来源:** 当前的 `subjectOf` 关系仅将事实直接链接到顶级文档。无法了解转换链,例如,该事实来自哪个页面,哪个块,使用了哪种提取方法。 ### 期望状态 1. **一次加载元数据:** 文档元数据应该只加载一次,并附加到顶级文档节点,而不是重复包含在每个三元组批次中。 <<<<<<< HEAD 2. **丰富的数据来源 DAG:** 捕获从原始文档到所有中间工件,再到提取出的事实的完整转换链。例如,一个 PDF 文档的转换过程: ======= 2. **丰富的数据来源 DAG:** 捕获从原始文档到所有中间工件再到提取事实的完整转换链。例如,一个 PDF 文档的转换过程: >>>>>>> 82edf2d (New md files from RunPod) ``` PDF file (source document with metadata) → Page 1 (decoded text) → Chunk 1 → Extracted edge/fact (via subjectOf) → Extracted edge/fact → Chunk 2 → Extracted edge/fact → Page 2 → Chunk 3 → ... ``` <<<<<<< HEAD 3. **统一存储:** 提取的知识及其来源信息(provenance)都存储在同一个知识图谱中。这使得对来源信息的查询方式与对知识的查询方式相同,即可以从任何事实出发,沿着链条追溯到其确切的来源位置。 4. **稳定的ID:** 每个中间产物(页面、段落)都具有一个稳定的ID,该ID在图中表示为一个节点。 5. **父子链接:** 从派生文档到其父文档,一直链接到顶层源文档,使用一致的关系类型。 ======= 3. **统一存储:** 提取的知识及其来源信息(provenance)都存储在同一个知识图谱中。这使得对来源信息的查询方式与对知识的查询方式相同——可以从任何事实出发,沿着链条追溯到其确切的来源位置。 4. **稳定的ID:** 每个中间产物(页面、段落)都有一个稳定的ID,作为图谱中的一个节点。 5. **父子链接:** 从派生文档到其父文档,一直链接到顶层原始文档,使用一致的关系类型。 >>>>>>> 82edf2d (New md files from RunPod) 6. **精确的事实归属:** 提取的边上的 `subjectOf` 关系指向直接的父节点(段落),而不是顶层文档。可以通过遍历DAG来恢复完整的来源信息。 ## 用例 ### UC1:GraphRAG响应中的来源归属 **场景:** 用户运行GraphRAG查询,并从代理接收到响应。 **流程:** 1. 用户向GraphRAG代理提交查询。 2. 代理从知识图谱中检索相关的事实,以构建响应。 3. 根据查询时期的来源信息规范,代理报告哪些事实对响应做出了贡献。 <<<<<<< HEAD 4. 每个事实通过来源信息DAG链接到其源段落。 5. 段落链接到页面,页面链接到源文档。 ======= 4. 每个事实通过来源信息DAG链接到其来源段落。 5. 段落链接到页面,页面链接到原始文档。 >>>>>>> 82edf2d (New md files from RunPod) **用户体验结果:** 界面会显示LLM响应以及来源归属信息。用户可以: 查看哪些事实支持了响应。 从事实 → 段落 → 页面 → 文档进行深入了解。 <<<<<<< HEAD 浏览原始的源文档以验证声明。 ======= 浏览原始来源文档以验证声明。 >>>>>>> 82edf2d (New md files from RunPod) 准确了解事实的来源(哪个页面,哪个部分)。 **价值:** 用户可以根据原始来源验证AI生成的响应,从而建立信任并实现事实核查。 ### UC2:调试提取质量 <<<<<<< HEAD 某个事实看起来不正确。追溯到段落 → 页面 → 文档,查看原始文本。是提取出现问题,还是原始来源本身就是错误的? ### UC3:增量重提取 源文档已更新。哪些段落/事实是从它派生的?仅使这些段落/事实失效并重新生成,而不是重新处理所有内容。 ### UC4:数据删除/被遗忘的权利 必须删除一个源文档(GDPR、法律等)。遍历DAG以查找并删除所有派生的事实。 ### UC5:冲突解决 两个事实相互矛盾。追溯到它们的来源,以了解原因并决定应该信任哪个(更权威的来源、更新的来源等)。 ======= 某个事实看起来不正确。追溯到段落 → 页面 → 文档,查看原始文本。是提取过程出现问题,还是原始来源本身就是错误的? ### UC3:增量重提取 原始文档被更新。哪些段落/事实是从它派生的?仅使这些段落/事实失效并重新生成,而不是重新处理所有内容。 ### UC4:数据删除/被遗忘的权利 必须删除一个原始文档(GDPR、法律等)。遍历DAG以查找并删除所有派生的事实。 ### UC5:冲突解决 两个事实相互矛盾。追溯到它们的来源,以了解原因,并决定应该信任哪个(更权威的来源、更新的来源等)。 >>>>>>> 82edf2d (New md files from RunPod) ### UC6:来源权威性加权 某些来源比其他来源更具权威性。可以根据其原始文档的权威性/质量对事实进行加权或过滤。 ### UC7:提取管道比较 <<<<<<< HEAD 比较来自不同提取方法/版本的输出。哪个提取器从相同的来源生成了更好的事实? ======= 比较不同提取方法/版本的输出。哪个提取器从相同的来源生成了更好的事实? >>>>>>> 82edf2d (New md files from RunPod) ## 集成点 ### Librarian librarian组件已经提供了文档存储,并具有唯一的文档ID。来源信息系统与此现有的基础设施集成。 #### 现有功能(已实现) **父子文档链接:** <<<<<<< HEAD `parent_id` 字段在 `DocumentMetadata` 中 - 将子文档链接到父文档。 `document_type` 字段 - 值:`"source"`(原始)或 `"extracted"`(派生)。 `add-child-document` API - 创建具有自动 `document_type = "extracted"` 的子文档。 `list-children` API - 检索父文档的所有子文档。 级联删除 - 删除父文档会自动删除所有子文档。 **文档识别:** 文档ID由客户端指定(不是自动生成)。 文档按复合 `(user, document_id)` 在Cassandra中键入。 对象ID(UUID)在内部生成,用于blob存储。 **元数据支持:** `metadata: list[Triple]` 字段 - RDF三元组用于结构化元数据。 `title`、`comments`、`tags` - 基本文档元数据。 `time` - 时间戳,`kind` - MIME类型。 **存储架构:** 元数据存储在Cassandra中(`librarian` 键空间,`document` 表)。 内容存储在MinIO/S3 blob存储中(`library` 存储桶)。 智能内容交付:小于 2MB 的文档嵌入,较大的文档流式传输。 #### 关键文件 `trustgraph-flow/trustgraph/librarian/librarian.py` - 核心 librarian 操作。 `trustgraph-flow/trustgraph/librarian/service.py` - 服务处理器,文档加载。 `trustgraph-flow/trustgraph/tables/library.py` - Cassandra 表存储。 `trustgraph-base/trustgraph/schema/services/library.py` - 模式定义。 #### 需要解决的问题 librarian 具有构建块,但目前: 1. 父子链接仅限于一级深度 - 没有多级DAG遍历辅助功能。 2. 没有标准的关系类型词汇表(例如,`derivedFrom`、`extractedFrom`)。 3. 来源信息元数据(提取方法、置信度、段落位置)尚未标准化。 4. 没有查询API来遍历从事实到源的全程来源信息链。 ======= `parent_id` 字段在 `DocumentMetadata` 中 - 将子文档链接到父文档 `document_type` 字段 - 值:`"source"`(原始)或 `"extracted"`(派生) `add-child-document` API - 创建具有自动 `document_type = "extracted"` 的子文档 `list-children` API - 检索父文档的所有子文档 级联删除 - 删除父文档会自动删除所有子文档 **文档识别:** 文档ID由客户端指定(不是自动生成) 文档按复合 `(user, document_id)` 在Cassandra中键入 对象ID(UUID)在内部生成,用于blob存储 **元数据支持:** `metadata: list[Triple]` 字段 - RDF三元组用于结构化元数据 `title`、`comments`、`tags` - 基本文档元数据 `time` - 时间戳,`kind` - MIME类型 **存储架构:** 元数据存储在Cassandra中(`librarian` keyspace,`document` table) 内容存储在MinIO/S3 blob存储中(`library` bucket) 智能内容交付:小于 2MB 的文档嵌入,较大的文档流式传输 #### 关键文件 `trustgraph-flow/trustgraph/librarian/librarian.py` - 核心 librarian 操作 `trustgraph-flow/trustgraph/librarian/service.py` - 服务处理器,文档加载 `trustgraph-flow/trustgraph/tables/library.py` - Cassandra 表存储 `trustgraph-base/trustgraph/schema/services/library.py` - 模式定义 #### 需要解决的关键问题 librarian 具有构建块,但目前: 1. 父子链接仅限于一级深度 - 没有多级DAG遍历辅助功能 2. 没有标准的关系类型词汇表(例如,`derivedFrom`、`extractedFrom`) 3. 来源信息元数据(提取方法、置信度、段落位置)尚未标准化 4. 没有查询API来遍历从事实到原始来源的完整来源信息链 >>>>>>> 82edf2d (New md files from RunPod) ## 端到端流程设计 管道中的每个处理器都遵循一致的模式: <<<<<<< HEAD 从上游接收文档ID。 从librarian中获取内容。 生成子产物。 对于每个子产物:保存到librarian,向图发出边,将ID转发到下游。 ### 处理流程 有两个流程,具体取决于文档类型: ======= 从上游接收文档ID 从librarian中获取内容 生成子产物 对于每个子产物:保存到librarian,向图发出边,向下游传递ID ### 处理流程 根据文档类型,有两种流程: >>>>>>> 82edf2d (New md files from RunPod) #### PDF文档流程 ``` ┌─────────────────────────────────────────────────────────────────────────┐ │ Librarian (initiate processing) │ │ 1. Emit root document metadata to knowledge graph (once) │ │ 2. Send root document ID to PDF extractor │ └─────────────────────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────────────────┐ │ PDF Extractor (per page) │ │ 1. Fetch PDF content from librarian using document ID │ │ 2. Extract pages as text │ │ 3. For each page: │ │ a. Save page as child document in librarian (parent = root doc) │ │ b. Emit parent-child edge to knowledge graph │ │ c. Send page document ID to chunker │ └─────────────────────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────────────────┐ │ Chunker (per chunk) │ │ 1. Fetch page content from librarian using document ID │ │ 2. Split text into chunks │ │ 3. For each chunk: │ │ a. Save chunk as child document in librarian (parent = page) │ │ b. Emit parent-child edge to knowledge graph │ │ c. Send chunk document ID + chunk content to next processor │ └─────────────────────────────────────────────────────────────────────────┘ │ ▼ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ Post-chunker optimization: messages carry both chunk ID (for provenance) and content (to avoid librarian round-trip). Chunks are small (2-4KB). ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ │ ▼ ┌─────────────────────────────────────────────────────────────────────────┐ │ Knowledge Extractor (per chunk) │ │ 1. Receive chunk ID + content directly (no librarian fetch needed) │ │ 2. Extract facts/triples and embeddings from chunk content │ │ 3. For each triple: │ │ a. Emit triple to knowledge graph │ │ b. Emit reified edge linking triple → chunk ID (edge pointing │ │ to edge - first use of reification support) │ │ 4. For each embedding: │ │ a. Emit embedding with its entity ID │ │ b. Link entity ID → chunk ID in knowledge graph │ └─────────────────────────────────────────────────────────────────────────┘ ``` #### 文本文档流程 文本文档会跳过 PDF 提取器,直接进入分块处理: ``` ┌─────────────────────────────────────────────────────────────────────────┐ │ Librarian (initiate processing) │ │ 1. Emit root document metadata to knowledge graph (once) │ │ 2. Send root document ID directly to chunker (skip PDF extractor) │ └─────────────────────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────────────────┐ │ Chunker (per chunk) │ │ 1. Fetch text content from librarian using document ID │ │ 2. Split text into chunks │ │ 3. For each chunk: │ │ a. Save chunk as child document in librarian (parent = root doc) │ │ b. Emit parent-child edge to knowledge graph │ │ c. Send chunk document ID + chunk content to next processor │ └─────────────────────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────────────────┐ │ Knowledge Extractor │ │ (same as PDF flow) │ └─────────────────────────────────────────────────────────────────────────┘ ``` 结果生成的有向无环图(DAG)的层数减少了一层: ``` PDF: Document → Pages → Chunks → Triples/Embeddings Text: Document → Chunks → Triples/Embeddings ``` 该设计同时适用于这两种情况,因为分块器以通用方式处理其输入 - 它使用接收到的任何文档 ID 作为父级,无论该 ID 是源文档还是页面。 ### 元数据模式 (PROV-O) 溯源元数据使用 W3C PROV-O 本体。这提供了一个标准词汇表,并为未来提取输出的签名/身份验证提供了支持。 #### PROV-O 核心概念 | PROV-O 类型 | TrustGraph 用法 | |-------------|------------------| | `prov:Entity` | 文档、页面、块、三元组、嵌入 | | `prov:Activity` | 提取操作的实例 | | `prov:Agent` | TG 组件(PDF 提取器、分块器等)及其版本 | #### PROV-O 关系 | 谓词 | 含义 | 示例 | |-----------|---------|---------| | `prov:wasDerivedFrom` | 一个实体源自另一个实体 | 页面 wasDerivedFrom 文档 | | `prov:wasGeneratedBy` | 一个实体由一个活动生成 | 页面 wasGeneratedBy PDFExtractionActivity | | `prov:used` | 一个活动使用一个实体作为输入 | PDFExtractionActivity used Document | | `prov:wasAssociatedWith` | 一个活动由一个代理执行 | PDFExtractionActivity wasAssociatedWith tg:PDFExtractor | #### 每个级别的元数据 **源文档(由 Librarian 产生):** ``` doc:123 a prov:Entity . doc:123 dc:title "Research Paper" . doc:123 dc:source . doc:123 dc:date "2024-01-15" . doc:123 dc:creator "Author Name" . doc:123 tg:pageCount 42 . doc:123 tg:mimeType "application/pdf" . ``` **页面 (由 PDF 提取器生成):** ``` page:123-1 a prov:Entity . page:123-1 prov:wasDerivedFrom doc:123 . page:123-1 prov:wasGeneratedBy activity:pdf-extract-456 . page:123-1 tg:pageNumber 1 . activity:pdf-extract-456 a prov:Activity . activity:pdf-extract-456 prov:used doc:123 . activity:pdf-extract-456 prov:wasAssociatedWith tg:PDFExtractor . activity:pdf-extract-456 tg:componentVersion "1.2.3" . activity:pdf-extract-456 prov:startedAtTime "2024-01-15T10:30:00Z" . ``` **块 (由分块器发出):** ``` chunk:123-1-1 a prov:Entity . chunk:123-1-1 prov:wasDerivedFrom page:123-1 . chunk:123-1-1 prov:wasGeneratedBy activity:chunk-789 . chunk:123-1-1 tg:chunkIndex 1 . chunk:123-1-1 tg:charOffset 0 . chunk:123-1-1 tg:charLength 2048 . activity:chunk-789 a prov:Activity . activity:chunk-789 prov:used page:123-1 . activity:chunk-789 prov:wasAssociatedWith tg:Chunker . activity:chunk-789 tg:componentVersion "1.0.0" . activity:chunk-789 tg:chunkSize 2048 . activity:chunk-789 tg:chunkOverlap 200 . ``` **强调 (由知识提取器发出):** ``` # The extracted triple (edge) entity:JohnSmith rel:worksAt entity:AcmeCorp . # Subgraph containing the extracted triples subgraph:001 tg:contains <> . subgraph:001 prov:wasDerivedFrom chunk:123-1-1 . subgraph:001 prov:wasGeneratedBy activity:extract-999 . activity:extract-999 a prov:Activity . activity:extract-999 prov:used chunk:123-1-1 . activity:extract-999 prov:wasAssociatedWith tg:KnowledgeExtractor . activity:extract-999 tg:componentVersion "2.1.0" . activity:extract-999 tg:llmModel "claude-3" . activity:extract-999 tg:ontology . ``` **嵌入 (存储在向量存储中,而不是三元组存储中):** 嵌入存储在向量存储中,包含元数据,而不是作为 RDF 三元组。每个嵌入记录包含: | 字段 | 描述 | 示例 | |-------|-------------|---------| | vector | 嵌入向量 | [0.123, -0.456, ...] | | entity | 嵌入所代表的节点 URI | `entity:JohnSmith` | | chunk_id | 源块 (来源) | `chunk:123-1-1` | | model | 使用的嵌入模型 | `text-embedding-ada-002` | | component_version | TG 嵌入器版本 | `1.0.0` | `entity` 字段将嵌入链接到知识图谱 (节点 URI)。 `chunk_id` 字段提供回溯到源块的来源信息,从而可以向上遍历 DAG,到达原始文档。 #### TrustGraph 命名空间扩展 在 `tg:` 命名空间下,定义了用于提取特定元数据的自定义谓词: | 谓词 | 域 | 描述 | |-----------|--------|-------------| | `tg:contains` | Subgraph | 指向此提取子图中包含的三元组 | | `tg:pageCount` | Document | 源文档中的总页数 | | `tg:mimeType` | Document | 源文档的 MIME 类型 | | `tg:pageNumber` | Page | 源文档中的页码 | | `tg:chunkIndex` | Chunk | 父级中的块索引 | | `tg:charOffset` | Chunk | 父级文本中的字符偏移量 | | `tg:charLength` | Chunk | 块的字符长度 | | `tg:chunkSize` | Activity | 配置的块大小 | | `tg:chunkOverlap` | Activity | 配置的块之间的重叠 | | `tg:componentVersion` | Activity | TG 组件的版本 | | `tg:llmModel` | Activity | 用于提取的 LLM | | `tg:ontology` | Activity | 用于指导提取的本体 URI | | `tg:embeddingModel` | Activity | 用于嵌入的模型 | | `tg:sourceText` | Statement | 从提取的三元组的精确文本 | | `tg:sourceCharOffset` | Statement | 源文本在块中的字符偏移量 | | `tg:sourceCharLength` | Statement | 源文本的字符长度 | #### 词汇引导 (每个集合) <<<<<<< HEAD 知识图谱是本体无关的,并且初始化为空。 当首次将 PROV-O provenance 数据写入集合时,必须使用 RDF 标签引导所有类和谓词的词汇。 这可确保在查询和 UI 中提供人类可读的显示。 ======= 知识图谱是本体无关的,并且初始化为空。当首次将 PROV-O provenance 数据写入集合时,必须使用 RDF 标签引导所有类和谓词的词汇。这可确保在查询和 UI 中提供人类可读的显示。 >>>>>>> 82edf2d (New md files from RunPod) **PROV-O 类:** ``` prov:Entity rdfs:label "Entity" . prov:Activity rdfs:label "Activity" . prov:Agent rdfs:label "Agent" . ``` <<<<<<< HEAD **PROV-O谓词:** ======= **PROV-O 谓词:** >>>>>>> 82edf2d (New md files from RunPod) ``` prov:wasDerivedFrom rdfs:label "was derived from" . prov:wasGeneratedBy rdfs:label "was generated by" . prov:used rdfs:label "used" . prov:wasAssociatedWith rdfs:label "was associated with" . prov:startedAtTime rdfs:label "started at" . ``` **TrustGraph谓词:** ``` tg:contains rdfs:label "contains" . tg:pageCount rdfs:label "page count" . tg:mimeType rdfs:label "MIME type" . tg:pageNumber rdfs:label "page number" . tg:chunkIndex rdfs:label "chunk index" . tg:charOffset rdfs:label "character offset" . tg:charLength rdfs:label "character length" . tg:chunkSize rdfs:label "chunk size" . tg:chunkOverlap rdfs:label "chunk overlap" . tg:componentVersion rdfs:label "component version" . tg:llmModel rdfs:label "LLM model" . tg:ontology rdfs:label "ontology" . tg:embeddingModel rdfs:label "embedding model" . tg:sourceText rdfs:label "source text" . tg:sourceCharOffset rdfs:label "source character offset" . tg:sourceCharLength rdfs:label "source character length" . ``` <<<<<<< HEAD **实施说明:** 这个词汇库初始化应该具有幂等性,即可以多次运行而不会创建重复项。 它可以触发在集合中的第一个文档处理过程中,或者作为单独的集合初始化步骤。 #### 子块来源信息(期望) 为了获得更细粒度的来源信息,记录三元组是从块中的哪个位置提取出来的将非常有用。 这可以实现: ======= **实施说明:** 此词汇引导过程应具有幂等性,即可以多次运行而不会创建重复项。 可以在集合中的首次文档处理过程中触发,也可以作为单独的集合初始化步骤。 #### 子块来源信息(期望) 为了获得更精细的来源信息,记录三元组是从块中的哪个位置提取出来的将非常有用。 这可以实现: >>>>>>> 82edf2d (New md files from RunPod) 在用户界面中突出显示确切的原始文本 验证提取的准确性与原始文本 在句子级别调试提取质量 **带有位置跟踪的示例:** ``` # The extracted triple entity:JohnSmith rel:worksAt entity:AcmeCorp . # Subgraph with sub-chunk provenance subgraph:001 tg:contains <> . subgraph:001 prov:wasDerivedFrom chunk:123-1-1 . subgraph:001 tg:sourceText "John Smith has worked at Acme Corp since 2019" . subgraph:001 tg:sourceCharOffset 1547 . subgraph:001 tg:sourceCharLength 46 . ``` **带有文本范围的示例(备选方案):** ``` subgraph:001 tg:contains <> . subgraph:001 prov:wasDerivedFrom chunk:123-1-1 . subgraph:001 tg:sourceRange "1547-1593" . subgraph:001 tg:sourceText "John Smith has worked at Acme Corp since 2019" . ``` <<<<<<< HEAD **实现注意事项:** 基于 LLM 的提取可能无法自然地提供字符位置。 可以提示 LLM 在提取的三元组旁边返回原始句子/短语。 或者,可以进行后处理,将提取的实体模糊匹配回原始文本。 提取复杂度和溯源粒度之间的权衡。 使用结构化提取方法可能更容易实现,而不是使用自由形式的 LLM 提取。 这被标记为期望目标 - 首先应实现基本的块级溯源,如果可行,子块跟踪可以作为未来的增强功能。 ======= **实现注意事项:** 基于 LLM 的提取可能无法自然地提供字符位置 可以提示 LLM 同时返回提取的三元组以及原始句子/短语 或者,可以进行后处理,将提取的实体模糊匹配回原始文本 提取复杂度和溯源粒度之间的权衡 使用结构化提取方法可能更容易实现,而不是使用自由形式的 LLM 提取 这被标记为期望目标 - 首先应该实现基本的块级溯源,如果可行,子块跟踪可以作为未来的增强功能。 >>>>>>> 82edf2d (New md files from RunPod) ### 双重存储模型 溯源 DAG 是随着文档在流水线中流动而逐步构建的: | 存储 | 存储内容 | 目的 | |-------|---------------|---------| | Librarian | 文档内容 + 父子链接 | 内容检索、级联删除 | | Knowledge Graph | 父子边 + 元数据 | 溯源查询、事实归属 | <<<<<<< HEAD 两个存储都维护相同的 DAG 结构。 Librarian 存储内容;Graph 存储关系,并支持遍历查询。 ======= 两个存储都维护相同的 DAG 结构。 Librarian 存储内容;Knowledge Graph 存储关系,并支持遍历查询。 >>>>>>> 82edf2d (New md files from RunPod) ### 关键设计原则 1. **文档 ID 作为流动的单位** - 处理程序传递 ID,而不是内容。 需要时从 Librarian 检索内容。 <<<<<<< HEAD 2. **在源头一次发出** - 元数据在处理开始时写入到 Graph 中,而不是在下游重复。 3. **一致的处理程序模式** - 每个处理程序都遵循相同的接收/检索/生成/保存/发出/转发模式。 4. **渐进的 DAG 构建** - 每个处理程序添加其级别到 DAG 中。 完整的溯源链是逐步构建的。 5. **分块后优化** - 在分块之后,消息同时携带 ID 和内容。 块很小(2-4KB),因此包含内容可以避免不必要的 Librarian 往返,同时通过 ID 保持溯源。 ======= 2. **在源头一次发出** - 元数据在处理开始时写入 Knowledge Graph 一次,而不是在下游重复。 3. **一致的处理程序模式** - 每个处理程序都遵循相同的接收/检索/生成/保存/发出/转发模式。 4. **渐进式 DAG 构建** - 每个处理程序添加其级别到 DAG。 完整的溯源链是逐步构建的。 5. **分块后优化** - 在分块之后,消息同时携带 ID 和内容。 块很小(2-4KB),因此包含内容可以避免不必要的 Librarian 交互,同时通过 ID 保持溯源。 >>>>>>> 82edf2d (New md files from RunPod) ## 实现任务 ### Librarian 更改 #### 当前状态 <<<<<<< HEAD 通过将文档 ID 发送到第一个处理程序来启动文档处理。 没有连接到三元存储 - 元数据与提取输出一起打包。 `add-child-document` 创建一级父子链接。 `list-children` 仅返回直接子节点。 ======= 通过将文档 ID 发送到第一个处理程序来启动文档处理 没有连接到三元存储 - 元数据与提取输出一起打包 `add-child-document` 创建一级父子链接 `list-children` 仅返回直接子节点 >>>>>>> 82edf2d (New md files from RunPod) #### 需要的更改 **1. 新接口:三元存储连接** <<<<<<< HEAD Librarian 需要直接将文档元数据边发射到知识图谱,以在启动处理时进行操作。 向 Librarian 服务添加三元存储客户端/发布器。 在处理启动时:以图谱边的方式(一次)发射根文档元数据。 ======= Librarian 需要直接将文档元数据边发射到 Knowledge Graph,以在启动处理时。 向 Librarian 服务添加三元存储客户端/发布器 在处理启动时:以图边形式(一次)发射根文档元数据 >>>>>>> 82edf2d (New md files from RunPod) **2. 文档类型词汇表** 标准化子文档的 `document_type` 值: <<<<<<< HEAD `source` - 原始上传的文档。 `page` - 从源头提取的页面(PDF 等)。 `chunk` - 从页面或源头派生的文本块。 ======= `source` - 原始上传的文档 `page` - 从源文件(PDF 等)提取的页面 `chunk` - 从页面或源文件派生的文本块 >>>>>>> 82edf2d (New md files from RunPod) #### 接口更改摘要 | 接口 | 更改 | |-----------|--------| <<<<<<< HEAD | 三元存储 | 新的出站连接 - 发射文档元数据边 | | 处理启动 | 在转发文档 ID 之前,向图谱发射元数据 | ======= | 三元存储 | 新的外部连接 - 发射文档元数据边 | | 处理启动 | 在转发文档 ID 之前,向图发射元数据 | >>>>>>> 82edf2d (New md files from RunPod) ### PDF 提取器更改 #### 当前状态 <<<<<<< HEAD 接收文档内容(或流式传输大型文档)。 从 PDF 页面提取文本。 将页面内容转发到分块器。 没有与 Librarian 或三元存储交互。 ======= 接收文档内容(或流式传输大型文档) 从 PDF 页面提取文本 将页面内容转发到分块器 没有与 Librarian 或三元存储交互 >>>>>>> 82edf2d (New md files from RunPod) #### 需要的更改 **1. 新接口:Librarian 客户端** PDF 提取器需要将每个页面作为子文档保存到 Librarian 中。 <<<<<<< HEAD 向 PDF 提取器服务添加 Librarian 客户端。 对于每个页面:使用父文档 ID 调用 `add-child-document`。 **2. 新接口:三元存储连接** PDF 提取器需要将父子边发射到知识图谱。 添加三元存储客户端/发布器。 对于每个页面:发射一个链接页面文档到父文档的边。 ======= 向 PDF 提取器服务添加 Librarian 客户端 对于每个页面:使用父文档 ID 调用 `add-child-document` **2. 新接口:三元存储连接** PDF 提取器需要将父子边发射到 Knowledge Graph。 添加三元存储客户端/发布器 对于每个页面:发射将页面文档链接到父文档的边 >>>>>>> 82edf2d (New md files from RunPod) **3. 更改输出格式** 而是直接转发页面内容,而是转发页面文档 ID。 Chunker 将使用 ID 从 librarian 获取内容 #### 接口变更摘要 | 接口 | 变更 | |-----------|--------| | Librarian | 新的输出 - 保存子文档 | | Triple store | 新的输出 - 发射父子边 | | 输出消息 | 从内容更改为文档 ID | ### Chunker 变更 #### 当前状态 接收页面/文本内容 分割成块 将块内容转发到下游处理器 不与 librarian 或 triple store 交互 #### 必需的变更 **1. 更改输入处理** 接收文档 ID 而不是内容,从 librarian 获取。 向 chunker 服务添加 librarian 客户端 使用文档 ID 获取页面内容 <<<<<<< HEAD **2. 新接口:Librarian 客户端(写入)** ======= **2. 新接口:Librarian 客户端 (写入)** >>>>>>> 82edf2d (New md files from RunPod) 将每个块保存为 librarian 中的子文档。 对于每个块:使用 parent = 页面文档 ID 调用 `add-child-document` **3. 新接口:Triple store 连接** 向知识图谱发射父子边。 添加 triple store 客户端/发布器 对于每个块:发射链接块文档到页面文档的边 **4. 更改输出格式** 同时转发块文档 ID 和块内容(块处理后的优化)。 下游处理器接收 ID 用于溯源 + 用于处理的内容 #### 接口变更摘要 | 接口 | 变更 | |-----------|--------| | 输入消息 | 从内容更改为文档 ID | <<<<<<< HEAD | Librarian | 新的输出(读取 + 写入)- 获取内容,保存子文档 | ======= | Librarian | 新的输出 (读取 + 写入) - 获取内容,保存子文档 | >>>>>>> 82edf2d (New md files from RunPod) | Triple store | 新的输出 - 发射父子边 | | 输出消息 | 从仅包含内容更改为 ID + 内容 | ### Knowledge Extractor 变更 #### 当前状态 接收块内容 提取三元组和嵌入 发送到 triple store 和 embedding store <<<<<<< HEAD `subjectOf` 关系指向顶级文档(不是块) ======= `subjectOf` 关系指向顶级文档 (而不是块) >>>>>>> 82edf2d (New md files from RunPod) #### 必需的变更 **1. 更改输入处理** 接收块文档 ID 以及内容。 <<<<<<< HEAD 使用块 ID 用于溯源链接(内容已包含在优化中) **2. 更新三元组溯源** 将提取的三元组链接到块(而不是顶级文档)。 ======= 使用块 ID 用于溯源链接 (内容已包含在优化中) **2. 更新三元组溯源** 将提取的三元组链接到块 (而不是顶级文档)。 >>>>>>> 82edf2d (New md files from RunPod) 使用重构来创建指向边的边 `subjectOf` 关系:三元组 → 块文档 ID 首次使用现有的重构支持 **3. 更新嵌入溯源** 将嵌入实体 ID 链接到块。 发射边:嵌入实体 ID → 块文档 ID #### 接口变更摘要 | 接口 | 变更 | |-----------|--------| <<<<<<< HEAD | 输入消息 | 期望块 ID + 内容(不是仅包含内容) | ======= | 输入消息 | 期望块 ID + 内容 (而不是仅包含内容) | >>>>>>> 82edf2d (New md files from RunPod) | Triple store | 使用重构进行三元组 → 块溯源 | | 嵌入溯源 | 将实体 ID 链接到块 ID | ## 引用 查询时溯源:`docs/tech-specs/query-time-provenance.md` PROV-O 标准用于溯源建模 <<<<<<< HEAD 知识图谱中现有的源元数据(需要审计) ======= 知识图谱中现有的源元数据 (需要审计) >>>>>>> 82edf2d (New md files from RunPod)