【SpringAI 22】对话机器人,历史会话功能

延续上一次所讲,获取到了会话id,我们这次就实现一些获取历史会话消息

1,编写controller

java 复制代码
package com.example.chatai.controller;

import com.example.chatai.repository.ChatHistoryRepository;
import com.example.chatai.service.ChatService;
import com.example.chatai.vo.MessageVo;
import lombok.RequiredArgsConstructor;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.memory.ChatMemory;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.*;
import reactor.core.publisher.Flux;
import java.util.List;
@RequiredArgsConstructor
@RestController
@RequestMapping("/ai/history")
public class ChatHistoryController {
    private final ChatHistoryRepository chatHistoryRepository;
    @GetMapping("/{type}")
    public List<String> getChatIds(@PathVariable("type") String type) {

        return chatHistoryRepository.getChatIds(type);
    }

    @GetMapping("/{type}/{chatId}")
    public List<MessageVo>  getChatHistory(@PathVariable("type") String type, @PathVariable("chatId") String chatId) {
        return chatHistoryRepository.getChatHistory(type,chatId);
    }
}

2,会话记忆Message源码解读

java 复制代码
public interface Message extends Content {

	/**
	 * Get the message type.
	 * @return the message type
	 */
	MessageType getMessageType();
}
java 复制代码
/*
 * Copyright 2023-present the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.ai.chat.messages;

/**
 * Enumeration representing types of {@link Message Messages} in a chat application. It
 * can be one of the following: USER, ASSISTANT, SYSTEM, FUNCTION.
 */
public enum MessageType {

	/**
	 * A {@link Message} of type {@literal user}, having the user role and originating
	 * from an end-user or developer.
	 * @see UserMessage
	 */
	USER("user"),

	/**
	 * A {@link Message} of type {@literal assistant} passed in subsequent input
	 * {@link Message Messages} as the {@link Message} generated in response to the user.
	 * @see AssistantMessage
	 */
	ASSISTANT("assistant"),

	/**
	 * A {@link Message} of type {@literal system} passed as input {@link Message
	 * Messages} containing high-level instructions for the conversation, such as behave
	 * like a certain character or provide answers in a specific format.
	 * @see SystemMessage
	 */
	SYSTEM("system"),

	/**`/*
 * Copyright 2023-present the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.ai.content;

import java.util.Map;

import org.jspecify.annotations.Nullable;

/**
 * Data structure that contains content and metadata. Common parent for the
 * {@link org.springframework.ai.document.Document} and the
 * {@link org.springframework.ai.chat.messages.Message} classes.
 *
 * @author Mark Pollack
 * @author Christian Tzolov
 * @since 1.0.0
 */
public interface Content {

	/**
	 * Get the content of the message.
	 * @return the content of the message
	 */
	@Nullable String getText();

	/**
	 * Get the metadata associated with the content.
	 * @return the metadata associated with the content
	 */
	Map<String, Object> getMetadata();

}
`
	 * A {@link Message} of type {@literal function} passed as input {@link Message
	 * Messages} with function content in a chat application.
	 * @see ToolResponseMessage
	 */
	TOOL("tool");

	private final String value;

	MessageType(String value) {
		this.value = value;
	}

	public String getValue() {
		return this.value;
	}

}

3,编写Vo:

java 复制代码
package com.example.chatai.vo;

import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.ai.chat.messages.Message;
import org.springframework.ai.chat.messages.MessageType;

@NoArgsConstructor
@Data
public class MessageVo {
    private String role;
    private String context;

    public MessageVo(Message message) {
        MessageType messageType = message.getMessageType();
        switch (messageType) {
            case USER:
                role="user";
                break;
            case ASSISTANT:
                role="assistant";
                break;
            case SYSTEM:
                role="system";
                break;
            case TOOL:
                role="tool";
                break;
            default:
                role="unknow";
                break;
        }

        this.context = message.getText();
    }
}

4,编写service

java 复制代码
    List<MessageVo> getChatHistory(String type, String chatId);
java 复制代码
package com.example.chatai.repository.impl;

import com.example.chatai.repository.ChatHistoryRepository;
import com.example.chatai.vo.MessageVo;
import org.springframework.ai.chat.memory.ChatMemory;
import org.springframework.ai.chat.messages.Message;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.util.*;

@Service
public class ChatHistoryRepositoryImpl implements ChatHistoryRepository {

    private final Map<String, List<String>> chatHistory =new HashMap<>();

    private final ChatMemory chatMemory;

    public ChatHistoryRepositoryImpl(ChatMemory chatMemory) {
        this.chatMemory = chatMemory;
    }

    @Override
    public void sava(String type, String chatId) {

//        if(!chatHistory.containsKey(type)) {
//            chatHistory.put(type,new ArrayList<>());
//        }
//        List<String> chatIds = chatHistory.get(type);

        List<String> chatIds = chatHistory.computeIfAbsent(type, k -> new ArrayList<>());

        if(chatIds.contains(chatId)) {
            return;
        }
        chatIds.add(chatId);

    }

    @Override
    public List<String> getChatIds(String type) {
//        List<String> chatIds = chatHistory.get(type);
//        return chatIds==null? new ArrayList<>():chatIds;

        return chatHistory.getOrDefault(type, List.of());
    }

    @Override
    public List<MessageVo> getChatHistory(String type, String chatId) {
        List<Message> messages = chatMemory.get(chatId);
        if(CollectionUtils.isEmpty(messages)) {
            return List.of();
        }

        return messages.stream().map(MessageVo::new).toList();
    }

}

5,测试

会话1给机器人发送了3个问题。都得到了回答,我们看看机器人历史记忆功能,我调用

http://localhost:8080/ai/history/chat/1 接口

返回了之前的3次记录,这就实现了会话历史记忆功能

学到了这里是否理解前面讲的机器人有记忆概念,欢迎兄弟们留言

相关推荐
周航宇JoeZhou6 天前
JP3-2-3-MyItem智能业务
java·多智能体·rag·智能体·springai·mcp·skills
佛祖让我来巡山7 天前
SpringAI保姆级实战教程
spring ai·springai·springai教程·springai入门
周航宇JoeZhou7 天前
JP3-2-1-MyItem项目简介
java·ai·springboot·环境搭建·项目·管理·springai
Muscleheng8 天前
SpringBoot 集成 DeepSeek 实现 RAG 文档问答
java·spring boot·ai·springai
玉&心14 天前
关于配置mcp服务端sse-message-endpoint和sse-endpoint的注意点
spring·springai·mcp
ywl47081208722 天前
【SpringAI 09】SpringAI入门
人工智能·springai
ywl47081208722 天前
【SpringAI 15】会话记忆功能ChatMemory
对话机器人·springai
早春的树长在理想三旬22 天前
从LLM到Agent:ReAct推理模式的底层原理
人工智能·spring boot·springai·agent开发
早春的树长在理想三旬22 天前
工具抽象的艺术:可插拔Skill系统的设计哲学
人工智能·agent·springai