RAGSpine
核心概念

来源溯源

每个事实和每个答案都携带来源文档 id 加定位符——血缘端到端传递,绝不丢弃。

防编造机制的孪生不变量:**始终引用。**每个已存储的事实和每个答案都携带来源血缘——它来自哪份文档,以及在文档内的哪个位置。不存在任何只产出数字而不产出其来源的路径。

**保证。**每个事实/答案都携带 source_doc_id(chunk 为 doc_id)+ 一个 source_locator。血缘端到端传递,在通往答案的途中绝不丢弃。

血缘从存储层开始

血缘不是在作答时才补上的——它是栈底的一个 NOT-NULL 列。

结构化事实(storage/fact_store.py

Fact dataclass 的前十个字段按位置冻结;其中两个就是血缘:

@dataclass
class Fact:
    metric_code: str
    entity: str
    geography: str
    channel: str
    period_type: str
    period: str
    value: float
    unit: str
    source_doc_id: str    # which document
    source_locator: str   # where inside it (e.g. slide=2,table=1,row=REVENUE,col=FY2024)
    ...

fact_metric 表将两者都声明为 TEXT NOT NULL。一个事实不可能在不知道自己来自何处的情况下存在。(v2 还增加了可选的版本血缘——source_file_hashextractor_versionmapping_version——但那些是增量式的。)

叙事 chunk(retrieval/chunking/chunk_store.py

每个 chunk 携带 doc_id + source_locatorTEXT NOT NULL),以及 titleentityperiodsensitivity 等元数据。

血缘一路传递到答案

结构化查询 —— execute_query_metric 返回的 found 结果,其 source 子字典为 {"doc": fact.source_doc_id, "locator": fact.source_locator}
答案合成 —— _structured_answer 将每个已找到的事实渲染为 … 值 单位(来源:{doc} · {locator}),并把 source 字典作为 AgentResult.sources 返回。
叙事合成 —— _run_narrative(来源:{doc} {locator}) 构建片段块,并且作为兜底,模型遗漏的任何来源文档名都会被强制追加到答案中。
HTTP 响应 —— /v1/ask 路由把 result.sources 映射进 AskResponsesources 字段(service/api/routes.py)。
from ragspine.agent.agent import answer_question
from ragspine.agent.llm_provider import MockProvider
from ragspine.storage.fact_store import FactStore

store = FactStore("data/fact_metric.db"); store.init_schema()
result = answer_question("中国内地FY2024的REVENUE是多少", store, MockProvider())
print(result.answer)   # number + (来源:… · …)
print(result.sources)  # [{'doc': 'ACME_FY2024_Review.pptx', 'locator': 'slide=2,table=1,...'}]

哪些位置绝不能丢血缘

以下是那些容易丢失血缘——而这被明令禁止——的接缝:

  • 存储 —— 绝不写入没有血缘列的事实/chunk;它们被有意设计为 NOT NULL。(storage/CLAUDE.md:"never drop lineage"。)
  • 已找到事实的渲染 —— _structured_answer / _multi_subtask_answer 必须让 source 字典始终附着在每条渲染出的行上。
  • 叙事合成 —— _run_narrative 中的引用兜底之所以存在,正是为了让"忘记"引用的模型无法把来源溯源从答案上剥掉。
  • 服务边缘 —— /v1/ask 的映射必须原样传递 sources;FAQ 短路命中时也携带自己的 source(参见 FAQ 短路)。

隐私感知的 trace 记录血缘标识符(chunk_id、受控代码),但绝不记录事实值或 chunk 文本——来源溯源关乎在哪里,而不是把敏感的内容再暴露一次。参见 RESTRICTED 隔离

本页目录