☰
Spring AI 2.0:让 RAG 回答带上资料来源
2026/10/15 14:57:30 网站建设 项目流程

大家好,我是一言,继续更新 Spring AI 2.0 系列。

前几篇解决了怎么找资料。到了实际使用,用户还会追问:这句话依据哪份文档?

微软的开源 RAG 示例把这种体验做得很直观:答案旁有引用编号,下方的Citations列出 PDF 文件和页码。看图中第一条回答,依据指向Benefit_Options.pdf第 3 页。

这篇把答案和来源一起返回:模型选择引用编号,Java 校验编号,再补上文件名、章节和原文。用户就能顺着引用核对回答。

我们先实现后端的来源回填,暂不做图中的 PDF 跳页和逐句引用。

先看实际返回什么

问“电子发票的抬头写错了怎么办?”,本地模型返回:

{"answer":"电子发票的抬头写错了,应先申请红冲原发票,再按正确抬头重新开具。","citationIds":["S1"]}

Java 根据S1找到本次检索的资料,补出:

{"id":"S1","file":"invoice.md","section":"修改抬头","text":"电子发票抬头写错后,应先申请红冲原发票,再按正确抬头重新开具。"}

文件名和原文取自后端保存的资料,模型只负责选编号。页面可以把来源做成展开项,查看对应章节和原文。

给本次检索结果编号

准备四段演示资料,分别记录银行卡退款、银行卡注销、定制退货和发票抬头修改。每段保留来源标签:

Document.builder().id("invoice-title").text("电子发票抬头写错后,应先申请红冲原发票,再按正确抬头重新开具。").metadata("source","invoice.md").metadata("section","修改抬头").build();

检索取 Top 2,再给它们分配当前请求的编号:

varsources=newLinkedHashMap<String,Document>();for(inti=0;i<retrieved.size();i++){sources.put("S"+(i+1),retrieved.get(i));}

发给模型的上下文按下面的格式组织:

[S1] 第一份检索资料的原文 [S2] 第二份检索资料的原文

S1只在当前请求里有效。下一次提问的S1可能对应另一份资料,校验时要保留本次映射。

用 Java 对象接住引用

复用前面讲过的 Structured Output:

图左侧对应getFormat(),告诉模型按什么格式返回;右侧对应convert(),把返回文本接成下面的Draft对象。

recordDraft(Stringanswer,List<String>citationIds){}varconverter=newBeanOutputConverter<>(Draft.class);Stringraw=client.prompt().system(system).user("资料:\n"+context(sources)+"\n问题:"+question+"\n"+converter.getFormat()).call().content();Draftdraft=converter.convert(raw);

提示词要求:只引用直接支持回答的资料,citationIds填S1这样的编号,不生成文件名或网址。

Spring AI 提供模型调用和对象转换。引用协议、编号映射和校验由我们实现,转换成功也不代表编号合法。

编号先校验,再返回

校验的核心是:引用必须出现在本次候选中。

for(Stringid:draft.citationIds()){Documentdoc=sources.get(id);if(doc==null){thrownewIllegalArgumentException("UNKNOWN_CITATION: "+id);}// 从 doc 取文件名、章节和原文,组装 sources 返回}

完整实现还检查空答案、空引用,并去重。资料不足时模型可以返回空引用,这版示例会拦下该结果;产品中可另设“资料不足”状态。

我用固定输入验证了三个异常场景:

NEGATIVE invented=S99 rejected=true NEGATIVE outsideRequest=invoice-title rejected=true NEGATIVE missingCitation rejected=true

即使invoice-title是知识库中已有的文档 ID,也不能绕过本次S1/S2映射直接引用。这三条是 Java 校验测试,没有让模型故意生成错误编号。

有引用,仍要看原文

真实模型测试用了 Java 21、Spring AI 2.0.1,Qwen3-Embedding 0.6B 和 Qwen2.5 3B。向量在内存中按余弦相似度排序,三道题各调用一次回答模型。

银行卡注销这题,回答带出了人工处理要求,也返回了对应章节和原文:

问题返回来源原文核对
银行卡注销后退款多久到银行卡注销章节回答包含人工处理和到账限制
定制商品不想要了能否退定制商品章节引用有效,回答漏掉质量问题条件
发票抬头写错怎么办修改抬头章节回答与红冲重开规则一致

第二题尤其值得看:

S1确实存在,校验会通过;原文中的“存在质量问题时”,却被回答省略了。编号校验负责拦住无效引用,原文核对才能发现这种条件遗漏。

本地执行RUN_REAL_MODEL_TEST=true mvn test,结果为:

Tests run: 5, Failures: 0, Errors: 0, Skipped: 0 BUILD SUCCESS

其中四项检查引用解析与异常,另一项执行三道题的真实模型对照。引用让答案有据可查,无法自动保证每句话都正确。


作者:一言。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询