代码语言

知识点思维导图

22 个知识节点

项目实战(05) - 多模态 RAG:版面、图片、表格与文本的证据闭环

读完后,你应能:

  • 能验证“多模态 RAG 不是“把图片丢给视觉模型””,并保存输入、输出与失败样本。
  • 能验证“企业文档的答案常同时依赖段落、表格单元格、截图标注和页码关系”,并保存输入、输出与失败样本。
  • 能验证“如果解析时丢掉版面结构,后续再强的模型也无法还原证据”,并保存输入、输出与失败样本。

多模态 RAG 不是“把图片丢给视觉模型”。 企业文档的答案常同时依赖段落、表格单元格、截图标注和页码关系; 如果解析时丢掉版面结构,后续再强的模型也无法还原证据。

一、全链路架构

flowchart LR
    I[PDF/Office/图片/音视频] --> S[对象存储与病毒扫描]
    S --> P[版面解析/OCR/ASR]
    P --> U[统一 DocumentElement]
    U --> C[结构化 Chunk]
    C --> T[ES BM25]
    C --> V[文本/图像向量库]
    C --> O[原图与裁剪证据]
    Q[用户问题与 ACL] --> H[多路检索]
    T --> H
    V --> H
    H --> R[Rerank 与证据拼装]
    O --> R
    R --> G[VLM/LLM 生成]
    G --> K[引用与权限复核]

二、统一元素而不是直接拼纯文本

解析输出建议包含 element_iddocument_idpagebboxelement_typetextasset_uriparent_idreading_orderacl_groups 和解析置信度。 表格要保存行列坐标与合并单元格,图片要保存标题、邻近段落和裁剪位置,才能实现页级引用回跳。

# requirements.txt
pydantic>=2,<3

三、切分与 Embedding 策略

元素 切分方式 Embedding 必留证据
正文 按标题层级和语义边界,保留少量重叠 多语言文本模型 页码、段落、标题路径
表格 表头与行组共同成块,宽表按业务列拆分 表格转规范文本后做文本向量 原表截图、行列坐标
图片/图表 图片说明 + 邻近段落 + OCR 文字 文本向量;确有图搜图需求再加视觉向量 裁剪图、bbox
音视频 ASR 按说话人和时间窗切分 文本向量 起止时间、媒体地址

Embedding 先以“能否在真实问题集上召回正确证据”选型, 再比较吞吐、维度、最大长度、语言覆盖、私有化成本。 不要一开始就维护两套视觉向量; 如果用户只用文字提问,图片描述和 OCR 往往已覆盖主要价值。

四、在线检索与校验

  1. 先基于用户、租户和权限组构造不可由模型修改的过滤器。
  2. 并行执行 BM25、文本向量、结构化表格查询;只有图像相似需求才开启视觉向量路。
  3. 用稳定 element_id 去重,RRF 融合,再让跨编码器或 VLM 对少量候选精排。
  4. Context 同时传规范文本和受控图片,限制总像素、图片数和 Token。
  5. 答案中的每个关键结论必须引用 element_id;引用服务再次校验权限后返回页码和裁剪图。
  6. 若 OCR 置信度低、证据冲突或引用覆盖不足,明确拒答或转人工复核。

五、稳定性、成本与安全

  • 解析器和 OCR 都要版本化;升级后并行重建新索引,以金标集验收后切别名。
  • 文件先做类型嗅探、病毒扫描、解压炸弹限制和沙箱解析,不能只信扩展名。
  • OCR/VLM 是主要成本项:对内容哈希去重,先用原生文本层,低置信页才 OCR,候选精排后才调用 VLM。
  • 对象地址使用短期签名 URL,日志不记录原图;敏感图片先脱敏再进入外部模型。
  • 监控每种模态的解析成功率、低置信率、Recall@K、引用覆盖率、P95 和单文档/单问成本。

六、截图策略

验收清单

  • 扫描 PDF、跨页表格、图表、纯文本和音频各有不少于一组金标问题。
  • 任一答案可回跳到页码、表格行或时间片,而不是只显示文件名。
  • 删除或降权后,ES、向量库、对象存储引用和缓存同步失效。
  • 无权限用户无法从标题、缩略图、命中数、错误信息或模型答案推断文档存在。
  • 单路解析或检索失败时可降级,证据不足时不生成确定性答案。

多模态 RAG 的分水岭不是支持多少文件格式, 而是能否把每个结论闭环到可验证、可授权、可回放的证据。

七、总结

  • 统一元素而不是直接拼纯文本:解析输出建议包含 element_id、document_id、page、bbox、element_type、text、asset_uri、parent_id、reading_order、acl_groups 和解析置信度。
  • 切分与 Embedding 策略:| 正文 | 按标题层级和语义边界,保留少量重叠 | 多语言文本模型 | 页码、段落、标题路径 |
  • 在线检索与校验:先基于用户、租户和权限组构造不可由模型修改的过滤器。 -> 并行执行 BM25、文本向量、结构化表格查询; -> 用稳定 element_id 去重,RRF 融合,再让跨编码器或 VLM 对少量候选精排。 -> Context 同时传规范文本和受控图片,限制总像素、图片数和 Token。
  • 稳定性、成本与安全:文件先做类型嗅探、病毒扫描、解压炸弹限制和沙箱解析,不能只信扩展名。
  • 可运行实验:多模态 RAG:OCR、表格与图片引用:调整参数并注入失败,重点对比正常路径、保护条件和失败诊断;

八、可运行实验:多模态 RAG:OCR、表格与图片引用

调整参数并注入失败,重点对比正常路径、保护条件和失败诊断; 运行源码与文章保存在同一个 Markdown 文件。

<!doctype html>
<html lang="zh-CN">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width,initial-scale=1">
  <title>AA-16 在线实验</title>
  <style>
    :root{color-scheme:dark;font-family:Inter,system-ui,sans-serif}*{box-sizing:border-box}body{margin:0;background:#0f1211;color:#e7ece9;font-size:13px}.shell{padding:16px}.top{display:flex;justify-content:space-between;gap:16px;margin-bottom:14px}h1{margin:3px 0;font-size:18px}.id,.value{color:#68e0b5;font-family:ui-monospace,monospace}.summary{margin:4px 0;color:#a5afa9}.run{border:0;border-radius:6px;background:#68e0b5;color:#07110d;padding:8px 14px;font-weight:700}.grid{display:grid;grid-template-columns:minmax(220px,.8fr) minmax(0,1.8fr);gap:12px}.panel{border:1px solid #29322e;background:#141817;padding:12px}.control{display:grid;gap:5px;margin-bottom:11px}.head{display:flex;justify-content:space-between;gap:8px}select,input{width:100%;accent-color:#68e0b5;background:#0d100f;color:#e7ece9}.toggle{display:flex;justify-content:space-between;border-top:1px solid #29322e;padding-top:9px}.toggle input{width:18px}.metrics{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:7px}.metric{border:1px solid #29322e;padding:8px}.metric b{display:block;color:#68e0b5;font-size:16px}.stages{display:flex;gap:6px;overflow:auto;margin:10px 0}.stage{border:1px solid #8a6230;padding:7px;min-width:90px}.stage.ok{border-color:#367a61}.stage.fail{border-color:#8b4545}table{width:100%;border-collapse:collapse}td{border-top:1px solid #29322e;padding:7px}.diagnosis{margin-top:9px;border-left:3px solid #68e0b5;background:#101412;padding:9px;line-height:1.5}.danger{border-color:#ef7f7f}@media(max-width:680px){.top,.grid{display:grid;grid-template-columns:1fr}.metrics{grid-template-columns:repeat(2,1fr)}}
  </style>
</head>
<body>
  <main class="shell">
    <header class="top"><div><div class="id">AA-16 · DETERMINISTIC LAB</div><h1 id="title"></h1><p class="summary" id="summary"></p></div><button class="run" id="run">运行实验</button></header>
    <section class="grid"><div class="panel"><div id="controls"></div><label class="toggle"><span>注入典型故障</span><input id="failure" type="checkbox"></label></div><div class="panel"><div class="metrics" id="metrics"></div><div class="stages" id="stages"></div><table><tbody id="rows"></tbody></table><div class="diagnosis" id="diagnosis"></div></div></section>
  </main>
  <script>
    const scenario = { title: '多模态 RAG:OCR、表格与图片引用', summary: '观察版面解析、OCR、表格结构和坐标信息如何影响跨模态引用。', controls: [
    { key: 'ocr', label: 'OCR 准确率', type: 'range', min: 50, max: 100, value: 92, suffix: '%' },
    { key: 'layout', label: '版面解析', type: 'select', value: 'layout', options: [['plain', '纯文本'], ['layout', '版面感知'], ['vision', '视觉模型 + 版面']] },
    { key: 'coordinates', label: '保留坐标', type: 'select', value: 'yes', options: [['no', '否'], ['yes', '是']] }
  ] };
    const controls = document.querySelector('#controls');
    const failure = document.querySelector('#failure');
    document.querySelector('#title').textContent = scenario.title;
    document.querySelector('#summary').textContent = scenario.summary;
    function renderControl(control) {
      const label = document.createElement('label'); label.className = 'control';
      const head = document.createElement('span'); head.className = 'head'; head.innerHTML = '<span>' + control.label + '</span><span class="value" data-value="' + control.key + '"></span>'; label.appendChild(head);
      const input = document.createElement(control.type === 'select' ? 'select' : 'input'); input.dataset.key = control.key;
      if (control.type === 'select') control.options.forEach(option => { const item = document.createElement('option'); item.value = option[0]; item.textContent = option[1]; item.selected = option[0] === control.value; input.appendChild(item); });
      else { input.type = 'range'; input.min = control.min; input.max = control.max; input.step = control.step || 1; input.value = control.value; }
      input.addEventListener('input', updateValues); label.appendChild(input); return label;
    }
    function updateValues() { scenario.controls.forEach(control => { const input = controls.querySelector('[data-key="' + control.key + '"]'); document.querySelector('[data-value="' + control.key + '"]').textContent = control.type === 'select' ? input.options[input.selectedIndex].text : input.value + (control.suffix || ''); }); }
    function readValues() { const values = {}; scenario.controls.forEach(control => { const input = controls.querySelector('[data-key="' + control.key + '"]'); values[control.key] = control.type === 'range' ? Number(input.value) : input.value; }); values.failure = failure.checked; return values; }
    function stage(name, state, detail) { return { name, state, detail }; }
    const aiStage = stage;
    function clamp(value, minimum, maximum) { return Math.min(maximum, Math.max(minimum, value)); }
    function simulate(values) { const fail = values.failure;
      /** 版面策略对结构保真的基础增益。 */
      const layoutBonus = values.layout === 'plain' ? 0 : values.layout === 'layout' ? 8 : 13;
      /** OCR、版面和坐标共同决定的引用准确率。 */
      const citation = clamp(values.ocr * 0.72 + layoutBonus + (values.coordinates === 'yes' ? 8 : 0) - (fail ? 20 : 0), 0, 99);
      /** 表格结构是否可以在问答中安全使用。 */
      const tableSafe = values.layout !== 'plain' && values.ocr >= 80 && !fail;
      return { metrics: [[values.ocr + '%', 'OCR 准确率'], [citation.toFixed(1) + '%', '引用准确率'], [tableSafe ? 'KEPT' : 'BROKEN', '表格结构'], [values.coordinates === 'yes' ? 'VISIBLE' : 'TEXT ONLY', '可视化引用']], stages: [aiStage('页面渲染', 'ok', 'page image'), aiStage('OCR', values.ocr >= 80 ? 'ok' : 'warn', values.ocr + '%'), aiStage('版面识别', values.layout === 'plain' ? 'warn' : 'ok', values.layout), aiStage('表格解析', tableSafe ? 'ok' : 'fail', tableSafe ? 'cells' : 'flattened'), aiStage('跨模态索引', fail ? 'fail' : 'ok', 'text + image'), aiStage('坐标引用', values.coordinates === 'yes' ? 'ok' : 'warn', values.coordinates)], rows: [['Chunk 元数据', values.coordinates === 'yes' ? '保留 page、bbox、element_id 和 source_uri' : '只有文本,无法高亮原页位置'], ['表格处理', tableSafe ? '表头、行列和合并单元格关系保留' : '纯文本展开后数值可能失去所属字段'], ['故障注入', fail ? 'OCR 把金额小数点识别错误,数值校验阻断入库' : '关键数字通过格式与范围校验']], diagnosis: tableSafe && citation >= 80 && values.coordinates === 'yes' ? '文本、版面和坐标契约完整,可返回可验证的多模态引用。' : '需要提高 OCR、结构解析或坐标保留质量。', danger: fail || !tableSafe };
     }
    function render() { const result = simulate(readValues()); document.querySelector('#metrics').innerHTML = result.metrics.map(item => '<div class="metric"><b>' + item[0] + '</b><span>' + item[1] + '</span></div>').join(''); document.querySelector('#stages').innerHTML = result.stages.map(item => '<div class="stage ' + item.state + '"><b>' + item.name + '</b><div>' + item.detail + '</div></div>').join(''); document.querySelector('#rows').innerHTML = result.rows.map(item => '<tr><td>' + item[0] + '</td><td>' + item[1] + '</td></tr>').join(''); const diagnosis = document.querySelector('#diagnosis'); diagnosis.textContent = result.diagnosis; diagnosis.className = 'diagnosis' + (result.danger ? ' danger' : ''); }
    scenario.controls.forEach(control => controls.appendChild(renderControl(control))); updateValues(); document.querySelector('#run').addEventListener('click', render); render();
  </script>
</body>
</html>

学完自测

选择所有正确答案;提交后逐项核对判断依据。

1在“多模态 RAG:版面、图片、表格与文本的证据闭环”中,需要同时满足“统一元素而不是直接拼纯文本”与“切分与 Embedding 策略”。给定正文约束“表格要保存行列坐标与合并单元格,图片要保存标题、邻近段落和裁剪位置,才能实现页级引用回跳。”,哪些判断保持了原有处理机制?多选
2“多模态 RAG:版面、图片、表格与文本的证据闭环”出现偏差:“在“多模态 RAG:版面、图片、表格与文本的证据闭环 / 在线检索与校验”中,即使不满足“5. 答案中的每个关键结论必须引用 elementid”,结果与副作用仍会保持不变。”已成为实际行为。围绕“在线检索与校验”与“稳定性、成本与安全”,哪些判断能定位被改变的职责或边界?多选
3评审“多模态 RAG:版面、图片、表格与文本的证据闭环”方案时,验收条件包含“运行源码与文章保存在同一个 Markdown 文件。”。关于“可运行实验:多模态 RAG:OCR、表格与图片引用”与“多模态 RAG 不是“把图片丢给视觉模型””的哪些决策符合正文机制?多选