上季度我们组接了一个餐饮小程序的需求PRD发下来我第一反应是头皮发麻20多页文档里有8页是截图有UI界面图、有下单流程示意图、还有一张密密麻麻的价格对比表。产品和开发在会上聊得热火朝天我坐在角落拿着笔脑子里只有一个问题——这些图里的信息我到底要怎么把它变成测试用例传统做法是人肉读图。我把截图里的文字一个个敲进需求记录把流程示意图的箭头走向画成逻辑图再对着表格手工列字段。一份40分钟的PRD光“看图”这一步就消耗了两个小时而且漏掉的东西还不一定能发现。后来我搭了一套“OCR LLM”的图文混合需求解析方案把截图文字自动抽出来再让大语言模型把结构化需求翻译成带前置条件和预期结果的case。这套管线跑了几个月今天把完整思路和踩过的坑一起写出来给同样被图文需求折磨的测试同学参考。1. 图文混合需求到底难在哪三类图三种拆法1.1 三类图三种难度UI截图、流程示意图、票据表格我接触过的图文混合需求文档里面的“图”基本可以分成三类它们的解析难度完全不是一个量级。第一类是UI界面截图比如“下单页长这样上面有菜品名、价格、规格选择、提交按钮”。这类图的特点是文字零散分布在页面各处没有固定格式。解析的关键在于把“文字内容”和“界面元素类型”对应起来——哪个文本块是按钮、哪个是输入框、哪个是商品列表。OCR能给你坐标和文字但“这个文字是按钮还是提示语”得靠LLM根据位置关系去判断。第二类是流程示意图典型的就是“用户下单后经过支付、商家接单、骑手配送、完成订单”这种带箭头的图。流程图里的文字量通常不大但箭头指向关系才是核心信息。OCR对箭头的识别能力几乎为零我用的办法是让LLM结合图片描述来理解把整张流程图经图像描述模型转成一段文本说明再配合OCR的节点文字让LLM重建出“前置节点→当前节点→后置节点”的状态流。这一步对生成case太关键了因为case里的“步骤顺序”就是从这来的。第三类是票据和表格比如合同扫描件、价目表截图、配置参数表。这类图的文字密度高、行列关系复杂最容易出问题的是表头和数据行对不齐。我在后面第5章会专门讲表格合并单元格这个坑这里先提一句遇到表格OCR输出的纯文字流基本是不能直接用的必须做行列重建。1.2 OCR 和 LLM 各自的半桶水组合起来才完整我最早犯过的错误是只上OCR。当时觉得“能转文字就行”结果转出来的是一堆没有逻辑顺序的字符串比如“鸡腿饭”“提交”“18元”“满减”我根本不知道这些词的层级关系。后来只上LLM也不行让模型直接看截图它对图里的中文字符基本是瞎的经常一本正经地编造“截图里有一个XX按钮”实际上根本没有。这两者其实是互相补位的OCR解决的是“图里文字怎么变成可编辑文本”擅长处理印刷体、表格线、坐标定位LLM解决的是“这些文本组合起来是什么意思”擅长把零散信息还原成模块、规则、步骤。OCR负责把“像素”变成“词”LLM负责把“词”变成“需求”最后再把“需求”变成“case”。少了任何一环整条链路的输出质量都会断崖式下降。2. OCR 引擎选型本地离线与云端 API 的取舍2.1 四个主流引擎的真实表现我做选型时把市面上常见的方案过了一遍这里直接说结论。引擎部署方式中文识别效果表格/结构化能力多语言典型坑PaddleOCRpaddlex pipeline本地Python库强印刷体和扫描件都能打可输出表格结构但复杂表格易错中英日韩等体积大官方模型更新快依赖版本要对齐Tesseract本地命令行/C库一般对模糊图片掉字明显基本没有原生表格能力靠语言包缺traineddata就废语言包漏配如韩文需要kor.traineddata百度OCR云端APIHTTP调用Java/Python都方便强且有专门的票据、合同场景模型有表格识别专用接口多语言支持全数据出网涉密/合同类文档要慎重Umi-OCR / RapidOCR本地工具/ONNX中上UI截图够用一般部分支持和PaddleOCR一样存在部署环境问题我实际测试过一个场景一张点餐小程序的商品列表截图PaddleOCR对整个页面文字的召回率在96%左右Tesseract在87%左右但Tesseract对“满减优惠”这种带符号的文案会直接把“满减”识别成“满诚”。云端API效果最好但每次调用都要考虑费用和数据隐私。RapidOCR走的是ONNX本地推理优点是部署简单、不用装巨大的Paddle框架缺点是效果略逊于Paddle的完整版模型。2.2 按项目类型选择引擎的决策逻辑我的选择逻辑就三条其他花里胡哨的指标基本不用看。第一条是数据能不能出网。做合同类项目时合同扫描件里有大量敏感信息公司合规明确要求本地处理那云端API直接出局。即使是公网项目我也建议前提是客户接受数据走第三方服务。本地方案里我优先选PaddleOCR因为它的中文印刷体识别最稳而且paddlex的sub-pipeline机制可以直接在代码里串详细流程。第二条是文档类型。如果是固定模板的票据比如发票、结算单、气表读数百度OCR这类有垂直场景专用模型的API效果明显更强它能直接输出“收入”“单位”“时间”这样的结构化字段而不是让LLM再去猜。热搜里有人问“java对接百度ocr上传合同文件读取关键字段”这条路在云端场景下是通的返回的JSON里字段键值都排好了但前提仍然是你的业务能接受外呼。第三条是工程成本。Tesseract虽然轻但预处理器、图像矫正、语言包都要自己搞我只有在做文字量极小且语言明确的简单OCR时才用它。Umi-OCR是我本地临时看图的备选真正跑批量管线还是Paddle。2.3 语言包、置信度、编码三个隐藏坑选型阶段有个热搜问题我记得很清楚“以下OCR代码识别不了韩文”。这大概率不是代码问题而是模型和语言包的问题。PaddleOCR的识别模型是按语言分开的如果只下了中文模型遇到韩文自然输出乱码Tesseract则需要去下相应的traineddata文件并放到tessdata目录缺了它其他语言全废。所以做多语言项目前先检查模型和语言包清单别拿着中文字库去硬拆韩文截图。置信度是另一个容易忽略的参数。PaddleOCR每个识别框都会返回一个置信度分数很多教程里喜欢让低于某个阈值的直接丢弃但我后面会讲这其实是个陷阱——尤其当需求文档里恰好有手写备注或印章遮挡时直接丢文本等于把需求弄丢了。合理的做法是低置信度文本保留但打标记交给LLM结合上下文二次判断。最后是编码。OCR输出在Windows环境下偶尔会出现GBK/UTF-8编码混乱导致中文乱码。这个坑排查起来非常隐蔽因为OCR本身识别是好的只是在写入文件或传给LLM时编码转错了。我的习惯是管线里所有文本传输统一UTF-8写文件时显式指定encoding参数省掉一堆玄学问题。3. 主链路搭建图片进 OCROCR 结果进 LLM3.1 完整管线从 PRD 页面到结构化需求 JSON下面是我现在跑通的管线按顺序分五步文档拆页与图片抽取把PRDPDF或Word按页拆开同时把内嵌截图导成独立图片文件。图像预处理矫正倾斜、去水印、增强对比度。OCR推理对每张图跑识别拿到文本块、坐标框、置信度。结构化清洗坐标归一化、按区块合并文本、重建表格关系。LLM结构化把清洗后的OCR结果连同图像描述一起发给LLM输出统一的需求JSON。以那个点餐小程序的需求为例PRD第7页是一张“提交订单”的界面截图第8页有一段文字说明“用户选择地址之后才能点击提交按钮未选择地址时按钮置灰”。这两页信息分开看都没问题但case要写“未选地址时提交按钮不可点”就必须把第7页的UI元素和第8页的规则文字在语义上合并。我的做法是让LLM把所有页面的输出整合成一份JSON每个需求节点带上source_page字段回溯时能查到它是从哪张图、哪个OCR文本块来的。3.2 图像预处理别急着上 OCR新手最容易犯的错是拿到图就往OCR里塞。实际场景中PRD里的截图质量千奇百怪有手机拍照的、有PPT转PDF压模糊的、有带着半透明水印的。不处理直接识别召回率能掉20个点以上。我固定的预处理流程有三项。第一是倾斜矫正先用检测模型拿到文本行的旋转角度再做仿射变换把文字拉平这一步对拍照版PRD效果极其明显。第二是水印和背景噪点去除需求文档里经常有“内部资料”“草稿”之类的半透明水印它们会干扰检测框让OCR在文字区域输出一堆垃圾。用形态学操作把水印层减淡后识别率会干净很多。第三是低分辨率截图的放大增强我试过双线性插值和超分模型对于UI截图放大2倍再识别能明显减少小字号文字漏检。这里插一句有人在相关搜索里问“php ocr识别验证码”这类问题我只想说方向不一样。验证码识别是另一个专门领域用的是对抗训练和端到端识别模型跟咱们这里“把需求文档里的图片转成文本”完全不是一回事千万别把需求解析的prompt和OCR参数照搬到那边去。3.3 OCR 结果的“干净化”处理OCR吐出来的原始结果长什么样是一串带坐标的文本块类似bbox: [x1, y1, x2, y2]text: “提交订单”score: 0.99bbox: [x1, y1, x2, y2]text: “选择地址”score: 0.97直接把这个结果扔给LLM它也能工作但效果差一截因为文本块的阅读顺序是乱的。所以我在中间加了三步清洗坐标归一化到0到1的范围避免不同页面分辨率影响后续处理按空间位置把文本块聚成区块比如把同一张截图里的标题、输入框提示、按钮归到一块对从左到右、从上到下的阅读顺序做重排。表格场景要额外做行列重建。单独的文本行拆开谁能看出来“鸡腿饭 18元 5份”是三列我的简易方案是先按表格线检测如果原图有清晰的线框或者按文本块x坐标的聚类无表格线场景推断列边界再把同一行内属于同一列的文本拼接起来。这个环节做到什么程度直接决定后面LLM能不能理解表格语义。3.4 LLM 结构化解析的提示词要点我把提示词分成三段写缺一不可。第一段给角色和任务你是一名需求分析师请基于OCR提取的需求文档文本块还原出功能模块、业务规则和界面元素清单。只允许使用给定的文本块和图像描述禁止编造文中不存在的内容。注意“只允许使用给定材料”这个约束后文防止case幻觉全靠这道闸。第二段给输出格式要求输出一个JSON数组每个元素包含module所属功能模块、element界面元素或数据项、rule业务规则、source_block对应的OCR文本块编号。让模型必须在每个字段标出来源这就是给后续生成的每个case留证据链。第三段给示例拿一个已经标注好的示例few-shot做示范比如“示例module 为下单流程element 为提交按钮rule 为必须选择地址后置亮source_block 为 b12”。模型有示例比没示例的输出稳定得多我试过无示例时它爱自由发挥字段名都不统一。4. 自动生成 case从需求要素到测试用例的翻译4.1 给 LLM 定义 case 的要素模型如果直接把整段需求文字丢给LLM说“帮我生成测试用例”它也能生成但结果跟用ChatGPT写周报一样——格式飘逸、覆盖看缘。想让case生成稳定可复用得先把case的“骨架”定义好。我定义的最小case模型包含八个字段case编号、所属模块、前置条件、测试数据、操作步骤、预期结果、优先级、需求来源编号。操作步骤用有序列表预期结果和前置条件必须能对应到需求JSON里的某条rule或某个element。一个case如果找不到需求来源直接判定无效。拿点餐项目举例需求JSON里有一条“未选择地址时提交订单按钮置灰”LLM生成的标准case就是前置条件“用户已选菜但未选地址”步骤“进入结算页并观察提交订单按钮状态”预期结果“按钮置灰且不可点击页面提示请选择收货地址”。这里的“提交订单按钮”正对应第7页截图的OCR文本块“提交订单”“置灰”对应第8页文本说明。整条链路是有据可查的。4.2 两级生成策略功能用例与边界用例我试过让LLM一次把所有case全生成结果它把主要流程写得又长又全边界条件却漏得厉害。后来改成两级生成。第一级是功能用例按“主要流程→分支流程→异常流程”的顺序生成。比如点餐正常下单、修改订单、取消订单、支付超时取消。这一级交给LLM做因为它在理解“业务闭环”上确实比我手写快。第二级是边界和异常用例这一级不能只靠LLM自由发挥得给它丢“边界线索”。我的做法是把结构化JSON里的所有数字、状态字段、必填字段先抽出来做成一张“边界清单”比如“库存数量”“优惠券金额”“订单状态”这些字段再让LLM针对每个字段生成边界值case库存为0、金额为0.01元、状态为已取消后再次操作等。这样生成的case才不是流水账。4.3 用 key/query/value 三层视角提升抽取质量我在调prompt的过程中发现一个特别有用的视角就是相关搜索里那句话key是“我是谁”query是“我在找什么”value是“我能提供什么”。放在case生成场景可以这样理解key对应需求里的实体和界面元素比如“菜品”“价格”“提交按钮”“订单状态”它们是case的“主语”。query对应用户的操作目的和动作比如“选择地址”“提交订单”“取消支付”它们是case的“步骤动词”。value对应系统的反馈和规则结果比如“按钮置灰”“弹窗提示”“订单状态变为待支付”它们是case的“预期结果”。我让LLM先生成一张三层结构表再基于这张表写case。这样做的收益是case的“前置条件”天然来自key字段“步骤”来自query字段“预期结果”来自value字段三者一一对上不会出现步骤写了个操作、预期结果却答非所问的情况。这比直接生成case更稳也更容易做自动化校验。4.4 生成后的校验别让幻觉混进测试库LLM生成case最大的隐患是幻觉它会把“用户点击会员中心”写成“用户点击优惠券中心”而截图里根本没有后者。我的校验办法是“反向追溯”——把生成case里出现的所有界面元素、按钮名、提示语全部抽取出来和结构化JSON里的element字段做比对。如果某个元素在证据链中完全不存在要么是OCR漏了要么是LLM在编两者都需要人工核实。这个校验听着麻烦实际上用字符串匹配就能完成大部分检查。我做了个简单脚本把case文本和OCR原始文本块做模糊匹配匹配率过低的case自动打上“疑似幻觉”标签提交时优先人工审。后来我在评审会上把这条逻辑讲给开发听开发当场就信服了说这比拍脑袋写的case可信度高得多。5. 实测中最容易翻车的五个环节5.1 表格合并单元格识别错位表格合并单元格是本方案我翻车最多的地方。需求文档里的价目表经常出现“跨两行的菜品类别”“跨两列的套餐组合”OCR识别出来的文本流是平的行列关系全部丢失。比如文本块A“招牌套餐”合并了多行文本块B“鸡腿饭可乐”在下方两行如果不做行列重建LLM会以为“招牌套餐”和“鸡腿饭可乐”是上下关系而不是包含关系。我踩了几次之后总结出一个相对好用的办法合并单元格场景下先把有表格线的图交给表格结构识别模型输出单元格坐标再把OCR文本按单元格坐标归位没有表格线的图别硬猜行列直接把整块原始文本喂给LLM让它根据语义判断层级。实践证明后者在“没有线框但视觉有明显分组”的截图上准确率反而更高。5.2 低置信度字段直接丢弃的错误决策之前提过置信度阈值的问题这里展开讲。有个合同识别场景扫描件上有手写的“已确认”三个字盖在打印文字上面OCR置信度只有0.4。我第一次设了个0.6的过滤阈值结果“已确认”和下面的“付款期限”两个框一起被清掉了合同关键信息直接缺一块。后来我改了策略置信度低于阈值不丢而是加前缀标记比如“【低置信度】”。LLM看到这个标记就知道这段文字可能不准会自动结合上下文做纠错。比如“付款期限【低置信度】↓30天”LLM根据“↓”符号和前后文判断可能是“30天”它会在结构化JSON里标注“低置信度建议人工复核”。这样既保住信息又不盲目信任。5.3 Token 预算与大文档切分长PRD一次性塞给LLM会把上下文烧穿尤其是加了图像描述之后几千token都是小意思。我一开始按页切分每页一调结果跨页的规则碎片对不上第3页说“付款方式支持微信”第5页又冒出来“微信支付仅限单笔500以内”按页切分后模型根本看不到第3页的约束生成的case就会缺失金额限制。正确做法是按“语义模块”切分先让OCR识别出整篇文档的标题层级把属于同一功能模块的页面聚合成一个chunk再把这个chunk丢给LLM。如果模块还是太长就只允许模型分两次调用第一次抽取要素第二次生成case。我实测下来一个中大型模块控制在3000到4000token以内生成的case质量和上下文完整性能达到最好平衡。5.4 OCR 错别字被 LLM 当成原文OCR对相似字形的识别错误太常见了“金额”被识别成“金颗”“选择”变成“远择”“提交”变成“捉交”。LLM有个致命特点你给它错别字它大概率会顺着错别字编下去因为它默认输入是“真”的。我处理这个问题分两道工序。第一道在OCR清洗阶段用常用业务词库过一遍文本把疑似错别字自动替换成词库中的正确词并记录替换记录。比如“提捉”→替换“提交”记录“原文本为提捉”。第二道在生成case阶段让LLM输出前必须把case中引用的界面元素与原始OCR文本块逐字对照如果存在不一致把差异列出来。这个“对照清单”在评审时特别有用测试负责人可以直接看到哪些地方是OCR纠错、哪些是模型猜测。5.5 图片清晰度与方向问题拍照版PRD里的图片五花八门角度歪的、背光的、压缩过度的。我预处理流程里加了自动方向判别因为有个真实的bugOCR模型把横版的流程图按竖版读文本块顺序全乱LLM生成的quality case步骤完全不对。超分模型图像增强能救一部分低清图但救不了大范围失焦。我的经验是图片质量实在差到OCR都搜不到关键元素时别硬撑直接回到人工把关键文字补录一遍再把补录结果用同样的文本块格式喂给LLM。半自动比全自动在边缘场景下更靠谱——管线设计时就要留这个“人工补录口”否则遇到烂图整条流水线就断了。6. 效果衡量与后续可扩展空间6.1 用数据说服团队一套简单的评估指标方案上线前我给自己定了一套可量化的指标不然评审的时候容易变成“我觉得好用”这样的主观描述。第一是字段时间召回率用一批人工标注过的样例图算OCR正确识别的字符占比我的PaddleOCR管线在点餐截图场景能到94%到97%。第二是case可用率跑完生成的case找三个资深测试同学盲评标出“可直接执行/需小幅修改/需重写/不可用”四档直接可执行加小幅修改的占比大概在78%左右。第三是case漏测率用生成case集合和现有测试存量做覆盖比对看哪些功能点完全没有case覆盖这个指标帮我发现了“地址管理”模块一个长期被遗漏的边界逻辑。第四是评审修改率上线三个月后生成case在评审会上被要求修改的比例从初期的三成下降到了不到一成说明提示词和校验逻辑在不断收敛。这套数据我建议新项目一定要留档时间拉长了能看到方案的真正价值。我们合同抽取场景里同样一份合同人工录入关键字段要30分钟这套管线加一轮人工复核只需要5分钟省出来的时间都投到了评审和疑难case上。6.2 扩展方向RAG、本体与持续回归现在这套方案已经不是一个孤立的“截图转case”脚本了我把它接到了三条扩展线上。第一条是RAG与历史需求库。把历史项目的需求JSON、当时的case、线上故障报告全部做成向量库新需求进来时先检索相似旧需求把相关历史case作为参考样例喂给LLM。源头上有知识库兜底新需求的case生成质量会明显高于从零开始。第二条是GraphRAG和业务本体。相关搜索里几次提到“rag graphrag llm wiki 本体rag”其实落到我们场景就是把“菜品”“订单”“支付”“优惠券”这些核心实体以及它们之间的“依赖”“约束”“互斥”关系先定义成一张业务本体图。LLM生成case时需要调用某个实体关系时从本体图里找对应约束这个比纯RAG的向量检索更精确尤其适合强规则行业。我们下一个迭代就在做这件事目标是让LLM自动识别“满减和折扣不能同享”这类隐含约束。第三条是生成结果的持续回归。我每天把先生成的case自动对一轮线上测试报告发现case里出现的按钮文案或页面标题和线上实际页面不一致时立刻告警。这等于把“需求变更后case没同步更新”这个测试组老大难问题变成了一条自动触发机制。最后再说说我的一个习惯跑这套管线时OCR的原始JSON和LLM的结构化输出我都会留档保存而且用的是同一个批次号。后续一旦发现case有问题我能马上定位是“图里的字识别错了”还是“需求理解错了”还是“case生成错了”。这个习惯帮我少吵了无数架因为开发拿着bug单来找我的时候我能直接甩出到底是需求文档本身的问题还是我们解析的问题。图文混合需求解析的本质就是把“看图凭感觉”变成“看图有依据”这套管线最大的价值不是省了那两小时而是让测试用例的每一条结论都带着来路。