先看成果演示:
首先是父引入依赖:
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-bom</artifactId>
<version>1.0.0-M6</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>3.3.5</version>
<type>pom</type>
<scope>import</scope>
</dependency>
子工程引入免费的ai模型依赖(智普清言: GLM-4-Flash):
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-zhipuai-spring-boot-starter</artifactId>
</dependency>
添加yml文件:
spring:
ai:
zhipuai:
api-key: 你的apikey
chat:
options:
model: GLM-4-Flash
temperature: 0.8 # 模型温度,值越大,结果越随机[0,1]
base-url: https://open.bigmodel.cn/api/paas
没有的自己去智普清言官网申请一下 ,挺简单的。接着是控制层代码:
@Slf4j
@RestController
@RequestMapping("/agent/ai")
public class AgentController {
@Autowired
private ChatClient chatClient;
@Autowired
private ChatRedisMemory chatRedisMemory;
@Anonymous
@PostMapping(value = "chat", produces = "text/html;charset=utf-8")
public Flux<String> service(@RequestParam(required = true, defaultValue = "你好") String prompt,
@RequestParam(required = true) String chatId) {
// 2.请求模型
return chatClient.prompt()
.user(prompt)
.advisors(new MessageChatMemoryAdvisor(chatRedisMemory, chatId, 10))
.stream()
.content();
}
}
接着是配置类:
@Configuration
public class AIConfig {
@Bean
public ChatClient chatClient(ZhiPuAiChatModel model, ChatMemory chatMemory, ServiceTools service) {
System.out.println("初始化AI智能体……");
ToolCallback[] from = ToolCallbacks.from(service);
return ChatClient.builder(model)
.defaultTools(from)
.defaultSystem("你是一个AI智能助手,名字叫做六六")
.defaultAdvisors(new SimpleLoggerAdvisor(), new MessageChatMemoryAdvisor(chatMemory)) // 添加日志增强
.build();
}
@Bean
public ChatMemory chatMemory() {
return new ChatRedisMemory();
}
}
注意这里重写了SpringAI官网提供的持久化聊天实现。(引用“majinzhong”的)
@Slf4j
@Component
public class ChatRedisMemory implements ChatMemory {
private static final String KEY_PREFIX = "chat:history:";
@Resource
private RedisTemplate<String, Object> redisTemplate;
@Override
public void add(String conversationId, List<Message> messages) {
String key = KEY_PREFIX + conversationId;
List<MessageVO> listIn = new ArrayList<>();
for (Message msg : messages) {
MessageVO ent = new MessageVO(msg);
ent.setChatId(conversationId);
listIn.add(ent);
}
redisTemplate.opsForList().rightPushAll(key, listIn.toArray());
redisTemplate.expire(key, 30, TimeUnit.MINUTES);
}
@Override
public List<Message> get(String conversationId, int lastN) {
String key = KEY_PREFIX + conversationId;
Long size = redisTemplate.opsForList().size(key);
if (size == null || size == 0) {
return Collections.emptyList();
}
int start = Math.max(0, (int) (size - lastN));
List<Object> listTmp = redisTemplate.opsForList().range(key, start, -1);
List<Message> listOut = new ArrayList<>();
ObjectMapper objectMapper = new ObjectMapper();
for (Object obj : listTmp) {
MessageVO chat = objectMapper.convertValue(obj, MessageVO.class);
if (MessageType.USER.getValue().equals(chat.getRole())) {
listOut.add(new UserMessage(chat.getContent()));
} else if (MessageType.ASSISTANT.getValue().equals(chat.getRole())) {
listOut.add(new AssistantMessage(chat.getContent()));
} else if (MessageType.SYSTEM.getValue().equals(chat.getRole())) {
listOut.add(new SystemMessage(chat.getContent()));
}
}
return listOut;
}
@Override
public void clear(String conversationId) {
redisTemplate.delete(KEY_PREFIX + conversationId);
}
}
接着是配置类:
/**
* @author vibelin
*/
@Configuration
public class AIConfig {
@Bean
public ChatClient chatClient(ZhiPuAiChatModel model, ChatMemory chatMemory, ServiceTools service) {
System.out.println("初始化AI智能体……");
return ChatClient.builder(model)
.defaultSystem("你是一个AI智能助手,名字叫做六六")
.defaultAdvisors(new SimpleLoggerAdvisor(), new MessageChatMemoryAdvisor(chatMemory)) // 添加日志增强
.build();
}
@Bean
public ChatMemory chatMemory() {
return new ChatRedisMemory();
}
}
接着是获取聊天记录的控制层:
@RestController
@RequestMapping("agent/history")
public class ChatHistoryController {
@Autowired
private ChatRedisMemory chatMemory;
@GetMapping("{chatId}")
public List<MessageVO> listHistory(@PathVariable("chatId") String chatId) {
List<Message> messages = chatMemory.get(chatId, Integer.MAX_VALUE);
if (messages == null) {
return List.of();
}
System.out.println(messages);
return messages.stream().map(MessageVO::new).collect(Collectors.toList());
}
@DeleteMapping("{chatId}")
public void deleteHistory(@PathVariable("chatId") String chatId) {
chatMemory.clear(chatId);
}
}
消息VO对象:
import lombok.Data;
import org.springframework.ai.chat.messages.Message;
import org.springframework.ai.chat.messages.MessageType;
@Data
public class MessageVO {
public MessageVO() {
}
private String chatId;
private String role;
private String content;
public MessageVO(Message message) {
MessageType messageType = message.getMessageType();
switch (messageType) {
case USER:
this.role = "user";
break;
case ASSISTANT:
this.role = "assistant";
break;
default:
this.role = "";
break;
}
this.content = message.getText();
}
}
浏览器打开输入对应的路径测试:
至此SpringAI的初体验就到这里咯