LangChain4j(15)——RAG高级之跳过检索

[复制链接]
发表于 2025-9-18 02:18:02 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

×
之前的文章中,我们先容了RAG的利用,但是,每次提问时,都会通过RAG举行检索。偶然,检索是不须要实行的,比如,当用户只是说“你好”时。于是,我们必要有条件的跳过检索过程。
跳过决定可以通过多种方式实行:

  • 利用规则(比方,取决于用户的权限、位置等)
  • 利用关键字(比方,如果查询包罗特定单词)
  • 利用语义相似性
  • 利用 LLM 做出决定
留意:本例接纳利用LLM做出决定的方案
测试代码
  1. package com.renr.langchain4jnew.app5;
  2. import com.renr.langchain4jnew.constant.CommonConstants;
  3. import dev.langchain4j.community.model.zhipu.ZhipuAiChatModel;
  4. import dev.langchain4j.data.document.Document;
  5. import dev.langchain4j.data.document.DocumentParser;
  6. import dev.langchain4j.data.document.loader.FileSystemDocumentLoader;
  7. import dev.langchain4j.data.document.parser.TextDocumentParser;
  8. import dev.langchain4j.data.document.splitter.DocumentSplitters;
  9. import dev.langchain4j.data.message.AiMessage;
  10. import dev.langchain4j.data.segment.TextSegment;
  11. import dev.langchain4j.memory.chat.MessageWindowChatMemory;
  12. import dev.langchain4j.model.chat.ChatLanguageModel;
  13. import dev.langchain4j.model.embedding.EmbeddingModel;
  14. import dev.langchain4j.model.embedding.onnx.bgesmallenv15q.BgeSmallEnV15QuantizedEmbeddingModel;
  15. import dev.langchain4j.model.input.Prompt;
  16. import dev.langchain4j.model.input.PromptTemplate;
  17. import dev.langchain4j.rag.DefaultRetrievalAugmentor;
  18. import dev.langchain4j.rag.RetrievalAugmentor;
  19. import dev.langchain4j.rag.content.retriever.ContentRetriever;
  20. import dev.langchain4j.rag.content.retriever.EmbeddingStoreContentRetriever;
  21. import dev.langchain4j.rag.query.Query;
  22. import dev.langchain4j.rag.query.router.QueryRouter;
  23. import dev.langchain4j.service.AiServices;
  24. import dev.langchain4j.store.embedding.EmbeddingStore;
  25. import dev.langchain4j.store.embedding.EmbeddingStoreIngestor;
  26. import dev.langchain4j.store.embedding.inmemory.InMemoryEmbeddingStore;
  27. import java.net.URISyntaxException;
  28. import java.net.URL;
  29. import java.nio.file.Path;
  30. import java.nio.file.Paths;
  31. import java.time.Duration;
  32. import java.util.Collection;
  33. import static java.util.Collections.emptyList;
  34. import static java.util.Collections.singletonList;
  35. public class AdvancedRAG04_SkipRetrieval {
  36.     public static void main(String[] args) throws URISyntaxException {
  37.         ChatLanguageModel chatModel = ZhipuAiChatModel.builder()
  38.                 // 模型key
  39.                 .apiKey(CommonConstants.API_KEY)
  40.                 // 精确度
  41.                 .temperature(0.9)
  42.                 .model("GLM-4-Flash")
  43.                 .maxRetries(3)
  44.                 .callTimeout(Duration.ofSeconds(60))
  45.                 .connectTimeout(Duration.ofSeconds(60))
  46.                 .writeTimeout(Duration.ofSeconds(60))
  47.                 .readTimeout(Duration.ofSeconds(60))
  48.                 .logRequests(true)
  49.                 .logResponses(true)
  50.                 .build();
  51.         URL fileUrl = AdvancedRAG01_QueryCompression.class.getClassLoader().getResource("document/miles-of-smiles使用条款.txt");
  52.         Path path = Paths.get(fileUrl.toURI());
  53.         // 指定文档解析器
  54.         DocumentParser documentParser = new TextDocumentParser();
  55.         // 加载文档数据
  56.         Document document = FileSystemDocumentLoader.loadDocument(path, documentParser);
  57.         EmbeddingModel embeddingModel = new BgeSmallEnV15QuantizedEmbeddingModel();
  58.         EmbeddingStore<TextSegment> embeddingStore = new InMemoryEmbeddingStore<>();
  59.         EmbeddingStoreIngestor ingestor = EmbeddingStoreIngestor.builder()
  60.                 .documentSplitter(DocumentSplitters.recursive(300, 0))
  61.                 .embeddingModel(embeddingModel)
  62.                 .embeddingStore(embeddingStore)
  63.                 .build();
  64.         ingestor.ingest(document);
  65.         ContentRetriever contentRetriever = EmbeddingStoreContentRetriever.builder()
  66.                 .embeddingStore(embeddingStore)
  67.                 .embeddingModel(embeddingModel)
  68.                 .maxResults(2)
  69.                 .minScore(0.6)
  70.                 .build();
  71.         // 自定义QueryRouter,当需要跳过检索时,QueryRouter 将返回一个空列表
  72.         QueryRouter queryRouter = new QueryRouter() {
  73.             // 设置提示词模版
  74.             private final PromptTemplate PROMPT_TEMPLATE = PromptTemplate.from(
  75.                     "以下提问是否与汽车租赁公司的业务有关?只回答'是'、'否'或'可能'。提问内容:{{it}}"
  76.             );
  77.             @Override
  78.             public Collection<ContentRetriever> route(Query query) {
  79.                 // 提示词对象中,关联用户的提问内容
  80.                 Prompt prompt = PROMPT_TEMPLATE.apply(query.text());
  81.                 // 针对用户的提问,先通过大模型判断是否满足我们的提示词的要求
  82.                 AiMessage aiMessage = chatModel.chat(prompt.toUserMessage()).aiMessage();
  83.                 System.out.println("LLM decided: " + aiMessage.text());
  84.                 // 如果大模型返回 否,则返回空列表,表示不进行检索
  85.                 if (aiMessage.text().toLowerCase().contains("否")) {
  86.                     return emptyList();
  87.                 }
  88.                 return singletonList(contentRetriever);
  89.             }
  90.         };
  91.         RetrievalAugmentor retrievalAugmentor = DefaultRetrievalAugmentor.builder()
  92.                 .queryRouter(queryRouter)
  93.                 .build();
  94.         Assistant assistant = AiServices.builder(Assistant.class)
  95.                 .chatLanguageModel(chatModel)
  96.                 .retrievalAugmentor(retrievalAugmentor)
  97.                 .chatMemory(MessageWindowChatMemory.withMaxMessages(10))
  98.                 .build();
  99.         // 第一次提问,不会走检索过程
  100.         String answer1 = assistant.chat("你好");
  101.         System.out.println(answer1);
  102.         // 第二次提问,执行检索
  103.         String answer2 = assistant.chat("我可以取消预定吗?");
  104.         System.out.println(answer2);
  105.     }
  106. }
复制代码
本例自界说QueryRouter,当必要跳过检索时,QueryRouter 将返回一个空列表,表现不必要颠末任何检索对象。
实行流程

 第一次提问"你好"时
先发送是否必要走检索的提问:
  1. 13:29:30.244 [main] DEBUG dev.langchain4j.community.model.zhipu.RequestLoggingInterceptor - Request:
  2. - method: POST
  3. - url: https://open.bigmodel.cn/api/paas/v4/chat/completions
  4. - headers: [Authorization: Bearer ey...fQ.ew0KICAiZXhwIiA6IDE3NDU5OTI3NzAxNjAsDQogICJhcGlfa2V5IiA6ICJkMjMzNGIyZTk4NTE0YWUwOTIzMDQ4YTM4NDEzNTMwNiIsDQogICJ0aW1lc3RhbXAiIDogMTc0NTk5MDk3MDE2MA0KfQ.fwtna_6nALdYVqiTM_jJphWexVme4FRJxg-Lc1AXOTk]
  5. - body: {
  6.   "model" : "GLM-4-Flash",
  7.   "messages" : [ {
  8.     "role" : "user",
  9.     "content" : "以下提问是否与汽车租赁公司的业务有关?只回答'是'、'否'或'可能'。提问内容:你好"
  10.   } ],
  11.   "stream" : false,
  12.   "temperature" : 0.9,
  13.   "max_tokens" : 512,
  14.   "tool_choice" : "auto"
  15. }
复制代码
自动先根据我们例子中设置的提示词模版,扣问是否与“汽车租赁公司业务相干”。
大模型根据提问内容,给出的回复:
  1. {
  2.         "choices": [{
  3.                 "finish_reason": "stop",
  4.                 "index": 0,
  5.                 "message": {
  6.                         "content": "否",
  7.                         "role": "assistant"
  8.                 }
  9.         }],
  10.         "created": 1745990971,
  11.         "id": "2025043013293102690e02a7674aa7",
  12.         "model": "GLM-4-Flash",
  13.         "request_id": "2025043013293102690e02a7674aa7",
  14.         "usage": {
  15.                 "completion_tokens": 3,
  16.                 "prompt_tokens": 33,
  17.                 "total_tokens": 36
  18.         }
  19. }
复制代码
通过响应看出,大模型返回了"否",表现不消举行检索。
发送用户提出的题目
  1. 13:29:31.281 [main] DEBUG dev.langchain4j.community.model.zhipu.RequestLoggingInterceptor - Request:
  2. - method: POST
  3. - url: https://open.bigmodel.cn/api/paas/v4/chat/completions
  4. - headers: [Authorization: Bearer ey...fQ.ew0KICAiZXhwIiA6IDE3NDU5OTI3NzAxNjAsDQogICJhcGlfa2V5IiA6ICJkMjMzNGIyZTk4NTE0YWUwOTIzMDQ4YTM4NDEzNTMwNiIsDQogICJ0aW1lc3RhbXAiIDogMTc0NTk5MDk3MDE2MA0KfQ.fwtna_6nALdYVqiTM_jJphWexVme4FRJxg-Lc1AXOTk]
  5. - body: {
  6.   "model" : "GLM-4-Flash",
  7.   "messages" : [ {
  8.     "role" : "user",
  9.     "content" : "你好"
  10.   } ],
  11.   "stream" : false,
  12.   "temperature" : 0.9,
  13.   "max_tokens" : 512,
  14.   "tool_choice" : "auto"
  15. }
复制代码
可以看到这次只发送了"你好",并没有通过RAG举行检索。
第二次提问“我可以取消预定吗”时
仍旧必要先扣问大模型是否必要检索
发送哀求数据如下:
  1. 13:29:31.818 [main] DEBUG dev.langchain4j.community.model.zhipu.RequestLoggingInterceptor - Request:
  2. - method: POST
  3. - url: https://open.bigmodel.cn/api/paas/v4/chat/completions
  4. - headers: [Authorization: Bearer ey...fQ.ew0KICAiZXhwIiA6IDE3NDU5OTI3NzEyODEsDQogICJhcGlfa2V5IiA6ICJkMjMzNGIyZTk4NTE0YWUwOTIzMDQ4YTM4NDEzNTMwNiIsDQogICJ0aW1lc3RhbXAiIDogMTc0NTk5MDk3MTI4MQ0KfQ.iLHSgUhm6XqAB5LDPHXq2NnTUQ_NAiv0VE4ATTQbNWc]
  5. - body: {
  6.   "model" : "GLM-4-Flash",
  7.   "messages" : [ {
  8.     "role" : "user",
  9.     "content" : "以下提问是否与汽车租赁公司的业务有关?只回答'是'、'否'或'可能'。提问内容:我可以取消预定吗?"
  10.   } ],
  11.   "stream" : false,
  12.   "temperature" : 0.9,
  13.   "max_tokens" : 512,
  14.   "tool_choice" : "auto"
  15. }
复制代码
大模型以为用户的提问于知识库相干,于是返回内容:
  1. {
  2.         "choices": [{
  3.                 "finish_reason": "stop",
  4.                 "index": 0,
  5.                 "message": {
  6.                         "content": "是",
  7.                         "role": "assistant"
  8.                 }
  9.         }],
  10.         "created": 1745990972,
  11.         "id": "20250430132932c36dbc932d2a4d9d",
  12.         "model": "GLM-4-Flash",
  13.         "request_id": "20250430132932c36dbc932d2a4d9d",
  14.         "usage": {
  15.                 "completion_tokens": 3,
  16.                 "prompt_tokens": 37,
  17.                 "total_tokens": 40
  18.         }
  19. }
复制代码
大模型返回“是”,表现必要举行检索。
再次发送哀求
哀求数据中包罗第一次的用户提问和大模型回复的内容
  1. 13:29:32.050 [main] DEBUG dev.langchain4j.community.model.zhipu.RequestLoggingInterceptor - Request:
  2. - method: POST
  3. - url: https://open.bigmodel.cn/api/paas/v4/chat/completions
  4. - headers: [Authorization: Bearer ey...fQ.ew0KICAiZXhwIiA6IDE3NDU5OTI3NzE4MTgsDQogICJhcGlfa2V5IiA6ICJkMjMzNGIyZTk4NTE0YWUwOTIzMDQ4YTM4NDEzNTMwNiIsDQogICJ0aW1lc3RhbXAiIDogMTc0NTk5MDk3MTgxOA0KfQ.A7e2fMdaX_6oI5uaS6oRaZs8HRiHm9ixCsWtyiSaPJM]
  5. - body: {
  6.   "model" : "GLM-4-Flash",
  7.   "messages" : [ {
  8.     "role" : "user",
  9.     "content" : "你好"
  10.   }, {
  11.     "role" : "assistant",
  12.     "content" : "你好\uD83D\uDC4B!有什么可以帮助你的吗?"
  13.   }, {
  14.     "role" : "user",
  15.     "content" : "我可以取消预定吗?\n\nAnswer using the following information:\n8. 本条款的变更 我们可能随时通过修改此页面来修改这些使用条款。您应该不时查看此页面,以注意我们所做的任何更改。\n9. 接受这些条款 使用服务,即表示您承认您已阅读并理解这些条款,并同意受其约束。如果您不同意这些条款,请不要使用或访问我们的服务。\n\n1. 引言\n本服务条款(以下简称“条款”)适用于您(个人)从世界任何国家/地区访问或使用由在美国注册的 Miles of Smiles Car Rental Services 公司提供的应用程序、网站、内容、产品和服务(以下简称“服务”)。\n2. 服务\nMiles of Smiles 将车辆出租给最终用户。我们保留随时暂时或永久终止服务的权利,并且不对服务的任何修改、暂停或中止负责。\n3. 预订\n3.1 用户可以通过我们的网站或移动应用程序进行预订。\n3.2 您必须在预订过程中提供准确、最新和完整的信息。您负责您账户下产生的所有费用。\n3.3 所有预订均视车辆供应情况而定\n4. 取消政策"
  16.   } ],
  17.   "stream" : false,
  18.   "temperature" : 0.9,
  19.   "max_tokens" : 512,
  20.   "tool_choice" : "auto"
  21. }
复制代码
从哀求数据中可以看到,针对第二次提出的题目,举行了检索。 
通过上面的过程,可以看出,利用自界说QueryRouter后,每次发送题目之前,必要先根据我们的提示词模版,向大模型发送一个题目,让大模型判断是否必要举行检索。如果返回“否”,表现不必要举行检索;反之,必要通过RAG举行检索。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

登录后关闭弹窗

登录参与点评抽奖  加入IT实名职场社区
去登录
快速回复 返回顶部 返回列表