瀚铄智擎

2026/03/05

推理检索与证据定位机制:可追溯输出的工程实现

作者: 瀚铄智擎技术团队

本文面向企业级知识问答、制度检索、合规审查等高要求场景,目标不是“回答得像”,而是“回答可证明”。文中给出一套可直接实施的工程路径:以 ToC(Table of Contents)树为主索引,以树搜索驱动推理检索,以证据回传和审计日志保障可追溯输出。本文所述机制、流程与指标口径均基于瀚铄智擎技术团队的项目交付实践沉淀。全文按“输入、处理、输出、日志、验收”五个维度展开,便于团队按模块落地与联调。

一、问题定义与设计原则

在长文档检索中,仅依赖“固定 chunk + 向量相似度”通常会出现三类问题:

  1. 结构失真:chunk 与原始章节边界错位,导致模型拿到片段但失去上下文层级,容易出现“局部正确、整体错误”。
  2. 证据不可定位:向量召回返回的是切片编号,不天然对应文档章节、页码区间或父子路径,审计与复核成本高。
  3. 过程不可复盘:很难回答“为什么选到这个 chunk、为什么没选另一个”,无法形成稳定的检索责任链。

因此,工程目标应明确为四项:

  1. 相关性优先:优先命中正确章节,再进入段落级细化。
  2. 证据可定位:任何结论都能回到 node_id + 页码/索引区间 + 原文片段
  3. 过程可审计:每一步节点选择、扩展、放弃都写入日志。
  4. 结果可复盘:同一 query_id 可重放检索路径并复现结论。

对应设计原则如下:结构先行、推理驱动、证据闭环、无证据降级、指标验收。

二、索引构建:ToC 树结构(核心)

2.1 节点模型与字段定义

索引树是后续检索与审计的唯一结构锚点,节点字段建议固定如下。

字段类型/示例作用验收口径
node_id"2.3.1" 或 UUID节点唯一标识,用于检索命中、证据引用、日志关联全树唯一;可逆定位到原文
title"3.2 风险控制"章节语义入口,供 LLM 与规则检索共同使用与原文标题匹配率 ≥ 99%
start_index12(页码或段落索引)节点起始位置,定义证据边界start_index ≤ end_index
end_index19节点结束位置,约束回传范围不越界、不交叉
summary"本节定义审批时限与例外条件"低成本语义摘要,用于树上快速筛选与原文主题一致,无反向含义
sub_nodes/nodes子节点数组形成层级路径,支持由粗到细搜索无孤儿节点、无环

2.2 node_id -> 原文内容 映射机制

推荐建立独立的 node_content_map,避免树结构与内容载荷耦合:

  • 文本:node_id -> [{page, paragraph_id, text_span}]
  • 图像:node_id -> [{page, image_bbox, ocr_text, image_hash}]
  • 表格:node_id -> [{page, table_bbox, csv_snapshot, header_map}]

处理流程:

  1. 输入:解析后的文档页对象(含文本块、图像块、表格块)。
  2. 处理:按 start_index/end_index 汇总对象并生成内容引用。
  3. 输出:node_content_map(仅引用,不复制全文)。
  4. 日志:记录每个节点绑定的对象数、空节点率、异常对象率。
  5. 验收:随机抽样节点,人工核验“树节点与原文对象一致”。

2.3 三类入口流程

索引入口必须覆盖三种现实文档状态:

  1. 原文含目录且带页码。 处理:提取目录行后直接构建节点区间,再进行页码有效性与标题落点校验。 输出:高置信初始树。

  2. 原文含目录但不带页码。 处理:先抽取目录层级,再在正文中做标题定位;定位冲突时并发二次检索。 输出:带推断页码的树,并标记 inferred=true

  3. 原文无目录。 处理:启动自动结构抽取(标题模式 + 版式特征 + LLM 归并)。 输出:候选树并附结构置信度,低于阈值进入人工复核池。

2.4 索引质量控制与修复

质量控制建议在构建阶段一次做完,避免把错误传入检索层:

  • 物理页码有效性校验与截断:页码超出文档总页数时先截断,再触发修复流程。
  • 标题出现位置校验(并发检查):针对每个标题并发扫描正文,核对首次出现页与目录页是否一致。
  • 错误页码修复与重试:对错位节点执行“标题重定位 -> 区间重算 -> 子树联动修正”,失败则回退上层节点范围并记 needs_review

2.5 大节点递归细化(双阈值)

对过大的节点执行再切分,触发条件建议采用“页数阈值 + token 阈值”双阈值:

  • 条件:(end_index - start_index + 1) > PAGE_THRESHOLDtokens(node_text) > TOKEN_THRESHOLD
  • 动作:调用结构化再切分模型生成子节点,并继承父路径。
  • 终止:达到最小粒度或连续两次切分收益不足。
function build_or_repair_index(document):
    toc_info = detect_toc(document)

    if toc_info.has_toc and toc_info.has_page_num:
        tree = build_tree_from_toc_with_pages(toc_info)
    else if toc_info.has_toc and not toc_info.has_page_num:
        tree = build_tree_from_toc_without_pages(toc_info, document)
    else:
        tree = build_tree_from_auto_structure(document)

    tree = validate_physical_page_range(tree, total_pages=document.page_count)

    # 并发做标题定位校验
    mismatches = concurrent_title_location_check(tree, document)
    for node in mismatches:
        repaired = repair_node_page_range_by_title(node, document)
        if not repaired:
            mark(node, "needs_review")

    queue = [tree.root]
    while queue not empty:
        node = queue.pop()
        page_span = node.end_index - node.start_index + 1
        token_span = estimate_tokens(fetch_text(document, node))

        if page_span > PAGE_THRESHOLD or token_span > TOKEN_THRESHOLD:
            node.sub_nodes = split_large_node(node, document)

        queue.extend(node.sub_nodes)

    node_content_map = build_node_content_map(tree, document)
    qc_report = generate_index_qc_report(tree, mismatches)
    return tree, node_content_map, qc_report

三、推理检索:树搜索而非向量召回(核心)

3.1 LLM Tree Search 标准 I/O

建议统一接口,避免模型调用与检索编排耦合:

  • 输入:query + current_tree_view
  • 输出:thinking + node_list

其中 thinking 记录模型对当前树的判断理由(供审计,不直接展示给终端用户),node_list 为下一步待取证节点集合。该接口的核心价值在于“先选结构,再取原文”。

3.2 迭代检索闭环

标准闭环可固定为五步:

  1. 读树:读取当前可见层级与历史访问状态。
  2. 选节点:模型输出 node_list,并进行去重与合法性检查。
  3. 取原文:按 node_idnode_content_map 拉取正文证据。
  4. 判充分:执行证据充分性判定。
  5. 不充分则继续:扩展相邻节点、父子节点或文内引用节点,再次迭代。

3.3 证据充分性判定标准

建议以结构化判定结果替代“主观觉得够了”:

  • 覆盖度(Coverage):问题中的关键约束是否被证据逐项覆盖。
  • 一致性(Consistency):多条证据是否互相支持、是否同一版本口径。
  • 冲突情况(Conflict):是否出现相互矛盾条款,是否已标注优先级规则。
  • 缺失项(Missing):仍缺哪些关键信息,缺失是否影响结论有效性。

判定输出至少包含 is_sufficientmissing_infoconflict_notes 三类字段。

四、混合树搜索(Hybrid)工程化

纯 LLM 路径在复杂问题上准确性高,但成本与时延受上下文影响;纯打分路径成本可控,但容易只命中局部。工程上建议并行执行两条分支:

  1. LLM 树搜索分支:负责路径规划、跨层跳转、歧义消解。
  2. value-based 分支:以 chunk 为中间单元计算节点分数,仅用于节点优先级排序,不直接把 chunk 当最终证据返回。

4.1 队列与消费者机制

  • frontier_queue:候选节点队列,按综合分数排序。
  • visited_set:节点去重,防止循环访问。
  • consumer_pool:并发拉取节点原文,降低长文 I/O 时间。

4.2 提前终止(Early Stop)

当满足以下任一条件即可提前结束:

  1. is_sufficient=true 且冲突已解释。
  2. 新增节点连续 N 轮未提升覆盖度。
  3. 达到 max_steps 或预算阈值(token、时延、成本)。

4.3 节点打分示例公式

设节点下 chunk 集合为 C(node),chunk 相关度为 s_i,chunk token 为 t_i,可采用:

raw(node) = Σ( s_i ) norm(node) = sqrt( Σ(t_i) / 100 ) score(node) = raw(node) / max(norm(node), 1)

该做法可抑制“长节点因文本多而天然高分”的偏置,便于与 LLM 分支结果融合。

function retrieve_with_trace(query, tree, node_content_map):
    frontier = init_frontier(tree.root)
    visited = set()
    evidence_refs = []
    retrieval_steps = []

    for step in range(1, MAX_STEPS + 1):
        llm_out = llm_select_nodes(query, view_tree(frontier))
        # llm_out: {thinking, node_list}

        value_rank = value_score_nodes(query, frontier)  # chunk->node 聚合打分
        candidates = merge_and_rerank(llm_out.node_list, value_rank)
        candidates = deduplicate(candidates, visited)

        node_batch = consume_nodes(candidates, TOP_K_PER_STEP)
        for node in node_batch:
            visited.add(node.node_id)
            refs = fetch_evidence(node_content_map, node.node_id)
            evidence_refs.extend(refs)

        suff = judge_evidence_sufficiency(query, evidence_refs)
        retrieval_steps.append({
            "step": step,
            "thinking": llm_out.thinking,
            "selected_nodes": [n.node_id for n in node_batch],
            "sufficient": suff.is_sufficient,
            "missing": suff.missing_info
        })

        if suff.is_sufficient:
            break  # early stop

        frontier = expand_neighbors_and_references(node_batch, tree)

    final_answer = generate_answer_or_decline(query, evidence_refs)
    audit_log = build_audit_log(query, retrieval_steps, evidence_refs, final_answer)
    return final_answer, evidence_refs, audit_log

五、证据定位与可追溯输出(核心)

5.1 证据输出格式规范

最终响应必须返回“答案 + 证据对象数组”,单条证据对象最少包含:

  • node_id
  • start_index/end_index(或页码区间)
  • snippet(证据片段)
  • source_path(父子节点路径,如 1 > 1.2 > 1.2.3
  • confidence_note(置信说明与限制条件)

5.2 审计日志字段规范

审计日志最少字段:

  • query_id
  • retrieval_steps
  • selected_nodes
  • evidence_refs
  • final_answer
  • timestamp

建议补充:model_versionindex_versionlatency_mscost_tokensdegrade_reason,以支持线上问题回放。

5.3 JSON 输出示例

{
  "query_id": "Q-20260305-00017",
  "timestamp": "2026-03-05T14:38:21+08:00",
  "query": "采购审批超时后的责任划分与升级路径是什么?",
  "selected_nodes": ["3", "3.2", "3.2.1", "7.4"],
  "retrieval_steps": [
    {
      "step": 1,
      "thinking": "问题包含规则定义与例外条款,优先进入第3章制度条款。",
      "node_list": ["3", "3.2"],
      "sufficient": false,
      "missing": ["升级链路触发条件"]
    },
    {
      "step": 2,
      "thinking": "在3.2下找到超时责任描述,同时需补充跨章节升级流程。",
      "node_list": ["3.2.1", "7.4"],
      "sufficient": true,
      "missing": []
    }
  ],
  "evidence_refs": [
    {
      "node_id": "3.2.1",
      "start_index": 42,
      "end_index": 45,
      "snippet": "审批节点超出SLA 24小时,由节点责任人提交延迟说明并报部门负责人确认。",
      "source_path": "3 > 3.2 > 3.2.1",
      "confidence_note": "条款定义清晰,适用于常规采购流程。"
    },
    {
      "node_id": "7.4",
      "start_index": 103,
      "end_index": 105,
      "snippet": "连续两次超时或单次超时超过72小时,触发跨级升级至运营治理委员会。",
      "source_path": "7 > 7.4",
      "confidence_note": "与3.2.1互补,无冲突。"
    }
  ],
  "final_answer": "常规超时先由节点责任人说明并经部门负责人确认;出现连续两次超时或单次超过72小时时,按第7.4条升级至运营治理委员会处理。"
}

5.4 降级与人工复核

必须执行“无证据不下结论”:

  1. evidence_refs 为空或覆盖度不足时,返回“证据不足”状态,不输出确定性结论。
  2. 当冲突无法消解时,输出冲突条款并触发人工复核。
  3. 当命中节点均为低置信(如结构抽取节点)时,进入人工确认队列。

人工复核触发条件建议量化:coverage < 0.8conflict_count > 0 且无优先级规则、retry_count ≥ 2

六、验收口径与评测指标

上线前建议按查询集(不少于 500 条)进行离线评测,并在灰度期持续采集线上指标。以下口径可直接执行。

指标定义计算口径采集方式目标示例
节点召回率(Node Recall)应命中节点中被检索命中的比例命中真值节点数 / 真值节点总数离线标注集对比 selected_nodes≥ 0.92
证据覆盖率答案关键点被证据覆盖的比例被证据支持的关键点数 / 关键点总数规则抽取关键点 + 人工抽检≥ 0.90
引用准确率返回证据与答案断言一致的比例正确引用条目数 / 总引用条目数逐条校验 snippet 与断言映射≥ 0.95
无证据断言率无证据仍输出确定性结论的比例无证据确定性回答数 / 总回答数线上审计日志扫描 evidence_refs≤ 0.5%
P95 检索时延检索阶段 95 分位耗时P95(retrieval_latency_ms)APM/日志平台按 query_id 聚合≤ 1200ms
端到端响应时延从请求到最终响应总耗时response_ts - request_ts 的 P95网关日志 + 应用日志对齐≤ 2500ms

指标解释要求:所有延迟指标必须带样本量、时间窗和查询类型分层(短问、复杂问、跨章节问),避免平均值掩盖长尾。

七、风险与边界

7.1 树结构错误导致漏检

风险:目录抽取或层级归并错误会直接影响召回上限。 对策:构建期执行校验重试;检索期提供“相邻节点扩展 + 父节点回退”;低置信节点进入人工兜底。

7.2 摘要偏差导致误选节点

风险:summary 与原文不一致时,LLM 会沿错误路径深入。 对策:摘要生成后做语义一致性校验;命中前二次取样原文;发现偏差即重算摘要并回写版本。

7.3 文内跳转引用遗漏

风险:制度文档常有“见第X章”跳转,若不跟踪引用边将漏掉关键约束。 对策:在索引中显式构建 reference_edges;检索不充分时优先扩展引用节点;日志记录“引用扩展命中率”。

7.4 长文多轮检索导致时延抬升

风险:复杂问题下迭代轮次增加,P95 时延显著上升。 对策:使用并发消费者、节点缓存、结果缓存;设置 early stop 与预算上限;超过预算自动降级到“证据不足 + 人工复核”。

实施建议与交付件清单

为便于项目制交付,建议最小交付件包含:

  1. index_builder:完成三类入口、校验修复、双阈值细化。
  2. retrieval_engine:完成 LLM 树搜索与 Hybrid 并行、去重队列、early stop。
  3. evidence_formatter:统一证据结构与答案输出协议。
  4. audit_logger:按 query_id 记录全链路检索与生成过程。
  5. evaluation_suite:离线指标计算脚本与线上看板口径。

上述五项全部可映射到“输入/处理/输出/日志/验收”闭环。达到指标后再扩展多模态(图像、表格)与跨文档检索,可显著降低一次性改造风险。

预约演示提交需求