RAG源代码笔记JAVA-高级RAG

一、压缩查询检索Query_Compression

背景:当用户问出代词时,指代是上文查询的名词,需要将名词替换进去,也就是对两次提问的压缩。

核心代码:

java 复制代码
// We will create a CompressingQueryTransformer, which is responsible for compressing
        // the user's query and the preceding conversation into a single, stand-alone query.
        // This should significantly improve the quality of the retrieval process.
        QueryTransformer queryTransformer = new CompressingQueryTransformer(chatModel);

最终一个能压缩查询的rag就返回好了,如下:检索增强是rag起点,两个属性查询转换是对于用户上下文查询进行一个压缩,压缩的llm可以和模型的llm不一致。和一个内容检索器,很好理解。

java 复制代码
 ContentRetriever contentRetriever = EmbeddingStoreContentRetriever.builder()
                .embeddingStore(embeddingStore)
                .embeddingModel(embeddingModel)
                .maxResults(2)
                .minScore(0.6)
                .build();

        // The RetrievalAugmentor serves as the entry point into the RAG flow in LangChain4j.
        // It can be configured to customize the RAG behavior according to your requirements.
        // In subsequent examples, we will explore more customizations.
        RetrievalAugmentor retrievalAugmentor = DefaultRetrievalAugmentor.builder()
                .queryTransformer(queryTransformer)
                .contentRetriever(contentRetriever)
                .build();

        return AiServices.builder(Assistant.class)
                .chatModel(chatModel)
                .retrievalAugmentor(retrievalAugmentor)
                .chatMemory(MessageWindowChatMemory.withMaxMessages(10))
                .build();

二、查询路由检索Query_Routing

背景:当用户的rag来源有很多时,有git仓库里的,文档中的,数据库中的等等,可能需要多个contentRetriever,很不方便,这时就需要Query_Routing

核心代码:将不同的contentRetriver映射到map中,设置到retrievalAugmentor检索增强中。

java 复制代码
 // Let's create a query router.
        Map<ContentRetriever, String> retrieverToDescription = new HashMap<>();
        retrieverToDescription.put(biographyContentRetriever, "biography of John Doe");
        retrieverToDescription.put(termsOfUseContentRetriever, "terms of use of car rental company");
        QueryRouter queryRouter = new LanguageModelQueryRouter(chatModel, retrieverToDescription);

        RetrievalAugmentor retrievalAugmentor = DefaultRetrievalAugmentor.builder()
                .queryRouter(queryRouter)
                .build();

构建返回Assistant:

java 复制代码
 return AiServices.builder(Assistant.class)
                .chatModel(chatModel)
                .retrievalAugmentor(retrievalAugmentor)
                .chatMemory(MessageWindowChatMemory.withMaxMessages(10))
                .build();

三、重排序re-ranking

背景:在最开始的初始检索阶段,经常会用更快更高效的模型处理大量数据,但是会有检索不匹配甚至幻觉的代价。

核心代码:源码利用的是cohere的评分模型,内容聚合器ContentAggregator对小于0.8分数的进行过滤。cohere是一个挺厉害的公司,可以了解一下。

java 复制代码
        // To register and get a free API key for Cohere, please visit the following link:
        // https://dashboard.cohere.com/welcome/register
        ScoringModel scoringModel = CohereScoringModel.builder()
                .apiKey(System.getenv("COHERE_API_KEY"))
                .modelName("rerank-multilingual-v3.0")
                .build();

        ContentAggregator contentAggregator = ReRankingContentAggregator.builder()
                .scoringModel(scoringModel)
                .minScore(0.8) // we want to present the LLM with only the truly relevant segments for the user's query
                .build();

构建返回Assistant:

java 复制代码
        RetrievalAugmentor retrievalAugmentor = DefaultRetrievalAugmentor.builder()
                .contentRetriever(contentRetriever)
                .contentAggregator(contentAggregator)
                .build();

        ChatModel model = OpenAiChatModel.builder()
                .apiKey(OPENAI_API_KEY)
                .modelName(GPT_4_O_MINI)
                .build();

        return AiServices.builder(Assistant.class)
                .chatModel(model)
                .retrievalAugmentor(retrievalAugmentor)
                .chatMemory(MessageWindowChatMemory.withMaxMessages(10))
                .build();

四、元数据Metadata

背景:将文档和元数据融入到llm提示词中

核心代码:分割后的每个片段,会把「文本内容 + file_name + index + 向量」绑定存储;只有当检索到相似片段时,才会把这些信息(文本 + 文件名 + 索引)注入到 Prompt 中。

java 复制代码
 ContentRetriever contentRetriever = EmbeddingStoreContentRetriever.builder()
                .embeddingStore(embeddingStore)
                .embeddingModel(embeddingModel)
                .build();

        // Each retrieved segment should include "file_name" and "index" metadata values in the prompt
        ContentInjector contentInjector = DefaultContentInjector.builder()
                // .promptTemplate(...) // Formatting can also be changed
                .metadataKeysToInclude(asList("file_name", "index"))
                .build();

构建Assistant:

java 复制代码
RetrievalAugmentor retrievalAugmentor = DefaultRetrievalAugmentor.builder()
                .contentRetriever(contentRetriever)
                .contentInjector(contentInjector)
                .build();

        ChatModel chatModel = OpenAiChatModel.builder()
                .apiKey(OPENAI_API_KEY)
                .modelName(GPT_4_O_MINI)
                .logRequests(true)
                .build();

        return AiServices.builder(Assistant.class)
                .chatModel(chatModel)
                .retrievalAugmentor(retrievalAugmentor)
                .chatMemory(MessageWindowChatMemory.withMaxMessages(10))
                .build();

五、元数据过滤Metadata_Filter

背景:可以过滤掉一些内容,增加检索准确性。

核心代码:对于文本贴上元数据标签,进行过滤。

java 复制代码
        TextSegment dogsSegment = TextSegment.from("Article about dogs ...", metadata("animal", "dog"));
  Filter onlyDogs = metadataKey("animal").isEqualTo("dog");

        ContentRetriever contentRetriever = EmbeddingStoreContentRetriever.builder()
                .embeddingStore(embeddingStore)
                .embeddingModel(embeddingModel)
                .filter(onlyDogs) // by specifying the static filter, we limit the search to segments only about dogs
                .build();

六、检索跳过Skip_Retrieval

背景:有些提问不需要检索,可以路由到一个空的路由检索器中。

核心代码:

java 复制代码
 // Let's create a query router.
        QueryRouter queryRouter = new QueryRouter() {

            private final PromptTemplate PROMPT_TEMPLATE = PromptTemplate.from(
                    "Is the following query related to the business of the car rental company? " +
                            "Answer only 'yes', 'no' or 'maybe'. " +
                            "Query: {{it}}"
            );

            @Override
            public Collection<ContentRetriever> route(Query query) {

                Prompt prompt = PROMPT_TEMPLATE.apply(query.text());

                AiMessage aiMessage = chatModel.chat(prompt.toUserMessage()).aiMessage();
                System.out.println("LLM decided: " + aiMessage.text());

                if (aiMessage.text().toLowerCase().contains("no")) {
                    return emptyList();
                }

                return singletonList(contentRetriever);
            }
        };

构建Assistant: 依旧是增强+builder

java 复制代码
RetrievalAugmentor retrievalAugmentor = DefaultRetrievalAugmentor.builder()
                .queryRouter(queryRouter)
                .build();

        return AiServices.builder(Assistant.class)
                .chatModel(chatModel)
                .retrievalAugmentor(retrievalAugmentor)
                .chatMemory(MessageWindowChatMemory.withMaxMessages(10))
                .build();

七、多个内容检索器Multiple_Retrievers

背景:有多个文档

核心代码:和一个是一样的,不过是再写一遍罢了。可以结合路由一起看,一般都是一起使用的。

java 复制代码
// Let's create our second content retriever.
        EmbeddingStore<TextSegment> embeddingStore2 =
                embed(toPath("documents/biography-of-john-doe.txt"), embeddingModel);
        ContentRetriever contentRetriever2 = EmbeddingStoreContentRetriever.builder()
                .embeddingStore(embeddingStore2)
                .embeddingModel(embeddingModel)
                .maxResults(2)
                .minScore(0.6)
                .build();

 // Let's create a query router that will route each query to both retrievers.
        QueryRouter queryRouter = new DefaultQueryRouter(contentRetriever1, contentRetriever2);

        RetrievalAugmentor retrievalAugmentor = DefaultRetrievalAugmentor.builder()
                .queryRouter(queryRouter)
                .build();

背景:联网搜索,This example requires "langchain4j-web-search-engine-tavily" dependency.

核心代码:

java 复制代码
 // Let's create our web search content retriever.
        WebSearchEngine webSearchEngine = TavilyWebSearchEngine.builder()
                .apiKey(System.getenv("TAVILY_API_KEY")) // get a free key: https://app.tavily.com/sign-in
                .build();

        ContentRetriever webSearchContentRetriever = WebSearchContentRetriever.builder()
                .webSearchEngine(webSearchEngine)
                .maxResults(3)
                .build();

// Let's create a query router that will route each query to both retrievers.
        QueryRouter queryRouter = new DefaultQueryRouter(embeddingStoreContentRetriever, webSearchContentRetriever);

        RetrievalAugmentor retrievalAugmentor = DefaultRetrievalAugmentor.builder()
                .queryRouter(queryRouter)
                .build();

九、返回检索源Return_Sources

背景:想要知道来源

核心代码:你看不到 answer 的具体实现(因为 createAssistant() 会通过 LangChain4j 的 AiServices 动态生成),但它底层执行的是完整的 RAG 流程。关键:Result<String> 不是普通字符串!------ 它返回的不是 "一句话",而是「带溯源的回答对象]

java 复制代码
interface Assistant {

        Result<String> answer(String query);
    }

    public static void main(String[] args) {

        Assistant assistant = createAssistant();

        Logger log = LoggerFactory.getLogger(shared.Assistant.class);
        try (Scanner scanner = new Scanner(System.in)) {
            while (true) {
                log.info("==================================================");
                log.info("User: ");
                String userQuery = scanner.nextLine();
                log.info("==================================================");

                if ("exit".equalsIgnoreCase(userQuery)) {
                    break;
                }

                Result<String> result = assistant.answer(userQuery);
                log.info("==================================================");
                log.info("Assistant: " + result.content());

                log.info("Sources: ");
                List<Content> sources = result.sources();
                sources.forEach(content -> log.info(content.toString()));
            }
        }
    }

十、数据库检索SQL_Database_Retreiver

背景:如题,不建议用于生产,不能保证其无害。In this example we will use an in-memory H2 database with 3 tables: customers, products and orders. * See "resources/sql" directory for more details. *This example requires "langchain4j-experimental-sql" dependency.

核心代码:

java 复制代码
 DataSource dataSource = createDataSource();

        ChatModel chatModel = OpenAiChatModel.builder()
                .apiKey(OPENAI_API_KEY)
                .modelName(GPT_4_O_MINI)
                .build();

        ContentRetriever contentRetriever = SqlDatabaseContentRetriever.builder()
                .dataSource(dataSource)
                .chatModel(chatModel)
                .build();
java 复制代码
  private static DataSource createDataSource() {

        JdbcDataSource dataSource = new JdbcDataSource();
        dataSource.setURL("jdbc:h2:mem:test;DB_CLOSE_DELAY=-1");
        dataSource.setUser("sa");
        dataSource.setPassword("sa");

        String createTablesScript = read("sql/create_tables.sql");
        execute(createTablesScript, dataSource);

        String prefillTablesScript = read("sql/prefill_tables.sql");
        execute(prefillTablesScript, dataSource);

        return dataSource;
    }

    private static String read(String path) {
        try {
            return new String(Files.readAllBytes(toPath(path)));
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    private static void execute(String sql, DataSource dataSource) {
        try (Connection connection = dataSource.getConnection(); Statement statement = connection.createStatement()) {
            for (String sqlStatement : sql.split(";")) {
                statement.execute(sqlStatement.trim());
            }
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }
相关推荐
Lumos_yuan8 小时前
WHAT IS AI ? ANI OR AGI
ai·agi·ani
Agent手记9 小时前
跨境电商如何用AI Agent自动运营多平台店铺?企业级「龙虾」矩阵智能体全流程落地指南
大数据·人工智能·ai·矩阵
冬奇Lab10 小时前
Agent系列(七):知识库集成——Agent 调用 RAG 的正确姿势
人工智能·agent
程序员小假10 小时前
我们来说说 Agent 记忆压缩通常有哪些方法?
agent
智者知已应修善业11 小时前
【51单片机8位数码管动态显示日期小数点风格】2023-11-13
c++·经验分享·笔记·算法·51单片机
智者知已应修善业11 小时前
【51单片机有三个LED 分别第一个灯闪三下 再到第二个灯又闪三下 再到第三个灯又闪三下 就这样循环程序】2023-11-16
c++·经验分享·笔记·算法·51单片机
暴躁小师兄数据学院11 小时前
【AI大数据工程师特训笔记】第04讲:PostgreSQL 数据库内置函数详解
大数据·数据库·笔记·ai·语言模型
这是谁的博客?11 小时前
Mamba 状态空间模型深度解析:挑战 Transformer 的新一代架构
深度学习·ai·架构·transformer·ssm·mamba·状态空间模型
笨蛋©11 小时前
[实战] 2026年工程图纸数字化技术指南:GD&T识别与检验计划自动化
ai·数字化·质量管理·制造业·fai
大模型真好玩12 小时前
大模型训练全流程实战指南工具篇(十三)—— 大模型评测实战(数据集评测+自动化评测)
人工智能·agent·deepseek