使用本地Ollama spring AI实现对话机器人,可进行历史对话记录显示
·
使用springAI发起与大模型的对话

本地配置Ollama
-
下载Ollama软件到本地,ollama官网下载
-
选择所需的ai model 进行下载,可以根据想要选择的model在页面上方进行搜索
-
这里下载的是qwen3:1.7b,可以在ollama中设置model的下载位置

-
选择适合自己的版本和大小

-
在本地命令行执行该命令,就可以实现在本地和模型聊天了

添加依赖
<properties>
<java.version>17</java.version>
<spring-ai.version>1.0.9</spring-ai.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-model-ollama</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.22</version>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.ai</groupId>
<!--spring ai 统一管理库-->
<artifactId>spring-ai-bom</artifactId>
<version>${spring-ai.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
配置
配置文件
spring:
application:
name: spring-ai-demo
ai:
ollama:
base-url: http://localhost:11434
chat:
options:
model: qwen3:1.7b
temperature: 0.7
max-tokens: 1024
top-p: 1.0
# 配置日志
logging:
level:
org:
springframework:
ai:
chat:
client: debug
前后端跨域设置
package cn.demo.conf;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
//配置跨域设置
@Configuration
public class MvcCommonConfiguration implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("*")
.allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS")
.allowedHeaders("*")
.exposedHeaders("Content-Disposition");
}
}
配置Ollama AI连接
package cn.demo.conf;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.client.advisor.MessageChatMemoryAdvisor;
import org.springframework.ai.chat.client.advisor.SimpleLoggerAdvisor;
import org.springframework.ai.chat.memory.ChatMemory;
import org.springframework.ai.chat.memory.ChatMemoryRepository;
import org.springframework.ai.chat.memory.InMemoryChatMemoryRepository;
import org.springframework.ai.chat.memory.MessageWindowChatMemory;
import org.springframework.ai.ollama.OllamaChatModel;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class CommonConfiguration {
//手动把chatClient放到ioc中 defaultSystem-设置全局系统设置
@Bean
public ChatClient ollamaChatClient(OllamaChatModel ollamaChatModel,ChatMemory chatMemory) {
ChatClient client = ChatClient.builder(ollamaChatModel)
.defaultSystem("您是一名资深导游,你的名字叫{systemName}。请以友好、专业和愉快的方式解答各种问题")
.defaultAdvisors(
new SimpleLoggerAdvisor(),
MessageChatMemoryAdvisor.builder(chatMemory).build()
)//添加日志
.build();
return client;
}
//聊天信息存储位置
@Bean
public ChatMemoryRepository chatMemoryRepository(){
return new InMemoryChatMemoryRepository();
}
//服务层 设置参数
@Bean
public ChatMemory chatMemory(ChatMemoryRepository chatMemoryRepository){
return MessageWindowChatMemory.builder()
.chatMemoryRepository(chatMemoryRepository)
.maxMessages(100)//单个会话保留最近的100条数据
.build();
}
}
实现回复功能
package cn.demo.controller;
import cn.demo.repository.ChatHistoryRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.memory.ChatMemory;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Flux;
@RestController
//final修饰的变量自动注入 不需要再编写@autowired 需要是final修饰并且没有默认值可以进行自动注入
@RequiredArgsConstructor
@RequestMapping("/ai")
public class ChatController {
private final ChatClient ollamaChatClient;
private final ChatHistoryRepository inMemoryChatHistoryRepository;
//同步调用 call
@GetMapping("/chatCall")
public String chat(String prompt) {
//prompt-- 提示词开始 user--用户的问题 call--同步调用模型 content--ai返回响应的文本内容
String content = ollamaChatClient.prompt()
.user(prompt)
.call()
.content();
return content;
}
/*
返回值中
"nativeUsage" : {
"promptTokens" : 45, 输入token
"totalTokens" : 881, 总token
"completionTokens" : 836 输出token
}
*/
//流式调用 stream 有思考写出的效果
@PostMapping(value = "/chat",produces = "text/html;charset=UTF-8")
public Flux<String> chatFlux(String prompt, String chatId) {
//保存会话记忆
inMemoryChatHistoryRepository.save("chat",chatId);
//stream--流式调用 system--系统设置 advisors--传入记忆id
Flux<String> content = ollamaChatClient.prompt()
.system(p->p.param("systemName","小小"))//为全局变量赋值
.user(prompt)
.advisors(p->p.param( ChatMemory.CONVERSATION_ID, chatId))
.stream()
.content();
return content;
}
}
实现历史对话功能
实现保存和查询会话记忆接口
package cn.demo.repository;
import java.util.List;
public interface ChatHistoryRepository {
void save(String type,String chatId);
List<String> getChatIds(String type);
}
实现类
package cn.demo.repository.impl;
import cn.demo.repository.ChatHistoryRepository;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@Component
public class InMemoryChatHistoryRepository implements ChatHistoryRepository {
//存储会话历史
private Map<String,List<String>> chatHistory = new HashMap<>();
@Override
public void save(String type, String chatId) {
//根据type进行查找,如果存在对应的集合数据 则计算,如果没有使用第二个参数执行
List<String> list = chatHistory.computeIfAbsent(type, k -> new ArrayList<>());
//如果当前list已经包含该chatId;直接结束无需操作
if (list.contains(chatId)) {
return;
}
list.add(chatId);
}
@Override
public List<String> getChatIds(String type) {
return chatHistory.getOrDefault(type, List.of());
}
}
返回vo
package cn.demo.vo;
import lombok.Data;
import org.springframework.ai.chat.messages.Message;
@Data
public class MessageVO {
private String role;
private String content;
public MessageVO(Message message) {
this.content = message.getText();
this.role = switch (message.getMessageType()){
case USER -> "user";
case ASSISTANT -> "assistant";
case SYSTEM -> "system";
default -> "";
};
}
}
实现会话历史查询controller
package cn.demo.controller;
import cn.demo.repository.ChatHistoryRepository;
import cn.demo.vo.MessageVO;
import lombok.RequiredArgsConstructor;
import org.springframework.ai.chat.memory.ChatMemory;
import org.springframework.ai.chat.messages.Message;
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.RestController;
import java.util.List;
@RestController
@RequestMapping("/ai/history")
@RequiredArgsConstructor
public class ChatHistoryController {
private final ChatHistoryRepository chatHistoryRepository;
private final ChatMemory chatMemory;
//获取会话id列表
@GetMapping("/{type}")
public List<String> getHistory(@PathVariable String type) {
return chatHistoryRepository.getChatIds(type);
}
//获取会话内容列表
@GetMapping("/{type}/{chatId}")
public List<MessageVO> getChatHistory(@PathVariable("type") String type, @PathVariable("chatId") String chatId) {
// List<String> ids = chatHistoryRepository.getChatIds(type);
List<Message> messages = chatMemory.get(chatId);
if(messages == null) {
return List.of();
}
return messages.stream().map(message -> new MessageVO(message)).toList();
}
}


DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。
更多推荐

所有评论(0)