从零搭建智能体检报告解析系统:OCR + 本地大模型实践
用 Spring Boot + Ollama + Dify,让 AI 帮你自动审核体检报告
一、背景与挑战
在招聘流程中,HR 需要审核大量候选人的体检报告,手动核对血压、血糖、血脂等十几项指标是否达标。传统方式面临三个核心痛点:
| 痛点 | 具体表现 |
|---|---|
| 效率低 | 一份报告人工核对需 5-10 分钟,批量处理成本高 |
| 格式多样 | 不同医院排版各异,字段名称不统一("WBC" vs "白细胞计数") |
| 标准不一 | 同一指标在不同医院的参考范围可能不同 |
二、技术选型
经过对比,我选择了"OCR + 本地大模型"的混合架构:
| 组件 | 选型 | 理由 |
|---|---|---|
| OCR 引擎 | 百度 OCR(主)+ Tesseract(备) | 百度 OCR 对医疗报告有专项优化,Tesseract 做离线兜底 |
| 本地大模型 | Ollama + Qwen2.5:7B | 开源免费,支持中文,可本地部署 |
| 工作流编排 | Dify | 可视化 Prompt 编排,提供标准化 API |
| 后端框架 | Spring Boot 3.x + Java 17 | 企业级微服务架构,便于集成 |
三、整体架构
┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ 上传报告 │───▶│ OCR 识别 │───▶│ 大模型解析 │───▶│ 录用建议 │
│ (PDF/图片) │ │ (百度/Tesseract) │ │ (Qwen2.5) │ │ (结构化输出)│
└─────────────┘ └─────────────┘ └─────────────┘ └─────────────┘
3.1 核心流程
-
文件上传:支持 PDF、Word、JPG、PNG 等格式
-
文本提取:文本型 PDF 直接提取,扫描件走 OCR
-
大模型解析:提取 14 项体检指标 + 被检人基本信息
-
规则校验:基于参考范围二次确认
-
录用建议生成:输出 HR 可直接阅读的结构化报告
3.2 多策略容灾设计
| 层级 | 主方案 | 备用方案 |
|---|---|---|
| OCR | 百度 OCR(云 API) | Tesseract(本地离线) |
| 解析 | 大模型(语义理解) | 正则表达式(兜底匹配) |
四、核心实现
4.1 OCR 引擎接口设计
public interface OcrEngine {
String extractText(byte[] imageBytes) throws IOException;
String getEngineName();
boolean isAvailable();
}
百度 OCR 实现要点:
@Component
public class BaiduOcrEngine implements OcrEngine {
private final BaiduOcrClient baiduOcrClient;
@Override
public String extractText(byte[] imageBytes) throws IOException {
// 1. 图片压缩(百度 OCR 限制图片 < 4M)
BufferedImage image = ImageIO.read(new ByteArrayInputStream(imageBytes));
byte[] preparedBytes = prepareImage(image);
// 2. 调用百度 OCR
JSONObject response = baiduOcrClient.basicGeneral(preparedBytes, options);
// 3. 解析结果
return parseResponse(response);
}
}
4.2 Dify 工作流配置
LLM 节点 Prompt(核心指令):
你是一个专业的体检报告解析助手。请从以下体检报告文本中提取:
一、被检人基本信息:姓名、性别、年龄、电话、身份证号、体检日期
二、14项体检指标:血常规(3项)、血压(2项)、血脂(2项)、血糖(1项)、肾功能(3项)、同型半胱氨酸(1项)、心电图、胸部DR
请严格按照 JSON 格式输出,status 取值为:normal / abnormal / missing
4.3 Java 规则引擎配置
@Configuration
public class RuleConfig {
@Bean
public List<CheckRule> getCheckRules() {
return Arrays.asList(
CheckRule.builder()
.id("tg").name("甘油三酯")
.aliases(Arrays.asList("TG", "甘油三酯"))
.unit("mmol/L")
.min(0.5).max(1.7)
.passMsg("甘油三酯正常")
.failMsg("甘油三酯异常")
.build()
// ... 其他指标
);
}
}
4.4 异步处理优化
针对大模型推理耗时(30-60秒),设计了异步处理方案:
@PostMapping("/upload")
public ResponseEntity<Map<String, String>> upload(@RequestParam("file") MultipartFile file) {
String taskId = UUID.randomUUID().toString();
CompletableFuture.runAsync(() -> {
String ocrText = documentParserService.extractText(file);
DifyResponse result = difyClient.parseReport(ocrText);
resultCache.put(taskId, result);
});
return ResponseEntity.ok(Map.of("taskId", taskId, "status", "processing"));
}
五、踩坑与解决方案
5.1 百度 OCR 图片尺寸超限
问题:image size error (code=216202)
原因:PDF 渲染为图片后尺寸过大(5162×7308)
解决方案:
private byte[] prepareImage(BufferedImage image) {
// 1. 缩放至最长边 1024 像素
BufferedImage resized = resizeImage(image, 1024);
// 2. 转为 JPG 格式(PNG 体积太大)
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ImageIO.write(resized, "jpg", baos);
return baos.toByteArray();
}
5.2 大模型推理速度慢
问题:7B 模型在 CPU 上推理需 40-60 秒
解决方案:
-
换用更小模型:
qwen2.5:1.5b(速度提升 3-5 倍) -
异步处理 + 轮询结果
-
增加超时配置
5.3 参考范围不一致
问题:不同医院同一指标的参考范围不同
解决方案:规则配置化,支持按医院动态调整
# 扩展设计
rules:
tg:
default: {min: 0.5, max: 1.7}
"南京秦淮新城医院": {min: 0.5, max: 1.7}
"北京协和医院": {min: 0.56, max: 1.7}
六、项目成果
| 指标 | 效果 |
|---|---|
| 指标提取准确率 | >95% |
| 单份报告处理时间 | 25-45秒 |
| 支持指标数量 | 14项 |
| 支持文件格式 | PDF、Word、JPG、PNG、BMP |
七、技术栈全景
text
┌─────────────────────────────────────────────────────────────┐ │ 前端层:HTTP API / Postman │ ├─────────────────────────────────────────────────────────────┤ │ 业务层:Spring Boot 3.x / Java 17 │ │ - 文件解析:PDFBox / Apache POI │ │ - 规则引擎:自定义配置化规则 │ │ - 服务治理:Eureka / Resilience4j │ ├─────────────────────────────────────────────────────────────┤ │ AI 层:百度 OCR + Dify + Ollama + Qwen2.5 │ ├─────────────────────────────────────────────────────────────┤ │ 部署层:Docker Compose / Maven │ └─────────────────────────────────────────────────────────────┘
八、总结与展望
经验总结
-
大模型是"理解"问题的答案,不是"匹配"问题:正则表达式适合固定格式,大模型适合理解语义,两者结合效果最佳
-
容灾设计要足够务实:用百度 OCR 做主力,Tesseract 做备用;用大模型做主力,正则做备用
-
异步处理是长耗时任务的标配:不要把 60 秒的推理时间放在同步请求里
优化方向
-
针对特定医院报告格式进行微调
-
增加更多异常指标的业务解释
-
支持批量报告处理
-
考虑数据内部流转,可以深耕正则匹配替代外部API,防止数据泄露
同时总结了工具安装的教程,详见Dify + Ollama + Docker Desktop 本地部署配置指南-CSDN博客
欢迎各位大神评论区留言、交流
更多推荐

所有评论(0)