当前位置: 首页 > article >正文

springai 简易聊天机器人设计

# 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.kler.cn/a/453346.html

相关文章:

  • 基于STM32单片机矿井矿工作业安全监测设计
  • 五十六:Stream的状态变迁
  • 【安全编码】Web平台如何设计防止重放攻击
  • 计算机网络概要与习题
  • redis——岁月云实战
  • bypy上传配置
  • 矩阵碰一碰发视频源码技术解析,支持OEM
  • EXCEL使用宏实现筛选重复项并对该行进行填充内容的操作
  • 配置Beremiz,环境ubuntu:20.04
  • 【Python】基于界面库PyQt5+Qt Dsigner的环境配置和界面绘制
  • Three.js中调整相机视角的方式
  • 【conda】Ubuntu 24.04 安装CUDA 12.04
  • 【AIGC-ChatGPT进阶副业提示词】星际占卜师:探索星象能量的艺术【限时免费阅读,一天之后自动进入进阶课程】
  • js控制文字溢出显示省略号
  • Go快速开发框架2.6.0版本更新内容快速了解
  • Unity设置中文
  • MongoDB深度解析与实践案例
  • 解决PDF.js部署到IIS服务器上后报错mjs,.ftl 404 (Not Found)
  • 【Linux】编写简易shell 深度理解命令行解释器 环境变量 内建命令
  • 一、Hadoop概述
  • 南山企业保租房瑧山河周边转转
  • 运维相关知识科普
  • DeepSeek V3:新一代开源 AI 模型,多语言编程能力卓越
  • 关于JS中的this指向问题
  • Xdebug
  • vue固定页面高度不出现滚动条