1. 项目概述
最近AI技术发展迅猛,各种AI框架层出不穷。作为一名Java开发者,我一直在探索如何将AI能力集成到Spring Boot应用中。经过多次尝试,终于成功实现了一个基于Spring Boot 3 + LangChain4j + Vue的本地AI编程助手项目。这个项目最大的特点是可以调用本地部署的Ollama大模型,避免了依赖云端API带来的延迟和费用问题。
在开发过程中,我踩了不少坑,也积累了一些经验。本文将详细介绍整个项目的搭建过程、核心功能实现以及遇到的问题和解决方案。无论你是想学习AI集成,还是对LangChain框架感兴趣,相信这篇文章都能给你带来帮助。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 环境准备
2.1 开发环境配置
我的开发环境配置如下:
- 操作系统:macOS 15.7
- IDE:IntelliJ IDEA最新版
- Java版本:JDK 21
- Spring Boot版本:3.5.11
选择Java 21是因为LangChain4j框架最低要求Java 17,而21是目前最新的LTS版本,性能更好且更稳定。Spring Boot选择了3.5.11这个稳定版本,避免使用太新的版本可能带来的兼容性问题。
2.2 项目初始化
使用IDEA创建Spring Boot项目非常简单:
- 选择Spring Initializr
- 填写项目基本信息
- 选择Java 21作为SDK版本
- 添加必要的依赖:
- Spring Web:提供Web开发支持
- Lombok:简化代码编写
- Spring Boot DevTools:开发工具支持
提示:建议使用yaml格式的配置文件,相比properties文件更清晰易读。
3. LangChain4j集成
3.1 添加依赖
LangChain4j提供了多种集成方式,我们需要在pom.xml中添加以下核心依赖:
xml复制<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j-ollama</artifactId>
<version>1.11.0</version>
</dependency>
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j-spring-boot-starter</artifactId>
<version>1.11.0-beta19</version>
</dependency>
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j-ollama-spring-boot-starter</artifactId>
<version>1.11.0-beta19</version>
</dependency>
这些依赖分别提供了:
- LangChain4j与Ollama的集成
- Spring Boot自动配置支持
- Ollama与Spring Boot的深度集成
3.2 配置Ollama模型
在application.yml中配置Ollama模型参数:
yaml复制langchain4j:
ollama:
chat-model:
base-url: http://localhost:11434
model-name: deepseek-r1:14b
log-requests: true
log-responses: true
temperature: 0.7
timeout: 60s
max-retries: 3
这里使用的是本地部署的deepseek-r1:14b模型。如果你还没有安装Ollama和模型,可以通过以下命令安装:
bash复制ollama pull deepseek-r1:14b
4. 核心功能实现
4.1 基础聊天功能
首先实现一个最简单的聊天服务:
java复制@Service
@Slf4j
public class AiCodeHelper {
@Resource
private ChatModel deepseekChatModel;
public String chat(String message) {
UserMessage userMessage = UserMessage.from(message);
ChatResponse chatResponse = deepseekChatModel.chat(userMessage);
AiMessage aiMessage = chatResponse.aiMessage();
log.info("AI输出: " + aiMessage.text());
return aiMessage.text();
}
}
这个服务注入了配置好的ChatModel,接收用户消息后返回AI的回复。可以通过简单的单元测试验证功能:
java复制@SpringBootTest
public class AiCodeHelperTest {
@Autowired
private AiCodeHelper aiCodeHelper;
@Test
void testChat() {
String response = aiCodeHelper.chat("你好,我是开发者");
System.out.println(response);
assertNotNull(response);
}
}
4.2 AI Service高级封装
LangChain4j提供了更高级的AI Service模式,可以大大简化代码:
java复制public interface AiCodeHelperService {
@SystemMessage("你是一位编程小助手")
String chat(@UserMessage String userMessage);
}
然后通过工厂类创建实现:
java复制@Configuration
public class AiCodeHelperServiceFactory {
@Resource
private ChatModel deepseekChatModel;
@Bean
public AiCodeHelperService aiCodeHelperService() {
return AiServices.builder(AiCodeHelperService.class)
.chatModel(deepseekChatModel)
.build();
}
}
这种方式底层使用了Java动态代理,开发者只需要定义接口,实现由框架自动完成。
踩坑经验:在测试时遇到了NullPointerException,经过排查发现是依赖版本冲突问题。最终使用1.11.0-beta19版本解决了这个问题。
5. 高级功能实现
5.1 对话记忆
为了让AI能记住上下文对话,可以添加对话记忆功能:
java复制@Bean
public AiCodeHelperService aiCodeHelperService() {
ChatMemory chatMemory = MessageWindowChatMemory.withMaxMessages(10);
return AiServices.builder(AiCodeHelperService.class)
.chatModel(deepseekChatModel)
.chatMemory(chatMemory)
.build();
}
这样AI就能记住最近的10条对话。测试方法:
java复制@Test
void testChatMemory() {
String response1 = aiCodeHelperService.chat("我叫小明");
String response2 = aiCodeHelperService.chat("我叫什么名字?");
System.out.println(response2); // 应该能回答"小明"
}
5.2 结构化输出
有时我们需要AI返回特定格式的数据,可以通过结构化输出实现:
java复制public record PersonInfo(String name, Integer age, Double height, Boolean married, String occupation) {}
public interface AiCodeHelperService {
@SystemMessage(fromResource = "system-prompt.txt")
PersonInfo extractPersonInfo(@UserMessage String text);
}
system-prompt.txt内容:
code复制你是一个专业的信息提取助手。请从给定文本中提取人员信息,
并严格按照以下JSON格式返回结果:
{
"name": "人员姓名",
"age": 年龄数字,
"height": 身高(米),
"married": true/false,
"occupation": "职业"
}
5.3 RAG检索增强
RAG(Retrieval-Augmented Generation)可以让AI基于特定知识库回答问题:
- 首先配置Embedding模型:
yaml复制langchain4j:
ollama:
embedding-model:
base-url: http://localhost:11434
model-name: Qwen3-Embedding:4b
timeout: 60s
- 创建RAG配置类:
java复制@Configuration
public class RagConfig {
@Resource
private EmbeddingModel qwenEmbeddingModel;
@Bean
public ContentRetriever contentRetriever() {
// 加载文档
List<Document> documents = FileSystemDocumentLoader.loadDocuments("src/main/resources/docs");
// 文档分割配置
DocumentByParagraphSplitter splitter = new DocumentByParagraphSplitter(1000, 200);
// 创建内容检索器
EmbeddingStoreIngestor ingestor = EmbeddingStoreIngestor.builder()
.documentSplitter(splitter)
.embeddingModel(qwenEmbeddingModel)
.embeddingStore(new InMemoryEmbeddingStore<>())
.build();
ingestor.ingest(documents);
return EmbeddingStoreContentRetriever.builder()
.embeddingStore(new InMemoryEmbeddingStore<>())
.embeddingModel(qwenEmbeddingModel)
.maxResults(5)
.minScore(0.75)
.build();
}
}
- 在AI Service中使用:
java复制public interface AiCodeHelperService {
@SystemMessage("你是一位编程助手")
Result<String> chatWithRag(@UserMessage String question);
}
6. 前端集成
6.1 Vue前端搭建
使用Vue CLI创建前端项目:
bash复制vue create ai-assistant-frontend
cd ai-assistant-frontend
npm install axios element-plus
6.2 调用后端API
创建API服务:
javascript复制import axios from 'axios';
const api = axios.create({
baseURL: 'http://localhost:8081/api',
timeout: 60000
});
export default {
async chat(message) {
const response = await api.post('/chat', { message });
return response.data;
},
async chatWithMemory(message, memoryId) {
const response = await api.post('/chat/memory', { message, memoryId });
return response.data;
}
};
6.3 聊天界面实现
简单的聊天组件:
vue复制<template>
<div class="chat-container">
<div v-for="(msg, index) in messages" :key="index" :class="msg.sender">
{{ msg.text }}
</div>
<input v-model="inputMessage" @keyup.enter="sendMessage" />
</div>
</template>
<script>
import api from '@/services/api';
export default {
data() {
return {
messages: [],
inputMessage: '',
memoryId: null
};
},
methods: {
async sendMessage() {
const userMsg = { sender: 'user', text: this.inputMessage };
this.messages.push(userMsg);
const response = await api.chatWithMemory(this.inputMessage, this.memoryId);
this.memoryId = response.memoryId;
const aiMsg = { sender: 'ai', text: response.message };
this.messages.push(aiMsg);
this.inputMessage = '';
}
}
};
</script>
7. 部署与优化
7.1 本地部署
- 启动Ollama服务:
bash复制ollama serve
- 加载模型:
bash复制ollama pull deepseek-r1:14b
ollama pull Qwen3-Embedding:4b
- 启动Spring Boot应用:
bash复制mvn spring-boot:run
- 启动Vue前端:
bash复制npm run serve
7.2 性能优化
本地运行大模型对硬件要求较高,可以采取以下优化措施:
- 使用量化模型:选择4bit或8bit量化的模型版本
- 调整模型参数:降低temperature值减少随机性
- 限制上下文长度:控制chatMemory的大小
- 使用流式响应:减少用户等待时间
8. 常见问题解决
8.1 模型加载失败
问题现象:启动时报错,无法连接Ollama服务
解决方案:
- 确认Ollama服务已启动
- 检查application.yml中的base-url配置
- 确保模型已正确下载
8.2 响应速度慢
问题现象:AI响应需要很长时间
解决方案:
- 使用性能更好的硬件
- 选择更小的模型
- 启用流式响应
8.3 内存溢出
问题现象:运行一段时间后应用崩溃
解决方案:
- 增加JVM内存:-Xmx8g
- 减少chatMemory大小
- 定期清理不需要的缓存
9. 项目总结
通过这个项目,我成功将本地部署的大模型集成到了Spring Boot应用中,实现了以下功能:
- 基础聊天功能
- 上下文记忆
- 结构化输出
- 基于知识库的问答
- 前端交互界面
最大的收获是深入理解了LangChain4j框架的工作原理和使用方法。虽然过程中遇到了不少问题,但通过查阅文档和不断尝试,最终都找到了解决方案。
对于想要尝试类似项目的开发者,我的建议是:
- 从简单功能开始,逐步增加复杂度
- 注意依赖版本兼容性
- 合理控制模型大小和参数
- 做好错误处理和日志记录
这个项目还有很多可以扩展的方向,比如:
- 支持更多模型类型
- 实现多模态交互
- 增加插件系统
- 优化前端用户体验
