package cn.iocoder.yudao.module.qcreport.service.aiimport.document; import java.util.List; /** * 一个文件被适配器抽取后的中间结果。 *

* 刻意不是树、也不是 HTML:它只是「这份文件能交给模型的东西是什么」这个更小的问题的答案。 * 树要表达版式,HTML 要表达样式,两者都会把「模型能看见什么」放大到不可控;这里只留两个通道, * 迫使下游(提示词构造、调用计划)不必再考虑第三种形态。 * *

为什么是双通道而不是一种

* 电子版 PDF / Word / Excel 有真实文本层,抽取成字符串最省 token,模型也读得最准; * 扫描件与照片没有文本层,只能把页面渲染成图交给多模态模型。 * 这两条路的调用方式({@code chat} 与 {@code chatWithImage})、计费口径、失败模式都不同, * 所以必须在类型上分开,而不是塞进一个字段里靠猜。 * * @param channel 通道类型,见 {@link Channel} * @param text TEXT 通道的正文,多页时页间以 {@code --- 第 N 页 ---} 分隔;IMAGES 通道恒为空串 * @param images IMAGES 通道的图片,每页一张;TEXT 通道恒为空列表 * @param pageCount 页数,用于超限校验与提示文案 * @param extractor 实际生效的抽取器标识(如 {@code pdfbox-text}),会写进日志便于排查 * @param notes 抽取阶段的软提示,直接进响应的 warnings 展示给用户 */ public record QcReportDocumentExtract( Channel channel, String text, List images, int pageCount, String extractor, List notes) { /** * 纵向合并续格的占位标记。 *

* 它属于「抽取出来的文本长什么样」这条契约,所以和 {@code text} 放在一处, * 由生产者({@link OfficeImportAdapter})与解释者({@code QcReportTemplatePromptBuilder}) * 共用一份,避免两边各写一个字面量后各自漂移。 *

* 为什么不能留空:{@code w:vMerge} 的续格在 Word 里表示「这一格与上一行是同一个值」, * 但 POI 取出来就是空串。留空的话,模型区分不出「合并续格」与「原件本来就没填」, * 于是整张表看起来是一层平铺的检验项,看不出分组结构。 */ public static final String VERTICAL_MERGE_MARK = "↑同上"; /** * 抽取通道。 */ public enum Channel { /** 有文本层,整份文件拼成一段文本,一次模型调用 */ TEXT, /** 无文本层(扫描件/照片),逐页转图,每页一次模型调用 */ IMAGES } /** * IMAGES 通道里的一页图。 * * @param bytes 图片字节,执行器负责转成裸 base64 * @param mimeType 形如 {@code image/png} 的裸类型,不带 {@code data:} 前缀 * @param label 页标签,用于拼错误与提示文案,例如「扫描件.pdf 第 2 页」 */ public record ImagePart(byte[] bytes, String mimeType, String label) { } /** * 文本通道的工厂。{@code notes} 允许为空表示没有软提示。 */ public static QcReportDocumentExtract ofText(String text, int pageCount, String extractor, List notes) { return new QcReportDocumentExtract(Channel.TEXT, text, List.of(), pageCount, extractor, notes); } /** * 图片通道的工厂。 */ public static QcReportDocumentExtract ofImages(List images, int pageCount, String extractor, List notes) { return new QcReportDocumentExtract(Channel.IMAGES, "", List.copyOf(images), pageCount, extractor, notes); } }