springai 简易聊天机器人设计

embedded/2024/12/27 13:11:02/
aidu_pl">

# 1. 引言

**Spring AI Alibaba 开源项目基于 Spring AI 构建,是阿里云通义系列模型及服务在 Java AI 应用开发领域的最佳实践,提供高层次的 AI API 抽象与云原生基础设施集成方案,帮助开发者快速构建 AI 应用。**

![image-20241112230716389](https://picgo-clouddz.oss-cn-fuzhou.aliyuncs.com/note/image-20241112230716389.png)

# 2. 效果展示

![20241112_223517](https://picgo-clouddz.oss-cn-fuzhou.aliyuncs.com/note/20241112_223517.gif)

**源代码  **[simple-chatboot: 一个简易的聊天机器人,使用spring ai aibaba (gitee.com)](https://gitee.com/DailySmileStart/simple-chatboot)

# 3. 代码实现

**依赖**

```
        <dependency>
            <groupId>com.alibaba.cloud.ai</groupId>
            <artifactId>spring-ai-alibaba-starter</artifactId>
            <version>1.0.0-M2</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
```

**注意:由于 spring-ai 相关依赖包还没有发布到中央仓库,如出现 spring-ai-core 等相关依赖解析问题,请在您项目的 pom.xml 依赖中加入如下仓库配置。**

```
<repositories>
  <repository>
    <id>spring-milestones</id>
    <name>Spring Milestones</name>
    <url><https://repo.spring.io/milestone></url>
    <snapshots>
      <enabled>false</enabled>
    </snapshots>
  </repository>
</repositories>
@SpringBootApplication
public class SimpleChatbootApplication {

    public static void main(String[] args) {
        SpringApplication.run(SimpleChatbootApplication.class, args);
    }

}
```

**配置自定义ChatClient**

```
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.client.advisor.MessageChatMemoryAdvisor;
import org.springframework.ai.chat.memory.ChatMemory;
import org.springframework.ai.chat.memory.InMemoryChatMemory;
import org.springframework.ai.chat.model.ChatModel;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class ChatClientConfig {

   static ChatMemory chatMemory = new InMemoryChatMemory();
   @Bean
    public ChatClient chatClient(ChatModel chatModel) {
       return ChatClient.builder(chatModel)
                .defaultAdvisors(new MessageChatMemoryAdvisor(chatMemory))
                .build();
    }

}
```

**controller类**

```
import ch.qos.logback.core.util.StringUtil;
import com.hbduck.simplechatboot.demos.function.WeatherService;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.model.ChatModel;
import org.springframework.ai.chat.model.ChatResponse;
import org.springframework.http.MediaType;
import org.springframework.http.codec.ServerSentEvent;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Flux;

import java.util.UUID;

import static org.springframework.ai.chat.client.advisor.AbstractChatMemoryAdvisor.CHAT_MEMORY_CONVERSATION_ID_KEY;
import static org.springframework.ai.chat.client.advisor.AbstractChatMemoryAdvisor.CHAT_MEMORY_RETRIEVE_SIZE_KEY;

@RestController
@RequestMapping("/ai")
public class ChatModelController {

    private final ChatModel chatModel;
    private final ChatClient chatClient;

    public ChatModelController(ChatModel chatModel, ChatClient chatClient) {
        this.chatClient = chatClient;
        this.chatModel = chatModel;
    }

    @GetMapping("/stream")
    public String stream(String input) {

        StringBuilder res = new StringBuilder();
        Flux<ChatResponse> stream = chatModel.stream(new Prompt(input));
        stream.toStream().toList().forEach(resp -> {
             res.append(resp.getResult().getOutput().getContent());
        });

        return res.toString();
    }
    @GetMapping(value = "/memory", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
    public Flux<ServerSentEvent<String>> memory(@RequestParam("conversantId") String conversantId, @RequestParam("input") String input) {
        if (StringUtil.isNullOrEmpty(conversantId)) {
            conversantId = UUID.randomUUID().toString();
        }
        String finalConversantId = conversantId;

        Flux<ChatResponse> chatResponseFlux = chatClient
                .prompt()
                .function("getWeather", "根据城市查询天气", new WeatherService())
                .user(input)
                .advisors(spec -> spec.param(CHAT_MEMORY_CONVERSATION_ID_KEY, finalConversantId)
                        .param(CHAT_MEMORY_RETRIEVE_SIZE_KEY, 10))
                .stream().chatResponse();

        return Flux.concat(
                // First event: send conversationId
                Flux.just(ServerSentEvent.<String>builder()
                        .event("conversationId")
                        .data(finalConversantId)
                        .build()),
                // Subsequent events: send message content
                chatResponseFlux.map(response -> ServerSentEvent.<String>builder()
                        .id(UUID.randomUUID().toString())
                        .event("message")
                        .data(response.getResult().getOutput().getContent())
                        .build())
        );
    }
}
```

**配置文件**

```
server:
  port: 8000

spring:
  thymeleaf:
    cache: true
    check-template: true
    check-template-location: true
    content-type: text/html
    enabled: true
    encoding: UTF-8
    excluded-view-names: ''
    mode: HTML5
    prefix: classpath:/templates/
    suffix: .html
  ai:
    dashscope:
      api-key: ${AI_DASHSCOPE_API_KEY}
    chat:
      client:
        enabled: false
```

**前端页面**

```
<!DOCTYPE html>
<html>
<head>
    <title>AI Chat Bot</title>
    <style>
        #chatBox {
            height: 400px;
            border: 1px solid #ccc;
            overflow-y: auto;
            margin-bottom: 10px;
            padding: 10px;
        }
        .message {
            margin: 5px;
            padding: 5px;
        }
        .user-message {
            background-color: #e3f2fd;
            text-align: right;
        }
        .bot-message {
            background-color: #f5f5f5;
            white-space: pre-wrap;  /* 保留换行和空格 */
            word-wrap: break-word;  /* 长单词换行 */
        }
    </style>
</head>
<body>
    <h1>AI Chat Bot</h1>
    <div id="chatBox"></div>
    <input type="text" id="userInput" placeholder="Type your message..." style="width: 80%">
    <button οnclick="sendMessage()">Send</button>

    <script>
        let conversationId = null;
        let currentMessageDiv = null;

        function addMessage(message, isUser) {
            const chatBox = document.getElementById('chatBox');
            const messageDiv = document.createElement('div');
            messageDiv.className = `message ${isUser ? 'user-message' : 'bot-message'}`;
            messageDiv.textContent = message;
            chatBox.appendChild(messageDiv);
            chatBox.scrollTop = chatBox.scrollHeight;
            return messageDiv;
        }

        async function sendMessage() {
            const input = document.getElementById('userInput');
            const message = input.value.trim();

            if (message) {
                addMessage(message, true);
                input.value = '';

                // Create bot message container
                currentMessageDiv = addMessage('', false);

                const eventSource = new EventSource(`/ai/memory?conversantId=${conversationId || ''}&input=${encodeURIComponent(message)}`);

                eventSource.onmessage = function(event) {
                    const content = event.data;
                    if (currentMessageDiv) {
                        currentMessageDiv.textContent += content;
                    }
                };

                eventSource.addEventListener('conversationId', function(event) {
                    if (!conversationId) {
                        conversationId = event.data;
                    }
                });

                eventSource.onerror = function(error) {
                    console.error('SSE Error:', error);
                    eventSource.close();
                    if (currentMessageDiv && currentMessageDiv.textContent === '') {
                        currentMessageDiv.textContent = 'Sorry, something went wrong!';
                    }
                };

                // Close the connection when the response is complete
                eventSource.addEventListener('complete', function(event) {
                    eventSource.close();
                    currentMessageDiv = null;
                });
            }
        }

        // Allow sending message with Enter key
        document.getElementById('userInput').addEventListener('keypress', function(e) {
            if (e.key === 'Enter') {
                sendMessage();
            }
        });
    </script>
</body>
</html>


http://www.ppmy.cn/embedded/149179.html

相关文章

PDF书籍《手写调用链监控APM系统-Java版》第4章 SPI服务模块化系统

本人阅读了 Skywalking 的大部分核心代码&#xff0c;也了解了相关的文献&#xff0c;对此深有感悟&#xff0c;特此借助巨人的思想自己手动用JAVA语言实现了一个 “调用链监控APM” 系统。本书采用边讲解实现原理边编写代码的方式&#xff0c;看本书时一定要跟着敲代码。 作者…

D类音频应用EMI管理

1、前言 对于EMI&#xff0c;首先需要理解天线。频率和波长之间的关系&#xff0c;如下图所示。   作为有效天线所需的最短长度是λ/4。在空气中&#xff0c;介电常数是1&#xff0c;但是在FR4或玻璃环氧PCB的情况下&#xff0c;介电常数大约4.8。这种效应会导致信号在FR4材…

基于遥感与通信技术的灾害应急测绘

基于遥感与通信技术的灾害应急测绘研究评述与展望 摘要 本研究围绕基于遥感与通信技术的灾害应急测绘展开&#xff0c;深入探讨其在灾害管理中的重要性及当前发展现状。遥感技术凭借高分辨率、广覆盖的特性&#xff0c;已成为获取灾害信息的核心手段。结合5G通信与低轨卫星技术…

英语单词拼读小程序开发制作介绍

英语单词拼读小程序开发制作介绍本英语单词拼读小程序系统开发的主要功能有&#xff1a; 1、按年级分类展示每个年级阶段的英语单词信息。 2、点击选择的单词进入单词拼读页面&#xff0c;展示英语单词的拼读音标、中文意思、单词发音、拆分词汇发音、用户通过朗读发音对比。通…

在Ubuntu上通过Docker部署NGINX服务器

Yo伙计们&#xff0c;今天我们要讨论的话题是如何在Ubuntu系统上通过Docker来部署NGINX服务器。NGINX是一个高性能的Web服务器&#xff0c;适合处理静态内容、反向代理和负载均衡。想要搞定这个家伙&#xff0c;就跟着我来吧&#xff01; Docker和NGINX简介 让我来简要介绍一下…

文件解析漏洞中间件(iis和Apache)

IIS解析漏洞 IIS6.X #环境 Windows Server 2003 在iis6.x中&#xff0c;.asp文件夹中的任意文件都会被当做asp文件去执行 在默认网站里创建一个a.asp文件夹并创建一个1.jpg写进我们的asp代码 <%now()%> #asp一句话 <%eval request("h")%> 单独创建一…

Ubuntu系统部署Mysql8.0后设置不区分大小写

部署MySQL # 更新系统软件包列表 sudo apt update# 安装MySQL Server sudo apt install mysql-server# 在安装时&#xff0c;系统会自动进行初始化&#xff0c;安装完成后MySQL已经处于运行状态# MySQL常见命令 #启动MySQL sudo systemctl start mysql#停止MySQL sudo systemc…

【微信小程序】3|首页搜索框 | 我的咖啡店-综合实训

首页-搜索框-跳转 引言 在微信小程序中&#xff0c;首页的搜索框是用户交互的重要入口。本文将通过“我的咖啡店”小程序的首页搜索框实现&#xff0c;详细介绍如何在微信小程序中创建和处理搜索框的交互。 1. 搜索函数实现 onClickInput函数在用户点击搜索框时触发&#x…