百度智能云+SpringBoot=AI对话【人工智能】
前言
2024-3-20 19:41:47
使用百度千帆平台的大模型,完成一个简单的AI对话聊天
以下内容源自《【人工智能】》
仅供学习交流使用
版权
禁止其他平台发布时删除以下此话
本文首次发布于CSDN平台
作者是CSDN@日星月云
博客主页是https://jsss-1.blog.csdn.net
禁止其他平台发布时删除以上此话
推荐
Gitee项目地址: 日星月云 / AI对话
GitHub项目地址:jsss-1/qianfan
百度智能云+SpringBoot=AI对话【人工智能】
通过上篇,我们成功地完成了初次对大模型的使用
本篇,我将带大家开发一个AI对话聊天框
效果演示
登录
输入用户名,点击登录
返回"登录成功"
查询状态
AI对话
页面
输入内容,点击回车即可提问
项目结构
后端开发
pom和properties
pom.xml
SpringBoot2.4.2+JDK8
xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.4.2</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>qianfan</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>qianfan</name>
<description>qianfan</description>
<properties>
<java.version>8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- 千帆大模型平台-->
<dependency>
<groupId>com.baidubce</groupId>
<artifactId>qianfan</artifactId>
<version>0.0.1</version>
</dependency>
<!-- thymeleaf 依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<!-- lombok 依赖-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!-- mysql 依赖-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<!-- mybatis 依赖-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.4</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
application.properties
yml
spring.application.name=qianfan
# Qianfan
QIANFAN_ACCESS_KEY=你的AK
QIANFAN_SECRET_KEY=你的SK
# ServerProperties
server.port=8080
server.servlet.context-path=
# ThymeleafProperties
spring.thymeleaf.cache=false
# mysql
spring.datasource.url=jdbc:mysql://localhost:3306/ai?characterEncoding=utf-8&useSSL=false&serverTimezone=Hongkong
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
# MyBatis
mybatis.mapper-locations=classpath:mapper/*.xml
mybatis.type-aliases-package=com.jsss.entity
# 生成主键
mybatis.configuration.useGeneratedKeys=true
# 驼峰命名
mybatis.configuration.mapUnderscoreToCamelCase=true
sql_table和entity
ai.sql
sql
create table conversation
(
id int auto_increment
primary key,
username varchar(16) null,
user_message text null,
bot_message text null,
create_time varchar(32) null
);
**enntity.Conversation **
java
package com.jsss.qianfan.entity;
import lombok.AllArgsConstructor;
import lombok.Data;
@Data
@AllArgsConstructor
public class Conversation {
private Integer id;
private String username;
private String userMessage;
private String botMessage;
private String createTime;
}
dao和mapper
**dao.ChatMapper **
java
package com.jsss.qianfan.dao;
import com.jsss.qianfan.entity.Conversation;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
@Mapper
public interface ChatMapper {
List<Conversation> getByUsername(String username);
void insert(Conversation conversation);
}
**mapper/ChatMapper.xml **
xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.jsss.qianfan.dao.ChatMapper">
<!-- 定义SQL映射关系 -->
<!-- 根据username查询conversation记录 -->
<select id="getByUsername" resultType="com.jsss.qianfan.entity.Conversation" parameterType="string">
SELECT * FROM conversation WHERE username = #{username}
</select>
<!-- 插入新的conversation记录 -->
<insert id="insert" parameterType="com.jsss.qianfan.entity.Conversation">
INSERT INTO conversation (
username, user_message, bot_message, create_time
) VALUES (
#{username},
#{userMessage},
#{botMessage},
#{createTime}
)
</insert>
</mapper>
service和impl
service.ChatService
java
package com.jsss.qianfan.service;
import com.jsss.qianfan.entity.Conversation;
import java.util.List;
public interface ChatService {
void addChat(Conversation conversation);
List<Conversation> searchByUsername(String username);
}
impl.ChatServiceImpl
java
package com.jsss.qianfan.service.impl;
import com.jsss.qianfan.dao.ChatMapper;
import com.jsss.qianfan.entity.Conversation;
import com.jsss.qianfan.service.ChatService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class ChatServiceImpl implements ChatService {
@Autowired
ChatMapper chatMapper;
@Override
public void addChat(Conversation conversation) {
chatMapper.insert(conversation);
}
@Override
public List<Conversation> searchByUsername(String username) {
return chatMapper.getByUsername(username);
}
}
config和util
configuration.QianfanConfig
java
package com.jsss.qianfan.configuration;
import com.baidubce.qianfan.Qianfan;
import com.baidubce.qianfan.core.auth.Auth;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class QianfanConfig {
@Value("${QIANFAN_ACCESS_KEY}")
String ak;
@Value("${QIANFAN_SECRET_KEY}")
String sk;
@Bean
public Qianfan qianFan() {
return new Qianfan(Auth.TYPE_OAUTH, ak, sk);
}
}
util.QianfanUtil
java
package com.jsss.qianfan.util;
import com.baidubce.qianfan.Qianfan;
import com.baidubce.qianfan.model.chat.ChatResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class QianfanUtil {
@Autowired
Qianfan qianfan;
public String addMessage(String content) {
ChatResponse response = qianfan.chatCompletion()
//.model("ERNIE-Bot-4") //使用model指定预置模型 默认模型是ERNIE-Bot-turbo
.addMessage("user", content) // 添加用户消息 (此方法可以调用多次,以实现多轮对话的消息传递)
.temperature(0.7) // 自定义超参数
.execute(); // 发起请求
return response.getResult();
}
public void executeStream(String content) {
qianfan.chatCompletion()
.addMessage("user", content)
.executeStream() // 发起流式请求
.forEachRemaining(chunk -> System.out.print(chunk.getResult())); // 流式迭代,并打印消息
}
}
LoginController和ChatController
controller.LoginController
java
package com.jsss.qianfan.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
@Controller
public class LoginController {
@GetMapping("/login")
public String login(){
return "login";
}
@PostMapping("/login")
@ResponseBody
public String login(HttpServletRequest request,String username){
HttpSession session = request.getSession();
session.setAttribute("username",username);
return "登录成功";
}
@GetMapping("/logout")
@ResponseBody
public String logout(HttpServletRequest request,String username){
HttpSession session = request.getSession();
session.removeAttribute("username");
return "登出成功";
}
@GetMapping("/status")
@ResponseBody
public String status(HttpServletRequest request){
HttpSession session = request.getSession();
String username =(String)session.getAttribute("username");
if (username!=null&&!username.isEmpty()){
return username;
}else {
return "没有登录";
}
}
}
controller.ChatController
java
package com.jsss.qianfan.controller;
import com.jsss.qianfan.entity.Conversation;
import com.jsss.qianfan.service.ChatService;
import com.jsss.qianfan.util.QianfanUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.Map;
@Controller
@RequestMapping("chat")
public class ChatController {
// List<Conversation> conversations=new ArrayList<>();
//
// static int id=1;
//
// {
// conversations.add(new Conversation(id++,"1","你好","抱歉,网络出现异常,请你重试或联系客服!TooManyRequests", format(new Date())));
// conversations.add(new Conversation(id++,"1","你好","抱歉,网络出现异常,请你重试或联系客服!TooManyRequests", format(new Date())));
// }
@Autowired
QianfanUtil qianfanUtil;
@Autowired
ChatService chatService;
private String format(Date date){
return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(date);
}
@GetMapping("/list")
public String getChat(HttpServletRequest request,Model model){
String username= (String) request.getSession().getAttribute("username");
List<Conversation> conversations=chatService.searchByUsername(username);
model.addAttribute("conversations",conversations);
return "chat";
}
@PostMapping("/chat")
public String chat(HttpServletRequest httpServletRequest,@RequestBody Map<String, String> request){
String username= (String) httpServletRequest.getSession().getAttribute("username");
String content = request.get("content");
System.out.println(content);
// String res="回复";
// Conversation conversation = new Conversation(id++,username, content, res, format(new Date());
// conversations.add(conversation);
String res = qianfanUtil.addMessage(content);
Conversation conversation = new Conversation(null, username, content, res, format(new Date()));
chatService.addChat(conversation);
return "redirect:list";
}
}
前端开发
css和js
css/style.css
css
/* 设置用户发送消息的样式 */
.user-message {
background-color: #4CAF50; /* 绿色背景 */
color: white;
padding: 10px;
margin: 10px;
border-radius: 10px;
white-space: pre;
}
/* 设置ChatGPT发送消息的样式 */
.bot-message {
background-color: #f2f2f2; /* 灰色背景 */
padding: 10px;
margin: 10px;
border-radius: 10px;
white-space: pre;
}
.question-container {
display: flex;
justify-content: flex-end;
}
.question {
display: flex;
flex-direction: row;
align-items: center;
}
.question td:first-child {
margin-left: auto;
}
.answer-container {
display: flex;
justify-content: flex-start;
}
.answer {
display: flex;
flex-direction: row;
align-items: center;
}
.answer td:last-child {
margin-left: auto;
}
/* 设置提问和回复消息的表格样式 */
.question, .answer {
display: flex;
align-items: center;
padding: 10px;
border-radius: 10px;
}
/* 设置输入框和发送按钮的样式 */
.form-container {
display: flex;
justify-content: center;
align-items: center;
position: fixed;
bottom: 0;
width: 100%;
padding: 10px;
background-color: #f9f9f9;
border-top: 1px solid #ccc;
}
.form-row {
display: flex;
flex: 1;
}
.form-group {
flex: 1;
margin-right: 10px;
}
.form-group input {
width: 100%;
padding: 10px;
border: 1px solid #ccc;
border-radius: 5px;
outline: none;
}
.message-container {
max-height: 700px; /* 设置最大高度,超出部分可滚动 */
overflow-y: auto; /* 竖直方向溢出部分可滚动 */
}
.send-message-container {
flex: 1; /* 占据剩余空间 */
display: flex;
align-items: center;
background-color: #f5f5f5;
}
textarea {
width: 1800px; /* 设置输入框的宽度为300像素,您可以根据需要调整这个值 */
height: 100px; /* 设置输入框的高度为200像素,您可以根据需要调整这个值 */
font-size: 16px; /* 设置输入框中文字的字体大小为16像素,您可以根据需要调整这个值 */
resize: none; /* 禁止用户调整输入框的尺寸 */
}
js/onload.js
java
window.onload = function() {
// 找到消息容器
var messageContainer = document.querySelector(".message-container");
// 找到消息容器中最后一个子元素
var lastMessage = messageContainer.lastElementChild;
// 将最后一个子元素滚动到可见区域
lastMessage.scrollIntoView();
};
js/textarea.js
java
var textarea = document.getElementById("messageInput");
textarea.addEventListener("keydown", function(event) {
if (event.key === "Enter" && !event.shiftKey) {
event.preventDefault();
var message = textarea.value.trim();
textarea.value = "";
// 发送 POST 请求
fetch('/chat/chat', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ content: message })
}).then(function(response) {
// 刷新页面
location.reload();
});
}
});
textarea.addEventListener("keydown", function(event) {
if (event.key === "Enter" && event.shiftKey) {
// 在 Shift+Enter 情况下允许换行
textarea.value += "\n";
event.preventDefault();
}
});
login.html和chat.html
html/login.html
html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org" xmlns="http://www.w3.org/1999/html">
<head>
<title>AI对话</title>
</head>
<body>
<div class="chat-container">
<h1 class="title">登录</h1>
<div class="login-container">
<form th:action="@{/login}" method="post">
<div class="form-container">
<div class="form-row">
<span class="form-group no-border">
<input id="username" name="username" placeholder="输入用户名">
</span>
<button>登录</button>
</div>
</div>
</form>
</div>
</div>
</body>
</html>
html/chat.html
java
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org" xmlns="http://www.w3.org/1999/html">
<head>
<link rel="stylesheet" th:href="@{/css/style.css}">
<title>AI对话</title>
</head>
<body>
<div class="chat-container">
<h1 class="title">AI 对话</h1>
<div class="message-container">
<!-- 用户消息和ChatGPT消息显示部分 -->
<div th:each="conversation:${conversations}">
<div class="question-container">
<table class="question">
<td>
<span th:utext="${conversation.createTime}"></span>
<div class="user-message" th:utext="${conversation.userMessage}"></div>
</td>
<td type="text" th:text="${conversation.username}">提问</td>
</table>
</div>
<div class="answer-container">
<table class="answer">
<td type="text">AI</td>
<td>
<span th:utext="${conversation.createTime}"></span>
<div class="bot-message" th:utext="${conversation.botMessage}"></div>
</td>
</table>
</div>
</div>
</div>
<div class="send-message-container">
<!-- 发送消息部分 -->
<form th:action="@{/chat/chat}" method="post">
<div class="form-container">
<div class="form-row">
<span class="form-group no-border">
<textarea id="messageInput" placeholder="问我任何问题...(Shift + Enter 换行,按下Enter 发送)"></textarea>
</span>
</div>
</div>
</form>
</div>
</div>
</body>
<script th:src="@{/js/onload.js}"></script>
<script th:src="@{/js/textarea.js}"></script>
</html>
后言
待完善的功能:
-
用户对话之后,需要等待回复,才能弹出对话内容
-
等待期间,还能输入聊天框
-
并且,没有终止生成
-
没有左边框-新建对话
-
没有md格式的复制
最后
2024-3-20 21:06:39
迎着日光月光星光,直面风霜雨霜雪霜。