Giới thiệu
Bài viết này hướng dẫn cách tích hợp Spring Boot 3 với LangChain4j để xây dựng một trợ lý AI y tế, sử dụng MongoDB để lưu trữ bộ nhớ cuộc trò chuyện và Elasticsearch 8 để lưu trữ và truy xuất dữ liệu vector.
Các công nghệ chính được sử dụng:
- Spring Boot 3.2.6
- LangChain4j 1.0.0-beta3
- DeepSeek, Ollama, Qwen (Aliyun) - các mô hình ngôn ngữ lớn
- MongoDB - lưu trữ bộ nhớ cuộc trò chuyện
- Elasticsearch 8 - lưu trữ vector và RAG
- Function Calling - tùy chỉnh logic kinh doanh
- Đầu ra luồng - phản hồi thời gian thực
Cấu hình dự án Spring Boot 3 và LangChain4j
Môi trường JDK 17
Spring Boot 3 yêu cầu JDK 17 trở lên. Hãy đảm bảo bạn đã cài đặt JDK 17.
Thêm dependency vào 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>
<langchain4j.version>1.0.0-beta3</langchain4j.version>
</properties>
<dependencies>
<!-- Web core -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Testing -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<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>
Cấu hình application.properties
# Cổng
server.port=8080
Lớp khởi động
package com.example.ai;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class MedicalAssistantApplication {
public static void main(String[] args) {
SpringApplication.run(MedicalAssistantApplication.class, args);
System.out.println("Ứng dụng đã khởi động thành công!");
}
}
Tích hợp các mô hình ngôn ngữ lớn (LLM)
DeepSeek
Đăng ký tài khoản trên trang web DeepSeek và lấy API key.
Cấu hình application.properties
# DeepSeek API
DEEP_SEEK_API_KEY=sk-cf1a************94674e7e9
langchain4j.open-ai.chat-model.base-url=https://api.deepseek.com
langchain4j.open-ai.chat-model.api-key=${DEEP_SEEK_API_KEY}
langchain4j.open-ai.chat-model.model-name=deepseek-chat
Ollama
Tải và cài đặt Ollama, sau đó tải mô hình DeepSeek: `ollama run deepseek-r1:1.5b`.
Dependency và Cấu hình
<!-- Ollama -->
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j-ollama-spring-boot-starter</artifactId>
</dependency>
# Ollama
langchain4j.ollama.chat-model.base-url=http://localhost:11434
langchain4j.ollama.chat-model.model-name=deepseek-r1:1.5b
Qwen (Aliyun)
Đăng ký tài khoản trên Aliyun và lấy API key.
Dependency và Cấu hình
<!-- Aliyun Qwen -->
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j-community-dashscope-spring-boot-starter</artifactId>
</dependency>
# Aliyun Qwen
DASH_SCOPE_API_KEY=sk-3f0ebccc3*******c5a728e8974
langchain4j.community.dashscope.chat-model.api-key=${DASH_SCOPE_API_KEY}
langchain4j.community.dashscope.chat-model.model-name=qwen-max
Bộ nhớ cuộc trò chuyện (Chat Memory)
Bộ nhớ trong (In-memory)
Sử dụng `MessageWindowChatMemory` để lưu trữ một số lượng tin nhắn nhất định.
@Configuration
public class ChatMemoryConfig {
@Bean
ChatMemory chatMemory() {
return MessageWindowChatMemory.withMaxMessages(10);
}
}
Bộ nhớ được phân lập theo ID cuộc trò chuyện
Sử dụng `ChatMemoryProvider` để tạo bộ nhớ riêng cho mỗi cuộc trò chuyện.
@Configuration
public class IsolatedChatMemoryConfig {
@Bean
ChatMemoryProvider chatMemoryProvider() {
return memoryId -> MessageWindowChatMemory.builder()
.id(memoryId)
.maxMessages(10)
.build();
}
}
Lưu trữ bộ nhớ vào MongoDB
Tạo một lớp implements `ChatMemoryStore` để lưu trữ và truy xuất dữ liệu từ MongoDB.
@Component
public class MongoChatMemoryStore implements ChatMemoryStore {
@Autowired
private MongoTemplate mongoTemplate;
@Override
public List<ChatMessage> getMessages(Object memoryId) {
// ... logic to retrieve messages from MongoDB
}
@Override
public void updateMessages(Object memoryId, List<ChatMessage> messages) {
// ... logic to update messages in MongoDB
}
@Override
public void deleteMessages(Object memoryId) {
// ... logic to delete messages from MongoDB
}
}
Template Prompt và System Message
Sử dụng `@SystemMessage` để định nghĩa vai trò và hành vi của AI, và `@UserMessage` để nhận đầu vào từ người dùng.
@AiService(wiringMode = EXPLICIT, chatModel = "qwenChatModel", chatMemoryProvider = "chatMemoryProvider")
public interface MedicalAssistant {
@SystemMessage(fromResource = "medical-assistant-prompt.txt")
String chat(@MemoryId Long memoryId, @UserMessage String userMessage);
}
Tệp `medical-assistant-prompt.txt` chứa hướng dẫn cho AI:
Bạn là một trợ lý y tế chuyên nghiệp. Hãy trả lời các câu hỏi y tế một cách chính xác và hữu ích.
Function Calling (Công cụ tùy chỉnh)
Định nghĩa các công cụ để AI có thể gọi các hàm kinh doanh.
Ví dụ: Công cụ đặt lịch hẹn
@Component
public class AppointmentTools {
@Autowired
private AppointmentService appointmentService;
@Tool(name = "Đặt lịch hẹn", value = "Đặt lịch hẹn cho bệnh nhân")
public String bookAppointment(Appointment appointment) {
// Logic to book an appointment
}
@Tool(name = "Hủy lịch hẹn", value = "Hủy lịch hẹn của bệnh nhân")
public String cancelAppointment(Appointment appointment) {
// Logic to cancel an appointment
}
}
Lưu trữ Vector với Elasticsearch 8
Cài đặt Elasticsearch 8
Tải và cài đặt Elasticsearch 8. Cấu hình `elasticsearch.yml` để tắt bảo mật nếu cần.
Tạo Index Vector
PUT medical-knowledge
{
"mappings": {
"properties": {
"vector": {
"type": "dense_vector",
"dims": 1024,
"index": true,
"similarity": "l2_norm"
},
"text": { "type": "text" }
}
}
}
Tích hợp Elasticsearch trong dự án
<!-- Elasticsearch -->
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j-elasticsearch</artifactId>
</dependency>
<dependency>
<groupId>co.elastic.clients</groupId>
<artifactId>elasticsearch-java</artifactId>
<version>8.17.0</version>
</dependency>
# Elasticsearch
es.host=127.0.0.1
es.port=9200
es.index-name=medical-knowledge
Cấu hình Content Retriever
@Configuration
public class RAGConfig {
@Autowired
private RestClient restClient;
@Autowired
private EmbeddingModel embeddingModel;
@Bean
ContentRetriever contentRetriever() {
ElasticsearchEmbeddingStore embeddingStore = ElasticsearchEmbeddingStore.builder()
.restClient(restClient)
.indexName("medical-knowledge")
.build();
return EmbeddingStoreContentRetriever.builder()
.embeddingModel(embeddingModel)
.embeddingStore(embeddingStore)
.maxResults(1)
.minScore(0.4)
.build();
}
}
Đầu ra luồng (Streaming Output)
Sử dụng WebFlux để cung cấp phản hồi theo thời gian thực.
Dependency
<!-- WebFlux và Reactor -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j-reactor</artifactId>
</dependency>
Cấu hình và Controller
@AiService(
wiringMode = EXPLICIT,
streamingChatModel = "qwenStreamingChatModel",
chatMemoryProvider = "chatMemoryProvider",
tools = "appointmentTools",
contentRetriever = "contentRetriever"
)
public interface MedicalAssistant {
Flux<String> chat(@MemoryId Long memoryId, @UserMessage String userMessage);
}
@RestController
@RequestMapping("/api/assistant")
public class AssistantController {
@Autowired
private MedicalAssistant medicalAssistant;
@PostMapping("/chat")
public Flux<String> chat(@RequestBody ChatRequest request) {
return medicalAssistant.chat(request.getMemoryId(), request.getMessage());
}
}