4. 使用SpringBoot快速集成LangChain4j, 實現AI的絲滑調用
1. 簡介
前幾章簡單測試了一下LangChain4J的特性, 本章使用SpringBoot快速集成LangChain4J, 實現絲滑調用大模型, 往期內容傳送門
LangChain4J官方提供了SpringBoot Starter, 本章就使用Starter進行快速集成.
2. 環境信息
使用SDK版本信息如下:
Java: 21
SpringBoot: 3.4.5
LangChain4j: 1.0.1
LLM: (使用在線的百煉(阿里)平臺)
embedding模型: text-embedding-v3
chat模型: qwen-plus
PGVector(postgresql版本的向量數據庫, 文章最后有相關的docker-compose): 0.8.0-pg17
3. Maven
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.4.5</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.ldx</groupId>
<artifactId>langchain-test</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>langchain-test</name>
<description>langchain-test</description>
<properties>
<java.version>21</java.version>
<guava.version>33.0.0-jre</guava.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j-bom</artifactId>
<version>1.0.1</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- 這里使用的是OpenAI Starter, 因為百煉平臺的模型支持OpenAI, 如果本地使用Ollama部署的模型, 導入其對應的Starter即可 -->
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j-open-ai-spring-boot-starter</artifactId>
</dependency>
<!-- 該包實現了AI Services的自動注入, 如果想自己聲明AI Services去掉該依賴即可 -->
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j-open-ai</artifactId>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>${guava.version}</version>
</dependency>
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j-reactor</artifactId>
</dependency>
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j-pgvector</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</project>
4. 配置信息
4.1 application.yml
通過在配置文件中聲明模型信息 即可實現對應模型的自動注入
langchain4j:
open-ai:
# 普通聊天模型
chat-model:
api-key: ${LLM_API_KEY}
model-name: qwen-plus
base-url: https://dashscope.aliyuncs.com/compatible-mode/v1
# 流式相應模型
streaming-chat-model:
api-key: ${LLM_API_KEY}
model-name: qwen-plus
base-url: https://dashscope.aliyuncs.com/compatible-mode/v1
# 向量模型
embedding-model:
api-key: ${LLM_API_KEY}
model-name: text-embedding-v3
base-url: https://dashscope.aliyuncs.com/compatible-mode/v1
4.2 配置類
主要聲明了
- 聊天記憶提供類, 關聯了記憶存儲對象
- 向量存儲對象,這里使用的是pgvector
- 內容檢索器(RAG-檢索實現)
package com.ldx.langchaintest.config;
import com.ldx.langchaintest.service.PersistentChatMemoryStore;
import dev.langchain4j.data.segment.TextSegment;
import dev.langchain4j.memory.chat.ChatMemoryProvider;
import dev.langchain4j.memory.chat.MessageWindowChatMemory;
import dev.langchain4j.model.embedding.EmbeddingModel;
import dev.langchain4j.rag.content.retriever.ContentRetriever;
import dev.langchain4j.rag.content.retriever.EmbeddingStoreContentRetriever;
import dev.langchain4j.store.embedding.EmbeddingStore;
import dev.langchain4j.store.embedding.pgvector.DefaultMetadataStorageConfig;
import dev.langchain4j.store.embedding.pgvector.PgVectorEmbeddingStore;
import dev.langchain4j.store.memory.chat.InMemoryChatMemoryStore;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* config
*
* @author ludangxin
* @date 2025/5/15
*/
@Configuration
public class AiConfiguration {
/**
* 聊天記憶 提供者
*
* @param persistentChatMemoryStore 對話內容持久化對象
* @return 對話記憶 provider
*/
@Bean
public ChatMemoryProvider jdbcChatMemoryProvider(PersistentChatMemoryStore persistentChatMemoryStore) {
return memoryId -> MessageWindowChatMemory
.builder()
.id(memoryId)
// 這里使用了自定義的會話存儲對象, 可以通過其實現對話過程內容的持久化
// 本地測試的話可以使用 InMemoryChatMemoryStore對象實現內存存儲
.chatMemoryStore(persistentChatMemoryStore)
.maxMessages(5)
.build();
}
/**
* 向量存儲對象
*
* @param embeddingModel 向量模型
* @return 向量存儲對象
*/
public EmbeddingStore<TextSegment> embeddingStore(EmbeddingModel embeddingModel) {
return PgVectorEmbeddingStore
.builder()
.host("localhost") // 必需:PostgresSQL 實例的主機
.port(5431) // 必需:PostgresSQL 實例的端口
.database("postgres") // 必需:數據庫名稱
.user("root") // 必需:數據庫用戶
.password("123456") // 必需:數據庫密碼
.table("my_embeddings") // 必需:存儲嵌入的表名
.dimension(embeddingModel.dimension()) // 必需:嵌入的維度
.metadataStorageConfig(DefaultMetadataStorageConfig.defaultConfig()) // 元數據存儲配置
.build();
}
/**
* 內容檢索器
*
* @param embeddingModel 向量模型
* @return 內容檢索器
*/
@Bean
public ContentRetriever contentRetriever(EmbeddingModel embeddingModel) {
return EmbeddingStoreContentRetriever
.builder()
.embeddingStore(this.embeddingStore(embeddingModel))
.embeddingModel(embeddingModel)
.maxResults(10)
.minScore(0.65)
.build();
}
}
4.3 聊天記憶持久化
實現了ChatMemoryStore接口, 這里測試使用的是map存儲的, 生產環境中可以持久化到數據庫中
chat過程中消息會通過ChatMemory調用ChatMemoryStore對聊天內容進行持久化/獲取
package com.ldx.langchaintest.service;
import com.google.common.collect.ArrayListMultimap;
import dev.langchain4j.data.message.ChatMessage;
import dev.langchain4j.store.memory.chat.ChatMemoryStore;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class PersistentChatMemoryStore implements ChatMemoryStore {
final ArrayListMultimap<Object, ChatMessage> messagesStore = ArrayListMultimap.create();
@Override
public List<ChatMessage> getMessages(Object memoryId) {
return messagesStore.get(memoryId);
}
@Override
public void updateMessages(Object memoryId, List<ChatMessage> messages) {
messagesStore.put(memoryId, messages.getLast());
}
@Override
public void deleteMessages(Object memoryId) {
messagesStore.removeAll(memoryId);
}
}
4.4 tools
package com.ldx.langchaintest.tools;
import dev.langchain4j.agent.tool.P;
import dev.langchain4j.agent.tool.Tool;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
/**
* tools
*
* @author ludangxin
* @date 2025/5/15
*/
@Slf4j
@Component
public class SysTools {
@Tool("根據用戶的名稱獲取對應的code")
public String getUserCodeByUsername(@P("用戶名稱") String username) {
log.info("get user code by username:{}", username);
if ("張鐵牛".equals(username)) {
return "003";
}
return "000";
}
}
5. 核心源碼
5.1 Ai Services
@AiService 將實現AiService的自動注入
?
wiringMode = EXPLICIT: 用戶自己指定相關的bean? 缺省:
wiringMode = AUTOMATIC: 項目啟動時自動在環境中找對應的對象實現注入,如果有多個(比如:chatModel),啟動報錯這里舉了幾種典型的場景 如
- 普通聊天 chat()
- 聊天記憶&流式輸出 chatWithStream()
- 提取指定內容并將結果結構化 extractPerson()
- 提示詞占位替換 mockUsername()
- rag text-sql chatWithSql()
package com.ldx.langchaintest.service;
import com.ldx.langchaintest.domain.Person;
import dev.langchain4j.service.MemoryId;
import dev.langchain4j.service.SystemMessage;
import dev.langchain4j.service.UserMessage;
import dev.langchain4j.service.V;
import dev.langchain4j.service.spring.AiService;
import reactor.core.publisher.Flux;
import java.util.List;
import static dev.langchain4j.service.spring.AiServiceWiringMode.EXPLICIT;
/**
* ai svc
*
* @author ludangxin
* @date 2025/5/16
*/
@AiService(wiringMode = EXPLICIT,
chatModel = "openAiChatModel",
streamingChatModel = "openAiStreamingChatModel",
chatMemoryProvider = "chatMemoryProvider",
contentRetriever = "contentRetriever",
tools = {"sysTools"})
public interface AiSqlAssistantService {
String chat(String message);
@SystemMessage("?? 將文本改寫成類似小紅書的 Emoji 風格")
Flux<String> chatWithStream(@MemoryId String memoryId, @UserMessage String message);
@SystemMessage("請在用戶提供的文本中提取出人員信息")
Person extractPerson(@UserMessage String message);
@UserMessage("需要你幫我mock人員姓名, 幫我生成{{total}}個")
List<String> mockUsername(@V("total") Integer total);
@SystemMessage("你是一名sql分析專家 我會將sql相關的ddl給你, 需要你根據ddl生成合理且可執行的sql語句并返回")
String chatWithSql(@MemoryId String memoryId, @UserMessage String message);
}
5.2 Controller
package com.ldx.langchaintest.controller;
import com.ldx.langchaintest.domain.Person;
import com.ldx.langchaintest.service.AiSqlAssistantService;
import dev.langchain4j.data.document.Document;
import dev.langchain4j.data.document.DocumentSplitter;
import dev.langchain4j.data.document.loader.ClassPathDocumentLoader;
import dev.langchain4j.data.document.splitter.DocumentByRegexSplitter;
import dev.langchain4j.data.embedding.Embedding;
import dev.langchain4j.data.segment.TextSegment;
import dev.langchain4j.model.embedding.EmbeddingModel;
import dev.langchain4j.model.output.Response;
import dev.langchain4j.store.embedding.EmbeddingStore;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Flux;
import java.io.IOException;
import java.util.List;
/**
* ai controller
*
* @author ludangxin
* @date 2025/5/16
*/
@Slf4j
@RestController
@RequestMapping("/ai/chat")
@RequiredArgsConstructor
public class AiServiceController {
private final EmbeddingModel embeddingModel;
private final EmbeddingStore<TextSegment> embeddingStore;
private final AiSqlAssistantService aiSqlAssistantService;
@GetMapping("/test")
public String test() {
return aiSqlAssistantService.chat("你是誰");
}
@GetMapping
public String chat(@RequestParam String userMessage) {
return aiSqlAssistantService.chat(userMessage);
}
@GetMapping(value = "/{id}/stream/memory", produces = "text/stream;charset=utf-8")
public Flux<String> streamMemory(@PathVariable String id, @RequestParam String userMessage) {
final Flux<String> chatResponse = aiSqlAssistantService.chatWithStream(id, userMessage);
return chatResponse
.doOnNext(partial -> log.info("chat stream partial data:{}", partial))
.doOnError(e -> log.error("stream output error", e))
.doOnComplete(() -> log.info("chat stream complete"));
}
@GetMapping("/extract/person")
public Person extractPerson(@RequestParam String userMessage) {
return aiSqlAssistantService.extractPerson(userMessage);
}
@GetMapping("/mock/username")
public List<String> mockUsername(@RequestParam(defaultValue = "0") Integer total) {
return aiSqlAssistantService.mockUsername(total);
}
@GetMapping(value = "/embedding")
public String aiEmbedding() throws IOException {
final Document document = ClassPathDocumentLoader.loadDocument("student_ddl.sql");
// 創建 SQL 感知的文檔分割器
DocumentSplitter splitter = new DocumentByRegexSplitter(";",";",
2000, // 最大片段長度
100 // 重疊長度
);
final List<TextSegment> textSegments = splitter.split(document);
final Response<List<Embedding>> embedResult = embeddingModel.embedAll(textSegments);
final List<Embedding> content = embedResult.content();
embeddingStore.addAll(content, textSegments);
return "success";
}
@GetMapping(value = "/{id}/sql/generate")
public String aiEmbedding(@PathVariable String id, @RequestParam String userMessage) {
return aiSqlAssistantService.chatWithSql(id, userMessage);
}
}
6. 測試
6.1 測試chat

6.2 測試tool

6.3 測試流式&聊天記憶
第一次會話:

后端日志如下

第二次會話:

6.4 測試抽取用戶信息

6.5 測試mock

6.6 測試embedding

數據庫中信息如下:

6.7 測試text2sql

7. 小結
本章通過使用SpringBoot實現快速集成LangChain4J, 通過簡單的配置實現了AI的調用, 總體使用感受還不錯, 雖然是剛發布的正式版但是整體的集成、方法調用都挺絲滑的, 到這里這關于LangChain4J的全部內容已經完結了, 后續會出個SpringAI正式版的體驗對比,感興趣的可以關注下.
8. 源碼
測試過程中的代碼已全部上傳至github, 歡迎點贊收藏 倉庫地址: https://github.com/ludangxin/langchain4j-test
PGVector
version: '3'
services:
pgvector:
container_name: pgvector
restart: "no"
image: pgvector/pgvector:0.8.0-pg17
privileged: true
ports:
- 5431:5432
environment:
POSTGRES_USER: root
POSTGRES_PASSWORD: 123456
PGDATA: /var/lib/postgresql/data/
volumes:
- ./data:/var/lib/postgresql/data/

浙公網安備 33010602011771號