# langchain4j-springboot-example
**Repository Path**: wujialia/langchain4j-springboot-example
## Basic Information
- **Project Name**: langchain4j-springboot-example
- **Description**: langchain4j+springboot的学习项目
- **Primary Language**: Java
- **License**: MIT
- **Default Branch**: master
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 35
- **Forks**: 19
- **Created**: 2025-03-06
- **Last Updated**: 2025-07-31
## Categories & Tags
**Categories**: Uncategorized
**Tags**: SpringBoot, langchain4j, Java
## README
## 项目简介
**简介**
**langchain4j**+**springboot**的学习项目,基于java17,spring-boot:3.4.3,langchain4j-spring-boot:1.0.0-beta1
平台为阿里云百炼,语言模型qwen-max,嵌入模型使用text-embedding-v2
向量数据库:redis stack 7.4.2
相关文档:[官方文档langchain4j](https://docs.langchain4j.dev/get-started)
项目地址:[gitee](https://gitee.com/wujialia/langchain4j-springboot-example) | [github](https://github.com/jialiawu/langchain4j-springboot-example.git)
**能学到什么?**
1、理解市面上常见的Ai产品的实现逻辑
2、使用langchain4j+springboot实现常见的功能,如聊天记忆,知识库构建,rag(EmbeddingStore),FunctionCall等功能
**功能**
1、日志(基于ChatModelListener,方便debug和学习)
2、对话记忆(基于内存)
3、FunctionCall,实现了一些简单的工具
4、知识库,可上传文件、url,支持的文件格式见[Tika](https://tika.apache.org/2.9.1/formats.html)
5、EmbeddingStoreRAG
6、联网搜索websearch,支持开关
**为什么还是java?**
技术栈切换成本高,想快速入门大模型应用开发,就是用你最熟悉的语言。先入门,理解了实现逻辑,再去考虑语言和生态的问题
**为什么使用langchain4j?**
首先是官方文档详细,示例代码多,其次是(个人觉得)api的设计要比SpringAi要清晰。
这里也给大家推荐一个个人感觉不错的SpringAi的学习项目《[起凡](https://www.jarcheng.top/blog/project/spring-ai/intro.html)》,b站有配套的视频
**要注意什么?**
1、这**不是**一个脚手架项目
2、目前不管是langchain4j还是SpringAi,api都在快速迭代中,很多api再以后可能就没用了。这是因为大模型底层的能力或交互方式还在变化。比如最近新出的mcp就很有可能代替FunctionCall
3、本项目专注于上层应用的开发,没有涉及大模型的底层实现逻辑
4、你们看到的代码是经过迭代的版本,和官网提供的示例可能有所区别。
**大模型开发到底在开发什么?**
大模型本质:数据压缩+概率续写(个人理解)
首先我们要知道语言模型有哪些能力
分别使用deepseek官网的直接问答和调用api的方式,来直观的体验基础大模型应用的能力

一样的问题,使用api调用


通过对比你可以看到,通过api调用的方式没有”记忆“了,也不能联网了,文件解析的功能也没有了。
这些语言模型本身不具备的能力就需要额外开发,这个开发工作就是大模型应用开发
(当然以上的例子只是最基础的功能)
## 组件
1、**(必选)**从阿里百炼获取到apikey,设置到系统环境变量API_KEY_DASH_SCOPE,也可将配置文件中的API_KEY_DASH_SCOPE替换为自己的apikey
2、**(必选)**安装redis stack 7.4.2
```
docker pull redis/redis-stack
docker run -d --name redis-stack --restart=always -v 持久卷:/data -p 宿主机连接端口:6379 -p 宿主机web管理端口:8001 -e REDIS_ARGS="--requirepass 密码" redis/redis-stack:latest
```
3、(可选)天气api使用的是高德的,如需天气工具可从高德开发者平台可申请到apikey,设置到系统环境变量API_KEY_GAODE
4、(可选)需要联网搜索功能时,需要安装[searxng](https://docs.searxng.org/index.html),国内搜索引擎支持较差。用它的主要原因是免费。[docker部署](https://docs.searxng.org/admin/installation-docker.html),以下为部署命令,部署完成后修改application.yaml中searxng下的相关配置
```
docker pull searxng/searxng
docker run --rm -p 宿主机端口:8080 -v "持久卷:/etc/searxng" -e "BASE_URL=http://宿主机Ip:宿主机端口/" -e "INSTANCE_NAME=my-instance" --name searxng -d searxng/searxng
```
## 最简单的对话
引入openai依赖(deepseek符合openai,可以兼容openai的sdk)
```xml
dev.langchain4j
langchain4j-open-ai
1.0.0-beta1
```
测试代码
```java
public class Test {
public static void main(String[] args) {
//从环境变量中获取apikey
String apiKey = System.getenv("API_KEY_DEEPSEEK");
OpenAiChatModel model = OpenAiChatModel.builder()
.apiKey(apiKey)
.baseUrl("https://api.deepseek.com")
.modelName("deepseek-reasoner")
.build();
String answer = model.chat("你好!你是谁?");
System.out.println(answer);
}
}
```
## 核心代码
入口controller
```java
@RestController
@RequiredArgsConstructor
@RequestMapping("/chat")
@Slf4j
public class ChatController {
//注入AI Service,装配代码见下方
private final QwenAssistant qwenAssistant;
//返回Flux
@GetMapping(value = "/stream/flux", produces = TEXT_EVENT_STREAM_VALUE)
public Flux chatStreamFlux(@RequestParam(value = "sessionId") String sessionId,
@RequestParam(value = "role", required = false, defaultValue = "智能问答助手") String role,
@RequestParam(value = "question") String question,
@RequestParam(value = "webSearchEnable", required = false, defaultValue = "false") Boolean webSearchEnable,
@RequestParam(value = "extraInfo", required = false, defaultValue = "") String extraInfo) {
return qwenAssistant.chatStreamFlux(sessionId, role, question, extraInfo);
}
//返回Flux>
@GetMapping(value = "/stream/sse", produces = TEXT_EVENT_STREAM_VALUE)
public Flux> chatStreamSse(@RequestParam(value = "sessionId") String sessionId,
@RequestParam(value = "role", required = false, defaultValue = "智能问答助手") String role,
@RequestParam(value = "question") String question,
@RequestParam(value = "webSearchEnable", required = false, defaultValue = "false") Boolean webSearchEnable,
@RequestParam(value = "extraInfo", required = false, defaultValue = "") String extraInfo) {
Sinks.Many> sink = Sinks.many().unicast().onBackpressureBuffer();
TokenStream tokenStream = qwenAssistant.chatStreamTokenStream(sessionId, role, question, extraInfo);
tokenStream.onRetrieved(contents ->
sink.tryEmitNext(ServerSentEvent.builder(toJson(convert(contents))).event("Retrieved").build()));
tokenStream.onPartialResponse(partialResponse -> sink.tryEmitNext(ServerSentEvent.builder(partialResponse).event("AiMessage").build()));
tokenStream.onError(sink::tryEmitError);
tokenStream.onCompleteResponse(aiMessageResponse -> sink.tryEmitComplete());
tokenStream.start();
return sink.asFlux();
}
}
```
自动配置
```java
@Configuration
@EnableConfigurationProperties(QwenProperties.class)
public class QwenConfiguration {
//装配AI Service,它由多个组件协同工作(例如:提示模板、聊天记忆、RAG组件)
@Bean
@ConditionalOnProperty(QwenProperties.PREFIX + ".chat-model.api-key")
public QwenAssistant qwenAssistant(QwenChatModel qwenChatModel,
QwenStreamingChatModel qwenStreamingChatModel,
ChatMemoryStore dbChatMemoryStore,
Collection tools,
ContentRetriever contentRetriever,
RetrievalAugmentor retrievalAugmentor) {
return AiServices.builder(QwenAssistant.class)
//流式聊天模型
.streamingChatLanguageModel(qwenStreamingChatModel)
//普通聊天模型
.chatLanguageModel(qwenChatModel)
//聊天记忆
.chatMemoryProvider(memoryId -> MessageWindowChatMemory.builder()
.id(memoryId)
.maxMessages(10)
.chatMemoryStore(dbChatMemoryStore)
.build())
//function call工具集
.tools(tools.toArray()) //注意这个地方传集合的话必须传Collection