OCR如何接入LLM数据管道:从图片PDF到干净文本的完整实践

📅 2026/8/27 4:38:55
OCR如何接入LLM数据管道:从图片PDF到干净文本的完整实践
如果你做过 RAG检索增强生成或者知识库问答应用八成会遇到一个尴尬时刻你精心准备了合同扫描件、产品手册 PDF、客户发来的图片表格结果把这些材料喂给 LLM 之后模型回答前后矛盾或者干脆告诉你“我无法读取这个文件”。多数人第一反应是“模型不行”但真正的问题常常出在更底层——这些文档里的文字根本没有变成 LLM 能读的文本。这就是 OCR 在 LLM 应用生态里被严重低估的地位。OCR It这个项目标题其实点破了一个很现实的需求把那些“无法复制”的文档——扫描件、图片型 PDF、截图、拍照件——中的文本抽出来再交给 LLM 去处理。听起来简单真正做起来会碰到识别率、版面顺序、中英文混排、表格结构、超长文本切片等一系列问题。这篇文章我不打算只讲“OCR 概念是什么”而是围绕“OCR 怎么接入 LLM 数据管道”这个核心给你一套可以直接落地的方案。你会看到为 LLM 准备的 OCR 和传统“文字识别”有什么本质区别如何在本机搭建 OCR 环境如何用 Python 把图片和 PDF 变成干净的文本又如何把识别结果组装成 LLM 能高效消费的提示词以及实际项目中最容易踩的坑。1. 为什么 LLM 应用缺不了 OCR先说一个容易被忽视的事实LLM 本质上是一个“文本到文本”的模型。它吃进去的是 token吐出来的也是 token。你给它的 PDF、图片、扫描件在它眼里只是二进制文件它并不能直接“看懂”里面的文字。只有当你把这些文件里的文字提取成纯文本LLM 才有机会理解内容。所以 OCR 在这一环扮演的是“文档数字化管道”的前置角色。没有 OCR后面的 RAG、Agent、知识库问答都无从谈起。1.1 没有 OCR 之前我们是怎么让 LLM 读文档的回顾一下做知识库问答的常见路径把 PDF 切分、向量化、存入向量数据库。很多 PDF 是文本型的可以用 PyMuPDF 这类库直接抽取文字。但有一类 PDF 是图片型 PDF——里面的每一页压根就是一张扫描图没有任何文字层。对这种 PDF直接抽取文本得到的是空内容或者一堆乱码。另一个常见场景是截图。产品经理发来一张需求截图客服发来一张用户报错截图你要把这些图里的信息整理成文档再交给 LLM 总结。手工把图里的字打一遍既低效又容易出错。这些场景的共同点是文档里的信息明明存在但被“锁”在了非文本的介质里。OCR 就是那把开锁的钥匙。1.2 LLM 时代 OCR 和传统 OCR 有什么不同传统 OCR 的目的主要是“把图变成可编辑文本”用户是人。比如扫描一份纸质合同识别成 Word 文档再由人去校对。这个场景下识别结果能看、能编辑就算成功。LLM 时代的 OCR 目的变了变为“把非结构化文档变成模型可消费的结构化数据”用户是 LLM 智能体。这就带来了几个新要求文本顺序必须正确。LLM 对文本的阅读顺序很敏感版面分析错了段落前后颠倒模型理解就会出偏差。结构信息要尽量保留。表格、标题、列表这些结构在传统 OCR 里可能被忽略但对 LLM 理解内容非常重要。批量处理能力。知识库往往是成百上千份文档不能靠人工一张张识别。输出格式要干净。要避免把页码、页眉页脚、识别噪声混进正文否则会污染向量化结果。“OCR It”这类项目之所以有存在价值正是因为它在传统 OCR 之上增加了很多面向 LLM 数据管道的处理逻辑。2. OCR 与 LLM 的基础概念先搞懂它们在管道里的角色2.1 OCR 的基本原理OCR 的全称是 Optical Character Recognition光学字符识别。它的工作流程大致分为四步图像预处理把彩色图片转成灰度图做降噪、增强对比度让文字区域更清晰。版面分析检测图片里的文字区域、表格区域、图片区域理解布局结构。文本检测定位每个文字或每行文字的具体位置。文本识别把检测到的文字图像块转换成对应的字符或单词。现代 OCR 引擎大多基于深度学习比如 PaddleOCR、Tesseract 的新版本在印刷体和清晰手写体上的识别率已经相当高。但要注意OCR 不是 100% 准确的它对图片质量、字体类型、语言种类、版面复杂程度都有依赖。2.2 LLM 到底需要什么形式的文本LLM 消费的是 token 序列但这不意味着随便一段 OCR 出来的文本就能直接用。要让 LLM 高质量地理解文档输入文本最好满足三个条件语义完整一段话不能只截取一半一个表格不能丢了列名。结构清晰标题、段落、列表、表格用 Markdown 或清晰的符号表示。无关信息少页眉页脚、页码、水印、识别错误的乱码都会干扰模型注意力。这也是为什么直接拿 OCR 的原始输出喂给 LLM效果往往不好。OCR 输出是“一个人工智能逐字识别的结果”与“语义上有意义的文本组织”之间还需要一层清洗和组织。2.3 传统方式与 OCR LLM 方式的对比下面这个表格可以帮助你理解两者差异环节传统方式人工整理OCR LLM 方式扫描件转文本人工查看、手打录入OCR 自动识别PDF 文字提取依赖 PDF 是否带文字层图片型 PDF 也能处理信息结构化人工提炼字段LLM 从 OCR 文本中抽取字段批量处理低效易疲劳出错脚本批量执行处理成本人力成本高计算资源成本相对可控3. 本地 OCR 方案选型开源引擎与云服务怎么选在把 OCR 接入 LLM 管道之前先要选型。目前主流的方案有三类本地开源引擎、云 OCR 服务、端到端文档解析框架。我分别说一下适合谁用。3.1 Tesseract最老牌的开源 OCRTesseract 是 HP 实验室开发、后来由 Google 维护的开源 OCR 引擎支持 100 多种语言。它的优点是免费、开源、部署简单缺点是中文识别效果不如专门针对中文优化的引擎对复杂版面的处理能力较弱。适合场景需要本地离线处理文档以印刷体英文、数字为主文本排版规整。3.2 PaddleOCR中文场景更推荐PaddleOCR 是百度飞桨生态下的 OCR 工具链它在中文识别、版面分析、表格识别上做得比 Tesseract 更细致。它内置了文本检测、方向分类、文本识别三条流水线还提供了版面分析模型可以直接输出带标题、段落、表格结构的 Markdown。适合场景中文文档占比高需要处理表格、复杂版面希望把 OCR 结果直接转成 Markdown。3.3 云 OCR 服务比如百度 OCR、腾讯云 OCR、阿里云 OCR 等识别率通常更高部署零成本但需要注意数据隐私和调用费用。如果你处理的是公开资料用它没问题如果涉及客户隐私、企业内部数据就要仔细评估数据是否允许上传到云端。3.4 “OCR It”代表的思路面向 LLM 数据管道的封装回到本文开头的项目标题OCR It – pull text out of un-copyable documents for your LLM。它代表的不是某一款具体的 OCR 引擎而是一种方法论选择底层 OCR 引擎然后在其上封装出“文档进、干净文本出”的数据管线。项目层面你完全可以先试用开源的 PaddleOCR 或 Tesseract再根据自己的业务封装一层批处理脚本。选型建议汇总如下方案优势劣势适用场景Tesseract免费、离线、轻量中文和复杂版面识别一般英文印刷体、简单布局PaddleOCR中文强、版面分析好部署较复杂、模型较多中文文档、表格、复杂版面云 OCR识别率最高、接入快有费用、数据外发公开资料、无隐私要求文档解析框架一站式输出 Markdown资源占用高大规模文档知识库4. 环境准备以 Python 为主线的本地 OCR 环境搭建下面我们进入实操。以 Ubuntu 22.04 Python 3.10 为例分别安装 Tesseract 和 PaddleOCR。Windows 和 macOS 的差异我会在注释里说明。4.1 安装 Tesseract 引擎Tesseract 是一个系统级程序需要用包管理器安装而不是 pip 直接安装。pip 上的tesseract包只是 Python 调用它的封装。# Ubuntu / Debian sudo apt update sudo apt install -y tesseract-ocr tesseract-ocr-chi-sim # macOS brew install tesseract tesseract-lang # Windows 用户请到官方 GitHub Releases 下载安装包 # 安装时勾选 Chinese Simplified language data安装完成后验证版本tesseract --version tesseract --list-langs如果--list-langs输出中包含chi_sim说明简体中文语言包安装成功。4.2 Python 依赖安装我们需要安装pytesseractTesseract 的 Python 封装、Pillow图像处理、pdf2imagePDF 转图片调用系统 poppler 工具。pip install pytesseract pillow pdf2imagepdf2image依赖系统的poppler-utils需要单独安装# Ubuntu / Debian sudo apt install -y poppler-utils # macOS brew install poppler4.3 安装 PaddleOCR可选如果你的文档以中文为主我更推荐把 PaddleOCR 作为主力引擎。安装如下pip install paddlepaddle paddleocrPaddleOCR 2.6 以上版本提供了PaddleOCR类和PPStructure工具可以直接输出 Markdown。首次运行会自动下载模型文件网络情况不佳时可能需要多试几次。4.4 验证环境是否可用创建一个临时测试脚本先跑通最小流程# 文件路径test_ocr_env.py from PIL import Image import pytesseract # 生成一张包含文字的简单图片 img Image.new(RGB, (400, 100), white) from PIL import ImageDraw draw ImageDraw.Draw(img) draw.text((20, 30), Hello OCR - 你好OCR, fillblack) # 执行 OCR text pytesseract.image_to_string(img, langchi_simeng) print(text)运行python test_ocr_env.py如果能看到 “Hello OCR - 你好OCR” 或者接近的识别结果说明 Tesseract 环境正常。5. 完整示例从图片和 PDF 到 LLM 提示词的实现环境准备好之后我们来写一套完整的 OCR 文本提取管道代码。整体流程是读取图片或 PDF - 图像预处理 - OCR 识别 - 文本清洗 - 组装成 Markdown - 构造 LLM 提示词。5.1 单张图片 OCR基础版先从最简单的单张图片开始。这里的关键点是先做图像预处理再调用 OCR。直接识别一张暗光、模糊的截图效果会差很多。# 文件路径ocr_single_image.py import pytesseract from PIL import Image, ImageEnhance, ImageFilter def preprocess_image(image_path: str) - Image.Image: 图像预处理灰度化、增强对比度、适度锐化 img Image.open(image_path) # 转灰度 img img.convert(L) # 增强对比度 enhancer ImageEnhance.Contrast(img) img enhancer.enhance(2.0) # 适度锐化 img img.filter(ImageFilter.SHARPEN) return img def ocr_image(image_path: str, lang: str chi_simeng) - str: 对单张图片执行 OCR img preprocess_image(image_path) # 使用 pytesseract 识别指定语言为中文简体 英文 text pytesseract.image_to_string(img, langlang) # 清洗多余空白 lines [line.strip() for line in text.splitlines()] lines [line for line in lines if line.strip()] return \n.join(lines) if __name__ __main__: result ocr_image(sample.png) print(result)这段代码的要点convert(L)把彩色图转为灰度图减少颜色通道对 OCR 的干扰。ImageEnhance.Contrast增强对比度让文字更清晰。SHARPEN锐化边缘对模糊截图很有帮助。langchi_simeng表示同时启用简中和英文识别。5.2 图片型 PDF 批量 OCR进阶版PDF 是知识库应用里最常见的文档格式。处理图片型 PDF需要先把每页转成图片再逐页 OCR。这里要注意dpi参数分辨率太低文字识别不出来分辨率太高处理变慢。经验值在 200 到 300 之间。# 文件路径ocr_pdf.py import os import tempfile from pdf2image import convert_from_path import pytesseract from PIL import Image def ocr_pdf_to_markdown(pdf_path: str, output_dir: str output, dpi: int 250) - str: 将 PDF 每一页转换为图片逐页执行 OCR输出 Markdown 格式文本。 os.makedirs(output_dir, exist_okTrue) images convert_from_path(pdf_path, dpidpi) markdown_parts [] for page_num, img in enumerate(images, start1): # 图像预处理 img img.convert(L) # Tesseract 支持通过 config 参数指定 PSM页面分割模式 # 这里使用默认模式适合大多数文档 text pytesseract.image_to_string(img, langchi_simeng) # 清洗 lines [line.strip() for line in text.splitlines()] lines [line for line in lines if line.strip()] # 将每页内容包装成 Markdown 分页结构 page_md f## 第 {page_num} 页\n\n \n\n.join(lines) # 用分隔线区分页与页 page_md \n\n---\n\n markdown_parts.append(page_md) # 可选保存每页图片方便调试识别不准确的情况 img.save(os.path.join(output_dir, fpage_{page_num}.png)) return .join(markdown_parts) if __name__ __main__: md_text ocr_pdf_to_markdown(sample.pdf) with open(output/result.md, w, encodingutf-8) as f: f.write(md_text) print(OCR 完成结果已保存到 output/result.md)这里真正容易踩坑的地方是文本清洗。PDF 转图片后常常保留页眉、页脚、页码这些内容会混入正文。一个简单策略是观察输出的 Markdown在前几页识别出页眉页脚的固定模式然后用正则把它们过滤掉。5.3 把 OCR 结果组装成 LLM 提示词OCR 文本提取出来之后如何把它喂给 LLM直接决定了回答质量。一个常见错误是把整本几十页的 OCR 文本一次性塞给 LLM结果 token 超限。更合理的做法是按章节或按页分块每块单独交给 LLM 处理。下面是一个通用的提示词组装示例使用 OpenAI SDK 风格的调用方式但核心逻辑与具体模型无关# 文件路径build_llm_prompt.py from openai import OpenAI client OpenAI( base_urlhttp://localhost:11434/v1, # 以本地 Ollama 为例可替换为你的模型服务地址 api_keyEMPTY ) def build_context_prompt(document_block: str, instruction: str) - str: 将 OCR 文本块与任务指令组装成提示词 prompt_template 你是一个文档理解助手。下面的文本来自一份 OCR 识别的文档片段可能存在少量识别错误。 请基于这段文本完成用户指令。 【文档片段】 {context} 【用户指令】 {instruction} return prompt_template.format(contextdocument_block, instructioninstruction) def query_llm(ocr_text_path: str, instruction: str) - str: with open(ocr_text_path, r, encodingutf-8) as f: content f.read() # 按段落分块防止一次传入过多 token # 这里简单按 2000 字符切块实际项目建议按语义段落切分 chunks [content[i:i2000] for i in range(0, len(content), 2000)] answers [] for chunk in chunks: prompt build_context_prompt(chunk, instruction) resp client.chat.completions.create( modelqwen2.5, # 以本地模型为例 messages[ {role: user, content: prompt} ], temperature0.2 ) answers.append(resp.choices[0].message.content.strip()) return \n\n.join(answers) if __name__ __main__: result query_llm( ocr_text_pathoutput/result.md, instruction请提取这份文档中的合同编号、签约日期、甲方名称以 JSON 格式输出。 ) print(result)这段代码展示了三层思路分块避免 token 超限同时降低单次响应不稳定的风险。提示词模板明确告诉 LLM 这是 OCR 文本可能有错别字让它不要过度纠结于个别字词。多次调用合并处理长文档时先分块理解再统一汇总。5.4 用 PaddleOCR 实现更高质量的中文识别下面给出 PaddleOCR 的调用示例。PaddleOCR 的优势在于内置了版面分析和表格识别可以直接把结果输出为 Markdown 结构。# 文件路径ocr_paddle.py from paddleocr import PaddleOCR # 初始化 PaddleOCR # use_doc_orientation_classify 用于判断文档方向 # use_doc_unwarping 用于矫正弯曲的图片 ocr PaddleOCR( use_doc_orientation_classifyTrue, use_doc_unwarpingTrue, langch, ocr_versionPP-OCRv4 ) # 对图片执行识别 result ocr.predict(sample.jpg) # result 是多个页面结果的列表一般单张图只有一项 for page in result: # 打印识别出的文本行 for line in page[rec_texts]: print(line)PaddleOCR 的predict方法返回的是字典列表其中rec_texts是识别出的文本行rec_scores是置信度。如果你只需要简单文本直接遍历rec_texts即可。它比 Tesseract 的优势主要在中文字体、复杂背景上的鲁棒性但首次执行需要下载几个模型文件耗时较长。6. 运行结果与效果验证6.1 如何判断 OCR 是否成功OCR 没有“完全正确”一说更多是“够不够用”的问题。建议按下面几个梯度验证肉眼比对输出文本与原始文档比对看有没有大面积乱码、漏行、顺序错乱。字符准确率随机抽取 3 到 5 段文字统计识别错误的字符比例。如果小于 5%通常可以接受。任务完成度把 OCR 文本交给 LLM 完成一个具体任务比如提取字段、写摘要检查输出是否准确。6.2 一个可复现的验证脚本# 文件路径evaluate_ocr.py import pytesseract from PIL import Image def ocr_accuracy_check(image_path: str, ground_truth: str) - float: 简单字符级准确率评估 text pytesseract.image_to_string(Image.open(image_path), langchi_simeng) # 去空格比较 gt_clean ground_truth.replace( , ).replace(\n, ) ocr_clean text.replace( , ).replace(\n, ) min_len min(len(gt_clean), len(ocr_clean)) if min_len 0: return 0.0 correct sum(1 for i in range(min_len) if gt_clean[i] ocr_clean[i]) return correct / len(gt_clean) if __name__ __main__: print(ocr_accuracy_check(sample.png, 合同编号ABC-2024-001))这种评估方法比较粗糙但作为上线前的快速验证已经足够。它不能替代大规模评测却能帮你快速判断某个 OCR 引擎对特定文档类型是否适用。7. 常见问题与排查思路OCR LLM 管道在实际项目中会遇到不少问题我整理了一个排查表按出现频率排列问题现象可能原因排查方式解决方案识别出一堆乱码图片分辨率过低或字体过于潦草检查原图清晰度放大观察文字边缘提高扫描 dpi或增加图像预处理强度中文识别效果差语言包未安装或未指定中文语言执行tesseract --list-langs查看语言列表安装tesseract-ocr-chi-sim并设置langchi_simengPDF 页面全部为空白PDF 是图片型 PDF没有文字层用 PDF 阅读器查看能否选中文字使用pdf2image转图片后 OCR而不是直接抽取文本表格数据混乱传统 OCR 缺少版面分析能力观察输出文本顺序是否错乱换用 PaddleOCR 的 PPStructure 或table模式段落顺序不对多栏版面未被正确识别检查输出文本里是否左侧栏和右侧栏交错指定 PSM 模式如--psm 4假设单列文本token 超出限制整份文档一次性传给 LLM查看模型 context length 限制按段落或按页分块分批处理页眉页脚混入正文未做文本清理观察重复出现的行定位固定模式用正则过滤页眉页脚和页码隐私数据不能上云使用了云 OCR 服务检查数据出境合规要求改用本地 Tesseract 或 PaddleOCR识别速度很慢图片过大或 dpi 过高监控 CPU 和内存占用调整 dpi 到 200 到 250或压缩图片尺寸LLM 仍然答非所问OCR 文本噪声太大污染模型理解单独阅读 OCR 输出看是否存在大量错字增加文本清洗或提示词中明确“文本可能有 OCR 噪声”8. 最佳实践与工程建议8.1 把 OCR 从“脚本”升级为“服务”在真实项目中OCR 很少只跑一次。文件会持续增加上游系统会不断推送新文档。因此不要把它写成一次性脚本建议封装成独立的服务提供 REST API 或消息队列消费接口。这样可以统一升级 OCR 引擎而不影响下游 LLM 应用。8.2 建立 OCR 质量反馈闭环OCR 引擎会随着模型更新而改进但任何引擎都会在特定文档上失败。建议在管道中加入人工复核环节或者用 LLM 辅助校验。一个可行的做法是识别结果置信度低于阈值时自动把样本送入人工标注队列定期用这些样本微调或评估 OCR 模型。8.3 文本分块不要按固定字符数硬切第 5 节的示例为了演示简单按 2000 字符切块这在生产环境并不理想。更好的做法是按语义边界切块——先识别标题、段落然后以段落为单位组装必要时把属于同一表格的行合并。原因是固定字符切块很可能把一段话从中间切断导致 LLM 上下文语义不完整。8.4 用 LLM 做 OCR 后处理OCR 结果往往存在同音字错误、形近字错误。在将文本存入知识库之前可以先用 LLM 做一轮清洗。提示词可以是请对下面这段 OCR 文本进行纠错和规范化保持原文语义不变。对不确定的地方不要自行编造保留原文。{OCR 文本}这样虽然增加一次模型调用但对下游 RAG 检索效果有明显的正向作用。8.5 注意数据隐私与安全边界如果你的文档涉及客户个人信息、企业内部合同、未公开财报需要明确 OCR 工具运行在什么环境。本地部署 Tesseract 或 PaddleOCR 更稳妥而云 OCR 服务虽然方便但可能存在数据外发风险。另外对涉密或敏感的文档建议在识别完成后立即删除中间图片文件只保留处理后的文本。8.6 日志与可观测性给 OCR 管道加上结构化日志推荐记录以下字段文档 ID页数每页识别置信度处理耗时OCR 引擎版本图片尺寸与 dpi当某个文档的 LLM 问答质量明显下降时排查第一步就是回溯这个文档的 OCR 输出看问题出在识别环节还是下游提示词环节。9. 总结与后续学习方向这篇文章围绕“面向 LLM 的文档文本提取”这个主题展开讲了四点OCR 在 LLM 应用管道中的必要性Tesseract 与 PaddleOCR 的选型从图片和 PDF 中提取文本的完整代码实现以及把 OCR 文本高效组装成 LLM 提示词的方法。你可以把文中的代码连起来跑通一条“扫描件 - 图片 - OCR 文本 - Markdown - LLM 问答”的最小闭环。下一步值得深入研究的方向有三个第一版面分析当文档包含多栏排版、复杂表格时怎么把版面结构完整还原出来第二OCR 后处理与结构化抽取怎么用 LLM 从带噪声的 OCR 文本里稳定抽取字段第三多模态大模型替代传统 OCR 的可能性像 GPT-4V、Qwen-VL 这类模型能直接读图但在成本、批量处理速度、结构化输出方面的权衡是什么。想清楚这些你就能为一个知识库项目选择最合适的文档解析路径。如果这篇文章对你有帮助建议收藏备用。实际跑通之后你会发现 OCR 并不是一个“拿来即用”的组件它更像一道需要耐心调试的前置工序一旦调顺后面的 LLM 应用会稳很多。