LangChain4j 系統化知識學習筆記(接入模型、AiService、持久化記憶、增強RAG)

文章目錄

    • 前言
  • 一、認識**LangChain4j**
    • 1.1、歷史背景
    • 1.2、主要功能
    • 1.3、場景
  • 二、SpringBoot接入大模型
    • 2.1、項目基本配置 & pom引入依賴
    • 2.2、接入大模型
      • 2.2.1、**LangChain4j** 庫結構
      • 2.2.2、引入LangChain4j相關依賴
      • 2.2.3、補充LangChain4j單測來驗證與gpt交互
    • 2.3、整合langchain4j-springboot-starter
      • 2.3.1、替換補充langchain的starter依賴
      • 2.3.2、application.properties補充大模型配置
      • 2.3.3、補充啟動器單測
  • 三、接入其他大模型
    • 3.1、認識市面上大模型
    • 3.2、接入Deepseek
      • 3.2.1、獲取開發參數
      • 3.2.2、配置開發參數
      • 3.2.3、application.properties配置模型參數
    • 3.3、ollama本地部署
      • 3.3.1、認識ollama
      • 3.3.2、常用命令介紹
      • 3.3.3、SpringBoot快速集成ollama
        • 1)引入pom.xml
        • 2)配置模型參數
        • 3)補充測試用例
    • 3.4、接入阿里百煉平臺
      • 3.4.1、認識阿里百煉平臺
      • 3.4.2、配置百煉開發參數
      • 3.4.3、SpringBoot快速集成百煉
        • 1)引入pom.xml依賴
        • 2)配置模型參數
        • 3)補充測試用例
      • 3.4.4、實戰案例集成:通義萬象
      • 3.4.5、實戰案例集成:百煉deepseek
  • 四、人工智能AiService
    • 4.1、認識AiService
    • 4.2、創建AiService服務
      • 4.2.1、引入pom依賴
      • 4.2.2、實現Assistant接口(aiservice)
      • 4.2.3、aiservice實例化測試驗證
        • 方式一:代碼方式手動實例化aiservice并指定ai大模型驗證
        • 方式二:spring依賴注入aiservice服務
      • 4.2.4、工作原理介紹
    • 4.3、聊天記憶 chat memory
      • 4.3.1、測試驗證普通chat對話是否有記憶
      • 4.3.2、聊天記憶的核心原理簡單實現(實現記憶效果)
      • 4.3.3、基于ChatMemory實現聊天記憶
      • 4.3.4、AiService注解模式實現ChatMemory增強
        • 4.3.4.1、創建記憶對話智能體MemoryChatAssistant
        • 4.3.4.2、配置ChatMemory
        • 4.3.4.3、測試驗證
    • 4.4、隔離聊天記錄
      • 4.4.1、創建記憶隔離對話智能體
      • 4.4.2、配置ChatMemoryProvider
      • 4.4.3、測試驗證用戶隔離對話
    • 4.5、持久化聊天記憶
      • 4.5.1、存儲介質選擇
      • 4.5.2、選擇MongoDB
        • 4.5.2.1、認識MongoDB
        • 4.5.2.2、快速安裝MongoDB
      • 4.5.3、快速整合SpringBoot-mongo
      • 4.5.4、持久化聊天核心實現
        • 4.5.4.1、自定義持久化ChatMemoryStore
        • 4.5.4.2、SeparateChatAssistantConfig注入持久化消息存儲對象
    • 4.6、提示詞Prompt
      • 4.6.1、系統提示詞
        • 4.6.1.1、配置@SystemMessage
        • 4.6.1.2、測試系統參數
        • 4.6.1.3、從資源中加載系統提示詞模板 fromTemplate
      • 4.6.2、用戶提示詞
        • 4.6.2.1、兩種方式配置用戶提示詞
        • 4.6.2.2、測試用戶提示詞+系統提示詞
    • 4.7、Function Calling函數調用
      • 4.7.1、入門案例
        • 4.7.1.1、創建tools計算器工具類
        • 4.7.1.2、配置工具類
        • 4.7.1.3、測試驗證function calling
        • Function calling函數調用原理探究
      • 4.7.2、**@Tool** 注解的可選字段
      • 4.7.3、@P注解
      • 4.7.4、@ToolMemoryId(支持在tool中去使用memoryId)
  • 五、檢索增強生成RAG
    • 5.1、如何讓大模型回答專業領域的知識
      • 5.1.1、微調大模型
      • 5.1.2、RAG
      • 5.1.3、RAG常用方法
    • 5.2、向量搜索vector search
      • 5.2.1、向量Vectors
      • 5.2.2、維度Dimensions
      • 5.2.3、相似度Similarity
      • 5.2.4、相似度測量Measures of similarity
    • 5.3、RAG過程
      • 5.3.1、索引階段
      • 5.3.2、檢索階段
    • 5.4、文檔加載器Document Loader
      • 5.4.1、常見文檔加載器Loader
      • 5.4.2、測試文檔加載器
    • 5.5、文檔解析器Parser
      • 5.5.1、常見文檔解析器
      • 5.5.2、本地測試文檔解析器
    • 5.6、文檔分割器 Document Splitter
      • 5.6.1、常見文檔分割器
      • 5.6.2、測試向量轉換和向量存儲
      • 5.6.3、測試文檔分割
      • 5.6.4、token和token計算
      • 5.6.5、工作方式
  • 六、向量模型和向量存儲
    • 6.1、向量大模型
      • 6.1.1、介紹
      • 6.1.2、模型配置
      • 6.1.3、文本向量化測試驗證
    • 6.2、向量化存儲
      • 6.2.1、Pinecone簡介
      • 6.2.2、Pinecone的使用
      • 6.2.3、Pinecone賬號注冊與配置
      • 6.2.4、集成Pinecone
        • 6.2.4.1、pom引入Pinecone依賴
        • 6.2.4.2、配置環境變量
        • 6.2.4.3、配置向量存儲對象EmbeddingStore
        • 6.2.4.4、測試向量存儲
        • 6.2.4.5、測試向量相似度匹配
    • 資料獲取

封面

前言

博主介紹:?目前全網粉絲3W+,csdn博客專家、Java領域優質創作者,博客之星、阿里云平臺優質作者、專注于Java后端技術領域。

涵蓋技術內容:Java后端、大數據、算法、分布式微服務、中間件、前端、運維等。

博主所有博客文件目錄索引:博客目錄索引(持續更新)

CSDN搜索:長路

視頻平臺:b站-Coder長路


一、認識LangChain4j

簡介:LangChain4j 的目標是簡化將大語言模型(LLM - Large Language Model)集成到 Java 應用程序中的過程。

適用jdk版本:

  • 0.35.0:最后適用jdk8
  • 其他:后續升級到jdk17。

1.1、歷史背景

官網: https://docs.langchain4j.dev

2022 年11月30日OpenAI發布了Chat GPT(GPT-3.5)

  • 早在 2022 年10月,Harrison Chase 發布了基于Python的LangChain。
  • 隨后同時包含了Python版和JavaScript(LangChain.js)版的LangChain 也發布了。

2023 年 11 月,Quarkus 發布了 LangChain4j 的 0.1 版本,2025 年 2 月發布了 1.0 - Beta1 版本,4 月發布了1.0 - Beta3 版本。

1.2、主要功能

與大型語言模型和向量數據庫的便捷交互

  • 通過統一的應用程序編程接口(API),可以輕松訪問所有主要的商業和開源大型語言模型以及向量數據庫,使你能夠構建聊天機器人、智能助手等應用。

專為 Java 打造

  • 借助Spring Boot 集成,能夠將大模型集成到ava 應用程序中。大型語言模型與 Java 之間實現了雙向集成:你可以從 Java 中調用大型語言模型,同時也允許大型語言模型反過來調用你的 Java 代碼

智能代理、工具、檢索增強生成(RAG)

  • 為常見的大語言模型操作提供了廣泛的工具,涵蓋從底層的提示詞模板創建、聊天記憶管理和輸出解析,到智能代理和檢索增強生成等高級模式。

1.3、場景

**場景1、**你想要實現一個自定義的由人工智能驅動的聊天機器人,它可以訪問你的數據,并按照你期望的方式運行:

  • 客戶支持聊天機器人,它可以:禮貌地回答客戶問題。

  • 處理 / 更改 / 取消訂單。

  • 教育助手,它可以:教授各種學科。

  • 解釋不清楚的部分:評估用戶的理解 / 知識水平。

**場景2:**你想要處理大量的非結構化數據(文件、網頁等),并從中提取結構化信息。例如:

  • 從客戶評價和支持聊天記錄中提取有效評價。
  • 從競爭對手的網站上提取有趣的信息。
  • 從求職者的簡歷中提取有效信息。

場景3:你想要生成信息,例如:

  • 為你的每個客戶量身定制的電子郵件。
  • 為你的應用程序 / 網站生成內容:
    • 博客文章
    • 故事

場景4:你想要轉換信息,例如:

  • 總結
  • 校對和改寫
  • 翻譯

二、SpringBoot接入大模型

2.1、項目基本配置 & pom引入依賴

創建maven項目,選擇jdk17

image-20250518020454988

image-20250518021259549

pom.xml:引入依賴與版本管理

<properties><maven.compiler.source>17</maven.compiler.source><maven.compiler.target>17</maven.compiler.target><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><spring-boot.version>3.2.6</spring-boot.version><knife4j.version>4.3.0</knife4j.version><langchain4j.version>1.0.0-beta3</langchain4j.version><mybatis-plus.version>3.5.11</mybatis-plus.version>
</properties><dependencies><!-- web應用程序核心依賴 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><!-- 編寫和運行測試用例 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><!-- 前后端分離中的后端接口測試工具 --><dependency><groupId>com.github.xiaoymin</groupId><artifactId>knife4j-openapi3-jakarta-spring-boot-starter</artifactId><version>${knife4j.version}</version></dependency>
</dependencies><dependencyManagement><dependencies><!--引入SpringBoot依賴管理清單--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-dependencies</artifactId><version>${spring-boot.version}</version><type>pom</type><scope>import</scope></dependency></dependencies>
</dependencyManagement>

LangChain4jApplication.java:補充SpringBoot啟動器

@SpringBootApplication
public class LangChain4jApplication {public static void main(String[] args) {SpringApplication.run(LangChain4jApplication.class, args);}}

application.properties:

# web服務訪問端口
server.port=8080

啟動服務訪問網站:http://localhost:8080/doc.html#/home,驗證下

image-20250518021442067


2.2、接入大模型

參考文檔: Get Started https://docs.langchain4j.dev/get-started

2.2.1、LangChain4j 庫結構

LangChain4j 具有模塊化設計,包括:

  1. langchain4j-core 模塊,它定義了核心抽象概念(如聊天語言模型和嵌入存儲)及其 API。

  2. 主 langchain4j 模塊,包含有用的工具,如文檔加載器、聊天記憶實現,以及諸如人工智能服務等高層功能。

  3. 大量的 langchain4j-{集成} 模塊,每個模塊都將各種大語言模型提供商和嵌入存儲集成到

LangChain4j 中。你可以獨立使用 langchain4j-{集成} 模塊。如需更多功能,只需導入主 langchain4j依賴項即可。


2.2.2、引入LangChain4j相關依賴

pom.xml中再次引入LangChain4j相關依賴:

<properties><langchain4j.version>1.0.0-beta3</langchain4j.version>
</properties><dependencies><!-- 基于open-ai的langchain4j接口:ChatGPT、deepseek都是open-ai標準下的大模型 --><dependency><groupId>dev.langchain4j</groupId><artifactId>langchain4j-open-ai</artifactId></dependency>
</dependencies><dependencyManagement><dependencies><!--引入langchain4j依賴管理清單--><dependency><groupId>dev.langchain4j</groupId><artifactId>langchain4j-bom</artifactId><version>${langchain4j.version}</version><type>pom</type><scope>import</scope></dependency></dependencies>
</dependencyManagement>

2.2.3、補充LangChain4j單測來驗證與gpt交互

image-20250518022506388

package com.changlu.ai.langchain4j;import dev.langchain4j.model.openai.OpenAiChatModel;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;/*** @description  start chat with openai gpt* @author changlu* @date 2025/5/18 02:25*/
@SpringBootTest
public class LLMTest {@Testpublic void testGPTDemo() {// 初始化模型OpenAiChatModel model = OpenAiChatModel.builder().baseUrl("http://langchain4j.dev/demo/openai/v1") //設置模型api地址(如果apiKey="demo",則可省略baseUrl的配置),新版本不能忽視.apiKey("demo")  //設置模型apiKey.modelName("gpt-4o-mini") //設置模型名稱.build();// 回答的結果String answer = model.chat("你好");System.out.println(answer);}}

運行下單測,結果如下:

image-20250518022618687

2.3、整合langchain4j-springboot-starter

2.2我們只是去初步嘗試與gpt進行交互,這里我們直接引入springboot-starter來完成langchain4j的集成。

2.3.1、替換補充langchain的starter依賴

pom.xml補充:

<!-- 基于open-ai的langchain4j接口:ChatGPT、deepseek都是open-ai標準下的大模型 -->
<!--        <dependency>-->
<!--            <groupId>dev.langchain4j</groupId>-->
<!--            <artifactId>langchain4j-open-ai</artifactId>-->
<!--        </dependency>-->
<dependency><groupId>dev.langchain4j</groupId><artifactId>langchain4j-open-ai-spring-boot-starter</artifactId>
</dependency>

2.3.2、application.properties補充大模型配置

application.properties配置文件新增內容如下:

#langchain4j測試模型
langchain4j.open-ai.chat-model.base-url=http://langchain4j.dev/demo/openai/v1
langchain4j.open-ai.chat-model.api-key=demo
langchain4j.open-ai.chat-model.model-name=gpt-4o-mini
#請求和響應日志
langchain4j.open-ai.chat-model.log-requests=true
langchain4j.open-ai.chat-model.log-responses=true
#啟用日志debug級別
logging.level.root=debug

2.3.3、補充啟動器單測

image-20250518023231791

import dev.langchain4j.model.openai.OpenAiChatModel;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;@SpringBootTest
public class LLMSpringBootTest {/*** 整合SpringBoot,讀取配置文件配置*/@Autowiredprivate OpenAiChatModel openAiChatModel;@Testpublic void testSpringBoot() {//向模型提問String answer = openAiChatModel.chat("你好,請問你是什么大模型");//輸出結果System.out.println(answer);}}

運行測試:

image-20250518023300169


三、接入其他大模型

3.1、認識市面上大模型

大語言模型排行榜: https://superclueai.com/,可查看目前市面上ai最新表單

image-20250518223111094

SuperCLUE 是由國內 CLUE 學術社區于 2023 年 5 月推出的中文通用大模型綜合性評測基準。

  • 評測目的:全面評估中文大模型在語義理解、邏輯推理、代碼生成等 10 項基礎能力,以及涵蓋數學、物理、社科等 50 多學科的專業能力,旨在回答在通用大模型發展背景下,中文大模型的效果情況,包括不同任務效果、與國際代表性模型的差距、與人類的效果對比等問題。

  • 特色優勢:針對中文特性任務,如成語、詩歌、字形等設立專項評測,使評測更符合中文語言特點。通過 3700 多道客觀題和匿名對戰機制,動態追蹤國內外主流模型,如 GPT-4、文心一言、通義千問等的表現差異,保證評測的客觀性和時效性。

  • 行業影響:作為中文領域權威測評社區,其評測結果被學界和產業界廣泛引用,例如商湯 “日日新5.0” 和百度文心大模型均通過 SuperCLUE 驗證技術突破,推動了中文 NLP 技術生態的迭代,為中文大模型的發展和優化提供了重要的參考依據,促進了中文大模型技術的不斷進步和應用。

LangChain4j支持接入的大模型:https://docs.langchain4j.dev/integrations/language-models/

image-20250518223343975

  • deepseek是遵循openai規范,這里如果支持openai,也就是支持deepseek。

3.2、接入Deepseek

3.2.1、獲取開發參數

訪問官網: https://www.deepseek.com/ 注冊賬號

進入開放平臺:https://platform.deepseek.com/usage,獲取base_url和api_key

3.2.2、配置開發參數

為了apikay的安全,建議將其配置在服務器的環境變量中。變量名自定義即可,例如:

DEEP_SEEK_API_KEY

mac配置環境變量如下:

vim ~/.zshrc# 配置內容
export DEEP_SEEK_API_KEY="xxxxxxxxx"# 生效配置文件
source ~/.zshrc

3.2.3、application.properties配置模型參數

image-20250518224030493

#DeepSeek
langchain4j.open-ai.chat-model.base-url=https://api.deepseek.com
langchain4j.open-ai.chat-model.api-key=${DEEP_SEEK_API_KEY}
#DeepSeek-V3
langchain4j.open-ai.chat-model.model-name=deepseek-chat
#DeepSeek-R1 推理模型
#langchain4j.open-ai.chat-model.model-name=deepseek-reasoner

由于deepseek的規范是基于openai的,所以我們這里參數名和一開始的openai一致即可,只是url、key用的是deepseek的。

接下來我們重新跑下LLMSpringBootTest驗證下:提問【你是誰?】

image-20250518224337557

deepseek接口調用通過。

3.3、ollama本地部署

3.3.1、認識ollama

Ollama 是一個本地部署大模型的工具。使用 Ollama 進行本地部署有以下多方面的原因:

  • 數據隱私與安全:對于金融、醫療、法律等涉及大量敏感數據的行業,數據安全至關重要。
  • 離線可用性:在網絡不穩定或無法聯網的環境中,本地部署的 Ollama 模型仍可正常運行。
  • 降低成本:云服務通常按使用量收費,長期使用下來費用較高。而 Ollama 本地部署,只需一次性投入硬件成本,對于需要頻繁使用大語言模型且對成本敏感的用戶或企業來說,能有效節約成本。
  • 部署流程簡單:只需通過簡單的命令 “ollama run < 模型名>”,就可以自動下載并運行所需的模型。
  • 靈活擴展與定制:可對模型微調,以適配垂直領域需求。

官網: https://ollama.com/

(1)下載并安裝ollama: OllamaSetup.exe

(2)查看模型列表,選擇要部署的模型,模型列表: https://ollama.com/search

(3)執行命令: ollama run deepseek-r1:1.5 運行大模型。如果是第一次運行則會先下載大模型

ollama run deepseek-r1:1.5b

image-20250518224802360

3.3.2、常用命令介紹

image-20250518224857515

3.3.3、SpringBoot快速集成ollama

1)引入pom.xml

參考文檔: https://docs.langchain4j.dev/integrations/language-models/ollama#get-started

<!-- 接入ollama -->
<dependency><groupId>dev.langchain4j</groupId><artifactId>langchain4j-ollama-spring-boot-starter</artifactId>
</dependency>
2)配置模型參數

注意:這里的參數名與之前langchain & deepseek配置不同,這里是langchain4j.ollama

#ollama
langchain4j.ollama.chat-model.base-url=http://localhost:11434
langchain4j.ollama.chat-model.model-name=deepseek-r1:1.5b
langchain4j.ollama.chat-model.log-requests=true
langchain4j.ollama.chat-model.log-responses=true
3)補充測試用例

OllamaSpringBootTest.java:

package com.changlu.ai.langchain4j;import dev.langchain4j.model.ollama.OllamaChatModel;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;@SpringBootTest
public class OllamaSpringBootTest {// 注意這里注入的是OllamaChatModel,讀取的ollama的配置項@Autowiredprivate OllamaChatModel ollamaChatModel;@Testpublic void test() {String message = ollamaChatModel.chat("你是誰?");System.out.println(message);}
}

image-20250518225423625


3.4、接入阿里百煉平臺

3.4.1、認識阿里百煉平臺

阿里云百煉:是 2023 年 10 月推出的。它集成了阿里的通義系列大模型和第三方大模型,涵蓋文本、圖像、音視頻等不同模態。

功能優勢:集成超百款大模型 API,模型選擇豐富;5-10 分鐘就能低代碼快速構建智能體,應用構建高效;提供全鏈路模型訓練、評估工具及全套應用開發工具,模型服務多元;在線部署可按需擴縮容,新用戶有千萬 token 免費送,業務落地成本低。

支持接入的模型列表: https://help.aliyun.com/zh/model-studio/models

模型廣場: https://bailian.console.aliyun.com/?productCode=p_efm#/model-market

進入廣場后,任意選擇模型,點擊詳情,默認百煉給我們每一個模型提供了100萬token使用:

image-20250519004542673

**如何生成key呢?**左下角有一個API-KEY注冊即可

image-20250519004629703

若是想要指定使用哪一個模型,指定這個code名即可:

image-20250519004912712

3.4.2、配置百煉開發參數

配置apiKey:配置在環境變量DASH_SCOPE_API_KEY中

vim ~/.zshrc# 配置內容
export DASH_SCOPE_API_KEY="xxxxxxxxx"# 生效配置文件
source ~/.zshrc

3.4.3、SpringBoot快速集成百煉

1)引入pom.xml依賴
<dependencies><!-- 接入阿里云百煉平臺 --><dependency><groupId>dev.langchain4j</groupId><artifactId>langchain4j-community-dashscope-spring-boot-starter</artifactId></dependency>
</dependencies><dependencyManagement><dependencies><!--引入百煉依賴管理清單--><dependency><groupId>dev.langchain4j</groupId><artifactId>langchain4j-community-bom</artifactId><version>${langchain4j.version}</version><type>pom</type><scope>import</scope></dependency></dependencies>
</dependencyManagement>
2)配置模型參數
#百煉
langchain4j.community.dashscope.chat-model.api-key=${DASH_SCOPE_API_KEY}
langchain4j.community.dashscope.chat-model.model-name=qwen-plus-latest

我們這里選擇的是最新的model:

image-20250519005901280

說明:這里沒有配置baseurl,是因為默認依賴包自帶的。

如果想要去指定,可以任意選擇一個模型,點擊查看API,如:https://bailian.console.aliyun.com/?productCode=p_efm&tab=api#/api/?type=model&url=https%3A%2F%2Fhelp.aliyun.com%2Fdocument_detail%2F2712576.html

image-20250519005737496

3)補充測試用例

image-20250519005812278

package com.changlu.ai.langchain4j;import dev.langchain4j.community.model.dashscope.QwenChatModel;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;/*** @description  百煉平臺測試* @author changlu* @date 2025/5/19 00:54*/
@SpringBootTest
public class TongyiSpringBootTest {@Autowiredprivate QwenChatModel qwenChatModel;@Testpublic void test() {String message = qwenChatModel.chat("你是誰");System.out.println(message);}}

測試結果如下:

image-20250519005831723

3.4.4、實戰案例集成:通義萬象

image-20250519005952491

選擇一個圖片生成模型:https://bailian.console.aliyun.com/?tab=model&accounttraceid=7e798ad0e507456cb5fddc6e66d33511phhb#/model-market?capabilities=%5B%22IG%22%5D&z_type_=%7B%22capabilities%22%3A%22array%22%7D

點擊API參考:https://bailian.console.aliyun.com/?tab=api&accounttraceid=7e798ad0e507456cb5fddc6e66d33511phhb#/api/?type=model&url=https%3A%2F%2Fhelp.aliyun.com%2Fdocument_detail%2F2862677.html

參考官方給出的Java的SDK調用:

image-20250519010112957

默認免費額度500張,這里選擇該模型-wanx2.1-t2i-plus:

image-20250519010320191

本地編寫單測用例:

image-20250519010447884

@Test
public void testTongyiwanxiang() {// 目前只支持這種方式調用WanxImageModel wanxImageModel = WanxImageModel.builder().modelName("wanx2.1-t2i-plus").apiKey(System.getenv("DASH_SCOPE_API_KEY")).build();Response<Image> response = wanxImageModel.generate("奇幻森林精靈:在一片彌漫著輕柔薄霧的\n" +"                古老森林深處,陽光透過茂密枝葉灑下金色光斑。一位身材嬌小、長著透明薄翼的精靈少女站在一朵碩大的蘑菇上。她\n" +"                有著海藻般的綠色長發,發間點綴著藍色的小花,皮膚泛著珍珠般的微光。身上穿著由翠綠樹葉和白色藤蔓編織而成的\n" +"                連衣裙,手中捧著一顆散發著柔和光芒的水晶球,周圍環繞著五彩斑斕的蝴蝶,腳下是鋪滿苔蘚的地面,蘑菇和蕨類植\n" +"                物叢生,營造出神秘而夢幻的氛圍。");System.out.println(response.content().url());
}

運行之后,會打印生成的圖片字符串:

image-20250519010535727

效果如下:

image-20250519010606685

3.4.5、實戰案例集成:百煉deepseek

模型平臺,選擇deepseek:

image-20250519010709165

同樣給出了100萬token使用,點擊API參考即可。

image-20250519011044817

配置文件使用原始2.3中使用的openai即可,依賴使用相同,我們需要將歷史的配置文件來進行覆蓋:

# 百煉平臺 deepseek
#集成百煉-deepseek
langchain4j.open-ai.chat-model.base-url=https://dashscope.aliyuncs.com/compatible-mode/v1
langchain4j.open-ai.chat-model.api-key=${DASH_SCOPE_API_KEY}
langchain4j.open-ai.chat-model.model-name=deepseek-v3
#溫度系數:取值范圍通常在 0 到 1 之間。值越高,模型的輸出越隨機、富有創造性;
# 值越低,輸出越確定、保守。這里設置為 0.9,意味著模型會有一定的隨機性,生成的回復可能會比較多樣化。
langchain4j.open-ai.chat-model.temperature=0.9

該url可以通過API文檔找到:

image-20250519011143456

測試驗證下:

image-20250519011217415

@SpringBootTest
public class LLMSpringBootTest {/*** 整合SpringBoot,讀取配置文件配置*/@Autowiredprivate OpenAiChatModel openAiChatModel;@Testpublic void testSpringBoot() {//向模型提問String answer = openAiChatModel.chat("你是誰?");//輸出結果System.out.println(answer);}}

測試如下:

image-20250519011442381


四、人工智能AiService

4.1、認識AiService

AIService使用面向接口和動態代理的方式完成程序的編寫,更靈活的實現高級功能。

**認識鏈:**鏈的概念源自 Python 中的 LangChain。其理念是針對每個常見的用例都設置一條鏈,比如聊天機器人、檢索增強生成(RAG)等。鏈將多個底層組件組合起來,并協調它們之間的交互。鏈存在的主要問題是不靈活,我們不進行深入的研究。

**應用落地場景:**在LangChain4j中我們使用AIService完成復雜操作。底層組件將由AIService進行組裝。

AIService:可處理最常見的操作

  • 為大語言模型格式化輸入內容
  • 解析大語言模型的輸出結果

它們還支持更高級的功能:

  • 聊天記憶 Chat memory
  • 工具 Tools
  • 檢索增強生成 RAG

4.2、創建AiService服務

4.2.1、引入pom依賴

<!--langchain4j高級功能-->
<dependency><groupId>dev.langchain4j</groupId><artifactId>langchain4j-spring-boot-starter</artifactId>
</dependency>

4.2.2、實現Assistant接口(aiservice)

image-20250524224359675

實現接口類:Assistant

package com.changlu.ai.langchain4j.assistant;public interface Assistant {String chat(String userMessage);}

4.2.3、aiservice實例化測試驗證

方式一:代碼方式手動實例化aiservice并指定ai大模型驗證

image-20250524224532696

@SpringBootTest
public class AIServiceTest {// 方式一:api代碼模式創建Assistant代理類,實現aiservice的bean實例創建@Autowiredprivate QwenChatModel qwenChatModel;@Testpublic void testChat() {// 創建aiservice,指定大模型 千問Assistant assistant = AiServices.create(Assistant.class, qwenChatModel);// 調用service的接口String answer = assistant.chat("你好呀,你是誰,請介紹下自己?");System.out.println(answer);}}

image-20250524224624848


方式二:spring依賴注入aiservice服務

首先我們需要給Assistant接口來進行使用注解,來完成aiservice被spring容器掃描進行實例化。

image-20250524225528438

// 這里大模型明確指定(EXPLICIT)模型的beanName,即千問
@AiService(wiringMode = AiServiceWiringMode.EXPLICIT, chatModel = "qwenChatModel")
public interface Assistant {String chat(String userMessage);}

接著編寫測試類來進行注入測試驗證:

image-20250524225516966

// 方式二:直接依賴注入Assistant實現類
@Autowired
private Assistant assistant;@Test
public void testChatByAssistant() {String answer = assistant.chat("你好呀,你是誰,請再介紹下自己?");System.out.println(answer);
}

測試驗證如下:

image-20250524230219876

4.2.4、工作原理介紹

AiServices會組裝Assistant接口以及其他組件,并使用反射機制創建一個實現Assistant接口的代理對象。這個代理對象會處理輸入和輸出的所有轉換工作。在這個例子中,chat方法的輸入是一個字符串,但是大模型需要一個 UserMessage 對象。所以,代理對象將這個字符串轉換為 UserMessage ,并調用聊天語言模型。chat方法的輸出類型也是字符串,但是大模型返回的是 AiMessage 對象,代理對象會將其轉換為字符串。

簡單理解就是:代理對象的作用是輸入轉換和輸出轉換。


4.3、聊天記憶 chat memory

4.3.1、測試驗證普通chat對話是否有記憶

image-20250524231142452

package com.changlu.ai.langchain4j.aiservice;import com.changlu.ai.langchain4j.assistant.Assistant;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;@SpringBootTest
public class AiMemoryTest {@Autowiredprivate Assistant assistant;/*** 測試驗證是否普通chat對話有記憶* @param  * @return void*/@Testpublic void test01_memory() {String answer1 = assistant.chat("我是小明");System.out.println(answer1);String answer2 = assistant.chat("你知道我叫什么嗎?");System.out.println(answer2);}}

image-20250524231133158

很顯然,第二次對話的時候已經沒有了記憶。

4.3.2、聊天記憶的核心原理簡單實現(實現記憶效果)

image-20250525102619859

// 注入千問模型
@Autowired
private QwenChatModel qwenChatModel;/*** 簡單實現聊天記憶* @param* @return void*/
@Test
public void test02_memory() {// 第一輪對話System.out.println("第一輪對話:");UserMessage userMessage1 = UserMessage.userMessage("我是長路");ChatResponse chatResponse1 = qwenChatModel.chat(userMessage1);AiMessage aiMessage1 = chatResponse1.aiMessage();// 獲取到ai message信息System.out.println(aiMessage1);System.out.println();// 第二輪對話System.out.println("第二輪對話:");UserMessage userMessage2 = UserMessage.userMessage("你知道我是誰嗎?");// 將第一次用戶對話 & ai回應消息 & 用戶第二次對話信息 都發送給aiChatResponse chatResponse2 = qwenChatModel.chat(Arrays.asList(userMessage1, aiMessage1, userMessage2));AiMessage aiMessage2 = chatResponse2.aiMessage();System.out.println(aiMessage2);
}

image-20250525102644969

4.3.3、基于ChatMemory實現聊天記憶

前面4.3.2使用的是自己封裝的方式去完成聊天記錄的實現,這里我們直接使用官方給我們提供的實現庫來完成聊天記憶。

使用AIService可以封裝多輪對話的復雜性,使聊天記憶功能的實現變得簡單。

image-20250525103246092

// 注入千問模型
@Autowired
private QwenChatModel qwenChatModel;/*** 基于MessageWindowChatMemory實現類來實現聊天記憶* @param* @return void*/
@Test
public void test03_memory() {// 創建chatMemory(基于內存實現的)MessageWindowChatMemory chatMemory = MessageWindowChatMemory.withMaxMessages(10);// 創建AiServiceAssistant assistant = AiServices.builder(Assistant.class).chatLanguageModel(qwenChatModel).chatMemory(chatMemory).build();String answer1 = assistant.chat("我是長路");System.out.println(answer1);String answer2 = assistant.chat("你知道我是誰嗎?");System.out.println(answer2);String answer3 = assistant.chat("我想知道你是誰?并且使用江蘇話跟我說我是誰?");System.out.println(answer3);
}

image-20250525103331593

4.3.4、AiService注解模式實現ChatMemory增強

當AIService由多個組件(大模型,聊天記憶,等)組成的時候,我們就可以稱他為 智能體 了。

4.3.4.1、創建記憶對話智能體MemoryChatAssistant

image-20250525104816639

package com.changlu.ai.langchain4j.assistant;import dev.langchain4j.service.spring.AiService;
import dev.langchain4j.service.spring.AiServiceWiringMode;// chatModel:指定大模型
// chatMemory:指定記憶體
@AiService(wiringMode = AiServiceWiringMode.EXPLICIT,chatModel = "qwenChatModel",chatMemory = "chatMemory"
)
public interface MemoryChatAssistant {String chat(String message);}
4.3.4.2、配置ChatMemory

image-20250525104902938

@Configuration
public class MemoryChatAssistantConfig {@Beanpublic ChatMemory chatMemory() {// 設置聊天記錄的message數量return MessageWindowChatMemory.withMaxMessages(10);}}
4.3.4.3、測試驗證

image-20250525105411370

@Autowired
private MemoryChatAssistant memoryChatAssistant;
/*** AiService注入形式實現memory記憶* @param* @return void*/
@Test
public void test04_memory() {String answer1 = memoryChatAssistant.chat("我是長路");System.out.println(answer1);System.out.println("=====");String answer2 = memoryChatAssistant.chat("你知道我是誰嗎?");System.out.println(answer2);System.out.println("=====");String answer3 = memoryChatAssistant.chat("我想知道你是誰?并且使用江蘇話跟我說我是誰?");System.out.println(answer3);
}

image-20250525105433516


4.4、隔離聊天記錄

目的:為每個用戶的新聊天或者不同的用戶區分聊天記憶。

4.4.1、創建記憶隔離對話智能體

image-20250525111335236

// chatMemoryProvider:用于編寫lambda表達式,可實現根據memoryId去實現擴展
@AiService(wiringMode = AiServiceWiringMode.EXPLICIT,chatModel = "qwenChatModel",chatMemory = "chatMemory",chatMemoryProvider = "chatMemoryProvider"
)
public interface SeparateChatAssistant {/*** 分離聊天記錄* @param memoryId 聊天id* @param userMessage 用戶消息* @return ai回答結果*/String chat(@MemoryId int memoryId, @UserMessage String userMessage);}	

4.4.2、配置ChatMemoryProvider

image-20250525111435461

@Configuration
public class SeparateChatAssistantConfig {@Beanpublic ChatMemoryProvider chatMemoryProvider() {// 這里封裝了一個接口實現,一個memoryId對應一個用戶同時去維護一組歷史10條記錄return memoryId -> MessageWindowChatMemory.builder().id(memoryId).maxMessages(10).build();}}

4.4.3、測試驗證用戶隔離對話

image-20250525111716275

@Autowired
private SeparateChatAssistant separateChatAssistant;
/*** 實現用戶隔離聊天記錄* @param* @return void*/
@Test
public void test05_memory() {// 用戶1:memoryId=1String answer1 = separateChatAssistant.chat(1, "我是長路");System.out.println(answer1);System.out.println("=====1");String answer2 = separateChatAssistant.chat(1, "你知道我是誰嗎?");System.out.println(answer2);// 用戶2:memoryId=2System.out.println("=====2");String answer3 = separateChatAssistant.chat(2, "我想知道你是誰?并且使用江蘇話跟我說我是誰?");System.out.println(answer3);System.out.println("=====3");// 再次測試用戶1String answer4 = separateChatAssistant.chat(1, "我叫什么名字?");System.out.println(answer4);System.out.println("=====4");
}

測試驗證無誤,做到了用戶回答隔離:

image-20250525111754515


4.5、持久化聊天記憶

默認情況下,聊天記憶存儲在內存中。如果需要持久化存儲,可以實現一個自定義的聊天記憶存儲類,以便將聊天消息存儲在你選擇的任何持久化存儲介質中。

4.5.1、存儲介質選擇

大模型中聊天記憶的存儲選擇哪種數據庫,需要綜合考慮數據特點、應用場景和性能要求等因素,以下是一些常見的選擇及其特點:

1)MySQL

  • 特點:關系型數據庫。支持事務處理,確保數據的一致性和完整性,適用于結構化數據的存儲和查詢。

  • 適用場景:如果聊天記憶數據結構較為規整,例如包含固定的字段如對話 ID、用戶 ID、時間戳、消息內容等,且需要進行復雜的查詢和統計分析,如按用戶統計對話次數、按時間范圍查詢特定對話等,MySQL 是不錯的選擇。

2)Redis

  • 特點:內存數據庫,讀寫速度極高。它適用于存儲熱點數據,并且支持多種數據結構,如字符串、哈希表、列表等,方便對不同類型的聊天記憶數據進行處理。
  • 適用場景:對于實時性要求極高的聊天應用,如在線客服系統或即時通訊工具,Redis 可以快速存儲和獲取最新的聊天記錄,以提供流暢的聊天體驗。

3)MongoDB

  • 特點:文檔型數據庫,數據以 JSON - like 的文檔形式存儲,具有高度的靈活性和可擴展性。它不需要預先定義嚴格的表結構,適合存儲半結構化或非結構化的數據。

  • 適用場景:當聊天記憶中包含多樣化的信息,如文本消息、圖片、語音等多媒體數據,或者消息格式可能會頻繁變化時,MongoDB 能很好地適應這種靈活性。例如,一些社交應用中用戶可能會發送各種格式的消息,使用 MongoDB 可以方便地存儲和管理這些不同類型的數據。

4)Cassandra

  • 特點:是一種分布式的 NoSQL 數據庫,具有高可擴展性和高可用性,能夠處理大規模的分布式數據存儲和讀寫請求。適合存儲海量的、時間序列相關的數據。

  • 適用場景:對于大型的聊天應用,尤其是用戶量眾多、聊天數據量巨大且需要分布式存儲和處理的場景,Cassandra 能夠有效地應對高并發的讀寫操作。例如,一些面向全球用戶的社交媒體平臺,其聊天數據需要在多個節點上進行分布式存儲和管理,Cassandra 可以提供強大的支持。


4.5.2、選擇MongoDB

4.5.2.1、認識MongoDB

MongoDB 是一個基于文檔的 NoSQL 數據庫,由 MongoDB Inc. 開發。

NoSQL,指的是非關系型的數據庫。NoSQL有時也稱作Not Only SQL的縮寫,是對不同于傳統的關系型數據庫的數據庫管理系統的統稱。

MongoDB 的設計理念是為了應對大數據量、高性能和靈活性需求。

MongoDB使用集合(Collections)來組織文檔(Documents),每個文檔都是由鍵值對組成的。

  • 數據庫(Database):存儲數據的容器,類似于關系型數據庫中的數據庫。
  • 集合(Collection):數據庫中的一個集合,類似于關系型數據庫中的表。
  • 文檔(Document):集合中的一個數據記錄,類似于關系型數據庫中的行(row),以 BSON 格式存儲。

MongoDB 將數據存儲為一個文檔,數據結構由鍵值(key=>value)對組成,文檔類似于 JSON 對象,字段值可以包含其他文檔,數組及文檔數組:

image-20250525112113863

4.5.2.2、快速安裝MongoDB

我電腦目前是Mac,這里使用docker快速安裝:

docker run -d --name mongodb -p 27017:27017 mongo:7

客戶端工具使用mongodb-compass:https://www.mongodb.com/try/download/compass

下載即可:

image-20250525112731677

配置下之后快速連接;

image-20250525112842560

測試sql:

  • 查看當前數據庫: db
  • 顯示數據庫列表: show dbs
  • 切換到指定數據庫: use <database_name>
  • 執行查詢操作: db.<collection_name>.find()
  • 插入文檔: db.<collection_name>.insertOne({ … })
  • 更新文檔: db.<collection_name>.updateOne({ … })
  • 刪除文檔: db.<collection_name>.deleteOne({ … })
  • 退出 MongoDB Shell: quit() 或者 exit
# 插入文檔
test> db.mycollection.insertOne({ name: "Alice", age: 30 })
# 查詢文檔
test> db.mycollection.find()
# 更新文檔
test> db.mycollection.updateOne({ name: "Alice" }, { $set: { age: 31 } })
# 刪除文檔
test> db.mycollection.deleteOne({ name: "Alice" })
# 退出 MongoDB Shell
test> quit()

image-20250525113544573

4.5.3、快速整合SpringBoot-mongo

1)pom.xml引入依賴:

<!-- Spring Boot Starter Data MongoDB -->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>

2)application.properties配置文件補充

#MongoDB連接配置
spring.data.mongodb.uri=mongodb://localhost:27017/chat_memory_db

3)創建實體類ChatMessage

image-20250525115519683

@Data
@AllArgsConstructor
@NoArgsConstructor
@Document("chat_messages")
public class ChatMessages {//唯一標識,映射到 MongoDB 文檔的 _id 字段,自動生成@Idprivate ObjectId messageId;
//    private Long messageId;private String content; //存儲當前聊天記錄列表的json字符串}

4)編寫測試類MongoCrudTest:

image-20250525115559013

@SpringBootTest
public class MongoCrudTest {@Autowiredprivate MongoTemplate mongoTemplate;/*** 插入文檔*/
//    @Test
//    public void testInsert() {
//        mongoTemplate.insert(new ChatMessages(1L, "聊天記錄"));
//    }/*** 插入文檔*/@Testpublic void testInsert2() {ChatMessages chatMessages = new ChatMessages();chatMessages.setContent("聊天記錄列表測試一下");mongoTemplate.insert(chatMessages);}/*** 根據id查詢文檔*/@Testpublic void testFindById() {ChatMessages chatMessages = mongoTemplate.findById("683291a72d611b27e4a456fe",ChatMessages.class);System.out.println(chatMessages);}/*** 修改文檔*/@Testpublic void testUpdate() {Criteria criteria = Criteria.where("_id").is("683291a72d611b27e4a456fe");Query query = new Query(criteria);Update update = new Update();update.set("content", "新的聊天記錄列表");//修改或新增mongoTemplate.upsert(query, update, ChatMessages.class);}/*** 新增或修改文檔*/@Testpublic void testUpdate2() {Criteria criteria = Criteria.where("_id").is("100");Query query = new Query(criteria);Update update = new Update();update.set("content", "新的聊天記錄列表");//修改或新增mongoTemplate.upsert(query, update, ChatMessages.class);}/*** 刪除文檔*/@Testpublic void testDelete() {Criteria criteria = Criteria.where("_id").is("100");Query query = new Query(criteria);mongoTemplate.remove(query, ChatMessages.class);}}

測試驗證下即可:

image-20250525115607661


4.5.4、持久化聊天核心實現

4.5.4.1、自定義持久化ChatMemoryStore

image-20250525120541326

通過去實現ChatMemoryStore接口后續再ChatMemoryProvider配置類中完成store組件的替換:

@Component
public class MongoChatMemoryStore implements ChatMemoryStore {@Autowiredprivate MongoTemplate mongoTemplate;// 根據memoryId查詢到一組聊天信息@Overridepublic List<ChatMessage> getMessages(Object memoryId) {Criteria criteria = Criteria.where("memoryId").is(memoryId);Query query = new Query(criteria);ChatMessages chatMessages = mongoTemplate.findOne(query, ChatMessages.class);if(chatMessages == null) return new LinkedList<>();// 反序列化return ChatMessageDeserializer.messagesFromJson(chatMessages.getContent());}// 根據查詢id,更新一組message@Overridepublic void updateMessages(Object memoryId, List<ChatMessage> messages) {Criteria criteria = Criteria.where("memoryId").is(memoryId);Query query = new Query(criteria);Update update = new Update();update.set("content", ChatMessageSerializer.messagesToJson(messages));//根據query條件能查詢出文檔,則修改文檔;否則新增文檔mongoTemplate.upsert(query, update, ChatMessages.class);}// 根據memoryId刪除一組聊天信息記錄@Overridepublic void deleteMessages(Object memoryId) {Criteria criteria = Criteria.where("memoryId").is(memoryId);Query query = new Query(criteria);mongoTemplate.remove(query, ChatMessages.class);}
}
4.5.4.2、SeparateChatAssistantConfig注入持久化消息存儲對象

image-20250525120718485

@Configuration
public class SeparateChatAssistantConfig {@Autowiredprivate MongoChatMemoryStore mongoChatMemoryStore;@Beanpublic ChatMemoryProvider chatMemoryProvider() {// 這里封裝了一個接口實現,一個memoryId對應一個用戶同時去維護一組歷史10條記錄return memoryId -> MessageWindowChatMemory.builder().chatMemoryStore(mongoChatMemoryStore) // 指定持久化消息存儲對象.id(memoryId).maxMessages(10).build();}}

重新運行下單測test05:

image-20250525120803787

此時再看下mongodb中,消息能夠存儲下來。

image-20250525120937103

  • 你可以看到存儲數據中用戶發送的消息type = USER,ai發送的消息為type = AI,后續底層原理實現,同樣是會將數據全部查詢出來一次性發給ai。

持久化之后我再次進行了測試,使用了memoryId為1的來進行聊天:

@Test
public void test05_memory() {String answer1 = separateChatAssistant.chat(1, "你知道我是誰嗎?");System.out.println(answer1);

image-20250525121020839

效果符合預期,知道我的名字。


4.6、提示詞Prompt

4.6.1、系統提示詞

@SystemMessage 設定角色,塑造AI助手的專業身份,明確助手的能力范圍。

4.6.1.1、配置@SystemMessage

@SystemMessage 的內容將在后臺轉換為 SystemMessage 對象,并與 UserMessage 一起發送給大語言模型(LLM)。

  • 發送給大模型的類型為:“type”: “SYSTEM”

image-20250525122313161

@SystemMessage("你是我的好朋友,請用東北話回答問題。")
String chat(@MemoryId int memoryId, @UserMessage String userMessage);

SystemMessaged的內容只會發送給大模型一次。


4.6.1.2、測試系統參數

image-20250525134901554

import com.changlu.ai.langchain4j.assistant.SeparateChatAssistant;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;@SpringBootTest
public class PromptTest {@Autowiredprivate SeparateChatAssistant separateChatAssistant;// 系統提示詞測試@Testpublic void test01_systemPrompt() {String answer = separateChatAssistant.chat(3, "我是長路,請跟我用英語說good morning");System.out.println(answer);}}

image-20250525134955260

看下mongodb中存儲的:

image-20250525135032731

你可以發現在整個聊天對話過程中,會將系統提示詞也發送給ai模型。

注意:如果你修改了SystemMessage的內容,新的SystemMessage會被發送給大模型,之前的聊天記憶會失效


4.6.1.3、從資源中加載系統提示詞模板 fromTemplate

image-20250525155208133

// 設置fromResource,指定文件
@SystemMessage(fromResource = "my-prompt-template.txt")
String chat(@MemoryId int memoryId, @UserMessage String userMessage);

模板文件如下:

  • 說明:{{current_date}}表示當前日期。
你是我的好朋友,請用啟東話回答問題,回答問題的時候適當添加表情符號。今天是 {{current_date}}

測試效果如下:

配置系統提示詞日期前的提示無法確定當前是哪一天:

image-20250525155318598

配置系統提示詞日期時間后,你可以發現實際系統模板參數替換為了當前時間:

image-20250525155521957

回答如下:

image-20250525155542608


4.6.2、用戶提示詞

@UserMessage:獲取用戶輸入

4.6.2.1、兩種方式配置用戶提示詞

方式一:單參數場景,使用{{it}}即可表示當前的入參

@UserMessage("你是我的好朋友,請用上海話回答問題,并且添加一些表情符號。 {{it}}") //{{it}}表示這里
唯一的參數的占位符
String chat(String message);

方式二:多參數場景

@V 明確指定傳遞的參數名稱,可填充到用戶參數中:

@SystemMessage(fromResource = "my-prompt-template.txt")
@UserMessage("請給我語句中添加一些表情符號。 {{userMessage}}") //若是只有一個參數,{{it}}表示這里客戶的入參;若是有多個參數需要補充@V
String chat(@MemoryId int memoryId, @V("userMessage") String userMessage);
4.6.2.2、測試用戶提示詞+系統提示詞

image-20250525160826818

// 用戶提示詞測試
@Test
public void test02_userPrompt() {String answer = separateChatAssistant.chat(4, "請告訴我今年是哪個生肖年");System.out.println(answer);System.out.println("==========");String answer2 = separateChatAssistant.chat(4, "我的幸運年是今年嗎?今天是幾號?");System.out.println(answer2);
}

測試效果如下:

image-20250525160857427

我們來看下mongodb中存儲的對話消息包含哪些?

image-20250525161028822

image-20250525161128950

具體可以看到系統提示詞是全局只有一個的,而用戶提示詞,每一次都會帶上指定的用戶提示詞模板


4.7、Function Calling函數調用

Function Calling 函數調用 也叫 Tools 工具

4.7.1、入門案例

例如,大語言模型本身并不擅長數學運算。如果應用場景中偶爾會涉及到數學計算,我們可以為他提供一個 “數學工具”。當我們提出問題時,大語言模型會判斷是否使用某個工具。

4.7.1.1、創建tools計算器工具類

image-20250525212219458

@Component
public class CalculatorTools {@Tooldouble sum(double a, double b) {System.out.println("調用加法運算");return a + b;}@Tooldouble squareRoot(double x) {System.out.println("調用平方根運算");return Math.sqrt(x);}}

所有聲明Tool注解的,都是function calling,后續可被ai大模型去指定選擇使用的。

4.7.1.2、配置工具類

image-20250525212510238

在指定的智能體中,我們指定聲明該智能體具備的tools:

// chatMemoryProvider:用于編寫lambda表達式,可實現根據memoryId去實現擴展
@AiService(wiringMode = AiServiceWiringMode.EXPLICIT,chatModel = "qwenChatModel",chatMemory = "chatMemory",chatMemoryProvider = "chatMemoryProvider",tools = "calculatorTools" //配置tools
)
public interface SeparateChatAssistant {
4.7.1.3、測試驗證function calling

image-20250525212732767

@SpringBootTest
public class FunctionToolsTest {@Autowiredprivate SeparateChatAssistant separateChatAssistant;@Testpublic void testCalculatorTools() {String answer = separateChatAssistant.chat(1, "1+2等于幾,475695037565的平方根是多少?");//答案:3,689706.4865System.out.println(answer);}}

測試效果如下:

1)沒有配置Tool前,能力非常弱:

image-20250525164524082

2)配置了Tool之后,可以看到已經能夠完成計算了

image-20250525212832798


Function calling函數調用原理探究

原理探究:

提出問題:

1、ai大模型是怎么知道有哪些function calls?

  • 第一次發起提問的時候,就將系統信息、用戶信息、function tools描述信息都發過去了。

2、中間的過程是ai主動發請求回來的嗎?

  • 第一次提問之后,ai會返回一條需要執行function tools消息,本地執行function tools之后,會將歷史消息及本地執行結果統一再發送給ai,如果中間涉及到多次function tools過程,則會重復發起ai請求調用,最終結束后ai得到結果會將結果值作為本次消息的最終結果返回過來。

整體提問過程如下:

核心源碼位置:DefaultAiServices#build中代理類的invoke邏輯

image-20250525174817348

image-20250525174429294


4.7.2、@Tool 注解的可選字段

@Tool 注解有兩個可選字段:

  • name(工具名稱):工具的名稱。如果未提供該字段,方法名會作為工具的名稱。
  • value(工具描述):工具的描述信息。

根據工具的不同,即使沒有任何描述,大語言模型可能也能很好地理解它(例如, add(a, b) 就很直觀),但通常最好提供清晰且有意義的名稱和描述。這樣,大語言模型就能獲得更多信息,以決定是否調用給定的工具以及如何調用。

4.7.3、@P注解

方法參數可以選擇使用 @P 注解進行標注。

@P 注解有兩個字段:

  • value:參數的描述信息,這是必填字段。
  • required:表示該參數是否為必需項,默認值為 true ,此為可選字段。

4.7.4、@ToolMemoryId(支持在tool中去使用memoryId)

如果你的AIService方法中有一個參數使用 @MemoryId 注解,那么你也可以使用 @ToolMemoryId 注解@Tool 方法中的一個參數。提供給AIService方法的值將自動傳遞給 @Tool 方法。如果你有多個用戶,或每個用戶有多個聊天記憶,并且希望在 @Tool 方法中對它們進行區分,那么這個功能會很有用。

image-20250525213215977

@Component
public class CalculatorTools {@Tool(name = "加法", value = "返回兩個參數相加之和")double sum(@ToolMemoryId int memoryId,@P(value="加數1", required = true) double a,@P(value="加數2", required = true) double b) {System.out.println("調用加法運算 " + memoryId);return a + b;}@Tool(name = "平方根", value = "返回給定參數的平方根")double squareRoot(@ToolMemoryId int memoryId, double x) {System.out.println("調用平方根運算 " + memoryId);return Math.sqrt(x);}}

五、檢索增強生成RAG

5.1、如何讓大模型回答專業領域的知識

LLM 的知識僅限于它所訓練的數據。 如果你想讓 LLM 了解特定領域的知識或專有數據,你可以:

  • 使用 RAG
  • 使用你的數據微調 LLM
  • 結合 RAG 和微調

5.1.1、微調大模型

在現有大模型的基礎上,使用小規模的特定任務數據進行再次訓練,調整模型參數,讓模型更精確地處理特定領域或任務的數據。更新需重新訓練,計算資源和時間成本高。

**優點:**一次會話只需一次模型調用,速度快,在特定任務上性能更高,準確性也更高。

缺點:知識更新不及時,模型訓成本高、訓練周期長。

**應用場景:**適合知識庫穩定、對生成內容準確性和風格要求高的場景,如對上下文理解和語言生成質量要求高的文學創作、專業文檔生成等

5.1.2、RAG

Retrieval-Augmented Generation 檢索增強生成:

將原始問題以及提示詞信息發送給大語言模型之前,先通過外部知識庫檢索相關信息,然后將檢索結果和原始問題一起發送給大模型,大模型依據外部知識庫再結合自身的訓練數據,組織自然語言回答問題。通過這種方式,大語言模型可以獲取到特定領域的相關信息,并能夠利用這些信息進行回復。

  • 優點:數據存儲在外部知識庫,可以實時更新,不依賴對模型自身的訓練,成本更低。

  • 缺點:需要兩次查詢:先查詢知識庫,然后再查詢大模型,性能不如微調大模型

**應用場景:**適用于知識庫規模大且頻繁更新的場景,如企業客服、實時新聞查詢、法律和醫療領域的最新知識問答等。

5.1.3、RAG常用方法

全文(關鍵詞)搜索:這種方法通過將問題和提示詞中的關鍵詞與知識庫文檔數據庫進行匹配來搜索文檔。根據這些關鍵詞在每個文檔中的出現頻率和相關性對搜索結果進行排序。

向量搜索 :也被稱為 “語義搜索”。文本通過 嵌入模型 被轉換為 數字向量 。然后,它根據查詢向量與文檔向量之間的余弦相似度或其他相似性 / 距離度量來查找和排序文檔,從而捕捉更深層次的語義含義

**混合搜索:**結合多種搜索方法(例如,全文搜索 + 向量搜索)通常可以提高搜索的效果。

5.2、向量搜索vector search

5.2.1、向量Vectors

可以將向量理解為從空間中的一個點到另一個點的移動。例如,在下圖中,我們可以看到一些二維空間中的向量。

介紹:a是一個從 (100, 50) 到 (-50, -50) 的向量,b 是一個從 (0, 0) 到 (100, -50) 的向量。

image-20250525215949869

很多時候,我們處理的向量是從原點 (0, 0) 開始的,比如b。這樣我們可以省略向量起點部分,直接說 b是向量 (100, -50)。

如何將向量的概念擴展到非數值實體上呢(例如文本)?

5.2.2、維度Dimensions

如我們所見,每個數值向量都有 x 和 y 坐標(或者在多維系統中是 x、y、z,…)。x、y、z… 是這個向量空間的軸,稱為維度。對于我們想要表示為向量的一些非數值實體,我們首先需要決定這些維度,并為每個實體在每個維度上分配一個值。

例如,在一個交通工具數據集中,我們可以定義四個維度:“輪子數量”、“是否有發動機”、“是否可以在地上開動”和“最大乘客數”。然后我們可以將一些車輛表示為:

image-20250525220038618

因此,我們的汽車Car向量將是 (4, yes, yes, 5),或者用數值表示為 (4, 1, 1, 5)(將 yes 設為 1,no 設為0)。

向量的每個維度代表數據的不同特性,維度越多對事務的描述越精確,我們可以使用“是否有翅膀”、“是否使用柴油”、“最高速度”、“平均重量”、“價格”等等更多的維度信息。

5.2.3、相似度Similarity

如果用戶搜索 “轎車Car” ,你希望能夠返回所有與 “汽車automobile” 和 “車輛vehicle” 等信息相關的結果。向量搜索就是實現這個目標的一種方法。

如何確定哪些是最相似的?

每個向量都有一個長度和方向。例如,在這個圖中,p 和 a 指向相同的方向,但長度不同。p 和 b 正好指向相反的方向,但有相同的長度。然后還有c,長度比p短一點,方向不完全相同,但很接近。

image-20250525220128708

那么,哪一個最接近 p 呢?

如果“相似”僅僅意味著指向相似的方向,那么a 是最接近 p 的。接下來是 c。b 是最不相似的,因為它正好指向與p 相反的方向。如果“相似”僅僅意味著相似的長度,那么 b 是最接近 p 的(因為它有相同的長度),接下來是 c,然后是 a。

由于向量通常用于描述語義意義,僅僅看長度通常無法滿足需求。大多數相似度測量要么僅依賴于方向,要么同時考慮方向和大小。

5.2.4、相似度測量Measures of similarity

相似度測量即相似度計算。四種常見的向量相似度計算方法(這里不展開討論):

  • 歐幾里得距離 Euclidean distance
  • 曼哈頓距離 Manhattan distance
  • 點積 Dot product
  • 余弦相似度 Cosine similarity

5.3、RAG過程

RAG 過程分為2個不同的階段:索引和檢索

5.3.1、索引階段

在索引階段,對知識庫文檔進行預處理,可實現檢索階段的高效搜索。

以下是索引階段的簡化圖:

  • 加載知識庫文檔 ==> 將文檔中的文本分段 ==> 利用向量大模型將分段后的文本轉換成向量 ==> 將向量存入向量數據庫

image-20250525220316540

為什么要進行文本分段?

大語言模型(LLM)的上下文窗口有限,所以整個知識庫可能無法全部容納其中。

  • 你在提問中提供的信息越多,大語言模型處理并做出回應所需的時間就越長。
  • 你在提問中提供的信息越多,花費也就越多。
  • 提問中的無關信息可能會干擾大語言模型,增加產生幻覺(生成錯誤信息)的幾率。

我們可以通過將知識庫分割成更小、更易于理解的片段來解決這些問題。

5.3.2、檢索階段

以下是檢索階段的簡化圖:

  • 通過向量模型將用戶查詢轉換成向量 ==> 在向量數據庫中根據用戶查詢進行相似度匹配 ==> 將用戶查詢和向量數據庫中匹配到的相關內容一起交給LLM處理

image-20250525220946096

5.4、文檔加載器Document Loader

5.4.1、常見文檔加載器Loader

image-20250525221413871

langchain4j 模塊:

  • 來自 langchain4j 模塊的文件系統文檔加載器(FileSystemDocumentLoader)
  • 來自 langchain4j 模塊的類路徑文檔加載器(ClassPathDocumentLoader)
  • 來自 langchain4j 模塊的網址文檔加載器(UrlDocumentLoader)

langchain4j-document-loader-amazon-s3模塊:

  • 來自 langchain4j-document-loader-amazon-s3 模塊的亞馬遜 S3 文檔加載器(AmazonS3DocumentLoader)

langchain4j-document-loader-azure-storage-blob 模塊:

  • 來自 langchain4j-document-loader-azure-storage-blob 模塊的 Azure Blob 存儲文檔加載器(AzureBlobStorageDocumentLoader)

langchain4j-document-loader-github 模塊:

  • 來自 langchain4j-document-loader-github 模塊的 GitHub 文檔加載器(GitHubDocumentLoader)

langchain4j-document-loader-google-cloud-storage 模塊:

  • 來自 langchain4j-document-loader-google-cloud-storage 模塊的谷歌云存儲文檔加載器(GoogleCloudStorageDocumentLoader)

langchain4j-document-loader-selenium 模塊

  • 來自 langchain4j-document-loader-selenium 模塊的 Selenium 文檔加載器(SeleniumDocumentLoader)

langchain4j-document-loader-tencent-cos 模塊

  • 來自 langchain4j-document-loader-tencent-cos 模塊的騰訊云對象存儲文檔加載器(TencentCosDocumentLoader)

5.4.2、測試文檔加載器

針對FileSystemDocumentLoader通用的加載文檔方式如下:

// 加載單個文檔
Document document = FileSystemDocumentLoader.loadDocument("E:/knowledge/file.txt", new
TextDocumentParser());
// 從一個目錄中加載所有文檔
List<Document> documents = FileSystemDocumentLoader.loadDocuments("E:/knowledge", new
TextDocumentParser());
// 從一個目錄中加載所有的.txt文檔
PathMatcher pathMatcher = FileSystems.getDefault().getPathMatcher("glob:*.txt");
List<Document> documents = FileSystemDocumentLoader.loadDocuments("E:/knowledge",
pathMatcher, new TextDocumentParser());
// 從一個目錄及其子目錄中加載所有文檔
List<Document> documents =
FileSystemDocumentLoader.loadDocumentsRecursively("E:/knowledge", new
TextDocumentParser())

image-20250525221806257

@SpringBootTest
public class DocumentLoaderTest {@Testpublic void testReadDocument() {//  使用FileSystemDocumentLoader讀取指定目錄下的知識庫文檔//  并使用默認的文檔解析器TextDocumentParser對文檔進行解析Document document = FileSystemDocumentLoader.loadDocument("/Users/edy/changlu_workspace/mymd/demo-exer/ai/LangChain4j-demo/src/main/resources/documents/測試.txt");System.out.println(document.text());}}

測試.txt文本如下:

北京協和醫院是一所歷史悠久的醫院。它在醫療領域有著卓越的成就。
醫院擁有眾多知名專家。他們為患者提供了高質量的醫療服務。
醫院的設施先進。這為診斷和治療提供了有力支持。
此外,醫院還注重科研和教學。培養了許多優秀的醫學人才。

5.5、文檔解析器Parser

5.5.1、常見文檔解析器

文檔可以是各種格式的文件,比如 PDF、DOC、TXT 等等。為了解析這些不同格式的文件,有一個 “文檔解析器”(DocumentParser)接口,并且我們的庫中包含了該接口的幾種實現方式:

  • 來自 langchain4j 模塊的文本文檔解析器(TextDocumentParser),它能夠解析純文本格式的文件(例如 TXT、HTML、MD 等)。

  • 來自 langchain4j-document-parser-apache-pdfbox 模塊的 Apache PDFBox 文檔解析器(ApachePdfBoxDocumentParser),它可以解析 PDF 文件。

  • 來自 langchain4j-document-parser-apache-poi 模塊的 Apache POI 文檔解析器(ApachePoiDocumentParser),它能夠解析微軟辦公軟件的文件格式(例如 DOC、DOCX、PPT、PPTX、XLS、XLSX 等)。

  • 來自 langchain4j-document-parser-apache-tika 模塊的 Apache Tika 文檔解析器(ApacheTikaDocumentParser),它可以自動檢測并解析幾乎所有現有的文件格式。

假設如果我們想解析PDF文檔,那么原有的 TextDocumentParser 就無法工作了,我們需要引入langchain4j-document-parser-apache-pdfbox。

5.5.2、本地測試文檔解析器

1)添加parser-pdfbox依賴

pom.xml:

<!--解析pdf文檔-->
<dependency><groupId>dev.langchain4j</groupId><artifactId>langchain4j-document-parser-apache-pdfbox</artifactId>
</dependency>

2)編寫測試類

image-20250525222926352

@SpringBootTest
public class DocumentParserTest {/*** 解析PDF*/@Testpublic void testParsePDF() {Document document = FileSystemDocumentLoader.loadDocument("/Users/edy/changlu_workspace/mymd/demo-exer/ai/LangChain4j-demo/src/main/resources/documents/尚硅谷-Java+大模型應用-硅谷小智(醫療版).pdf",new ApachePdfBoxDocumentParser());System.out.println(document);}}

測試效果,正常讀取pdf內容:

image-20250525223008407

開源項目核心模塊如:

image-20250525223053484


5.6、文檔分割器 Document Splitter

5.6.1、常見文檔分割器

LangChain4j 有一個 “文檔分割器”(DocumentSplitter)接口,并且提供了幾種開箱即用的實現方式:

  • 按段落文檔分割器(DocumentByParagraphSplitter)
  • 按行文檔分割器(DocumentByLineSplitter)
  • 按句子文檔分割器(DocumentBySentenceSplitter)
  • 按單詞文檔分割器(DocumentByWordSplitter)
  • 按字符文檔分割器(DocumentByCharacterSplitter)
  • 按正則表達式文檔分割器(DocumentByRegexSplitter)
  • 遞歸分割:DocumentSplitters.recursive (…)

默認情況下每個文本片段最多不能超過300個token。

5.6.2、測試向量轉換和向量存儲

Embedding (Vector) Stores 常見的意思是 “嵌入(向量)存儲” 。在機器學習和自然語言處理領域,

Embedding 指的是將數據(如文本、圖像等)轉換為低維稠密向量表示的過程,這些向量能夠保留數據的關鍵特征。而 Stores 表示存儲,即用于存儲這些嵌入向量的系統或工具。它們可以高效地存儲和檢索向量數據,支持向量相似性搜索,在文本檢索、推薦系統、圖像識別等任務中發揮著重要作用。

Langchain4j支持的向量存儲: https://docs.langchain4j.dev/integrations/embedding-stores/

1)引入pom.xml依賴

<!--簡單的rag實現-->
<dependency><groupId>dev.langchain4j</groupId><artifactId>langchain4j-easy-rag</artifactId>
</dependency>

2)測試demo驗證

image-20250525223822805

@SpringBootTest
public class DocumentSplitterTest {/*** 加載文檔并存入向量數據庫*/@Testpublic void testReadDocumentAndStore() {//使用FileSystemDocumentLoader讀取指定目錄下的知識庫文檔//并使用默認的文檔解析器對文檔進行解析(TextDocumentParser)Document document = FileSystemDocumentLoader.loadDocument("/Users/edy/changlu_workspace/mymd/demo-exer/ai/LangChain4j-demo/src/main/resources/documents/人工智能.md");//為了簡單起見,我們暫時使用基于內存的向量存儲InMemoryEmbeddingStore<TextSegment> embeddingStore = new InMemoryEmbeddingStore<>();//ingest//1、分割文檔:默認使用遞歸分割器,將文檔分割為多個文本片段,每個片段包含不超過 300個token,并且有 30個token的重疊部分保證連貫性//DocumentByParagraphSplitter(DocumentByLineSplitter(DocumentBySentenceSplitter(DocumentByWordSplitter)))//2、文本向量化:使用一個LangChain4j內置的輕量化向量模型對每個文本片段進行向量化//3、將原始文本和向量存儲到向量數據庫中(InMemoryEmbeddingStore)EmbeddingStoreIngestor.ingest(document, embeddingStore);//查看向量數據庫內容System.out.println(embeddingStore);}
}

image-20250525223906249

看下真實向量存儲,可以看到最終劃分為了多個文本片段,同時每個文本片段都有一個相似度的矩陣:

image-20250525223958728


5.6.3、測試文檔分割

image-20250525223822805

/*** 文檔分割*/
@Test
public void testDocumentSplitter() {//使用FileSystemDocumentLoader讀取指定目錄下的知識庫文檔//并使用默認的文檔解析器對文檔進行解析(TextDocumentParser)Document document = FileSystemDocumentLoader.loadDocument("/Users/edy/changlu_workspace/mymd/demo-exer/ai/LangChain4j-demo/src/main/resources/documents/人工智能.md");//為了簡單起見,我們暫時使用基于內存的向量存儲InMemoryEmbeddingStore<TextSegment> embeddingStore = new InMemoryEmbeddingStore<>();//自定義文檔分割器//按段落分割文檔:每個片段包含不超過 300個token,并且有 30個token的重疊部分保證連貫性//注意:當段落長度總和小于設定的最大長度時,就不會有重疊的必要。DocumentByParagraphSplitter documentSplitter = new DocumentByParagraphSplitter(300,30,//token分詞器:按token計算new HuggingFaceTokenizer());//按字符計算//DocumentByParagraphSplitter documentSplitter = new DocumentByParagraphSplitter(300, 30);EmbeddingStoreIngestor.builder().embeddingStore(embeddingStore).documentSplitter(documentSplitter).build().ingest(document);
}

說明:在EmbeddingStoreIngestor中就有一個向量話模型,默認的為:

image-20250525233122586

這個模型開源倉庫在:https://github.com/langchain4j/langchain4j-embeddings,其中的main分支。


5.6.4、token和token計算

deepseek:https://api-docs.deepseek.com/zh-cn/quick_start/token_usage

阿里百煉:https://bailian.console.aliyun.com/?tab=model#/efm/model_experience_center/text

LangChain4j:

image-20250525224508315

public class TokenCalculateTest {@Testpublic void testTokenCount() {String text = "這是一個示例文本,用于測試 token 長度的計算。";UserMessage userMessage = UserMessage.userMessage(text);//計算 token 長度//QwenTokenizer tokenizer = new QwenTokenizer(System.getenv("DASH_SCOPE_API_KEY"), "qwen-max");HuggingFaceTokenizer tokenizer = new HuggingFaceTokenizer();int count = tokenizer.estimateTokenCountInMessage(userMessage);System.out.println("token長度:" + count);}}

image-20250525224540011

5.6.5、工作方式

過程:

  • 1、實例化一個 “文檔分割器”(DocumentSplitter),指定所需的 “文本片段”(TextSegment)大小,并且可以選擇指定characters 或token的重疊部分。
  • 2、“文檔分割器”(DocumentSplitter)將給定的文檔(Document)分割成更小的單元,這些單元的性質因分割器而異。例如,“按段落分割文檔器”(DocumentByParagraphSplitter)將文檔分割成段落(由兩個或更多連續的換行符定義),而 “按句子分割文檔器”(DocumentBySentenceSplitter)使用 OpenNLP 庫的句子檢測器將文檔分割成句子,依此類推。
  • 3、然后,“文檔分割器”(DocumentSplitter)將這些較小的單元(段落、句子、單詞等)組合成 “文本片段”(TextSegment),嘗試在單個 “文本片段”(TextSegment)中包含盡可能多的單元,同時不超過第一步中設置的限制。如果某些單元仍然太大,無法放入一個 “文本片段”(TextSegment)中,它會調用一個子分割器。這是另一個 “文檔分割器”(DocumentSplitter),能夠將不適合的單元分割成更細粒度的單元。會向每個文本片段添加一個唯一的元數據條目 “index”。第一個 “文本片段”(TextSegment)將包含 index=0 ,第二個是 index=1 ,依此類推

模型上下文窗口 可以通過模型參數列表查看:

期望的文本片段最大大小

  1. 模型上下文窗口:如果你使用的大語言模型(LLM)有特定的上下文窗口限制,這個值不能超過模型能夠處理的最大 token 數。例如,某些模型可能最大只能處理 2048 個 token,那么設置的文本片段大小就需要遠小于這個值,為后續的處理(如添加指令、其他輸入等)留出空間。通常,在這種情況下,你可以設置為 1000 - 1500 左右,具體根據實際情況調整。
  2. 數據特點:如果你的文檔內容較為復雜,每個段落包含的信息較多,那么可以適當提高這個值,比如設置為 500 - 800 個 token,以便在一個文本片段中包含相對完整的信息塊。相反,如果文檔段落較短且信息相對獨立,設置為 200 - 400 個 token 可能就足夠了。
  3. 檢索需求:如果希望在檢索時能夠更精確地匹配到相關信息,較小的文本片段可能更合適,這樣可以提高信息的粒度。例如設置為 200 - 300 個 token。但如果更注重獲取完整的上下文信息,較大的文本片段(如 500 - 600 個 token)可能更有助于理解相關內容。

重疊部分大小

  1. 上下文連貫性:重疊部分的主要作用是提供上下文連貫性,避免因分割導致信息缺失。如果文檔內容之間的邏輯聯系緊密,建議設置較大的重疊部分,如 50 - 100 個 token,以確保相鄰文本片段之間的過渡自然,模型在處理時能夠更好地理解上下文。

  2. 數據冗余:然而,設置過大的重疊部分會增加數據的冗余度,可能導致處理時間增加和資源浪費。因此,需要在上下文連貫性和數據冗余之間進行平衡。一般來說,20 - 50 個 token 的重疊是比較常見的取值范圍。

  3. 模型處理能力:如果使用的模型對輸入的敏感性較高,較小的重疊部分(如 20 - 30 個 token)可能就足夠了,因為過多的重疊可能會引入不必要的干擾信息。但如果模型對上下文依賴較大,適當增加重疊部分(如 40 - 60 個 token)可能會提高模型的性能。

例如,在處理一般性的文本資料,且使用的模型上下文窗口較大(如 4096 個 token)時,設置文本片段最大大小為 600 - 800 個 token,重疊部分為 30 - 50 個 token 可能是一個不錯的選擇。但最終的設置還需要通過實驗和實際效果評估來確定,以找到最適合具體應用場景的參數值。


六、向量模型和向量存儲

**Local Documents:**本地文檔向量化存儲

**Query:**會將文本首先進行向量化,接著去根據向量化段來進行匹配相似度查詢向量數據庫,最終去組成提示詞發送給ai大模型。

最終得到answer回答。

image-20250526010525193

6.1、向量大模型

6.1.1、介紹

通用文本向量模型: https://help.aliyun.com/zh/model-studio/text-embedding-synchronous-api?spm=a2c4g.11186623.help-menu-2400256.d_2_5_0.592672a3yMJDRq&scm=20140722.H_2712515._.OR_help-T_cn~zh-V_1

text-embedding-v3模型:

  • 阿里云百煉平臺:https://bailian.console.aliyun.com/?tab=model#/model-market/detail/text-embedding-v3

image-20250525231908985

image-20250525231916186

使用通用文本向量 text-embedding-v3,維度1024,維度越多,對事務的描述越精準,信息檢索的精度越高。

可以在阿里模型廣場搜索到:

image-20250525232031650

ollama也提供向量模型的場景:https://ollama.com/search?q=bge

6.1.2、模型配置

使用 text-embedding-v3 依然需要添加 langchain4j-community-dashscope 依賴,我們之前已經添加過了。

配置向量模型 application.properties:

#集成阿里通義千問-通用文本向量-v3
langchain4j.community.dashscope.embedding-model.api-key=${DASH_SCOPE_API_KEY}
langchain4j.community.dashscope.embedding-model.model-name=text-embedding-v3

6.1.3、文本向量化測試驗證

我們這里EmbeddingModel使用的則是

@SpringBootTest
public class EmbeddingModelTest {@Autowiredprivate EmbeddingModel embeddingModel;@Testpublic void testEmbeddingModel(){Response<Embedding> embed = embeddingModel.embed("你好");System.out.println("向量維度:" + embed.content().vector().length);System.out.println("向量輸出:" + embed.toString());}}

測試效果如下:

image-20250525232803451

debug內容:

image-20250525232621950

執行向量化效果:

image-20250525232714460


6.2、向量化存儲

dify使用的向量數據庫為:Chroma,以易用性和高效性著稱,適合中小規模的數據存儲與檢索場景。不過,Dify 也支持替換為其他向量數據庫,如 Pinecone、Weaviate、Milvus 等。此外,Dify 云服務中默認使用的向量數據庫是 Weaviate。

  • RAG 落地必備的 1 個開源 AI 原生向量數據庫 —Chroma:https://mp.weixin.qq.com/s?__biz=MjM5MzQ5NTU0Mw==&mid=2455052377&idx=1&sn=38829994cbd6e8a0fb01d177384d488d&poc_token=HEE8M2ijcS-wO8X1KPfFlpO0KA6Z0QXXxglHuX98
  • Spring Boot 集成 Milvus 向量數據庫(DeepSeek RAG):https://juejin.cn/post/7473339067854553128
  • Springboot集成Milvus和Embedding服務,實現向量化檢索:https://blog.csdn.net/wxz258/article/details/145574017

6.2.1、Pinecone簡介

之前我們使用的是InMemoryEmbeddingStore作為向量存儲,但是不建議在生產中使用基于內存的向量存儲。因此這里我們使用Pinecone作為向量數據庫。

**官方網站:**https://www.pinecone.io/

訪問官方網站、注冊、登錄、獲取apiKey且配置在環境變量中。默認有2GB的免費存儲空間。

image-20250526005242237

默認使用github登陸之后,給到的空間量。

6.2.2、Pinecone的使用

得分的含義

  • 在向量檢索場景中,當我們把查詢文本轉換為向量后,會在嵌入存儲( EmbeddingStore )里查找與之最相似的向量(這些向量對應著文檔片段等內容)。為了衡量查詢向量和存儲向量之間的相似程度,會使用某種相似度計算方法(例如余弦相似度等)來得出一個數值,這個數值就是得分。得分越高,表明查詢向量和存儲向量越相似,對應的文檔片段與查詢文本的相關性也就越高。

得分的作用:

  • 篩選結果:通過設置 minScore 閾值,能夠過濾掉那些與查詢文本相關性較低的結果。在代碼里, minScore(0.8) 意味著只有得分大于等于 0.8 的結果才會被返回,低于這個閾值的結果會被舍棄。這樣可以確保返回的結果是與查詢文本高度相關的,提升檢索結果的質量。
  • 控制召回率和準確率:調整 minScore 的值可以在召回率和準確率之間進行權衡。如果把閾值設置得較低,那么更多的結果會被返回,召回率會提高,但可能會包含一些相關性不太強的結果,導致準確率下降;反之,如果把閾值設置得較高,返回的結果數量會減少,準確率會提高,但可能會遺漏一些相關的結果,使得召回率降低。在實際應用中,需要根據具體的業務需求來合理設置minScore 的值。

舉例說明:

  • 假設我們有一個關于水果的文檔集合,嵌入存儲中存儲了這些文檔片段的向量。當我們使用 “蘋果的營養價值” 作為查詢文本時,向量檢索會計算查詢向量與存儲向量的相似度得分。如果 minScore 設置為0.8,那么只有那些與 “蘋果的營養價值” 相關性非常高的文檔片段才會被返回,而一些只簡單提及蘋果但沒有詳細討論其營養價值的文檔片段可能由于得分低于 0.8 而不會被返回。

6.2.3、Pinecone賬號注冊與配置

參考文檔:https://docs.langchain4j.dev/integrations/embedding-stores/pinecone

langchain實現了很多的向量存儲對接:

image-20250526005940376

點擊創建一個index:https://app.pinecone.io/organizations/-OR7LiDThGfrtrg1qhK-/projects/9d89b9a4-6e75-48bd-abda-a595ba052634/indexes

image-20250526013933395

image-20250526014057795

服務器選擇:亞馬遜

image-20250526014132329

地區選擇第一個即可:

image-20250526014159821

最后create index即可:

image-20250526014217197

此時index創建完成:

image-20250526014252296

其中myindex可以當作我們的數據庫。

接下來測試去創建一條向量:

image-20250526014359037

輸入內容如下:

image-20250526014518332

此時在myindex中就有了一條記錄:

image-20250526014604709

測試下查詢條件:

image-20250526014716309

image-20250526014825230

其中底部就有一個score分數,用于表示匹配的權重。


6.2.4、集成Pinecone

6.2.4.1、pom引入Pinecone依賴

pom.xml引入依賴:

<!-- pinecone向量       -->
<dependency><groupId>dev.langchain4j</groupId><artifactId>langchain4j-pinecone</artifactId>
</dependency>
6.2.4.2、配置環境變量

配置pinecone的密鑰變量參數:

vim ~/.zshrc# 配置內容
# pinecone
export PINECONE_API_KEY="xxxxxxxxx"# 生效配置文件
source ~/.zshrc

注意:變量參數配置完之后,IDEA需要進行重啟。


6.2.4.3、配置向量存儲對象EmbeddingStore

image-20250526231919605

@Configuration
public class EmbeddingStoreConfig {@Autowiredprivate EmbeddingModel embeddingModel;@Beanpublic EmbeddingStore<TextSegment> embeddingStore() {//創建向量存儲EmbeddingStore<TextSegment> embeddingStore = PineconeEmbeddingStore.builder().apiKey(System.getenv("PINECONE_API_KEY")).index("myindex")//如果指定的索引不存在,將創建一個新的索引.nameSpace("my-namespace") //如果指定的名稱空間不存在,將創建一個新的名稱空間.createIndex(PineconeServerlessIndexConfig.builder().cloud("AWS") //指定索引部署在 AWS 云服務上。.region("us-east-1") //指定索引所在的 AWS 區域為 us-east-1。.dimension(embeddingModel.dimension()).build() //指定索引的向量維度,該維度與 embeddedModel 生成的向量維度相同。).build();return embeddingStore;}
}

關于配置參數:

image-20250526232020271

6.2.4.4、測試向量存儲

image-20250526232131358 .

@SpringBootTest
public class PineconeEmbededTest {@Autowiredprivate EmbeddingStore embeddingStore;@Autowiredprivate EmbeddingModel embeddingModel;@Testpublic void testPineconeEmbeded() {//將文本轉換成向量TextSegment segment1 = TextSegment.from("我喜歡羽毛球");Embedding embedding1 = embeddingModel.embed(segment1).content();//存入向量數據庫embeddingStore.add(embedding1, segment1);TextSegment segment2 = TextSegment.from("今天天氣很好");Embedding embedding2 = embeddingModel.embed(segment2).content();embeddingStore.add(embedding2, segment2);}}

debug測試:

image-20250526231502662

我們進入到Pinecone官網查看下,成功添加兩條向量:

image-20250526232208669


6.2.4.5、測試向量相似度匹配

此時會搜索到一個得分最高的向量:

image-20250526232448805

可以看到其得分:

image-20250526232507524

打印內容如下:

image-20250526232526267

資料獲取

大家點贊、收藏、關注、評論啦~

精彩專欄推薦訂閱:在下方專欄👇🏻

  • 長路-文章目錄匯總(算法、后端Java、前端、運維技術導航):博主所有博客導航索引匯總
  • 開源項目Studio-Vue—校園工作室管理系統(含前后臺,SpringBoot+Vue):博主個人獨立項目,包含詳細部署上線視頻,已開源
  • 學習與生活-專欄:可以了解博主的學習歷程
  • 算法專欄:算法收錄

更多博客與資料可查看👇🏻獲取聯系方式👇🏻,🍅文末獲取開發資源及更多資源博客獲取🍅

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/90120.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/90120.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/90120.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

什么是時序數據庫?——原理、特點與應用

在大數據和物聯網時代&#xff0c;數據的產生速度和數量都在飛速增長。尤其是在工業監控、金融分析、物聯網等領域&#xff0c;數據往往以“時間序列”的形式不斷產生。為了高效地存儲和分析這類數據&#xff0c;時序數據庫&#xff08;Time Series Database&#xff0c;簡稱TS…

FastAPI+React19 ERP系統實戰 第01期

一、基礎環境 1.1 項目依賴 package.json {"name": "erp-web","version": "1.0.0","description": "ERP系統前端 - React 19","main": "index.js","type": "module",…

【機器學習筆記 Ⅱ】1 神經網絡

神經網絡是一種受生物神經元啟發設計的機器學習模型&#xff0c;能夠通過多層非線性變換學習復雜的輸入-輸出關系。它是深度學習的基礎&#xff0c;廣泛應用于圖像識別、自然語言處理、游戲AI等領域。1. 核心思想 生物類比&#xff1a;模仿人腦神經元的工作方式&#xff0c;通過…

謝飛機的Java高級開發面試:從Spring Boot到分布式架構的蛻變之旅

面試現場&#xff1a;謝飛機的求職奇遇記 "請坐&#xff0c;謝先生。我看你簡歷上寫了精通Lombok&#xff1f;"面試官推了推金絲眼鏡。 謝飛機一愣&#xff1a;"啊...這個..."突然掏出手機&#xff0c;"您看我GitHub開源項目里用了SneakyThrows&…

一站式整合:解鎖高效后端管理利器——Motia

在當今的科技世界中&#xff0c;企業對于后端系統的要求越來越高。無論是處理復雜的 API 請求、管理后臺任務&#xff0c;還是集成 AI 代理&#xff0c;這些都需要一個強大的框架來支撐。而今天&#xff0c;我們要介紹的 Motia 正是這樣一個現代化、統一的后端框架&#xff0c;…

如何將信息從 iPhone 同步到Mac(完整步驟和示意圖)

如果您是 Apple 用戶&#xff0c;您一定知道在所有設備上保持同步是多么便捷。Apple 生態系統的一大亮點就是能夠在 iPhone 和 Mac 之間同步 iMessage 和短信。如果您想了解如何將信息從 iPhone 同步到 Mac&#xff0c;千萬不要錯過本教程。快速瀏覽一下這些方法&#xff1a;第…

【C++字符串變換】2022-7-20

緣由C問題-字符串變換-編程語言-CSDN問答 void 字符串變換() {string s "", t ""; char f; int x 0, g 1, l 0;cin >> s; l s.size();while (x <l){f s[x];if (f s[x 1]){g, s.erase(x, 1);}else{if (g > 1){s.erase(x, 1);t s.sub…

Web攻防-XMLXXE無回顯帶外SSRF元數據DTD實體OOB盲注文件拓展

知識點&#xff1a; 1、WEB攻防-XML&XXE-注入原理&分類&修復 2、WEB攻防-XML&XXE-文件讀取&SSRF&實體引用 3、WEB攻防-XML&XXE-無回顯&升級拓展&挖掘思路 一、演示案例-WEB攻防-XML&XXE-注入原理&分類&安全影響 詳細點 XML被…

Node中Unexpected end of form 錯誤

文章目錄Unexpected end of form 錯誤為什么 app.use(upload.any()) 會導致 Unexpected end of form 錯誤&#xff1f;1. 主要問題分析app.use(upload.any()) 的問題錯誤場景2. 解決方案? 方案 1&#xff1a;僅在需要文件上傳的路由使用 Multer&#xff08;推薦&#xff09;?…

通過Curtain 解決方案保障BIM模型安全共享—建筑業的防泄密實戰

某跨海大橋項目突發數據泄露事件&#xff1a;主橋鋼結構的BIM模型被外泄&#xff0c;核心參數流入競爭對手手中&#xff0c;導致項目風險評估升級。調查發現&#xff0c;泄漏源頭是一名施工方的項目經理。盡管BIM系統已經能夠控制哪些人可以閱讀、修改、甚至下載資料的權限&…

ULVAC愛發科RFS03D RF POWER SUPPLY INSTRUCTION MANUAL RF射頻電源

ULVAC愛發科RFS03D RF POWER SUPPLY INSTRUCTION MANUAL RF射頻電源

暑假算法日記第三天

目標?&#xff1a;刷完靈神專題訓練算法題單 階段目標&#x1f4cc;&#xff1a;【算法題單】滑動窗口與雙指針 LeetCode題目: 3439. 重新安排會議得到最多空余時間 I2134. 最少交換次數來組合所有的 1 II1297. 子串的最大出現次數2653. 滑動子數組的美麗值1888. 使二進制字符…

了解業務分析技術梗概

業務分析技術 以下基于BABOK V3框架&#xff0c;結合業務分析師&#xff08;BA&#xff09;的實際工作場景&#xff0c;系統梳理50項業務分析技術、常用工具、學習路徑及文檔應用指南。內容綜合BABOK官方標準及行業實踐&#xff0c;旨在提升BA的工作效能。 一、BABOK V3 技術體…

小紅的數字刪除 - 牛客

小紅的數字刪除 題目不難&#xff0c;忽略了一個 corner case&#xff0c;導致我在某次面試沒有 AK。 10003 對于這個 case&#xff0c;只考慮前導零 全部刪除是不對的&#xff0c;剩下的 3 也不能刪。 void solve(){string s;cin >> s;int res0;vector<int> a(…

Linux網絡: socket初識

一些概念 簡單了解一下TCP,UDP這兩個協議&#xff0c;和一些概念 TCP與UDP 學校教過TCP是 傳輸層協議有連接可靠傳輸面向字節流 而UDP是 傳輸層協議無連接不可靠傳輸面向數據報 當時完全不知道這些什么意思 網絡字節序 網絡通信&#xff0c;要接收和發送數據。我們知道…

AI時代的彎道超車之第二十七章:AI技術的發展方向

在這個AI重塑世界的時代,你還在原地觀望嗎?是時候彎道超車,搶占先機了! 李尚龍傾力打造——《AI時代的彎道超車:用人工智能逆襲人生》專欄,帶你系統掌握AI知識,從入門到實戰,全方位提升認知與競爭力! 內容亮點: AI基礎 + 核心技術講解 職場賦能 + 創業路徑揭秘 打破…

RabbitMQ用法的6種核心模式全面解析

文章目錄**一、RabbitMQ核心架構解析**1. AMQP協議模型2. 消息流轉原理**二、六大核心用法詳解****1. 簡單隊列模式&#xff08;Hello World&#xff09;****2. 工作隊列模式&#xff08;Work Queues&#xff09;****3. 發布/訂閱模式&#xff08;Pub/Sub&#xff09;****4. 路…

深入協程調試:協程調試工具與實戰

本文系統梳理主流協程調試工具&#xff0c;結合完整代碼示例與實戰技巧&#xff0c;助你高效解決異步編程難題一、協程調試的核心挑戰 協程的非線性執行流是調試的最大挑戰&#xff1a; 傳統斷點調試難以追蹤協程切換堆棧信息不完整或丟失上下文并發競爭條件難以復現 #mermaid-…

Git 日常開發實戰命令大全

&#x1f9f0; Git 日常開發實戰命令大全 本文整理了 Git 在日常開發中高頻使用的命令集合&#xff0c;覆蓋從基礎操作到進階技巧的完整流程&#xff0c;方便留存查閱&#x1f440; &#xff0c;最后附上所有指令。其中內容包括&#xff1a; ? 本地倉庫管理&#xff1a;添加文…

力扣 hot100 Day37

25. K 個一組翻轉鏈表 給你鏈表的頭節點 head &#xff0c;每 k 個節點一組進行翻轉&#xff0c;請你返回修改后的鏈表。 k 是一個正整數&#xff0c;它的值小于或等于鏈表的長度。如果節點總數不是 k 的整數倍&#xff0c;那么請將最后剩余的節點保持原有順序。 你不能只是…