package cn.iocoder.yudao.module.qcreport.service.aiimport.llm;
|
|
import cn.iocoder.yudao.module.qcreport.service.aiimport.document.QcReportDocumentExtract;
|
|
import java.util.ArrayList;
|
import java.util.Base64;
|
import java.util.List;
|
|
/**
|
* 把抽取结果翻译成「该发几次模型调用、每次发什么」。
|
* <p>
|
* 纯函数:没有 IO、没有 Spring、没有 {@code AiChatApi}。多页 / 多文件 / 超预算这些控制流
|
* 因此能在单测里被完整覆盖,而不必 mock 掉真正的外部依赖。
|
*/
|
public class QcReportLlmCallPlanner {
|
|
private QcReportLlmCallPlanner() {
|
}
|
|
/**
|
* 为一个文件排调用计划。
|
* <p>
|
* TEXT 通道整份文件一次调用;IMAGES 通道逐页一次——多模态接口一次只收一张图,
|
* 这是接口约束而不是取舍。
|
* <p>
|
* 这里**不做页数上限判断**:超限由适配器在抽取阶段就抛错,因为静默截断会让用户
|
* 以为整份文件都识别过了,比直接报错危险得多。
|
*
|
* @param extract 抽取结果
|
* @param sourceLabel 来源标签,通常是原始文件名,会拼进每页的标签里
|
* @return 按执行顺序排列的调用列表,至少一项
|
*/
|
public static List<QcReportLlmCall> plan(QcReportDocumentExtract extract, String sourceLabel) {
|
if (extract.channel() == QcReportDocumentExtract.Channel.TEXT) {
|
return List.of(QcReportLlmCall.ofText(sourceLabel, extract.text()));
|
}
|
List<QcReportLlmCall> calls = new ArrayList<>(extract.images().size());
|
for (QcReportDocumentExtract.ImagePart image : extract.images()) {
|
calls.add(QcReportLlmCall.ofImage(
|
sourceLabel + " " + image.label(),
|
Base64.getEncoder().encodeToString(image.bytes()),
|
image.mimeType()));
|
}
|
return calls;
|
}
|
|
}
|