Files
rag/docs/RAG引用跳转-前端实现说明.md
lacerate551 100d1a06eb init: RAG 知识库服务初始提交
- 后端 API(Flask + Gunicorn)
- RAG 引擎(混合检索 + 云端 Reranker + 引用溯源)
- 文档解析(MinerU + 多格式支持)
- Docker 生产部署配置
- 排除前端项目、敏感配置、模型文件
2026-06-04 17:35:27 +08:00

7.9 KiB
Raw Blame History

RAG 引用溯源跳转 — 接口说明与实现思路

一、整体流程

用户提问 → /rag 流式返回 → 回答文本中嵌入 [ref:chunk_id] 标记 + citations 数组
                                        ↓
                          前端解析标记,渲染为可点击的 [1] [2] 上标
                                        ↓
                          用户点击引用 → 前端调 /documents/{path}/preview 获取切片上下文
                                        ↓
                          前端展示:左侧原文档预览(跳转/高亮) + 右侧切片内容

二、/rag 返回的引用数据结构

SSE 流结束时,finish 事件的 citations 字段是一个数组,每个元素结构如下:

{
  // ===== 通用字段(所有文档类型都有) =====
  "chunk_id": "三峡公报.pdf_5",     // 原始切片 ID文件名_序号
  "chunk_index": 5,                  // 全局切片序号(从 0 开始递增,入库时写入)
  "source": "三峡公报.pdf",          // 来源文件名
  "collection": "public_kb",         // 所属知识库
  "doc_type": "pdf",                 // 文档类型pdf / word / excel / txt / other
  "section": "第一章 > 1.2 概述",     // 章节层级路径
  "preview": "元数据中的预览文本",      // 入库时生成的摘要/预览
  "content": "切片正文(截断至 300 字)", // 实际切片内容的前 300 字
  "chunk_type": "text",              // 切片类型text / table / image_caption

  // ===== PDF 专属字段 =====
  "page": 3,                         // 起始页码
  "page_end": 4,                     // 结束页码(跨页时存在)
  "bbox": [100, 200, 500, 600],      // 边界框坐标(可用于页内精准定位)
  "bbox_mode": "pixel",              // 坐标模式

  // ===== Word 专属字段 =====
  "section_chunk_id": "sec_3_para_2" // 章节内段落序号

  // ===== Excel 专属字段 =====
  "page": 1                          // 工作表序号
}

关键字段说明:

字段 用途 备注
chunk_index 调用 preview 接口的核心参数,精准定位切片在文档中的位置 从 0 开始,入库时按顺序分配
source + collection 拼接文档路径 {collection}/{source},用于调 preview 接口 例:public_kb/三峡公报.pdf
doc_type 前端据此选择预览方式PDF 渲染 / DOCX 渲染 / 纯文本)
page PDF 页码跳转、Excel 工作表定位
bbox PDF 页内矩形高亮(可选,精度更高)
section 给用户展示定位信息(如"第三章 > 3.1 市场分析"
content 截断的正文预览,用于 DOCX 文本匹配高亮 仅 300 字,完整内容需调 preview 接口

回答文本中的引用标记:

后端在 LLM 回答的段落末尾自动插入 [ref:chunk_id] 标记,前端需要将其解析为可点击的上标:

原文:根据相关研究,三峡工程年均发电量约 882 亿千瓦时。[ref:三峡公报.pdf_5]
渲染:根据相关研究,三峡工程年均发电量约 882 亿千瓦时。[5]  ← 可点击上标

三、文档预览接口

GET /api/documents/{collection}/{source}/preview?chunk_index={N}&context={K}

参数:

参数 类型 必填 说明
collection path 知识库名称(如 public_kb
source path 文件名(如 三峡公报.pdf
chunk_index query 目标切片序号(来自 citation 的 chunk_index
context query 前后各取几个切片,默认 2

响应示例:

{
  "success": true,
  "collection": "public_kb",
  "source": "三峡公报.pdf",
  "total_chunks": 42,          // 文档总切片数
  "target_index": 5,           // 请求的 chunk_index
  "chunks": [
    {
      "id": "uuid-xxx",
      "document": "完整切片正文...",   // 完整内容(不截断)
      "metadata": {
        "chunk_index": 3,
        "source": "三峡公报.pdf",
        "page": 2,
        "section": "第一章 > 1.1 项目背景",
        "doc_type": "pdf",
        ...
      },
      "is_target": false        // 是否为目标切片
    },
    {
      "id": "uuid-yyy",
      "document": "目标切片的完整正文...",
      "metadata": { "chunk_index": 5, "page": 3, ... },
      "is_target": true         // ← 这是用户点击的那个切片
    },
    // ... 上下文切片
  ]
}

四、前端实现思路

4.1 引用标记解析

1. 从 finish 事件取 answer回答全文和 citations引用数组
2. 用正则 /\[ref:([^\]]+)\]/g 从 answer 中按出现顺序提取所有 chunk_id
3. 与 citations 数组匹配,建立 chunk_id → 显示序号 [1] [2] ... 的映射
4. 渲染回答时,将 [ref:xxx] 替换为可点击的上标元素

4.2 点击引用后的跳转

用户点击引用 [N]
    │
    ├─ 1. 从 citation 中取 source + collection拼出文档路径
    │     docPath = `${collection}/${source}`
    │
    ├─ 2. 调用 preview 接口获取切片上下文
    │     GET /documents/${docPath}/preview?chunk_index=${chunk_index}&context=3
    │
    ├─ 3. 根据 doc_type 选择预览方式:
    │     ├─ PDF  → 用 page 字段跳转到对应页(可选:用 bbox 绘制高亮矩形)
    │     ├─ DOCX → 用目标切片的 document 字段做文本匹配,高亮并滚动到对应段落
    │     └─ TXT  → 直接显示文本,搜索目标内容并滚动定位
    │
    └─ 4. 右侧面板展示切片上下文列表
          ├─ 高亮标记 is_target=true 的切片
          ├─ 显示每个切片的 section、page 信息
          └─ 点击其他切片可切换预览位置

4.3 各文档类型的定位策略

PDF 文档:

最直接的方案是利用 page 字段做页码跳转。如果需要更精确的页内定位,bbox 字段提供了文本区域的坐标 [x1, y1, x2, y2],可以在 PDF 渲染层上叠加一个半透明高亮矩形。

Word 文档:

Word 没有页码概念,定位方式是文本匹配。用 preview 接口返回的目标切片 document(完整正文)在渲染后的文档中做模糊匹配,找到最相似的段落并高亮滚动。content 字段只有 300 字可能不够用,建议用 preview 接口的 document 字段。

纯文本 / 其他:

直接渲染文本内容,通过字符串搜索定位目标切片并滚动到视口中央。

4.4 弹窗内切片切换

右侧面板展示上下文切片后,用户可以点击其他切片切换预览位置。切换时需要:

1. 从切片的 metadata.chunk_index 获取序号(注意:切片的 id 是 UUID不能用来定位
2. 更新目标标记is_target
3. 用切片的 document 字段更新左侧文档的高亮/定位
4. 如果切片 metadata 中有 page 字段,同步更新 PDF 页码

五、注意事项

  1. chunk_index 是核心定位字段。它在入库时按文档切片顺序从 0 递增分配preview 接口通过它查找目标切片。不要与数组下标混淆——后端已按 metadata.chunk_index 排序后查找,不依赖数组位置。

  2. content 字段被截断到 300 字。这是为了控制 SSE 流的数据量。前端如果需要完整切片内容(比如 DOCX 高亮匹配),应调用 preview 接口获取 document 字段。

  3. bbox 坐标的可用性。目前 PDF 解析器会提取文本块的 bbox 信息,但不是所有切片都有。前端使用 bbox 时应做空值判断。

  4. collection 字段的回退策略。citation 中带有 collection 字段,但如果为空,前端可以用当前用户选中的知识库名称作为回退。

  5. LLM 可能自行生成 [1][2] 等引用标记,与后端注入的 [ref:chunk_id] 格式不同。前端应先清理 LLM 的 [数字] 标记,再处理后端的 [ref:xxx] 标记,避免冲突。