前言

根据上一篇,我们实现了mcp工具的调用,以及使用function calling去mysql中查询信息,这一节我们来实现ChatMemory会话记忆以及删除的功能

会话记忆

实现ChatMemory接口

为了实现会话记忆,我们需要去实现ChatMemory接口:

package com.hyk.mcpclient.memory;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RBucket;
import org.redisson.api.RedissonClient;
import org.springframework.ai.chat.memory.ChatMemory;
import org.springframework.ai.chat.messages.Message;
import org.springframework.ai.chat.messages.UserMessage;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.List;

@Slf4j
@Component
public class MyAgentChatMemory implements ChatMemory {

    @Resource
    private RedissonClient redissonClient;
    @Resource
    private ObjectMapper objectMapper;


    @Override
    public void add(String conversationId, List<Message> messages) {
        RBucket<Object> bucket = redissonClient.getBucket("conversationId:" + conversationId);

        String json;
        try {
            List<String> list = new ArrayList<>();
            messages.forEach(message -> {
                list.add(message.getText());
            });
            json = objectMapper.writeValueAsString(list);
        } catch (JsonProcessingException e) {
            log.error("json转换错误", e);
            throw new RuntimeException(e);
        }
        bucket.set(json);
    }

    @Override
    public List<Message> get(String conversationId) {
        RBucket<Object> bucket = redissonClient.getBucket("conversationId:" + conversationId);
        if(!bucket.isExists()){
            log.info("conversationId:{} 不存在, 会话记忆不存在,创建新会话记忆", conversationId);
            return new ArrayList<>();
        }
        String json = (String) bucket.get();

        if(json != null){;
            JavaType javaType = objectMapper.getTypeFactory().constructParametricType(List.class, String.class);
            try {
                List<String> list = objectMapper.readValue(json, javaType);
                List<Message> messages = new ArrayList<>();
                list.forEach(text -> {
                    messages.add(new UserMessage(text));
                });
                return messages;
            } catch (JsonProcessingException e) {
                throw new RuntimeException(e);
            }
        }
        return new ArrayList<>();
    }

    @Override
    public void clear(String conversationId) {
        RBucket<Object> bucket = redissonClient.getBucket("conversationId:" + conversationId);
        if(!bucket.isExists()){
            log.info("conversationId:{} 不存在,无法删除", conversationId);
            return;
        }
        bucket.delete();
        log.info("conversationId:{} 已删除", conversationId);
    }
}

其中Message是一个接口,不能直接使用jackson转json,可以使用它的实现类UserMessage或者直接像我一样把message中的text提取出来

Redisson操作redis数据库

这里我的会话是存在redis中的

添加redisson依赖:

<!-- Redis 缓存 -->
        <dependency>
            <groupId>org.redisson</groupId>
            <artifactId>redisson</artifactId>
            <version>3.44.0</version>
        </dependency>

新建redissonClient的配置类:

package com.hyk.mcpclient.config;

import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class RedissonConfig {
    @Value("${redis.host}")
    private String redisHost;
    @Value("${redis.port}")
    private String redisPort;
    @Value("${redis.database}")
    private int redisDatabase;

    @Bean  // 添加这个注解
    public RedissonClient redissonClient(){
        Config config = new Config();
        config.useSingleServer()
                .setAddress("redis://" + redisHost + ":" + redisPort)  // 修正地址格式
                .setDatabase(redisDatabase);

        return Redisson.create(config);
    }
}

使用redisson操作redis数据库:

package com.hyk.mcpclient;

import jakarta.annotation.Resource;
import org.junit.jupiter.api.Test;
import org.redisson.api.RBucket;
import org.redisson.api.RedissonClient;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
public class RedissonTest {

    @Resource
    private RedissonClient redissonClient;

    @Test
    public void test() {
        RBucket<Object> bucket = redissonClient.getBucket("test");
        bucket.set("hello world");
        System.out.println(bucket.get());
        System.out.println(bucket.getAndDelete());
    }
}

redisson操作redis就是去拿到一个bucket,然后对bucket进行增删改查的操作,不用手动提交,只要操作了就提交

配置类

把我们自己实现的chatmemory添加到chatClient的配置项中,我这里新建了一个配置项:

package com.hyk.mcpclient.client;

import com.hyk.mcpclient.common.prompt;
import com.hyk.mcpclient.memory.MyAgentChatMemory;
import com.hyk.mcpclient.tool.GetCityAdcodeTool;
import jakarta.annotation.Resource;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.client.ChatClientRequest;
import org.springframework.ai.chat.client.ChatClientResponse;
import org.springframework.ai.chat.client.advisor.MessageChatMemoryAdvisor;
import org.springframework.ai.chat.client.advisor.SimpleLoggerAdvisor;
import org.springframework.ai.chat.client.advisor.api.AdvisorChain;
import org.springframework.ai.chat.client.advisor.api.BaseChatMemoryAdvisor;
import org.springframework.ai.ollama.OllamaChatModel;
import org.springframework.ai.openai.OpenAiChatModel;
import org.springframework.ai.tool.ToolCallbackProvider;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class MemoryChatClientConfig {
    @Resource
    private OllamaChatModel ollamaChatModel;

    @Resource
    private OpenAiChatModel openAiChatModel;

    @Resource
    private ToolCallbackProvider toolCallbackProvider;

    @Resource
    private GetCityAdcodeTool getCityAdcodeTool;

    @Resource
    private MyAgentChatMemory myAgentChatMemory;
    @Bean
    public ChatClient memoryClient() {
        return ChatClient.builder(openAiChatModel)
                .defaultSystem(prompt.PROMPT_AGENT)
                .defaultAdvisors(new SimpleLoggerAdvisor(),
                        MessageChatMemoryAdvisor.builder(myAgentChatMemory)
                                .build()
                )
                .defaultTools(getCityAdcodeTool)
                .defaultToolCallbacks(toolCallbackProvider.getToolCallbacks())
                .build();
    }

}

Controller

然后因为每个会话需要有一个单独的会话id,我这里是作为路径参数传进来,controller代码如下:

@GetMapping(value = "/ai/chat", produces = "text/html; charset=utf-8")
    public Flux<String> testMemory(@RequestParam String message, @RequestParam String conversationId) {
        return memoryClient.prompt()
                .user(message)
                .advisors(advisorSpec -> advisorSpec.param(ChatMemory.CONVERSATION_ID, conversationId))
                .stream()
                .content();
    }

在官方文档里我只查到了这一种指定conversationId的方法,就是

.advisors(advisorSpec -> advisorSpec.param(ChatMemory.CONVERSATION_ID, conversationId))

然后再给前端写一个删除会话的接口:

package com.hyk.mcpclient.controller;


import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RBucket;
import org.redisson.api.RedissonClient;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@Slf4j
@RestController
@CrossOrigin(origins = "*")  // 允许所有来源
public class DeleteConversationController {
    @Resource
    private RedissonClient redissonClient;

    @GetMapping("/deleteConversation")
    public String deleteConversation(@RequestParam String conversationId) {
        RBucket<Object> bucket = redissonClient.getBucket("conversationId:" + conversationId);
        if(!bucket.isExists()){
            log.info("conversationId:{} 不存在,无法删除", conversationId);
            return "conversationId:{} 不存在,无法删除";
        }
        bucket.delete();
        log.info("conversationId:{} 已删除", conversationId);
        return "conversationId:{} 已删除";
    }
}

我这里前端页面和后端是不同源的,需要在controller添加跨域注解@CrossOrigin(origins = "*")  // 允许所有来源

前端页面

让ai帮我生成的前端页面:

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>AI 聊天助手</title>
    <style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }

        body {
            font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            min-height: 100vh;
            padding: 20px;
        }

        .container {
            max-width: 1200px;
            margin: 0 auto;
            background: white;
            border-radius: 15px;
            box-shadow: 0 20px 40px rgba(0,0,0,0.1);
            overflow: hidden;
            display: flex;
            height: 90vh;
        }

        .sidebar {
            width: 300px;
            background: #f8f9fa;
            border-right: 1px solid #e9ecef;
            display: flex;
            flex-direction: column;
        }

        .sidebar-header {
            padding: 20px;
            border-bottom: 1px solid #e9ecef;
            background: white;
        }

        .new-chat-btn {
            width: 100%;
            padding: 12px;
            background: #007bff;
            color: white;
            border: none;
            border-radius: 8px;
            cursor: pointer;
            font-size: 14px;
            transition: background 0.3s;
        }

        .new-chat-btn:hover {
            background: #0056b3;
        }

        .conversation-list {
            flex: 1;
            overflow-y: auto;
            padding: 10px;
        }

        .conversation-item {
            padding: 12px;
            margin: 5px 0;
            background: white;
            border-radius: 8px;
            cursor: pointer;
            border: 1px solid #e9ecef;
            transition: all 0.3s;
            display: flex;
            justify-content: space-between;
            align-items: center;
        }

        .conversation-item:hover {
            background: #e9ecef;
        }

        .conversation-item.active {
            background: #007bff;
            color: white;
            border-color: #007bff;
        }

        .delete-btn {
            background: #dc3545;
            color: white;
            border: none;
            border-radius: 4px;
            padding: 4px 8px;
            cursor: pointer;
            font-size: 12px;
        }

        .delete-btn:hover {
            background: #c82333;
        }

        .chat-area {
            flex: 1;
            display: flex;
            flex-direction: column;
        }

        .chat-header {
            padding: 20px;
            border-bottom: 1px solid #e9ecef;
            background: white;
        }

        .chat-messages {
            flex: 1;
            overflow-y: auto;
            padding: 20px;
            background: #f8f9fa;
        }

        .message {
            margin-bottom: 15px;
            max-width: 80%;
        }

        .user-message {
            margin-left: auto;
        }

        .message-bubble {
            padding: 12px 16px;
            border-radius: 18px;
            word-wrap: break-word;
        }

        .user-message .message-bubble {
            background: #007bff;
            color: white;
            border-bottom-right-radius: 4px;
        }

        .ai-message .message-bubble {
            background: white;
            border: 1px solid #e9ecef;
            border-bottom-left-radius: 4px;
        }

        .message-time {
            font-size: 12px;
            color: #6c757d;
            margin-top: 5px;
            text-align: right;
        }

        .ai-message .message-time {
            text-align: left;
        }

        .chat-input-area {
            padding: 20px;
            border-top: 1px solid #e9ecef;
            background: white;
        }

        .input-group {
            display: flex;
            gap: 10px;
        }

        .message-input {
            flex: 1;
            padding: 12px 16px;
            border: 1px solid #e9ecef;
            border-radius: 25px;
            outline: none;
            font-size: 14px;
            resize: none;
            height: 50px;
        }

        .message-input:focus {
            border-color: #007bff;
        }

        .send-btn {
            padding: 12px 24px;
            background: #007bff;
            color: white;
            border: none;
            border-radius: 25px;
            cursor: pointer;
            font-size: 14px;
            transition: background 0.3s;
        }

        .send-btn:hover:not(:disabled) {
            background: #0056b3;
        }

        .send-btn:disabled {
            background: #6c757d;
            cursor: not-allowed;
        }

        .typing-indicator {
            display: none;
            padding: 12px 16px;
            background: white;
            border-radius: 18px;
            border: 1px solid #e9ecef;
            margin-bottom: 15px;
            max-width: 80%;
        }

        .typing-dots {
            display: flex;
            gap: 4px;
        }

        .typing-dot {
            width: 8px;
            height: 8px;
            background: #6c757d;
            border-radius: 50%;
            animation: typing 1.4s infinite;
        }

        .typing-dot:nth-child(2) {
            animation-delay: 0.2s;
        }

        .typing-dot:nth-child(3) {
            animation-delay: 0.4s;
        }

        @keyframes typing {
            0%, 60%, 100% {
                transform: translateY(0);
            }
            30% {
                transform: translateY(-10px);
            }
        }

        .conversation-id {
            font-size: 12px;
            color: #6c757d;
            margin-top: 5px;
        }

        .error-message {
            color: #dc3545;
            background: #f8d7da;
            border: 1px solid #f5c6cb;
            padding: 10px;
            border-radius: 8px;
            margin: 10px 0;
        }
    </style>
</head>
<body>
<div class="container">
    <!-- 侧边栏 -->
    <div class="sidebar">
        <div class="sidebar-header">
            <button class="new-chat-btn" onclick="createNewConversation()">
                + 新建对话
            </button>
        </div>
        <div class="conversation-list" id="conversationList">
            <!-- 会话列表将通过 JavaScript 动态生成 -->
        </div>
    </div>

    <!-- 主聊天区域 -->
    <div class="chat-area">
        <div class="chat-header">
            <h2>AI 聊天助手</h2>
            <div class="conversation-id" id="currentConversationId">
                当前会话ID: 未选择
            </div>
        </div>

        <div class="chat-messages" id="chatMessages">
            <div class="welcome-message">
                <div class="message ai-message">
                    <div class="message-bubble">
                        你好!我是 AI 助手,请选择一个会话或创建新会话来开始聊天。
                    </div>
                </div>
            </div>
        </div>

        <div class="typing-indicator" id="typingIndicator">
            <div class="typing-dots">
                <div class="typing-dot"></div>
                <div class="typing-dot"></div>
                <div class="typing-dot"></div>
            </div>
        </div>

        <div class="chat-input-area">
            <div class="input-group">
                    <textarea
                            class="message-input"
                            id="messageInput"
                            placeholder="输入你的消息..."
                            rows="1"
                            onkeypress="handleKeyPress(event)"
                    ></textarea>
                <button class="send-btn" id="sendBtn" onclick="sendMessage()" disabled>
                    发送
                </button>
            </div>
        </div>
    </div>
</div>

<script>
    // 全局变量
    let currentConversationId = null;
    let conversations = JSON.parse(localStorage.getItem('conversations') || '{}');

    // 初始化页面
    document.addEventListener('DOMContentLoaded', function() {
        updateConversationList();
        updateSendButton();

        // 监听输入框变化
        document.getElementById('messageInput').addEventListener('input', updateSendButton);
    });

    // 生成唯一的会话ID
    function generateConversationId() {
        return 'conv_' + Date.now() + '_' + Math.random().toString(36).substr(2, 9);
    }

    // 创建新会话
    function createNewConversation() {
        const newId = generateConversationId();
        conversations[newId] = {
            id: newId,
            title: '新对话',
            createdAt: new Date().toISOString(),
            messages: []
        };

        saveConversations();
        switchConversation(newId);
        updateConversationList();
    }

    // 切换会话
    function switchConversation(conversationId) {
        currentConversationId = conversationId;
        document.getElementById('currentConversationId').textContent =
            `当前会话ID: ${conversationId}`;

        // 更新活跃状态
        document.querySelectorAll('.conversation-item').forEach(item => {
            item.classList.remove('active');
        });

        const activeItem = document.querySelector(`[data-conversation-id="${conversationId}"]`);
        if (activeItem) {
            activeItem.classList.add('active');
        }

        // 显示消息
        displayMessages(conversations[conversationId].messages);

        // 聚焦输入框
        document.getElementById('messageInput').focus();
    }

    // 删除会话
    function deleteConversation(conversationId, event) {
        event.stopPropagation();

        if (confirm('确定要删除这个会话吗?')) {
            // 调用后端删除接口
            fetch(`http://localhost:8081/deleteConversation?conversationId=${conversationId}`)
                .then(response => response.text())
                .then(result => {
                    console.log('删除结果:', result);
                })
                .catch(error => {
                    console.error('删除失败:', error);
                });

            // 从前端删除
            delete conversations[conversationId];

            if (currentConversationId === conversationId) {
                currentConversationId = null;
                document.getElementById('currentConversationId').textContent = '当前会话ID: 未选择';
                document.getElementById('chatMessages').innerHTML = `
                        <div class="welcome-message">
                            <div class="message ai-message">
                                <div class="message-bubble">
                                    你好!我是 AI 助手,请选择一个会话或创建新会话来开始聊天。
                                </div>
                            </div>
                        </div>
                    `;
            }

            saveConversations();
            updateConversationList();
        }
    }

    // 更新会话列表
    function updateConversationList() {
        const list = document.getElementById('conversationList');
        const sortedConversations = Object.values(conversations)
            .sort((a, b) => new Date(b.createdAt) - new Date(a.createdAt));

        if (sortedConversations.length === 0) {
            list.innerHTML = '<div style="padding: 20px; text-align: center; color: #6c757d;">暂无会话</div>';
            return;
        }

        list.innerHTML = sortedConversations.map(conv => `
                <div class="conversation-item ${currentConversationId === conv.id ? 'active' : ''}"
                     data-conversation-id="${conv.id}"
                     onclick="switchConversation('${conv.id}')">
                    <span>${conv.title}</span>
                    <button class="delete-btn" onclick="deleteConversation('${conv.id}', event)">删除</button>
                </div>
            `).join('');
    }

    // 发送消息 - 修复版本
    function sendMessage() {
        const input = document.getElementById('messageInput');
        const message = input.value.trim();

        if (!message || !currentConversationId) return;

        // 添加用户消息
        addMessage('user', message);
        input.value = '';
        updateSendButton();

        // 显示打字指示器
        showTypingIndicator();

        // 在流式输出开始前创建空的AI消息占位符
        const aiMessageId = 'ai_msg_' + Date.now();
        createEmptyAiMessage(aiMessageId);

        // 调用后端接口
        fetch(`http://localhost:8081/ai/chat?message=${encodeURIComponent(message)}&conversationId=${currentConversationId}`)
            .then(response => {
                if (!response.ok) {
                    throw new Error(`HTTP error! status: ${response.status}`);
                }
                const reader = response.body.getReader();
                const decoder = new TextDecoder();
                let aiResponse = '';

                function read() {
                    reader.read().then(({done, value}) => {
                        if (done) {
                            hideTypingIndicator();
                            // 保存完整的AI回复到历史记录
                            if (aiResponse) {
                                saveAiMessageToHistory(aiResponse);
                                updateConversationTitle(message);
                            } else {
                                // 如果没有收到回复,移除空的AI消息
                                removeEmptyAiMessage(aiMessageId);
                                addMessage('ai', '抱歉,没有收到回复。');
                            }
                            return;
                        }

                        const chunk = decoder.decode(value, {stream: true});
                        aiResponse += chunk;

                        // 实时更新AI消息内容
                        updateAiMessageContent(aiMessageId, aiResponse);

                        read();
                    }).catch(error => {
                        hideTypingIndicator();
                        console.error('读取流失败:', error);
                        removeEmptyAiMessage(aiMessageId);
                        addMessage('ai', '抱歉,响应读取失败。');
                    });
                }

                read();
            })
            .catch(error => {
                hideTypingIndicator();
                console.error('请求失败:', error);
                removeEmptyAiMessage(aiMessageId);

                let errorMessage = '抱歉,服务暂时不可用。';
                if (error.message.includes('Failed to fetch')) {
                    errorMessage = '网络连接失败,请检查网络连接和后端服务状态。';
                } else if (error.message.includes('HTTP error')) {
                    errorMessage = '服务器返回错误,请稍后重试。';
                }

                addMessage('ai', errorMessage);
            });
    }

    // 创建空的AI消息占位符
    function createEmptyAiMessage(messageId) {
        const messagesContainer = document.getElementById('chatMessages');
        const messageElement = document.createElement('div');
        messageElement.className = 'message ai-message';
        messageElement.id = messageId;
        messageElement.innerHTML = `
                <div class="message-bubble"></div>
                <div class="message-time">${new Date().toLocaleTimeString('zh-CN', {
            hour: '2-digit',
            minute: '2-digit'
        })}</div>
            `;
        messagesContainer.appendChild(messageElement);

        // 滚动到底部
        messagesContainer.scrollTop = messagesContainer.scrollHeight;

        return messageId;
    }

    // 更新AI消息内容
    function updateAiMessageContent(messageId, content) {
        const messageElement = document.getElementById(messageId);
        if (messageElement) {
            const bubble = messageElement.querySelector('.message-bubble');
            if (bubble) {
                bubble.textContent = content;
            }
        }

        // 滚动到底部
        const messagesContainer = document.getElementById('chatMessages');
        messagesContainer.scrollTop = messagesContainer.scrollHeight;
    }

    // 移除空的AI消息(在出错时使用)
    function removeEmptyAiMessage(messageId) {
        const messageElement = document.getElementById(messageId);
        if (messageElement) {
            const bubble = messageElement.querySelector('.message-bubble');
            if (bubble && !bubble.textContent.trim()) {
                messageElement.remove();
            }
        }
    }

    // 保存AI消息到历史记录
    function saveAiMessageToHistory(content) {
        const messages = conversations[currentConversationId].messages;
        const message = {
            sender: 'ai',
            content: content,
            timestamp: new Date().toISOString()
        };

        messages.push(message);
        saveConversations();
    }

    // 添加消息到界面
    function addMessage(sender, content) {
        const messages = conversations[currentConversationId].messages;
        const message = {
            sender: sender,
            content: content,
            timestamp: new Date().toISOString()
        };

        messages.push(message);
        saveConversations();

        // 只有用户消息才立即显示,AI消息通过流式输出处理
        if (sender === 'user') {
            displayMessages(messages);
        }
    }

    // 显示消息
    function displayMessages(messages) {
        const container = document.getElementById('chatMessages');

        // 过滤掉正在流式输出的消息
        const displayedMessages = messages.filter(msg =>
            msg.sender === 'user' || (msg.sender === 'ai' && msg.content)
        );

        container.innerHTML = displayedMessages.map(msg =>
            createMessageElement(msg.sender, msg.content, msg.timestamp).outerHTML
        ).join('');

        // 滚动到底部
        container.scrollTop = container.scrollHeight;
    }

    // 创建消息元素
    function createMessageElement(sender, content, timestamp = null) {
        const messageDiv = document.createElement('div');
        messageDiv.className = `message ${sender}-message`;

        const time = timestamp ? new Date(timestamp) : new Date();
        const timeString = time.toLocaleTimeString('zh-CN', {
            hour: '2-digit',
            minute: '2-digit'
        });

        messageDiv.innerHTML = `
                <div class="message-bubble">${content}</div>
                <div class="message-time">${timeString}</div>
            `;

        return messageDiv;
    }

    // 更新会话标题(使用第一条用户消息)
    function updateConversationTitle(firstMessage) {
        if (conversations[currentConversationId].title === '新对话') {
            conversations[currentConversationId].title =
                firstMessage.length > 20 ? firstMessage.substring(0, 20) + '...' : firstMessage;
            saveConversations();
            updateConversationList();
        }
    }

    // 显示/隐藏打字指示器
    function showTypingIndicator() {
        document.getElementById('typingIndicator').style.display = 'block';
        document.getElementById('chatMessages').scrollTop = document.getElementById('chatMessages').scrollHeight;
    }

    function hideTypingIndicator() {
        document.getElementById('typingIndicator').style.display = 'none';
    }

    // 更新发送按钮状态
    function updateSendButton() {
        const input = document.getElementById('messageInput');
        const button = document.getElementById('sendBtn');
        button.disabled = !input.value.trim() || !currentConversationId;
    }

    // 处理回车键发送
    function handleKeyPress(event) {
        if (event.key === 'Enter' && !event.shiftKey) {
            event.preventDefault();
            sendMessage();
        }
    }

    // 保存会话到本地存储
    function saveConversations() {
        localStorage.setItem('conversations', JSON.stringify(conversations));
    }
</script>
</body>
</html>

然后修改了一下提示词,让我们有一个真正的agent:

public static final String PROMPT_AGENT = """
                你是一个由hyk设计的ai智能体,你具有多种功能,
                
                你现在拥有的功能如下:
                功能1. 获取天气信息:使用getWeather工具获取指定城市的天气信息
                    查询流程:
                    1. 首先使用 getCityAdcode 工具确认城市的adcode
                    2. 然后使用 getWeatherByAdcode 工具查询具体天气 需要传递步骤1中查询到的城市adcode
                    3. 如果城市不存在,直接告知用户
                    
                    回复要求:
                    - 用友好的中文回复
                    - 包含完整的天气信息
                    - 如果查询失败,给出友好的错误提示
                    
                    示例回复:
                    "北京当前天气:晴,温度25°C,湿度60%,东南风3级,祝您有愉快的一天!🌞"
                    
                    如果查询失败,请告诉我失败的具体原因
                    
                功能2. 普通问答助手
                    作为ai和用户进行正常对话,无需进行工具调用
                    
                功能3. 城市adcode查询
                    查询流程:
                    1. 使用 getCityAdcode 工具确认城市的adcode
                    2. 如果城市不存在,直接告知用户
                    
                功能4. 查询今日新闻
                    查询流程:
                    1. 使用getNews工具查询今日新闻
                    2. 将得到的结果返回给用户
                    3. 如果出现问题,请告诉用户失败的原因
                    
                """;

最后的运行结果如下:

Logo

火山引擎开发者社区是火山引擎打造的AI技术生态平台,聚焦Agent与大模型开发,提供豆包系列模型(图像/视频/视觉)、智能分析与会话工具,并配套评测集、动手实验室及行业案例库。社区通过技术沙龙、挑战赛等活动促进开发者成长,新用户可领50万Tokens权益,助力构建智能应用。

更多推荐