☰
图表和截图搜不到:非文本内容怎么进检索链路
2026/10/14 9:52:43 网站建设 项目流程

说明:本文讨论的是把图表、截图、扫描页这类非文本内容接进检索链路的索引与切分层问题,属于 AI 前沿动态解读话题,不涉及具体模型版本与价格。AI 领域版本迭代极快,凡涉及版本号、价格、可用性,请以你阅读时的官方页面为准。文中代码为结构示意,请按自己的技术栈调整后再上生产。

一、非文本内容是怎么从检索里消失的

知识库里最值钱的那部分信息,往往不在正文段落里。季度分析里的毛利率走势、架构评审稿里的部署拓扑、扫描版合同里的付款条款,全都在图里。纯文本的抽取与切分链路处理不了这些内容,而且它丢东西的方式很隐蔽——检索不会报错,它只是安静地少返回几条。

丢法有三种,症状不同,修的位置也不在同一层。混在一起排查,最常见的后果是拿修「顺序乱」的方案去治「抽不到」,白改一遍。

抽不到:版面里本身就是像素

最常见的一类。扫描件、聊天截图、会议拍照,以及被报表工具导出成贴图的图表,抽取阶段拿到的字符序列里根本没有这些字。同一份 PDF 里可能同时存在两种丢法——正文有文字层,图表是贴图。

症状很好认:某个主题在原文里有一整页,检索结果里却一条都没有。这类内容在索引侧等于不存在,任何依托文本的召回都不可能命中。

顺序乱:能抽到文字,读出来是错的

第二类更隐蔽。文字确实在文件里,但版式是二维的:多栏排版、跨页表格、图注与图之间隔着半页正文。抽取器按默认阅读顺序把字符串成一段,拼出来的是乱的。

症状是召回片段读起来像打散的碎片——同一句话被拦腰截断,表格的行跑到列里,图注接到了别的段落尾巴上。用户看到的是一段语法说得通、语义已错位的文本,比完全召不回来更难发现。根在版面分析(layout analysis)与阅读顺序还原:先判出区块属于哪一栏、哪张表、哪个图注,再按区域内顺序拼。

语义散:抽得又对又全,语义已经不成形

第三类最难判断,因为抽取环节没有任何错误。表格被正确抽成一行行单元格,柱状图被正确抽成坐标轴标签与数字序列——每个字符都对,整体已经不是一个可检索的语义单元。

一张「各地区营收同比」的表,抽平之后是几十个数字与地区名的序列。用户问「华东的同比是多少」,向量检索拿到的是一堆离散数字,命中率取决于嵌入模型能不能从中认出结构,多数情况下认不出来。修复点在切分与描述层,不在抽取层。

丢法触发条件典型材料检索侧症状修在哪一层
抽不到版面内容本身是像素扫描件、截图、贴图图表该主题一条都召回不到抽取层或旁路索引
顺序乱文字在,但版式是二维的多栏排版、跨页表格召回片段语义错位版面分析与阅读顺序还原
语义散抽取正确但结构被拉平数据表、坐标图召回到一堆无语义碎片切分与结构化描述

三类里只有第一类必须动抽取链路,后两类在切分和描述侧就能救。

二、先问查询长什么样,再选做法

选做法之前先回答一个问题:用户会拿什么样的问句来查这批图。反过来,凭做法的先进程度倒推需求,几乎一定会选贵。

查询的四种形态

非文本内容的查询落到检索侧只有四种。

取数值——问「去年三季度毛利率是多少」,要的是图中那个数或某一行。找相似——手里有一张架构图,想知道库里有没有画法类似的,这要求图与图落在同一个可比空间里。问关系——问「接入流程的第二步是什么」,图周围文字与图注里往往已经写了步骤与顺序。找出处——问「哪份材料讲过灰度策略」,只需要定位到材料和页面,内容自己去看。

按查询形态选做法

四种形态对应的最少够用做法如下。「最少够用」的反面不是更保险——多给一层,是把成本和失效面一起放大了。

查询形态例子最少够用的做法为什么够
取数值去年三季度毛利率多少结构化描述要的是字段级的值与表头
找相似这张拓扑图还有类似的吗多模态嵌入需要图与图落在同一向量空间
问关系接入流程第二步是什么图注增强图周围文字已含步骤与顺序
找出处哪份材料讲过灰度策略图注增强只要能定位到材料即可

两种常见的选贵错法

上多模态嵌入最省事,是第一种。它把每张图的嵌入生成、查询侧嵌入、向量索引体积一起拉起来,而这三项里最贵的是维护:图一更新向量就得重算,重算的触发条件还得单独维护。如果「找相似」在实际查询里占比很低,这笔投入基本收不回来。

先全量跑一遍摘要,再想查询,是第二种。摘要按图生成,几万张图逐张调模型,是一次性大额开销;而摘要一生成就直接决定检索质量,跑完之前你无法知道查询能不能被满足。更稳的顺序是先圈定查询形态,再决定给多少张图生成摘要。

⚠️ 代码待验证

# 按查询形态决定走哪条检索路径,避免默认全上多模态QUERY_SHAPES={"value":{"path":"figure_text","need":["table_structured"]},"similar":{"path":"figure_vector","need":["image_embedding"]},"relation":{"path":"text","need":["caption"]},"locate":{"path":"text","need":["caption","doc_link"]},}defroute_query(shape:str,index_state:dict)->dict:plan=QUERY_SHAPES.get(shape)ifplanisNone:# 形态判不出来时不猜,退回文本路,交由合并层兜底return{"paths":["text"],"degraded":True}# 所需索引不存在时不能静默降级成空结果,要显式标记缺哪一项missing=[nforninplan["need"]ifnotindex_state.get(n)]return{"paths":[plan["path"]],"missing":missing,"degraded":bool(missing)}

missing这个字段是关键。索引还没建好时,检索层如果直接返回空,排查起来会误以为是召回质量差,实际是那一层压根没建。

三、图表进索引的四种做法

四种做法的效果、成本与失效面依次递增。它们不是四选一,而是两组:前两种解决「图里的信息能不能被文本检索到」,后两种解决「图本身能不能被直接检索」。

图注增强与结构化描述

图注增强(caption augmentation)用图周围的文字给图打标签:图号、图题、所在章节标题、正文里指向这张图的引用句。这些文字本来就在文档里,抽取成本近零,但覆盖面对写作习惯依赖大:图注写得清楚的收益大,只有「图 3」两个字的收益接近零。图注一改标签即失效,好在重生成很便宜。

结构化描述针对表格:把表格还原成表头加行的键值形式,让「华东」「同比」这类词和对应数值绑在一起,而不是一行行数字。这一步需要表格结构识别,跨页表和合并单元格最容易出错。

自然语言摘要与多模态嵌入

自然语言摘要让模型读图并写一段描述,把任意形态的图都转成文本,接进现成文本检索即可,不需要改检索层。代价是每张图一次模型调用,且这段文字会「替」图发言——描述偏了检索就跟着偏,偏在哪很难从结果里看出来。

多模态嵌入(multimodal embedding)把图与文映射到同一向量空间,支持图搜图与图文互搜。它能力最强也最重:每张图一次嵌入调用、查询侧也要嵌入、索引体积显著上涨、图一更新向量即失效,更适合文本描述不出来的「找相似」查询。

四种做法的成本与失效条件

做法生成成本查询侧成本更新失效面维护负担
图注增强复用已有文字,近零无额外图注、章节标题一改即失效低,可随时重跑
结构化描述一次性解析表格结构无额外表格结构变即失效中,跨页表易错
自然语言摘要每张图一次模型调用无额外图或内容更新即失效中高,描述偏差不可见
多模态嵌入每张图一次嵌入调用查询也要嵌入图一更新向量即失效高,需维护重算触发

看这张表不是比「谁更好」,而是比「谁贵在哪一层」:图注增强贵在覆盖率不稳定,结构化描述贵在解析准确率,摘要贵在调用量,多模态嵌入贵在维护——它把失效判定变成了一个必须持续运行的任务。

⚠️ 代码待验证

{"figure_id":"doc-8812-fig-03","doc_id":"doc-8812","page_no":12,"block_type":"table","caption_raw":"图 3 各地区营收与同比(2026 年三季度)","structured_rows":[{"region":"华东","revenue":1280,"yoy":0.14},{"region":"华南","revenue":940,"yoy":0.09}],"summary_text":"华东营收最高、增速也最快,华南次之。","descriptions_source":["caption","structured","summary"],"asset_uri":"s3://kb/doc-8812/p12-fig03.png","updated_at":"2026-10-14T09:20:00Z"}

descriptions_source记的是这条记录的文本从哪几层来。检索命中时能立刻判断是图注命中还是摘要命中,评估和排查都用得上。

四、切分与元数据

索引侧最容易做错的一步是切分单位。按「图」切,切出来的是没有语义的图片块;按「段落」切,图与其说明被拆到两个片段。

切分单位:图 + 图注 + 所在章节

先把非文本内容定义成一个可检索单元,组成是:图本体(或表格本体)+ 图注原文 + 所在章节路径 + 邻近引用句。四样拼成一个块,块内文本供文本检索,图本体供多模态检索。

章节路径要带层级,比如「第三章 > 3.2 成本结构 > 图 3-1」。检索时按它约束范围,命中后用它告诉用户图在文档里的位置。颗粒度上有个反直觉的点——一个块塞进整章正文,语义被稀释;只塞图注,信息太薄。通常做法是图注加所在小节标题,再加指向这张图的最近一句正文,这句往往是解释性的,命中价值最高。

元数据字段清单

元数据的用途只有三个:过滤、回链、判失效。少一个字段,就会有一个环节退化成人工。

字段类型用途缺失后果
doc_id字符串回链到原文召回后找不到出处
page_no整数定位到具体页用户要点开整篇翻找
block_type枚举按表格/图/扫描页过滤无法按类型收窄召回
caption文本图注原文,供文本路召回文本路召不回这张图
section_path文本所属章节层级无法按章节约束召回范围
figure_no字符串图号,用于相邻图去重同一张图重复入索引
asset_uri字符串指向图片本体召回了但渲染不出来
updated_at时间判断摘要与向量是否过期过期内容仍被召回

block_type要在抽取阶段就落下来,检索时再补需要重扫原文。asset_uri与正文分开存——正文索引条目多,把图片二进制塞进去会让索引体积失控。

⚠️ 代码待验证

# 一个非文本检索块,切分单位 = 图 + 图注 + 章节 + 邻近引用句block:id:"${doc_id}-${figure_no}"doc_id:"doc-8812"page_no:12block_type:table# 枚举:table / figure / scanned_pagecaption:"图 3 各地区营收与同比(2026 年三季度)"section_path:"第三章 > 3.2 成本结构"neighbor_sentence:"下表给出各地区营收与同比。"figure_no:"fig-03"asset_uri:"s3://kb/doc-8812/p12-fig03.png"text_for_index:"${caption} ${section_path} ${neighbor_sentence} ${structured_rows_text}"updated_at:"2026-10-14T09:20:00Z"# 失效判定:updated_at 晚于 summary_built_at 时,摘要与向量必须重算summary_built_at:"2026-10-10T00:00:00Z"embedding_built_at:"2026-10-10T00:00:00Z"

summary_built_at与embedding_built_at是两组独立的时间戳,因为摘要和向量的重建成本不同,触发条件也不该合并成一个。

相邻图的去重

一份材料里的图常常成组出现:总览图加局部放大图,或同一指标的多个时间切片。逐张处理会让同一主题生成多条高度相似的块,检索时一次占满结果位。

去重键有两层:doc_id + figure_no做精确去重,拦住同一张图被重复处理;以及内容相似度——同章节下、图注高度重叠的块,保留信息量最大的那张,其余降权而非删除,因为局部放大图可能在细节查询里更对题,删了就彻底召不回。

五、两路结果怎么合并

多数落地不是全走多模态,而是正文照旧按文本索引,非文本走旁路索引,检索时两路各自出结果再合并。合并这一步做不好,前面所有工作会被一次错误的排序抹掉。

分值口径统一

两路的分值量纲不一样:文本路可能是倒排检索的相关性分,非文本路可能是向量余弦相似度,直接相加或者直接比大小都是错的。

先在各路内部做归一(min-max 或分位数归一),压到统一的 0 到 1 区间,再各自乘权重求和。权重不能拍——它应该用一个两路都能覆盖的评估集标定,在这个集合上调整权重,看合并后的排序指标什么时候最好。

合并中的问题处理方式理由
两路分值量纲不同各路内部归一后再映射到统一区间相关性分与余弦值不可直接比
两路召回粒度不同按 doc_id + figure_no 去重同一张图可能被两路同时召回
某一路无结果标记缺失,不补零分零分会被当成强负样本压低排序
合并后排序加权求和,权重由评估集标定凭手感设权重必然偏

合并顺序与去重

顺序上有两种做法。先合并再截断:两路各取较多候选,合并去重后统一排序取前 N,质量更好,不会错过「文本路排名靠后但实际最相关」的条目。各路先截断再合并:每路只取前若干条,延迟更低。折中是分层——主路取足量,旁路只取少量高置信结果,其分数超过主路当前第 N 名时才插入。

去重必须在排序之前做,否则会留下「去重后有效结果不足 N 条」的空档。

哪一路作为兜底

两路都空是常态,尤其在新接入非文本索引的初期。这时不能返回空结果结束,要让主路放宽条件再召回一次。

反过来,即使非文本路的质量稳定高于文本路,也不建议直接以它为主。原因是可确认性:文本命中能把原文片段直接展示,图命中还得渲染,用户的确认成本更高。两路的分工是主路保覆盖、旁路保精度,而不是谁强谁上位。

⚠️ 代码待验证

defmerge_two_paths(text_hits:list,figure_hits:list,*,w_text:float,w_fig:float,top_n:int)->list:defnorm(hits):ifnothits:return[]lo=min(h["score"]forhinhits)hi=max(h["score"]forhinhits)span=(hi-lo)or1.0return[{"id":h["id"],"n":(h["score"]-lo)/span}forhinhits]merged={}# 各路在自己的范围内归一,权重才有多路可比的意义forhinnorm(text_hits):merged[h["id"]]=merged.get(h["id"],0.0)+w_text*h["n"]forhinnorm(figure_hits):# 同一张图被两路同时召回时分数相加,去重键为 idmerged[h["id"]]=merged.get(h["id"],0.0)+w_fig*h["n"]ranked=sorted(merged.items(),key=lambdakv:kv[1],reverse=True)[:top_n]# 两路都空时返回空列表,交给上层走"放宽文本路"的兜底分支return[{"id":i,"score":s}fori,sinranked]

norm是在每一路内部做的。如果对合并后的分数再归一,权重就失去意义了。

完整版资料清单:本文用到的两路合并对照表与实测记录都整理在里面了,扫码即可获取:

六、怎么评这类检索

非文本检索的评估不能沿用文本那套。文本检索可以拿命中率加人工判断,图表类不行——你无法判断一次提升到底是检索变准了,还是渲染变好了。

「问题 — 正确答案所在图」对照集

对照集每条记录三个字段:问题、答案所在的图(用doc_id + figure_no标识)、以及这个问题需不需要渲染图片才能回答。第三个字段最容易被漏掉:有些问题只要召回图旁边的文字就够了,有些必须真的看到图,混在一起算指标会把渲染问题误读成召回问题。

对照集要从真实检索日志里长出来,而不是凭工程师想象写。先把用户实际问过的问题、以及它们实际命中的图收集起来,人工标注「这张图是不是正确答案」,这样得到的对照集覆盖真实查询分布,也自带一批难例。

⚠️ 代码待验证

# 一条对照集记录(字段示例) question: "去年三季度华东的同比是多少" answer_ref: doc-8812 + fig-03 need_render: true # 必须看到图里的数值,只靠图注答不了 difficulty: medium label_source: "人工标注,取自 2026 年 9 月检索日志"

区分「没召回」与「召回了但没渲染」

这两类失败在用户侧看起来一样,修的位置却不同。判断方法是在检索层与展示层之间加一个埋点:检索返回的候选里有没有目标图。有,就是渲染或展示的问题(asset_uri失效、图片权限、前端不展示图块);没有,才是召回问题(切分、描述、嵌入)。

指标要分开算:召回率(目标图出现在候选里的比例)与可用率(召回后用户真的能看到内容的比例)。把两者乘起来当成一个「命中率」,出了问题就无法归因。

更新后的重评节奏

非文本索引的失效是持续发生的:材料换版本、图表重排版、章节结构调整,都会让摘要与向量对不上。可行的节奏是固定两件事——材料每次更新触发一次只针对它的定向重评,每个固定周期跑一次全量对照集。前者抓「更新引入的错」,后者抓「积累的漂移」。

七、落地顺序与观测

非文本检索涉及的层多,任何一层有问题都表现为「召回不好」,一起上会分不清是谁的锅。

先挑一类材料试点

试点材料的选择标准有两条:查询量够大、结构够规整。表格类通常同时满足——它是非文本内容里结构最规整的一种,版面解析的准确率比自由图形高,业务对表格数据的查询需求也最密集。

从表格切进去还有个好处是评估最好做:表格有明确的表头与行列,对照集里的「答案」可以精确到某个单元格,判定没有歧义。跑通这条链再往图形类扩展,改动的只是描述层和嵌入层。

要看的四组数

指标口径上升说明什么
图表召回率对照集里答案图被召回的比例检索在变好
索引体积非文本索引的条目数与存储量切分太细或相邻图重复入索引
摘要生成成本单位材料触发的模型调用次数更新过于频繁或重算条件过宽
更新后失效比例材料更新后摘要/向量未同步的比例更新管道没有接上

四组数要一起看。召回率上升但索引体积翻倍,说明提升是靠「多切多召回」换来的,长期会拖垮维护;摘要成本上升而召回率不动,说明重算被无谓触发了。

从试点到常态化的判据

扩到全量之前,先确认三件事都成立:抽取侧的block_type判定准确率稳定(否则后面的过滤与评估都建立在错误分类上)、更新管道能在材料变更后自动触发重算(否则失效比例会持续积累)、两路合并权重已在评估集上标定(否则旁路加进来只会让排序变差)。三件里任何一件没落地,扩量只会放大一个无法归因的问题。

⚠️ 代码待验证

# 试点脚本:只对表格类块跑一遍抽取与结构化,先看 block_type 判定准确率# 具体命令按自己的抽取管道调整python run_extract.py\--in./pilot_docs\--block-types table\--emit-block-type\--out./index/pilot_figures.jsonl# 统计 block_type 判定分布,误判率高就不要往下一步走jq-r'.block_type'./index/pilot_figures.jsonl|sort|uniq-c

完整版资料清单:本文用到的试点材料清单与观测指标定义都整理在里面了,扫码即可获取:

附表 A:关键取舍一览

本文涉及的所有工程判断集中在这里,方便按需回看。

取舍本文结论判断依据位置
先判断丢的是哪一类分抽不到、顺序乱、语义散三类三者修复层不同第一章
抽取层要不要动只有「抽不到」必须动抽取后两类在切分与描述侧即可救第一章
做法怎么选先定查询形态,再取最少够用的一层凭先进程度选会选贵第二章
多模态嵌入要不要上只在「找相似」占比高时上它的成本主要在维护重算第二章
摘要要不要全量跑先圈查询,再决定给多少张图生成一次性开销大且质量不可预知第三章
切分单位图 + 图注 + 章节路径 + 邻近引用句单切图无语义,单切段语义被稀释第四章
章节路径必须带层级,不能只留页码支持按章节约束召回与定位第四章
block_type 何时定抽取阶段就落枚举检索时再补要重扫原文第四章
相邻图怎么处理精确去重 + 相似图降权不删局部图在细节查询里可能更对题第四章
两路分值怎么比各自路内归一再加权求和不同量纲的相关性分不可直接比第五章
合并权重怎么定用两路都覆盖的评估集标定凭手感设权重必然偏第五章
某一路为空怎么办标记缺失,不补零分零分会被当成强负样本第五章
两路分工主路保覆盖,旁路保精度文本命中比图命中更易确认第五章
评估怎么建从真实检索日志长出对照集想象构造覆盖不到真实分布第六章
失败怎么归因召回率与可用率分开算合算会混淆召回与渲染问题第六章
重评节奏定向重评跟更新,全量重评按周期前者抓新错,后者抓漂移第六章
试点选什么材料表格类,查询量大且结构规整评估可精确到单元格第七章
什么时候能扩量block_type 稳、更新能自动触发、权重已标定三缺一都会得到不可归因的低召回第七章

附表 B:术语速查表

术语含义
非文本内容以图像形态存在、正文段落里没有对应字符的内容
版面分析判断页面区块所属栏、表、图注的分析步骤
阅读顺序还原按二维版式把字符重新拼成正确语序
图注增强用图周围文字(图号、图题、章节、引用句)给图打标签
结构化描述把表格还原成表头加行的键值形式,保留字段语义
自然语言摘要由模型读图并写出描述性文本,接进文本检索
多模态嵌入把图与文映射到同一向量空间,支持图文互搜
可检索单元图本体加图注、章节、引用句组成的索引基本块
章节路径带层级的所属章节标识,用于约束召回与定位
相邻图去重对成组出现的图做精确去重并对相似图降权
分值归一把各路检索分数压到统一区间再合并的做法
兜底路两路都空时放宽条件重新召回的那一条路
对照集问题、答案所在图、是否需渲染组成的评估集合
召回率目标图出现在候选结果里的比例
可用率召回后用户真的能看到内容的比例
失效比例材料更新后摘要或向量未同步的比例

写在最后:这篇用到的资料

写这篇文章时,我把几个模型侧与检索侧的文档、字段表和实测记录都对了一遍,顺手整理成几份配套的东西:

  • 大模型学习路线图:从 LLM 基础到 Agent 开发,各阶段该学什么、用什么资料
  • 大模型全套教程:按主题分好的视频与文档清单
  • 大模型实战好书:24 本,附每本适合的阶段

资料是我自己整理的,放在下面这个码上,扫码即可获取:

添加时备注「大模型」,优先通过。

拿到之后建议先看学习路线图那一份,先定位自己在哪个阶段,再决定学什么,比一上来就啃框架效率高得多。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询