最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

使用Java實(shí)現(xiàn)RAG(檢索增強(qiáng)生成)的完整指南

 更新時(shí)間:2026年04月26日 09:34:15   作者:花千樹_010  
RAG(Retrieval-Augmented Generation,檢索增強(qiáng)生成) 是目前最主流的企業(yè) AI 應(yīng)用模式,這篇文章主要為大家詳細(xì)介紹了使用Java實(shí)現(xiàn)RAG的相關(guān)方法,文中的示例代碼講解詳細(xì),有需要的小伙伴可以了解下

適合人群:需要構(gòu)建知識(shí)庫(kù)問答系統(tǒng)的 Java 開發(fā)者

核心技術(shù):RAG、向量數(shù)據(jù)庫(kù) Milvus、文本 Embedding

什么是 RAG?

RAG(Retrieval-Augmented Generation,檢索增強(qiáng)生成) 是目前最主流的企業(yè) AI 應(yīng)用模式。

解決的問題:大模型的訓(xùn)練數(shù)據(jù)有截止日期,且不包含你的私有數(shù)據(jù)(內(nèi)部文檔、產(chǎn)品手冊(cè)、規(guī)章制度等)。RAG 的思路是:

用戶提問 → 在私有知識(shí)庫(kù)中檢索相關(guān)內(nèi)容 → 將檢索結(jié)果 + 問題一起發(fā)給 LLM → 得到基于私有知識(shí)的回答

優(yōu)勢(shì)

  • 無需微調(diào)模型,成本極低
  • 知識(shí)庫(kù)可隨時(shí)更新,實(shí)時(shí)生效
  • 可溯源,能告訴用戶答案來自哪個(gè)文檔

完整 RAG 流程

PDF/Word文檔
     ↓
[文檔加載器] PdfboxLoader / ApachePoiDocxLoader
     ↓
[文本切分器] StanfordNLPTextSplitter
     ↓
[Embedding 模型] OllamaEmbeddings
     ↓
[向量數(shù)據(jù)庫(kù)] Milvus(存儲(chǔ))
     ↓ (查詢時(shí))
用戶問題 → [向量檢索] → 相關(guān)文檔塊 → [LLM] → 最終回答

前置配置

RAG Pipeline 依賴 Milvus 向量數(shù)據(jù)庫(kù)和 Tesseract OCR,需要在 application.yml 中顯式開啟:

rag:
  ocr:
    tesseract:
      use: true   # 啟用 Tesseract OCR(PDF 圖片文字識(shí)別)
  vector:
    milvus:
      use: true   # 啟用 Milvus 向量數(shù)據(jù)庫(kù)

說明:j-langchain 通過 @ConditionalOnProperty 控制這兩個(gè)組件的初始化,默認(rèn)不加載。只有配置 use: true 后,TesseractActuatorMilvusContainer Bean 才會(huì)被注冊(cè)到 Spring 容器中。未開啟時(shí)運(yùn)行 RAG 相關(guān)代碼會(huì)因 Bean 缺失而報(bào)錯(cuò)。

Step 1:加載文檔

j-langchain 內(nèi)置多種文檔加載器:

加載 PDF

@Test
public void loadPdfDocuments() {
    PdfboxLoader loader = PdfboxLoader.builder()
        .filePath("./files/pdf/en/Transformer.pdf")
        .build();
    loader.setExtractImages(false);  // 不提取圖片,只提取文本

    List<Document> documents = loader.load();

    System.out.println("總頁(yè)數(shù):" + documents.size());
    // 每個(gè) Document 對(duì)應(yīng) PDF 的一頁(yè)
}

加載 Word 文檔

ApachePoiDocxLoader loader = ApachePoiDocxLoader.builder()
    .filePath("./files/docx/en/Transformer.docx")
    .build();

List<Document> documents = loader.load();

每個(gè) Document 對(duì)象包含:

  • pageContent:頁(yè)面文本內(nèi)容
  • metadata:來源、頁(yè)碼等元數(shù)據(jù)

Step 2:文本切分

PDF 文檔一頁(yè)可能有幾千字,直接 Embedding 效果差,而且超出 LLM 的 context window。需要將長(zhǎng)文檔切分為小塊:

@Test
public void splitDocuments() {
    List<Document> documents = loader.load();
    System.out.println("切分前:" + documents.size() + " 頁(yè)");

    StanfordNLPTextSplitter splitter = StanfordNLPTextSplitter.builder()
        .chunkSize(1000)    // 每塊最多 1000 字符
        .chunkOverlap(100)  // 相鄰塊重疊 100 字符,保證上下文連貫
        .build();

    List<Document> splits = splitter.splitDocument(documents);
    System.out.println("切分后:" + splits.size() + " 塊");
}

為什么需要 chunkOverlap?

如果一句話跨兩個(gè)塊,重疊部分確保這句話完整出現(xiàn)在至少一個(gè)塊中,避免語(yǔ)義截?cái)唷?/p>

Step 3:向量化并存入 Milvus

將每個(gè)文本塊轉(zhuǎn)換為向量(浮點(diǎn)數(shù)數(shù)組),存入向量數(shù)據(jù)庫(kù):

@Test
public void embedAndStore() {
    // ... 加載、切分文檔 ...

    VectorStore vectorStore = Milvus.fromDocuments(
        splits,
        OllamaEmbeddings.builder()
            .model("nomic-embed-text")  // 本地 Embedding 模型,免費(fèi)
            .vectorSize(768)            // 向量維度
            .build(),
        "MyKnowledgeBase"               // Milvus collection 名稱
    );

    System.out.println("向量化完成!");
}

為什么用本地 Embedding?

nomic-embed-text 是一個(gè)高質(zhì)量的開源 Embedding 模型,通過 Ollama 本地運(yùn)行:

  • 零成本:不需要調(diào) OpenAI API
  • 隱私安全:數(shù)據(jù)不出本地
  • 效果好:在中英文 Embedding 上表現(xiàn)優(yōu)秀

啟動(dòng) Milvus(Docker 一鍵啟動(dòng)):

docker run -d --name milvus \
  -p 19530:19530 \
  milvusdb/milvus:latest standalone

Step 4:完整 RAG 問答鏈

這是核心步驟:用用戶的問題檢索相關(guān)文檔塊,拼接上下文,讓 LLM 基于上下文回答:

@Test
public void retrieveAndAsk() {
    // 假設(shè)文檔已經(jīng)存入 Milvus...
    BaseRetriever retriever = vectorStore.asRetriever();

    BaseRunnable<StringPromptValue, ?> prompt = PromptTemplate.fromTemplate(
        """
        請(qǐng)根據(jù)以下文檔內(nèi)容回答問題。如果文檔中沒有相關(guān)信息,請(qǐng)說"文檔中未找到相關(guān)信息"。
        
        文檔內(nèi)容:
        ${context}
        
        問題:${question}
        
        回答:
        """
    );

    Function<Object, String> formatDocs = input -> {
        List<Document> docs = (List<Document>) input;
        StringBuilder sb = new StringBuilder();
        for (Document doc : docs) {
            sb.append(doc.getPageContent()).append("\n\n");
        }
        return sb.toString();
    };

    FlowInstance ragChain = chainActor.builder()
        .next(retriever)   // 向量檢索:輸入問題,返回相關(guān)文檔列表
        .next(formatDocs)  // 將文檔列表拼接為字符串
        .next(input -> Map.of(
            "context",  input,
            "question", ContextBus.get().getFlowParam()  // 獲取原始問題
        ))
        .next(prompt)
        .next(llm)
        .next(new StrOutputParser())
        .build();

    ChatGeneration result = chainActor.invoke(
        ragChain,
        "Transformer 模型中的注意力機(jī)制是如何工作的?"
    );

    System.out.println(result.getText());
}

鏈路圖解

"Transformer注意力機(jī)制..." 
    → retriever(相似度檢索,返回最相關(guān)的5個(gè)文檔塊)
    → formatDocs(拼接文檔塊為字符串)
    → prompt(組裝 Prompt:上下文 + 問題)
    → LLM(基于上下文生成回答)
    → StrOutputParser(提取文本)
    → "注意力機(jī)制通過計(jì)算 Query、Key、Value..."

Step 5:文檔摘要(輕量版)

不需要向量庫(kù)的簡(jiǎn)單場(chǎng)景——直接讓 LLM 讀文檔摘要:

@Test
public void documentSummary() {
    // 加載 PDF
    List<Document> documents = loader.load();
    String content = documents.stream()
        .map(Document::getPageContent)
        .collect(Collectors.joining("\n"));

    // 長(zhǎng)文檔截取首尾
    String textToSummarize = content.length() < 2000 ? content
        : content.substring(0, 1000) + "\n...\n" + content.substring(content.length() - 1000);

    FlowInstance chain = chainActor.builder()
        .next(PromptTemplate.fromTemplate("請(qǐng)對(duì)以下內(nèi)容摘要(100字以內(nèi)):\n\n${text}"))
        .next(ChatOllama.builder().model("qwen2.5:0.5b").build())
        .next(new StrOutputParser())
        .build();

    ChatGeneration result = chainActor.invoke(chain, Map.of("text", textToSummarize));
    System.out.println(result.getText());
}

RAG vs 直接問 LLM

對(duì)比項(xiàng)直接問 LLMRAG
私有知識(shí)不知道知道(來自你的文檔)
知識(shí)時(shí)效性訓(xùn)練截止日期實(shí)時(shí)更新
回答可溯源不行可以(返回來源文檔)
成本稍高(Embedding + 向量庫(kù))
幻覺風(fēng)險(xiǎn)低(基于真實(shí)文檔)

完整架構(gòu)

離線階段(建庫(kù)):
文檔 → 加載 → 切分 → Embedding → Milvus

在線階段(問答):
問題 → Embedding → Milvus檢索 → 拼接上下文 → LLM → 回答

到此這篇關(guān)于使用Java實(shí)現(xiàn)RAG(檢索增強(qiáng)生成)的完整指南的文章就介紹到這了,更多相關(guān)Java實(shí)現(xiàn)RAG內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 滴滴二面之Kafka如何讀寫副本消息的

    滴滴二面之Kafka如何讀寫副本消息的

    這篇文章主要給大家介紹了關(guān)于滴滴二面之Kafka如何讀寫副本消息的相關(guān)資料,文中通過實(shí)例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2022-01-01
  • Java實(shí)現(xiàn)中序表達(dá)式的實(shí)例代碼

    Java實(shí)現(xiàn)中序表達(dá)式的實(shí)例代碼

    這篇文章主要介紹了Java實(shí)現(xiàn)中序表達(dá)式的實(shí)例代碼,需要的朋友可以參考下
    2018-08-08
  • springboot項(xiàng)目同時(shí)啟動(dòng)web服務(wù)和grpc服務(wù)的方法

    springboot項(xiàng)目同時(shí)啟動(dòng)web服務(wù)和grpc服務(wù)的方法

    本文主要介紹了springboot項(xiàng)目同時(shí)啟動(dòng)web服務(wù)和grpc服務(wù)的方法,通過實(shí)際代碼示例展示了實(shí)現(xiàn),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2024-02-02
  • 詳解MyBatis動(dòng)態(tài)代理機(jī)制

    詳解MyBatis動(dòng)態(tài)代理機(jī)制

    MyBatis的動(dòng)態(tài)代理機(jī)制是其核心特性之一,通過該機(jī)制,開發(fā)者可以專注于定義接口和 SQL 映射,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2026-03-03
  • Java如何實(shí)現(xiàn)支付寶電腦支付基于servlet版本

    Java如何實(shí)現(xiàn)支付寶電腦支付基于servlet版本

    這篇文章主要介紹了Java如何實(shí)現(xiàn)支付寶電腦支付基于servlet版本,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2019-11-11
  • SpringBoot 攔截器 (Interceptor)與切面 (AOP)示例、作用及適用場(chǎng)景分析

    SpringBoot 攔截器 (Interceptor)與切面 (AOP)示例、作用及適用

    攔截器是SpringMVC框架的組件,基于Java動(dòng)態(tài)代理實(shí)現(xiàn),作用于Web層(Controller 層),僅能攔截HTTP請(qǐng)求的處理過程,生命周期與 SpringMVC的請(qǐng)求處理流程綁定,本文介紹SpringBoot攔截器 (Interceptor)與切面(AOP)示例、作用及適用場(chǎng)景分析,感興趣的朋友一起看看吧
    2026-01-01
  • Java并發(fā)編程中的ConcurrentLinkedQueue詳解

    Java并發(fā)編程中的ConcurrentLinkedQueue詳解

    這篇文章主要介紹了Java并發(fā)編程中的ConcurrentLinkedQueue詳解,GetThread線程不會(huì)因?yàn)镃oncurrentLinkedQueue隊(duì)列為空而等待,而是直接返回null,所以當(dāng)實(shí)現(xiàn)隊(duì)列不空時(shí),等待時(shí),則需要用戶自己實(shí)現(xiàn)等待邏輯,需要的朋友可以參考下
    2023-12-12
  • Springboot整合通用mapper過程解析

    Springboot整合通用mapper過程解析

    這篇文章主要介紹了springboot整合通用mapper過程解析,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2019-11-11
  • Android中幾種圖片特效的處理的實(shí)現(xiàn)方法

    Android中幾種圖片特效的處理的實(shí)現(xiàn)方法

    這篇文章主要介紹了 Android中幾種圖片特效的處理的實(shí)現(xiàn)方法的相關(guān)資料,這里有放大縮小圖片,獲得圓角圖片,獲得帶倒影圖片的幾種方法,需要的朋友可以參考下
    2017-08-08
  • 詳解Spring Batch 輕量級(jí)批處理框架實(shí)踐

    詳解Spring Batch 輕量級(jí)批處理框架實(shí)踐

    這篇文章主要介紹了詳解Spring Batch 輕量級(jí)批處理框架實(shí)踐,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-06-06

最新評(píng)論

喀什市| 西乌| 常州市| 介休市| 盐亭县| 安顺市| 平利县| 德令哈市| 华蓥市| 乐陵市| 广河县| 抚松县| 海丰县| 湾仔区| 大洼县| 玛沁县| 荥经县| 政和县| 界首市| 徐水县| 铜梁县| 介休市| 平昌县| 额济纳旗| 阜新| 方城县| 达日县| 石台县| 蒙自县| 岱山县| 中山市| 斗六市| 定西市| 万源市| 平罗县| 黑山县| 临邑县| 双柏县| 翁牛特旗| 平舆县| 石嘴山市|