10 小时以前 9bad721754fe8bbe2e5f459d0706e0fefac569f3
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
package cn.iocoder.yudao.module.qcreport.service.aiimport.llm;
 
/**
 * 一次待执行的大模型调用。
 * <p>
 * 由 {@link QcReportLlmCallPlanner} 纯函数算出,交给薄执行器真正发出去。
 * 拆成「先算计划、再执行」是为了让多页 / 多文件 / 超预算这些控制流可以被单测完整覆盖——
 * 否则只能靠 mock 掉 {@code AiChatApi} 才能测,那测的是 mock 不是逻辑。
 * <p>
 * 每个调用自带全部入参(含 TEXT 通道的正文),执行器不需要回看抽取结果。
 *
 * @param kind        调用方式,决定走 {@code chat} 还是 {@code chatWithImage}
 * @param sourceLabel 来源标签,形如「扫描件.pdf 第 2 页」,用于软提示与错误文案
 * @param text        TEXT 通道的正文;IMAGE 通道恒为空串
 * @param imageBase64 IMAGE 通道的**裸 base64**(不含 {@code data:image/png;base64,} 前缀,
 *                    因为 {@code AiChatApiImpl} 直接对它做 Base64 解码);TEXT 通道恒为空串
 * @param mimeType    IMAGE 通道的裸 MIME 类型,如 {@code image/png}
 */
public record QcReportLlmCall(
        Kind kind,
        String sourceLabel,
        String text,
        String imageBase64,
        String mimeType) {
 
    /**
     * 调用方式。
     */
    public enum Kind {
        /** 纯文本,一次调用搞定整份文件 */
        TEXT,
        /** 多模态,一次调用只认一张图,所以逐页一调 */
        IMAGE
    }
 
    public static QcReportLlmCall ofText(String sourceLabel, String text) {
        return new QcReportLlmCall(Kind.TEXT, sourceLabel, text, "", "");
    }
 
    public static QcReportLlmCall ofImage(String sourceLabel, String imageBase64, String mimeType) {
        return new QcReportLlmCall(Kind.IMAGE, sourceLabel, "", imageBase64, mimeType);
    }
 
}