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);
        }
    }
相关推荐
诀窍的心灵4 小时前
deepcode安装实操
ai·deepcode·deepcode安装
生擒小朵拉6 小时前
ROS1学习笔记(二)
笔记·学习
啊阿狸不会拉杆6 小时前
《机器学习》第 1 章 - 机器学习概述
人工智能·机器学习·ai·ml
Root_Hacker6 小时前
include文件包含个人笔记及c底层调试
android·linux·服务器·c语言·笔记·安全·php
CoderJia程序员甲9 小时前
GitHub 热榜项目 - 日榜(2026-01-19)
git·ai·开源·llm·github
哥布林学者10 小时前
吴恩达深度学习课程五:自然语言处理 第二周:词嵌入(三)Word2Vec
深度学习·ai
burning_maple10 小时前
redis笔记
数据库·redis·笔记
魔芋红茶10 小时前
Spring Security 学习笔记 4:用户/密码认证
笔记·学习·spring
googleccsdn10 小时前
ENSP Pro Lab笔记:配置BGP VXLAN双栈(3)
网络·笔记
少林码僧11 小时前
2.30 传统行业预测神器:为什么GBDT系列算法在企业中最受欢迎
开发语言·人工智能·算法·机器学习·ai·数据分析