
LLM 能读几万字上下文但有一个前置问题经常被忽略大量文档根本复制不了。扫描版 PDF 里的内容是图片网页里的文字被禁选合同、银行流水、硬件文档、纸质笔记在屏幕上显示得清清楚楚却没法直接变成文本喂给模型。这类场景必须用 OCR。OCR It 这类方案要解决的就是把不可复制文档抽取成干净文本再交给 LLM 做总结、问答、提取结构化信息。如果你手里有一堆扫描件、截图或图片想让大模型直接分析里面的内容这篇文章会把完整链路拆开环境准备、单任务怎么跑、批量任务怎么处理、参数怎么调、出问题先查哪里。1. 为什么说“复制不了”是 LLM 场景里最容易被低估的卡点很多人觉得 LLM 都这么强了直接把 PDF 丢进去不就行了。实际上普通大模型读取的是文本 token不是屏幕像素。扫描版 PDF 没有文本层图片里的内容也不是天然可读的文本。这个卡点不解决后面的 Prompt 优化、RAG 检索、Agent 工具调用都接不上。1.1 不是所有文档都能直接复制截图、扫描件、加密 PDF 都会中断工作流我在实际整理资料时经常碰到这几类“复制不动”的情况。第一类是扫描版 PDF。很多书、论文、老资料都是扫描件整个文件就是一页页图片。鼠标选中时根本没有文字复制出来是空白。第二类是网页和知识库。部分站点禁右键、禁选中或者为了让排版好看把正文做进图片里。第三类是加密或限制编辑的 PDF。文件能打开、能看但复制功能被锁掉。第四类是截图和手机拍照纸质文档、单据、App 页面拍下来就是图片。这些文件如果直接交给 LLM多模态模型虽然能读图但长文档里几十页图片很难全部塞进上下文。即便能塞成本会很高识别质量也未必稳定。更常规的做法是先用 OCR 把文字抽取出来再交给模型处理。OCR 在这条链路里的角色相当于一个“文本还原层”。1.2 OCR 不是把图片变成文字这么简单还要考虑喂给 LLM 的格式质量不少人以为 OCR 跑完拿到一段字符串就算结束。真实场景里喂给 LLM 的文本需要的不是一串字符而是可读、有序、尽可能还原版面的内容。我一般会检查三件事。第一顺序不能乱。扫描 PDF 是逐页识别的如果页序错乱模型读到的内容就是跳跃的。第二标题和段落不要全粘在一起。LLM 可以处理连续文本但完全没有结构的文本会让总结效果打折扣。第三中英文混排时不能错乱。中文识别和英文识别经常需要不同语言包如果混排处理不好输出里会出现大量空格和乱码。如果你后续还要做 RAG 或知识库OCR 结果甚至需要保留位置信息和置信度。比如某个表格单元格在哪一页、哪个坐标这些元信息可以帮助你定位原文。OCR It 这类工具的价值不只是识别文字而是把“图片 → 可读文本 → 模型输入”这条链路整理清楚。2. 本地 OCR 方案怎么选先明确任务类型再选工具和依赖OCR 工具很多网上也经常看到“最强、最好用”的说法。我不建议直接抄别人的推荐而是先想清楚自己的任务类型再决定用什么引擎。2.1 常见 OCR 引擎和它们的适用边界这里给一个通用对比具体参数和能力要以你安装的版本为准。引擎优势适合场景注意点Tesseract老牌、离线、跨平台生态成熟印刷体英文、清晰扫描件中文识别需要额外语言包复杂版面一般PaddleOCR中文效果好自带方向分类和版面分析中文扫描件、拍照图、表格依赖较多安装比 Tesseract 重EasyOCR上手简单支持多语种快速验证、多语种混合模型体积大CPU 速度偏慢云 OCR接口稳定版面还原好不想折腾本地环境和批量调度需要联网按调用量计费截图型 OCR 工具快捷方便适合桌面取词单张截图、碎片化取字批量自动化能力有限如果只是偶尔从截图里提取一段文字用 Tesseract 或常见截图 OCR 工具就够了。如果经常处理中文扫描件PaddleOCR 这类带版面分析的方案会更合适。如果不想维护本地环境可以考虑云接口但要注意数据隐私和调用成本。2.2 环境准备Python、Tesseract/PaddleOCR、依赖安装的通用顺序环境准备不复杂但顺序不对容易浪费很多时间。我建议按这个顺序来。先确认系统里有没有 Python执行python --version和pip --version看版本是否匹配。很多 OCR 库对 Python 版本有要求版本太老容易装不上依赖。然后安装 OCR 引擎本身。Tesseract 在 Windows 上需要单独下载安装包Linux 上可以用系统包管理器安装安装之后顺手把语言包也装上中英文场景至少需要中文和英文语言包。接着安装 Python 封装库比如pytesseract、paddleocr、easyocr这类。最后做一个最小验证拿一张清晰的印刷体图片跑一次识别看能不能输出正常文本。python -c import pytesseract; print(pytesseract.get_tesseract_version())如果这一步报错先别急着调识别参数。大概率是引擎没装好或者 Python 封装库找不到系统里的 Tesseract 路径。2.3 低配置机器能不能跑主要看模型体积和输入图片大小热搜里经常有人问 OCR 能不能在 RK3588 这类 ARM 板子上跑。答案是能跑但不要按电脑配置去预期。OCR 资源占用有两个关键变量模型体积和输入图片大小。Tesseract 这种传统 OCR 对硬件要求很低CPU 就能跑内存占用也不大。PaddleOCR 或 EasyOCR 这类深度学习方案需要加载神经网络模型CPU 能跑但速度比 GPU 慢尤其是大图和高分辨率扫描件。如果是 ARM 板子部署时要注意 Python 版本、系统依赖、onnxruntime 或 paddle 的 ARM 支持情况。有些依赖在 ARM 上没有预编译包需要源码编译编译时间会比较长。低配置机器不是不能用而是要把输入图片的分辨率和批量数降下来。我的建议是先用单张小图验证环境能跑通再考虑批量任务。3. 用 OCR It 的方式跑通最小链路从图片文件到可读文本真正进入实操不要一上来就做复杂流程。我建议先用一张图片跑通最小链路再逐步扩展。3.1 单张图片识别先确认输出内容完整、编码正确拿一张清晰的印刷体截图先做单张识别。用 Tesseract 的话核心命令很简单from PIL import Image import pytesseract image_path sample.png text pytesseract.image_to_string(Image.open(image_path), langeng) print(text)如果是中文把lang参数改成chi_simeng表示同时使用简体中文和英文识别。第一次跑完先不要看准确率先看三件事。第一控制台有没有报错。第二输出文本里有没有大量乱码。第三文本顺序是否正常。如果图片文字很多但输出只有几个字符先检查图片路径、语言包和图片分辨率。这里最容易踩的坑是语言包没装。英文文本用eng没问题中文文本如果只指定eng输出会非常奇怪。还有一点图片越大识别越慢但不代表越清晰。如果图片本身模糊放大图片反而会让识别效果更差。3.2 保留版式和元信息Markdown 输出、置信度筛选和位置信息单张识别跑通之后考虑输出格式。普通文本适合直接看但喂给 LLM 时我更喜欢带基础 Markdown 结构的输出因为模型能更清楚地区分标题、列表和正文。如果你用 PaddleOCR识别结果里通常会包含文本框坐标和置信度。这些信息很有用。比如某一行文字置信度很低说明可能是脏污、模糊或特殊字体你可以选择去掉或标出来。不同 OCR 引擎的接口不一样但通用思路是识别每行文字 → 把坐标相近的行合并成段落 → 根据字号和位置判断标题层级 → 输出成 Markdown。这个过程不需要一开始就做得很完整简单保留标题和段落就够了。过度结构化反而可能引入错误判断。还有一类需求是保留位置信息。如果你要把 OCR 结果用于 RAG 检索最好在输出 JSON 里保留页码和坐标{ page: 3, text: 服务器配置参数, box: [120, 45, 240, 60], confidence: 0.98 }这样后续定位引用的时候可以回溯到原文位置。3.3 把识别文本交付给 LLM提示词里的上下文组织方式OCR 结果拿到后不是直接把大段文本塞进 Prompt 就完事。要让 LLM 理解这段文本的来源和任务需要做简单的上下文包装。我一般会在 Prompt 里说明文本来源和当前目标以下是某份扫描版文档经过 OCR 识别后的文本可能存在少量识别错误。请根据文本回答下面的问题遇到明显 OCR 错误的地方可以忽略或标注。 文档内容 {ocr_text} 问题这份合同里的付款期限是什么这样做的原因是OCR 文本有时会有少量错字如果模型不知道文本来自 OCR可能会把识别错误当成原文信息导致后续判断出错。提前说明模型会更有针对性地处理。如果文本很长还需要分段。比如把文档拆成 1000 到 2000 字的小块让模型分块总结再汇总。注意分块时要尽量保持语义完整不要把一个句子或一个表格从中间切断。4. 批量文档、长文档和 API 场景不能只用“识别单图”的思路单张图片能识别不代表批量任务能稳定跑完。批量里最常见的不是“能不能识别”而是“任务跑到一半卡住不知道哪张图失败了”。4.1 批量处理的三个关键点输入列表、输出命名、失败重试批量处理要单独考虑三件事输入文件列表、输出命名规则、失败重试机制。先列输入文件列表。不要直接在循环里os.listdir()读完所有文件后马上处理而是先打印一下文件数量和文件名确认有没有混杂非图片文件。有些文件名里带特殊字符比如空格、括号、中文在命令行或脚本里容易出问题。输出命名规则要能对应原始文件。我常用的规则是保留原名加后缀。比如scan_001.pdf输出为scan_001.txt或者放到ocr_output目录下。不要用无意义的递增编号否则失败重试时很难定位是哪张图。失败重试要分两类。第一类是脚本崩溃比如某张图片格式损坏导致整个程序退出。第二类是单张识别结果为空或置信度极低程序没报错但输出文件是空白的。空文件比报错更坑因为它不会打断流程但会造成下游数据缺失。我建议每次识别完检查输出文本长度为空时记录到日志里。4.2 长 PDF 先分页再识别注意内存和顺序长 PDF 不能直接当一张大图处理。内存和模型输入尺寸都有限制更合理的方式是分页。先提取页码信息再逐页转成图片。关键参数是 DPI也就是渲染分辨率。一般扫描件用 300 DPI 比较稳妥太高会明显降低速度太低则模糊。分页处理时要注意顺序。每页识别完按页号顺序写入同一个输出文件并加上分页标记--- 第 1 页 --- 第 1 页识别文本 --- 第 2 页 --- 第 2 页识别文本这样即使某页识别失败也不会影响其他页的内容。失败页码记录到error.log里方便重跑。如果 PDF 有几百页建议设置断点续跑。每次记录当前处理到第几页中断后从断点继续不要重新跑全部。这个思路在 GPU 资源受限的场景下尤其重要。4.3 API 化时需要约束并发、超时和返回结构当你的 OCR 能力要被多个任务调用时需要把它封装成接口。接口化不是简单写一个 Flask 路由而是要考虑并发和稳定性。并发数不要一开始就拉满。本地 OCR 服务在并发请求增加时CPU、内存、GPU 显存都会快速上升。如果超卖任务会排队接口超时率会上升最后可能全部卡死。更稳妥的做法是设置最大并发数比如同时最多 4 个识别任务超出并发数的请求进入队列每个请求设置超时时间。返回结构建议统一。成功返回文本、耗时、置信度失败返回错误码和原因。下游调用方可以根据返回状态决定是否重试而不是每次都自己解析裸文本。{ code: 0, data: { text: 识别出的文本内容, duration_ms: 1240, pages: 1 }, message: ok }如果 OCR 服务要接入 Agent 或 RAG 系统这个结构就很重要。Agent 工具需要知道调用是否成功以及结果能不能直接用。5. 识别质量不稳定时按输入、环境、参数、工具一路查下去OCR 识别质量变差时不要先怀疑模型能力。很多问题不是模型不行而是输入图片和环境没有处理干净。5.1 先看现象再确认输入分辨率、方向、语言、字体类型遇到识别错误先做一件事把原始图片打开看一遍而不是只看识别结果。看分辨率。文字太小、字迹太浅模型很难识别。看方向。有些扫描件或手机拍照是横向的OCR 引擎如果没有方向分类可能整页输出错乱。看语言。中英文混排时如果只设置了中文语言包英文单词会被识别成奇怪的中文字符。看字体。花体、艺术字、手写体、手机截图里的特殊字体传统 OCR 经常失败。这些问题的处理顺序是先提高输入质量再调整模型参数。不要一上来就调阈值或加图像增强因为输入本身有问题时增强也是白搭。5.2 环境问题依赖版本混杂、路径含中文、权限和资源占用环境问题经常伪装成识别问题。比如同样一段代码电脑 A 能跑电脑 B 输出全是乱码。这时先检查依赖版本。OCR 引擎和 Python 封装库版本需要兼容。有些封装库更新了接口旧代码调用新库会报参数错误有些系统里同时装了两个版本的 Tesseract导致路径指向错误。排查时先看版本再跑最小例子确认环境本身没问题。路径含中文也是常见问题。Windows 上有些 OCR 组件对中文字符路径支持不好图片路径或者输出目录包含中文时会随机报错。解决办法是先把文件复制到纯英文路径下测试。如果纯英文路径能跑通说明就是路径编码问题。还要看磁盘空间和内存。批量识别大量扫描件时输出文件可能很大临时文件也会占用空间。磁盘写满时程序可能不报错但输出文件是残缺的。5.3 参数调整节奏图像增强、阈值、语言包和分块策略环境没问题时再调参数。参数调整要有节奏一次只改一处改完跑同一张测试图。图像处理方面常见的操作有转灰度、二值化、锐化、去噪。但注意二值化阈值太激进可能把文字笔画断开反而让识别变差。更稳妥的做法是先看原图的对比度。如果背景干净、文字清晰直接识别如果图片偏暗、有阴影再做灰度化和自适应阈值。语言包方面中英文混排时设置chi_simeng但要注意语言包顺序。有些引擎语言包顺序会影响识别优先级实际以测试结果为准。分块策略方面超大图片不要一次性识别。比如一张超高分辨率的截图可能被压缩或超时。可以先按区域切分识别完再按坐标拼接。这个方式一次投入比较多但处理复杂版面时很有效。6. OCR It 的真正价值不在于“识别得准”而在于给 LLM 提供干净的“可推理文本”到了最后一步我想说一个容易被忽视的判断标准OCR 输出不是给人看的是给模型推理用的。因此“准确率”不是唯一指标还要看文本是否适合让 LLM 继续处理。6.1 判断 OCR 结果是否满足 LLM 使用文本可读性、结构化、错误密度我一般会用三个指标判断 OCR 结果能不能喂给 LLM。第一是可读性。通读一段输出如果不看原文也能理解内容说明文本基本可用。如果错误密集到没法阅读就不适合直接使用。第二是结构化。标题、列表、段落尽量保留模型在总结时更容易遵循逻辑结构。第三是错误密度。OCR 错字不可能完全避免但错误密度高到一定水平后模型的回答就会开始“一本正经地胡说八道”因为它会把错字当成原文知识。如果你做的是 RAG 或知识库OCR 文本的质量会直接影响检索效果。检索时文本如果错字太多向量匹配也会受影响。这也是为什么不少人把 Karpathy 提出的 LLM Wiki 这类知识整理思路接入 OCR 时会先做一轮文本清洗。6.2 边界提醒表格结构、手写体、复杂排版、低质量扫描件始终是难点OCR 不是万能提取器。几个场景需要降低预期。表格是最难的。识别出表格里的文字不难难的是把单元格对应关系恢复出来。简单表格可以靠坐标判断复杂表格比如跨行跨列、合并单元格普通 OCR 输出很容易丢掉结构。如果表格是数据提取的核心建议单独用表格识别模型而不是通用 OCR。手写体也难。中文手写识别和印刷体识别是两个难度级别。潦草字迹、连笔字识别效果基本不可用。低质量扫描件同样棘手模糊、倾斜、光线不均、墨水晕染这些都会大幅降低识别效果。我不太建议在博客里夸某项 OCR 技术“百发百中”。更实际的做法是先识别一部分人工检查质量再决定是否全量处理。OCR 的目的不是追求“完美识别”而是把大量需要人工录入的文本变成“机器可处理”剩余部分再靠人工或 LLM 修正。6.3 更稳的落地顺序先小样本验证再批量再对接 Agent 或 RAG最后给一个我反复使用的落地顺序。先用一个真实文档做小样本验证。不要用官方 Demo 图片要用你自己业务里的合同、扫描件、截图。确认识别结果能不能满足下游需要。然后小批量跑 5 到 10 个文件检查输出文本、日志、失败率这时候的重点是发现流程问题比如路径、命名、内存、依赖。流程稳定后再跑全量任务。最后再对接 LLM、RAG 或 Agent对接时不要把 OCR 结果裸传最好加上来源、页码、置信度等元信息。如果只是学习默认配置通常够用。如果要长期使用日志、输出目录、任务队列要提前整理好。OCR It 这类方案真正落地时最该盯住的不是功能列表而是输入格式、资源占用和失败重试。很多时候项目卡住的不是模型能力不够而是前置环境和输入材料没有处理干净。