|
|
@@ -9,21 +9,29 @@ import dev.langchain4j.model.chat.ChatLanguageModel;
|
|
|
import dev.langchain4j.model.output.Response;
|
|
|
import lombok.RequiredArgsConstructor;
|
|
|
import lombok.extern.slf4j.Slf4j;
|
|
|
+import org.springframework.beans.factory.annotation.Qualifier;
|
|
|
import org.springframework.stereotype.Service;
|
|
|
|
|
|
/**
|
|
|
* 报告解读服务
|
|
|
* 负责调用百炼模型进行检验报告的OCR识别和解读
|
|
|
+ * 采用两步流程:
|
|
|
+ * 1. 视觉模型提取结构化数据
|
|
|
+ * 2. 语言模型生成医学解读
|
|
|
*/
|
|
|
@Slf4j
|
|
|
@Service
|
|
|
@RequiredArgsConstructor
|
|
|
public class ReportInterpretService {
|
|
|
|
|
|
- private final ChatLanguageModel chatLanguageModel;
|
|
|
+ @Qualifier("visionModel")
|
|
|
+ private final ChatLanguageModel visionModel;
|
|
|
+
|
|
|
+ @Qualifier("chatModel")
|
|
|
+ private final ChatLanguageModel chatModel;
|
|
|
|
|
|
/**
|
|
|
- * 解读检验报告
|
|
|
+ * 解读检验报告(两步流程)
|
|
|
*
|
|
|
* @param imageBase64 图片的Base64编码(不含data:image前缀)
|
|
|
* @param promptType 提示词类型
|
|
|
@@ -31,47 +39,88 @@ public class ReportInterpretService {
|
|
|
*/
|
|
|
public String interpretReport(String imageBase64, String promptType) {
|
|
|
try {
|
|
|
- // 1. 加载提示词模板
|
|
|
- String prompt = loadPrompt(promptType);
|
|
|
+ log.info("开始报告解读流程,类型:{}", promptType);
|
|
|
+
|
|
|
+ // 第一步:使用视觉模型提取结构化数据
|
|
|
+ String extractedData = extractReportData(imageBase64, promptType);
|
|
|
+ log.info("数据提取完成,数据长度:{} 字符", extractedData.length());
|
|
|
+
|
|
|
+ // 第二步:使用语言模型生成医学解读
|
|
|
+ String interpretation = generateInterpretation(extractedData, promptType);
|
|
|
+ log.info("解读生成完成,结果长度:{} 字符", interpretation.length());
|
|
|
+
|
|
|
+ return interpretation;
|
|
|
+
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("报告解读失败", e);
|
|
|
+ throw new RuntimeException("报告解读失败:" + e.getMessage(), e);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 第一步:使用视觉模型提取报告数据
|
|
|
+ */
|
|
|
+ private String extractReportData(String imageBase64, String promptType) {
|
|
|
+ try {
|
|
|
+ // 1. 加载数据提取提示词
|
|
|
+ String extractPrompt = loadPrompt(promptType + "-extract");
|
|
|
|
|
|
- // 2. 构建用户消息(包含图片和文本提示词)
|
|
|
- // 注意:阿里云百炼需要完整的 Base64 URL 格式:data:image/jpeg;base64,{base64_string}
|
|
|
+ // 2. 构建用户消息(包含图片和提取提示词)
|
|
|
String imageUrl = "data:image/jpeg;base64," + imageBase64;
|
|
|
UserMessage userMessage = UserMessage.from(
|
|
|
- TextContent.from(prompt),
|
|
|
+ TextContent.from(extractPrompt),
|
|
|
ImageContent.from(imageUrl)
|
|
|
);
|
|
|
|
|
|
- // 3. 调用百炼模型
|
|
|
- log.info("开始调用百炼模型进行报告解读...");
|
|
|
- Response<AiMessage> response = chatLanguageModel.generate(userMessage);
|
|
|
+ // 3. 调用视觉模型
|
|
|
+ log.info("调用视觉模型提取数据...");
|
|
|
+ Response<AiMessage> response = visionModel.generate(userMessage);
|
|
|
|
|
|
- // 4. 获取并返回结果
|
|
|
- String result = response.content().text();
|
|
|
- log.info("报告解读完成,结果长度:{} 字符", result.length());
|
|
|
+ // 4. 返回提取的JSON数据
|
|
|
+ return response.content().text();
|
|
|
|
|
|
- return result;
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("数据提取失败", e);
|
|
|
+ throw new RuntimeException("数据提取失败:" + e.getMessage(), e);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 第二步:使用语言模型生成医学解读
|
|
|
+ */
|
|
|
+ private String generateInterpretation(String extractedData, String promptType) {
|
|
|
+ try {
|
|
|
+ // 1. 加载解读提示词
|
|
|
+ String interpretPrompt = loadPrompt(promptType + "-interpret-prompt");
|
|
|
+
|
|
|
+ // 2. 替换数据占位符
|
|
|
+ String fullPrompt = interpretPrompt.replace("{{EXTRACTED_DATA}}", extractedData);
|
|
|
+
|
|
|
+ // 3. 调用语言模型(语言模型使用纯文本消息,转换为UserMessage)
|
|
|
+ log.info("调用语言模型生成解读...");
|
|
|
+ UserMessage userMessage = UserMessage.from(TextContent.from(fullPrompt));
|
|
|
+ Response<AiMessage> response = chatModel.generate(userMessage);
|
|
|
+
|
|
|
+ // 4. 返回生成的解读
|
|
|
+ return response.content().text();
|
|
|
|
|
|
} catch (Exception e) {
|
|
|
- log.error("报告解读失败", e);
|
|
|
- throw new RuntimeException("报告解读失败:" + e.getMessage(), e);
|
|
|
+ log.error("解读生成失败", e);
|
|
|
+ throw new RuntimeException("解读生成失败:" + e.getMessage(), e);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 加载提示词模板
|
|
|
- *
|
|
|
- * @param promptType 提示词类型
|
|
|
- * @return 提示词内容
|
|
|
*/
|
|
|
- private String loadPrompt(String promptType) {
|
|
|
+ private String loadPrompt(String promptName) {
|
|
|
try {
|
|
|
- String promptPath = "prompts/" + promptType + "-report-prompt.txt";
|
|
|
+ String promptPath = "prompts/" + promptName + ".txt";
|
|
|
String prompt = ResourceUtil.readUtf8Str(promptPath);
|
|
|
log.debug("成功加载提示词:{}", promptPath);
|
|
|
return prompt;
|
|
|
} catch (Exception e) {
|
|
|
- log.warn("加载提示词失败:{},使用默认提示词", promptType, e);
|
|
|
+ log.warn("加载提示词失败:{},使用默认提示词", promptName, e);
|
|
|
return getDefaultPrompt();
|
|
|
}
|
|
|
}
|
|
|
@@ -81,7 +130,7 @@ public class ReportInterpretService {
|
|
|
*/
|
|
|
private String getDefaultPrompt() {
|
|
|
return """
|
|
|
- 你是一位专业的医学检验报告解读专家。请识别这张血常规检验报告,并提供详细的解读。
|
|
|
+ 你是一位专业的医学检验报告解读专家。请识别这张检验报告,并提供详细的解读。
|
|
|
|
|
|
要求:
|
|
|
1. 提取所有检验数据
|