来源标注与溯源¶
追踪哪些文档为知识摘要做出过贡献,跳过未变更的重复喂养,并按文档回滚其贡献,而不影响其余知识。
概述¶
带来源标注摄取文档(CLI 用 --source,Python 用 source_id=)会让知识摘要记住该文档:其原始提取结果会保存在来源台账中,并记录输入文本的 SHA-256 内容哈希。
溯源提供三项能力:
| 能力 | CLI | Python |
|---|---|---|
| 按文档回滚 | he remove --document ID |
ka.remove_source(ID) |
| 变更检测 | he feed --source 跳过未变更文档 |
ka.source_content_hash(ID) |
| 审计 | he info --sources |
ka.sources() |
图谱类知识摘要(graph、hypergraph、时序/空间图谱)默认开启来源追踪。
带来源标注的摄取¶
# 解析时标注(首个文档)
he parse doc1.md -t general/graph -o ./ka/ -l zh --source doc-1
# 喂养进现有知识库时标注
he feed ./ka/ doc2.md --source doc-2
from hyperextract import Template
ka = Template.create("general/graph", language="zh")
with open("doc1.md") as f:
result = ka.parse(f.read(), source_id="doc-1")
result.feed_text(doc2_text, source_id="doc-2")
result.dump("./ka/")
变更检测¶
同一来源再次喂养且内容未变化时,he feed 会比对内容哈希并完全跳过该文档——零 LLM 调用:
he feed ./ka/ doc2.md --source doc-2
# Source 'doc-2' is unchanged (content hash matches) — nothing to do.
# Use --refeed to re-ingest anyway.
查看来源台账¶
输出:
Source Ledger
┏━━━━━━━━━━━━┳━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
┃ Source ID ┃ Raw Items ┃ Content Hash ┃
┡━━━━━━━━━━━━╇━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┩
│ doc-1 │ 12 │ 9f86d081884c7d659a2febb0c0413a6e… │
│ doc-2 │ 8 │ 2c26b44babbfb4c6a5cc6b3f2a4b2f1d… │
└────────────┴───────────┴────────────────────────────────────┘
在 Python 中:
ka.sources()
# {
# "doc-1": {"raw_items": 12, "content_hash": "9f86d0…"},
# "doc-2": {"raw_items": 8, "content_hash": "2c26b4…"},
# }
回滚整个文档¶
删除某个文档贡献的全部知识:
report = ka.remove_source("doc-1") # exact(默认)
report = ka.remove_source("doc-1", strategy="touched")
result.dump("./ka/") # 持久化
策略决定与其他文档共享的 key 如何处理:
| 策略 | 共享 key | 独占 key |
|---|---|---|
exact(默认) |
从幸存来源的原始结果重新合并(经典合并策略下完全确定) | 删除 |
touched |
直接删除 | 删除 |
exact 保证其余每个文档的贡献完整保留;touched 保证被删文档的影响不留痕迹,代价是其他文档也支持的事实会被一并删除。使用 LLM 合并策略时,exact 的重新合并会保留语义、措辞近似。未知或无贡献的来源会报告 Nothing matched 并正常退出。无论哪种情况,已构建的搜索索引都会原地修补。
Note
基于哈希的变更检测内置于 he feed --source。Python 调用者可以在调用 feed_text 前,将 ka.source_content_hash(source_id) 与自行计算的文本哈希比对,实现同样效果。
事实级编辑¶
若只需保留节点但移除其中一条错误描述,请使用事实级编辑而非整体回滚:
report = ka.edit_node("Apple", remove_fact="founded by Steve Jobs", dry_run=True)
ka.edit_node("Apple", remove_fact="founded by Steve Jobs") # 应用
完整安全护栏(key 不变性、预览、备份)见 he remove。
持久化¶
ka.dump()——以及每次 he parse / he feed——会把台账写在 data.json 旁边:
./ka/
├── data.json # 提取的知识
├── metadata.json # 提取元数据
├── sources_nodes.json # 节点台账:每个来源的原始条目
├── sources_edges.json # 边台账:每个来源的原始条目
├── sources_chunks.json # 块台账(chunk 型知识库):每个来源的原始块
└── documents/ # 原始文档归档,每个来源保留一份当前副本
- 请将台账文件与知识摘要放在一起保存(像
data.json一样纳入版本控制):每个来源的原始结果只存在于这里,删除台账文件会导致该知识摘要无法回滚、也无法做变更检测。 he feed --source会同时把原始文档归档到documents/(保留原始字节,每个来源一份当前副本;用--no-store-doc关闭)。he remove --document时传入--purge-documents可在回滚的同时删除归档副本。- 每次写入前,
he remove会将data.json备份为data.json.bak.<时间戳>(除非传入--no-backup),因此回滚本身也可以撤销。
路线图¶
- 已落地(v0.8.0) —— 基于内容哈希的变更检测:
he feed --source会在任何 LLM 调用之前计算输入哈希并跳过未变更文档。 - 已落地(v0.9.0) —— 原始文档归档(
documents/),以及直接检索原始文本块的chunk_rag方法(chunk 级检索)。 - 规划中 —— 更细粒度的溯源:把图谱型知识库中的每个节点/边映射回它被提取自的确切文本块(chunk)。注意这与
chunk_rag不同——后者检索 chunk,但不做结构化。