图片问答答非所问,可能是模型根本没看清
小字、表格和长截图答错,常常不是模型能力不够,而是目标没有以合适分辨率进入上下文。用全图定位、局部切片、OCR 和证据合并,视觉问答才稳。
本篇阅读顺序 · 三遍读法
一篇文章,读出三种能力。
先弄懂它为什么这样工作,再把条件换一换,看方案还能不能站住,最后用代码和证据复盘一遍。顺序固定,读完才知道自己是真的会了,还是只记住了名词。
沿着直觉、公式和边界读正文,看到变量就问输入、状态、复杂度分别是什么。
把面试官的追问当作小型设计评审:更大流量、更少上下文、外部失败或安全约束出现时,局部如何重算。
沿代码、表格和证据卡复盘,最后用文末的 60 秒回答确认自己没有只记住名词。
用户上传一张 3000 像素宽的合同截图,问“第二页违约金是多少”。模型回答“未约定”,换更大的模型也没用。打开视觉输入日志才发现,整张图被压成 768 像素,违约金那一行只剩几根模糊横线。
先给一个能复述的答案
视觉问答的第一步不是换模型,而是确认目标区域有没有以足够分辨率进入上下文。长图和文档通常采用“低分辨率全图定位 + 高分辨率局部切片 + OCR/版面信息补充”的两阶段策略。切片要记录原图坐标、缩放比例和来源页码,重叠区域要去重,多个切片的结果要按证据位置合并。最终评测不能只看答案对不对,还要看目标区域召回率、坐标误差、数字读取准确率和切片带来的延迟。
图 1:切片是一次有坐标的检索,不是把图片随便裁成几块。
一、先判断:模型没看到,还是看到了但理解错
三个问题把原因分开
- 原图中目标文字是否清晰可读?
- 发送给模型的视图是否包含目标区域,分辨率是否足够?
- 模型看到了目标后,是否能把文字与问题对应起来?
可以在日志里保存一张简单的对照图:原图、模型实际收到的缩略图,以及每个 crop 的原图框。如果原图放大后看得清、模型视图却看不清,问题就在视觉输入链路,不该直接归因于模型推理能力。
| 症状 | 更可能的原因 | 首先检查 |
|---|---|---|
| 所有小字都丢失 | 缩放过度或 patch 太大 | 实际输入尺寸、目标像素高度 |
| 只漏掉页面边缘 | 采样或裁剪边界错误 | crop 坐标、padding |
| 数字读对但列对应错 | 版面关系丢失 | 表格线、OCR box、阅读顺序 |
| 同一内容回答两次 | 重叠切片未去重 | chunk_id、坐标 IoU |
二、为什么统一缩放会伤害长图
假设视觉编码器要求最长边不超过 S,原图宽高为 W × H,统一缩放比例是:
原图中的文字高度 h 会变成 r·h。当 r·h 小于模型视觉 patch 的有效像素范围时,字符笔画会被平均掉。长网页截图通常同时包含标题、正文、小字和表格,用一个比例兼顾所有区域是不现实的。
用目标像素高度做切片决策
比“每块 1024 像素”更有用的指标是目标文字在输入中的像素高度。可以先用 OCR 或版面检测得到候选框,再反推 crop 需要放大到多大:
def choose_scale(box_height, target_height=28, max_scale=4):
scale = target_height / max(box_height, 1)
return min(max(scale, 1.0), max_scale)
目标数字只有 8 像素高时,至少要通过 crop 让它接近 24~32 像素,再交给视觉模型或 OCR。
三、一个可落地的两阶段切片流程
阶段 1:全图只做定位
低分辨率全图的任务很简单:判断目标大概在哪一页、哪一列、哪个区域,不要求它直接读出最终数字。模型可以返回粗粒度框:
{
"regions": [
{"label": "违约金条款", "page": 2, "box": [0.12, 0.62, 0.91, 0.78], "confidence": 0.74}
]
}
坐标建议归一化到 [0, 1],这样缩放、压缩和不同设备的像素尺寸不会改变语义。
阶段 2:按候选框扩边并切片
切片不能只截出一行文字,要保留标题、表头或相邻上下文。常见做法是按比例扩边,再限制到页面边界:
def expand(box, margin=0.12):
x1, y1, x2, y2 = box
w, h = x2 - x1, y2 - y1
return [
max(0, x1 - margin * w),
max(0, y1 - margin * h),
min(1, x2 + margin * w),
min(1, y2 + margin * h),
]
每个 crop 要带 parent_image_id、page、box、scale 和 purpose,否则之后无法解释“模型为什么看到了这块”。
图 2:输出的不是孤立图片,而是带坐标和父图引用的证据对象。
四、OCR 和视觉模型应该怎么分工
OCR 擅长稳定地读字、给出坐标和置信度;视觉语言模型擅长解释图片、关联跨区域信息和回答自然语言问题。让它们各做擅长的部分,通常比让一个模型包办一切更稳。
| 任务 | OCR | 视觉语言模型 |
|---|---|---|
| 读取细小数字 | 强 | 视分辨率而定 |
| 判断两个区域关系 | 弱 | 强 |
| 识别表格行列 | 需版面后处理 | 可理解但易漏列 |
| 生成解释 | 不负责 | 强 |
| 输出坐标 | 原生支持 | 需要约束或后处理 |
一个稳妥的证据包可以同时保存两种结果:OCR 的词级框,以及视觉模型对区域关系的解释。生成阶段只允许引用证据包中的文字和坐标。
五、重叠切片为什么会带来重复和冲突
相邻 crop 为了不切断句子,通常会有重叠。如果两个切片都读出了同一段文本,需要按原图坐标合并。对文字框可以用 IoU 去重,对表格行可以用页码、行号和文本相似度联合判断。
candidate A: page=2, box=[.10,.62,.55,.75], text="违约金为..."
candidate B: page=2, box=[.48,.62,.92,.75], text="...合同金额的 10%"
merge: page=2, line=18, text="违约金为合同金额的 10%"
如果两个切片对同一个数字给出不同结果,不要让生成模型投票;应该保留原图框,重新以更高分辨率读取,并把冲突标记为 needs_review。
六、动态预算比固定切片更稳
切片数量、放大倍数和上下文 token 应该由问题风险共同决定。查一个普通标题不必切十块,但读取金额、合同期限或药品剂量时,宁可多花一次高分辨率调用,也不能用低置信结果自动写入业务系统。
budget = base_tokens
+ risk_level * evidence_tokens
+ ambiguity * retry_tokens
- cache_hit * reusable_tokens
可以按四档路由:
| 风险 | 路由 | 结果边界 |
|---|---|---|
| 低 | 全图 + OCR | 只回答可见文字,不做复杂推断 |
| 中 | 全图定位 + 1~2 个 crop | 必须带坐标和置信度 |
| 高 | 多 crop + OCR 交叉核验 | 冲突自动转人工 |
| 极高 | 结构化数据源 / 人工复核 | 视觉结果不能直接触发写操作 |
切片结果可按 parent_image_id + box + source_hash + parser_version 缓存。缓存命中后仍要重新检查租户和用途,不能因为图像相同就把一个用户的敏感 crop 复用给另一个用户。
七、把切片当成安全边界
图像里也可能藏着提示词注入、二维码和敏感信息。切片前做内容类型标记,生成阶段把“图片中出现的指令”当作不可信数据;OCR 文字、视觉模型描述和用户问题要分开传递。对身份证、合同金额等敏感区域,只保存必要的坐标和哈希,原图按权限短期保留。
user_question ─┐
ocr_evidence ──┼─> policy gate -> answer / refuse / human
vision_note ───┘ (untrusted)
审计日志记录谁请求了哪一页、切了哪些框、调用了哪个模型和为什么升级分辨率。这样出现泄露或错误引用时,能定位是采样、OCR、模型还是策略层的问题。
八、评测要看四层,不只看最终答案
| 层级 | 指标 | 说明 |
|---|---|---|
| 定位 | region recall | 目标区域是否进入 crop |
| 读取 | OCR/数字准确率 | 小字、金额、日期是否读对 |
| 关联 | grounding accuracy | 答案是否引用了正确区域 |
| 系统 | P95 延迟、成本 | 两阶段策略是否可上线 |
特别要加入“目标在边缘”“跨页表格”“多个相似数字”“低对比度扫描件”等对抗样本,否则平均准确率会掩盖真正的线上问题。
九、错误要能沿坐标和版本回放
一次视觉问答失败,至少要能回答四个问题:原图是哪一版、模型实际看到了哪些 crop、OCR 写出了什么、最后一个数字来自哪条证据。建议把回放记录固定成下面的结构:
{
"image_hash": "sha256:…",
"views": [{"box": [0.12, 0.62, 0.91, 0.78], "scale": 3.2, "model": "vision-v2"}],
"ocr": [{"text": "合同金额的 10%", "box": [0.18, 0.68, 0.66, 0.72], "confidence": 0.97}],
"answer": {"value": "10%", "evidence_view": 0, "status": "grounded"}
}
回归时固定图片哈希和问题,只替换切片器或模型版本。若答案变了,先看目标框是否漂移,再看 OCR 置信度和证据合并;不要直接把差异归因于模型随机性。对高风险数字可以设“两个独立读取一致才自动回答”的门槛。
十、切片器也要有停止条件
动态放大不能无限重试。可以规定:最多两轮重切,累计 crop 不超过 6 个,置信度仍低于阈值就转人工或请求用户提供更清晰图片。停止条件应写在任务合同里,否则 Agent 会用越来越多 token 掩盖输入质量问题。
if attempts >= 2 or crop_count >= 6:
return escalate(reason="visual_evidence_insufficient")
面试官的三层追问
L1:长图为什么要切片?
统一缩放会让小字低于视觉编码器的有效分辨率。切片可以让目标区域以更高分辨率进入模型,同时保留原图坐标。
L2:切片大小怎么定?
根据目标文字像素高度和上下文需求动态决定,而不是固定一个宽高。先定位,再按目标区域扩边并限制 token 预算。
L3:多个切片结果冲突怎么办?
按页码和坐标合并,无法判定时重新 crop 或交给 OCR/人工复核;不要让模型对两个无坐标的字符串自由投票。
L1:为什么不能所有图片都直接高分辨率?
成本、延迟和 token 会快速增长;先全图定位,再只放大目标区域,可以把资源用在真正影响答案的地方。
L2:如何决定目标区域需要多大上下文?
看问题类型和结构依赖:数字要带单位,表格单元格要带表头,条款要带编号和条件;按依赖扩边,而不是固定像素边距。
L2:缓存 crop 会不会泄露隐私?
缓存键加入租户和权限上下文,原图和 crop 设置过期时间,命中前再次做策略校验,并记录访问审计。
L3:二维码或图片文字包含指令怎么办?
把 OCR 和视觉描述都视为不可信证据,不能改变工具权限和系统策略;涉及执行的内容必须经过独立的策略层确认。
L3:切片策略升级后如何回归?
固定源图哈希和目标框,比较 region recall、坐标误差、数字准确率、P95 与成本;差异超阈值才进入人工复核。
L4:同一个目标在两个 crop 中读出不同数字怎么办?
先按原图坐标确认是否真的是同一位置,再提升分辨率或调用 OCR 交叉读取;若仍冲突,返回待确认并保留两条证据,不能让语言模型自由投票。
L5:什么时候应该放弃视觉链路?
当目标区域持续不可读、图片来源不可信或答案会触发高风险写操作时,应请求原始文档、转结构化接口或人工复核。停止自动重试本身也是可靠性设计。
L5:定位框很准,为什么答案仍可能是错的?
因为定位只证明“看到了这块”,不证明读懂了关系。表格数字还需要表头和单位,条款金额还需要条件和适用范围,跨页内容还需要前后文。验收时要分别测 region recall、文字读取、关系 grounding 和最终结论;任何一层不通过,都应返回缺口而不是把坐标当成正确性的替代品。
切片预算也要返回一张“定位—读取”回执
动态切片不是越多越好。每次升级分辨率或扩大上下文,都要留下为什么升级、得到什么证据、还缺什么的回执,方便把成本和准确率放在一起比较:
{
"slice_receipt": "sr_20260820_18",
"image_hash": "sha256:...",
"question_type": "contract_amount",
"attempts": [
{"stage": "locate", "box": [0.12, 0.62, 0.91, 0.78], "confidence": 0.83},
{"stage": "read", "scale": 3.2, "ocr_confidence": 0.97, "value": "10%"}
],
"budget": {"crop_count": 2, "vision_tokens": 1840, "latency_ms": 620},
"grounding": {"source_view": 1, "unit_present": true, "status": "grounded"},
"next": "answer"
}
attempts 把定位和读取拆开,能看出是框错了还是字没读清;budget 让“多看一眼”有成本账;grounding 只有坐标、单位和上下文都满足时才允许 answer。如果下一轮切片没有提高置信度,就应转人工或请求原图,而不是继续叠加 crop。
L5:如何证明动态切片真的比固定切片好?
固定图片哈希、问题集和模型,只比较切片策略;同时报告 region recall、数字准确率、grounding、P95 和单位成功成本。若准确率只涨一点却成本翻倍,就不应默认动态策略。
给每个视觉答案发一张“定位凭证”
视觉答案最容易被追问的一句是:“你说的这个数字,究竟来自图片哪里?”因此我会把最终答案和定位凭证一起交付,而不是只保存一段自然语言:
{
"answer": "违约金为合同金额的 10%",
"status": "grounded",
"citations": [
{
"page": 2,
"box": [0.18, 0.68, 0.66, 0.72],
"crop_id": "img-42-crop-03",
"ocr_text": "违约金为合同金额的10%",
"ocr_confidence": 0.97
}
],
"conflicts": [],
"parser_version": "layout-v5"
}
定位凭证有三个作用:前端可以让用户点回原图;回归测试可以比较同一框的答案是否漂移;策略层可以要求高风险数字必须有两个独立读取结果。若只能给出文字、没有坐标或来源哈希,答案应降级为 unverified,不能直接触发写操作。
crop 缓存命中后还要核对“父图版本”
视觉链路经常缓存 crop、OCR 和布局解析结果来省成本,但原图重新上传、旋转校正或脱敏后,旧 crop 的坐标就可能指向另一块内容。命中缓存时,我会先比对父图哈希、预处理版本和坐标系,再决定是否复用;只要其中一项不一致,就重新切片,并把旧结果标成不可引用。
{
"cache_version_receipt": "cvr_20260820_38",
"parent_sha256": "sha256:7a91...",
"cached_parent_sha256": "sha256:7a91...",
"preprocess_version": "deskew-v3",
"cached_preprocess_version": "deskew-v2",
"coordinate_space": "normalized-page",
"decision": "invalidate_and_reslice",
"reason": "preprocess_version_mismatch"
}
这张回执把“缓存命中”从性能事件变成证据事件。即使 OCR 文字看起来一样,也要确认页码、旋转角度和脱敏遮罩没有变化;否则引用坐标与原图对不上,最终答案就应该降级为 unverified。对高风险文档,缓存复用还要经过权限和保留期限检查。
L5:为什么 OCR 文字没变,也不能直接复用旧 crop?
文字相同不代表坐标仍然有效。旋转、裁边、遮罩和版面解析版本都可能改变定位框;视觉证据必须绑定父图版本,而不是只绑定一段 OCR 文本。
切片策略要支持“拒答”和人工回看
切片不是越多越好。遇到低清扫描、透视严重、关键数字落在裁剪边缘时,系统应该能明确说“当前证据不足”,而不是继续放大并生成一个看似完整的答案。可以给每个目标设三档出口:自动通过、需要二次 crop、转人工复核。
slice_decision:
target: invoice.total_amount
evidence:
ocr_confidence: 0.61
pixel_height: 8
edge_distance: 3
crop_conflict: true
route: human_review
reason: "数字过小且两张重叠 crop 读数不一致"
retained:
parent_hash: sha256:9b3...
crop_boxes: [[0.71, 0.42, 0.98, 0.55], [0.68, 0.40, 0.99, 0.57]]
model_views: 2
这张回执把“没读出来”和“读出了错误数字”区分开:前者可以通过更好的 crop 或人工处理,后者要进入事故样本和回归集。对财务金额、药品剂量、证件号码这类字段,宁可提高人工复核率,也不要用语言模型的常识补齐缺失字符。
L5:如何设自动通过阈值?
不要只看 OCR confidence。至少同时看目标文字像素高度、框距边缘的距离、重叠 crop 是否一致,以及证据区域是否覆盖了字段标签。阈值应该在按字段分层的 golden set 上校准,并把“拒答率”和“错答率”一起报告;只压低拒答率,通常会换来更隐蔽的错答。
Crop 缓存要绑定坐标变换链,而不是只缓存 OCR 文本
同一张图经过旋转、去黑边、脱敏遮罩或缩放后,旧 crop 的坐标就可能不再指向同一块证据。缓存条目要记录父图 hash、预处理 pipeline 版本、坐标原点、旋转角度和 bbox 变换矩阵;命中时先做几何回投影,再判断 OCR、区域和引用是否仍然一致。任何一步不匹配,都应该返回 cache miss 并重新切片。
对重叠 crop,缓存还要保留目标字段的 evidence_role,区分“定位用缩略图”和“最终引用用高清 crop”。这样即便定位模型换版,也不会把低清预览误当作可引用证据。高风险字段的缓存生命周期应短于文档权限和保留期限,避免旧图片长期留在可检索层。
crop_cache_receipt: ccr_20260820_52
parent_hash: sha256:9b3...
preprocess: [deskew-v2, redact-v1, resize-2x]
coordinate_frame: normalized_after_deskew
transform_hash: sha256:77a...
boxes:
- {role: locator, bbox: [0.10,0.22,0.88,0.44], quality: preview}
- {role: citation, bbox: [0.41,0.31,0.72,0.39], quality: hd}
gates:
parent_hash_match: true
transform_roundtrip: pass
acl_epoch_current: true
decision: reusable_for_citation
L5:为什么 OCR 结果相同仍要重新做坐标回投影?
文字相同只能说明识别结果相同,不能证明它仍来自正确字段。旋转、裁边和遮罩都可能让 bbox 偏移;回投影能验证引用区域是否还落在原始证据上,失败就应重新切片或转人工。
60 秒面试回答
图片问答答错时,我先确认模型实际看到的视图,而不是马上换模型。长图通常采用两阶段流程:低分辨率全图只负责定位目标区域,再根据目标文字的像素高度做局部高分辨率 crop,同时用 OCR 提供文字和坐标。每个切片都记录父图、页码、归一化坐标和缩放比例,重叠结果按坐标去重。最终评测会拆成区域召回、数字读取、证据定位和系统延迟四层;如果两个切片对同一数字冲突,就重新读取或人工复核,而不是让生成模型凭概率选一个。
带走一张检查清单
- 是否记录了模型实际接收到的图片,而不只是原图 URL?
- 是否用目标文字像素高度决定放大比例?
- 每个 crop 是否带原图坐标、页码和用途?
- 重叠区域是否有可解释的去重规则?
- 是否覆盖边缘、跨页、相似数字和低清扫描件?
相关笔记
参考
- AgentAlpha《Agent 岗面试宝典 v3》:多模态章节
- ARIS-in-AI-Offer