作为开发者,我们一直在寻找创新且经济高效的方案来简化复杂流程。本文将深入探讨如何利用 Spring AI 的强大功能,结合本地 向量数据库 和微调后的 Llama 3.2 模型,构建一套高效的 RAG (Retrieval Augmented Generation,检索增强生成) 系统,从而极大地简化文档搜索,并在资源有限的环境下实现卓越的性能。这种方案不仅满足了特定用例的需求,还充分利用了现有基础设施,并以最小的 GPU 需求高效运行。
Spring AI:RAG 系统的核心骨架
Spring AI 是本文方案的基石,它为 Spring Boot 应用集成了 AI 能力提供了一个健壮的框架。具体来说,我们使用了 Spring AI 1.0.0.M7 版本。Spring AI 的优势在于其模块化设计,方便开发者集成各种 AI 组件,例如:大型语言模型(LLM)、向量数据库以及嵌入模型等。更重要的是,Spring AI 提供了诸如 spring-ai-starter-ollama
,spring-ai-advisors-core
,spring-ai-rag
等组件,让开发者能轻松构建RAG应用。
实际案例: 假设一个大型企业拥有海量的内部文档,涵盖产品手册、技术规范、故障排除指南等等。使用传统的搜索方式,员工往往需要花费大量时间才能找到所需信息。借助 Spring AI,可以将这些文档导入到向量数据库中,利用 LLM 的语义理解能力,让员工通过自然语言提问,快速获得精准的答案,极大提升工作效率。
Ollama:本地 LLM 运行的理想选择
为了充分利用 Llama 3.2 模型,我们选择了 Ollama 作为本地 LLM 运行环境。spring-ai-starter-ollama
组件使得与 Ollama 服务器的无缝通信成为可能,从而我们可以利用本地 LLM 进行聊天和嵌入功能。Ollama 简化了 LLM 的部署和管理,无需复杂的配置和依赖,极大地降低了使用 LLM 的门槛。
优势:
- 本地部署: 数据和模型都保存在本地,避免了数据泄露的风险,符合对数据安全性有严格要求的场景。
- 离线运行: 无需依赖互联网连接,即使在网络环境不稳定的情况下也能正常工作。
- 灵活定制: 开发者可以根据自身需求,选择不同的 LLM 模型,并进行微调和优化。
实际案例: 一家医疗机构需要构建一个内部的智能问答系统,用于解答医护人员关于药物使用、疾病诊断等方面的疑问。由于医疗数据的敏感性,不允许将其上传到云端。通过使用 Spring AI 和 Ollama,可以将 Llama 3.2 模型部署在本地服务器上,并加载医疗知识库,构建一个安全可靠的智能问答系统。
本地向量数据库:构建企业专属知识库
为了实现高效的 RAG,我们需要一个能够快速检索相关文档的 向量数据库。文章作者选择了 Spring AI 的 SimpleVectorStore
,这是一个基于文件的本地向量数据库,具有成本效益和低延迟的优势。虽然 SimpleVectorStore
是一个很好的原型验证方案,但是在生产环境中通常需要一个更强大的向量数据库,例如PgVectorStore或PineconeVectorStore。
优势:
- 无需外部依赖: 不需要额外的数据库服务,降低了部署和维护的复杂度。
- 低成本: 基于文件存储,避免了昂贵的云服务费用。
- 易于集成: 与 Spring AI 无缝集成,使用方便。
实际案例: 一个律师事务所需要构建一个法律知识库,用于快速检索相关的法律条文、判例和法律咨询。将法律文档导入到 SimpleVectorStore
中,并利用 LLM 的语义理解能力,律师可以通过自然语言提问,快速找到相关的法律依据,提升案件处理效率。
Llama 3.2 模型:小而强大的语言模型
文章中选择 Llama 3.2 模型 是一大亮点,尤其是在资源有限的情况下。选择较小且经过微调的模型,可以在适度的硬件上获得强大的结果。对于特定任务而言,它能够在不消耗大型模型所需的大量资源的情况下,提供出色的性能。这意味着即使没有高端 GPU 集群,也能构建高效的 AI 应用。
微调的价值: 通过对 Llama 3.2 模型进行微调,可以使其更好地适应特定领域的知识和语言风格,从而提高 RAG 系统的准确性和相关性。例如,可以利用法律领域的语料库对 Llama 3.2 模型进行微调,使其更擅长处理法律问题。
实际案例: 一个在线教育平台需要构建一个智能答疑系统,用于解答学生在学习过程中遇到的问题。由于计算资源有限,无法使用大型 LLM 模型。通过对 Llama 3.2 模型进行微调,并加载教育领域的知识库,可以构建一个轻量级的智能答疑系统,为学生提供及时的帮助。
项目搭建:pom.xml 依赖配置
pom.xml
文件是 Maven 项目的核心配置文件,它定义了项目所需的依赖项。以下是文章中提供的 pom.xml
文件,展示了如何将 Spring AI、Ollama 和向量数据库整合到一起:
<?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.2.0</version> <!-- Use a recent stable Spring Boot version -->
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>spring-ai-rag-demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>spring-ai-rag-demo</name>
<description>Demo project for Spring AI RAG</description>
<properties>
<java.version>17</java.version>
<spring-ai.version>1.0.0-M7</spring-ai.version> <!-- Aligning version for consistency -->
</properties>
<dependencies>
<!-- Spring Boot Web Starter for building RESTful APIs -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Spring AI Starter for Ollama Model Integration -->
<!-- This dependency provides the necessary components to integrate with Ollama models. -->
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-ollama</artifactId>
</dependency>
<!-- Spring AI Advisors for Vector Store integration -->
<!-- This module includes the QuestionAnswerAdvisor used for RAG, which automatically
queries the vector store and injects context into LLM prompts. -->
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-advisors-core</artifactId>
</dependency>
<!-- Spring AI RAG module -->
<!-- Provides core RAG functionalities and components -->
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-rag</artifactId>
</dependency>
<!-- Spring AI Core -->
<!-- This is a foundational dependency for Spring AI projects. -->
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-core</artifactId>
<version>${spring-ai.version}</version> <!-- Use the consistent version from properties -->
</dependency>
<!-- Spring Boot Test Starter for unit and integration testing -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<!-- Spring AI Dependency Management -->
<!-- This section ensures that all Spring AI dependencies use consistent versions. -->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-bom</artifactId>
<version>${spring-ai.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
<!-- Spring Milestone Repository for Spring AI Snapshots/Milestones -->
<!-- Required to resolve Spring AI milestone dependencies. -->
<repositories>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
</project>
关键依赖项:
spring-ai-starter-ollama
:连接 Ollama,用于聊天和嵌入模型。spring-ai-rag
和spring-ai-advisors-core
:构建 RAG 管道,使 LLM 能够从向量数据库中检索相关信息。spring-ai-core
:提供基础 AI 抽象,包括InMemoryVectorStore
(SimpleVectorStore
扩展自它)和EmbeddingClient
。
配置:连接 Ollama 并定义模型
application.properties
文件用于配置 Ollama 连接并指定要使用的模型:
spring.application.name=spring-ai-ollama-vector-db
spring.ai.ollama.base-url=http://localhost:11434
# Ollama chat model
spring.ai.ollama.chat.options.model=llama3.2:3b
# Ollama embedding model (using the same fine-tuned model for embeddings)
spring.ai.ollama.embedding.options.model=llama3.2:3b
# Ollama auto-pull model configurations
spring.ai.ollama.init.pull-model-strategy=always
spring.ai.ollama.init.timeout=15m
spring.ai.ollama.init.max-retries=2
# Other app specific config
spring.application.name=ollama-huggingface-gguf
#spring.main.web-application-type=NONE
重要配置项:
spring.ai.ollama.base-url
:指向正在运行的 Ollama 实例。spring.ai.ollama.chat.options.model=llama3.2:3b
:指定用于聊天交互的 Llama 3.2 模型。spring.ai.ollama.embedding.options.model=llama3.2:3b
:使用相同的 Llama 3.2 模型 进行嵌入。虽然专用嵌入模型通常提供更好的语义搜索性能,但使用经过微调的 LLM 可以是一种经济高效的选择,尤其是在微调过程针对相关文本表示进行了优化的情况下。spring.ai.ollama.init.pull-model-strategy=always
:确保 Ollama 拉取指定的模型(如果尚未存在)。
本地向量数据库配置:VenctorStoreConfig.java
VenctorStoreConfig.java
文件用于配置本地向量数据库 SimpleVectorStore
:
import org.springframework.ai.embedding.EmbeddingModel;
import org.springframework.ai.vectorstore.SimpleVectorStore;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.io.File; // Don't forget to import File
@Configuration
public class VenctorStoreConfig {
@Bean
public SimpleVectorStore vectorStore(EmbeddingModel embeddingModel) {
SimpleVectorStore vectorStore = SimpleVectorStore.builder(embeddingModel).build();
// Load existing vector DB if it exists
File vectorDbFile = new File("vector_db.json");
if (vectorDbFile.exists()) {
vectorStore.load(vectorDbFile);
}
return vectorStore;
}
}
关键点:
SimpleVectorStore
使用EmbeddingModel
进行初始化,将文本数据转换为数值向量。- 如果存在
vector_db.json
文件,则加载现有的向量数据库,实现数据持久化。
核心交互:ChatController.java
ChatController.java
文件暴露了 REST 接口,用于管理知识库并与 LLM 进行交互:
import java.io.File;
import java.util.List;
import java.util.Map;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.client.advisor.vectorstore.QuestionAnswerAdvisor;
import org.springframework.ai.chat.prompt.Prompt;
import org.springframework.ai.chat.prompt.PromptTemplate;
import org.springframework.ai.document.Document;
import org.springframework.ai.embedding.EmbeddingModel;
import org.springframework.ai.vectorstore.SimpleVectorStore;
import org.springframework.ai.vectorstore.VectorStore;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
/**
* REST Controller for demonstrating Spring AI's RAG (Retrieval Augmented Generation) capabilities.
* This controller allows loading sample incident data into a vector store and then
* querying a ChatClient with contextual information retrieved from that vector store.
*/
@RestController
public class ChatController {
private final ChatClient chatClient;
// Autowire SimpleVectorStore to manage and persist document embeddings.
// In a real application, this would typically be a persistent vector store like PgVectorStore, PineconeVectorStore, etc.
@Autowired
SimpleVectorStore vectorStore;
/**
* Constructor for ChatController, injecting necessary components.
* @param chatClientBuilder Builder for configuring the ChatClient.
* @param embeddingModel The embedding model used by the vector store to convert text into numerical vectors.
* @param vectorstore The vector store used for document retrieval.
*/
public ChatController(ChatClient.Builder chatClientBuilder, EmbeddingModel embeddingModel, VectorStore vectorstore) {
// Configure the ChatClient with a QuestionAnswerAdvisor.
// The QuestionAnswerAdvisor automatically queries the configured vector store
// based on the user's prompt and injects relevant contextual documents into the LLM prompt.
this.chatClient = chatClientBuilder
.defaultAdvisors(new QuestionAnswerAdvisor(vectorstore))
.build();
}
/**
* Endpoint to load sample incident data into the SimpleVectorStore.
* This data is then used as context for the LLM when querying.
* The data is also persisted to a local 'vector_db.json' file for simple demonstration.
*/
@GetMapping("/load-data")
void load_data() {
// Define a list of detailed incident descriptions.
// These descriptions represent the "documents" that will be stored in the vector store.
// A real-world scenario would involve loading data from databases, logs, or knowledge bases.
final List<Map<String, String>> incidents = List.of(
Map.of("id", "INC1", "text", "User 'Alice' reported a critical system outage impacting login services. Initial diagnosis points to a database connection pool exhaustion. Escalated to DevOps team for urgent resolution."),
Map.of("id", "INC2", "text", "Customer 'Bob' cannot access their account dashboard. Suspected issue with session management after recent deployment. Investigating application logs for authentication errors and token invalidation."),
Map.of("id", "INC3", "text", "Performance degradation observed on the API gateway for 'Product X' service. Latency spikes during peak hours affecting user experience. Investigating potential bottlenecks in microservices architecture and caching layers."),
Map.of("id", "INC4", "text", "Security alert triggered for suspicious activity on a production server in the EU region. Potential unauthorized access attempt detected via SSH. Incident response team engaged to isolate the server and conduct forensic analysis."),
Map.of("id", "INC5", "text", "Email delivery failures reported for marketing campaigns. Mails are bouncing back with 'recipient not found' errors. Checking DNS records, SMTP configurations, and spam filters for misconfigurations."),
Map.of("id", "INC6", "text", "New feature 'Feature Y' deployed yesterday is causing UI rendering issues on mobile devices. Specifically affects iOS users on Safari browser. Frontend team analyzing compatibility with different browser versions and responsive design issues.")
);
// Iterate through each incident, create a Document, and add it to the vector store.
for (Map<String, String> incident : incidents) {
var doc = Document.builder()
.id(incident.get("id")) // Unique ID for the document
.text(incident.get("text")) // The actual content of the document
.build();
vectorStore.add(List.of(doc)); // Add the document to the vector store
}
// Persist the vector store to a JSON file.
// In a production setup, this would typically involve a dedicated vector database.
File f = new File("vector_db.json");
System.out.println("Saving vector store to: " + f.getAbsolutePath());
vectorStore.save(f);
System.out.println("Data loaded and saved to vector_db.json successfully.");
}
/**
* Endpoint to get a report based on a user's question.
* The QuestionAnswerAdvisor will automatically retrieve relevant context from the vector store
* and use it to inform the LLM's response.
* @param question The user's query about an incident. Default response provided if null.
* @return A detailed response from the LLM based on the provided question and retrieved context.
*/
@GetMapping("/report")
public String index(@RequestParam(required = false) String question) {
// Provide a default question if none is specified by the user.
question = (question == null || question.trim().isEmpty()) ? "provide a default response for no input" : question;
// Create a PromptTemplate for the LLM.
// The QuestionAnswerAdvisor will prepend the retrieved context to this prompt implicitly.
PromptTemplate pt = new PromptTemplate("""
Provide detail on {question} from the context provided.
If you find Spanish text in the user input, change it to English before processing.
Don't assume anything outside of the given context.
If the question cannot be answered from the context, state that clearly.""");
// Create the Prompt object using the template and the user's question.
Prompt p = pt.create(Map.of("question", question));
// Call the ChatClient with the prompt. The advisor will handle context injection.
return this.chatClient.prompt(p)
.call()
.content(); // Return the content of the LLM's response
}
}
重点:
QuestionAnswerAdvisor
:RAG 的关键,拦截提示,在向量数据库中执行语义搜索,然后将检索到的上下文添加到原始提示中。/load-data
端点:用示例“事件”填充本地向量数据库的简单方法。每个事件都转换为Document
并添加到SimpleVectorStore
,然后保存到vector_db.json
。/report
端点:主要交互点。它接受一个问题,使用PromptTemplate
构造请求,然后利用chatClient
(包括 RAG advisor)从 Llama 3.2 获得智能的、上下文感知的响应。注意提示中的翻译西班牙语文本的指令,展示了模型的语言能力。
运行应用程序:ApplicationStater.java
标准的 Spring Boot 应用程序入口点:
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class ApplicationStater {
public static void main(String[] args) {
SpringApplication.run(ApplicationStater.class, args); // Corrected class name
}
}
运行前:
- 启动 Ollama:确保 Ollama 服务器正在运行,并且已经拉取了
llama3.2:3b
模型。 - 构建和运行:
mvn clean install
mvn spring-boot:run
结论:经济高效的智能 RAG 方案
通过上述设置,我们成功构建了一个强大的 RAG 解决方案,特别适用于 L2 支持等场景。使用本地微调的 Llama 3.2 模型,结合基于文件的 向量数据库,意味着:
- 成本效益: 无需昂贵的云端向量数据库服务或高端 GPU 集群。
- 低延迟: 本地处理意味着快速响应时间。
- 数据控制: 知识库保留在您的环境中。
- 现有基础设施效率: 优化以在基本 GPU 设置甚至 CPU 上有效运行,最大限度地利用当前资源。
这种方法表明,复杂的 AI 解决方案并不总是需要过高的资源。通过战略性地将强大的开源工具与 Spring AI 的灵活性相结合,您可以释放难以置信的功能来简化复杂的操作流程。
那么,您希望使用 Spring AI 和本地 LLM 解决哪些挑战呢?欢迎在评论区分享您的想法!