从零搭建生产级AI智能客服系统(五):多轮对话与上下文管理,让AI拥有记忆
作者:大洪聊AI
本章目标:实现会话与消息持久化,基于滑动窗口实现多轮上下文管理,让AI能够记住历史对话,支持自然的多轮交流
前置条件:第四章RAG知识库100%完成,文档上传、检索、防编造功能正常
前言
上一章我们完成了RAG知识库系统,AI已经可以基于私有知识库准确回答问题,不会胡说八道了。但这时的系统还有两个非常影响体验的问题:
- 没有记忆:每次对话都是独立的,AI完全记不住你上一句说了什么,只能一问一答
- 刷新就丢:聊天记录只存在浏览器内存里,页面一刷新全部消失,没有任何留存
这一章我们就彻底解决这两个问题:用MySQL做会话与消息持久化,基于滑动窗口策略实现多轮上下文管理,让AI真正拥有“记忆”,支持连贯的多轮对话。
一、为什么需要持久化 + 滑动窗口上下文?
1.1 纯前端记忆的痛点
- 刷新丢失:页面刷新、浏览器关闭,聊天记录全部消失
- 无法跨端:换个设备就看不到历史对话
- 没有数据沉淀:无法做后续的数据分析、质检、优化
- 容易超Token:把所有历史都塞给大模型,对话多了直接超出Token限制报错
1.2 滑动窗口策略
大模型的上下文长度是有限的(比如doubao-pro-32k是32K Token),不可能把所有历史对话都传进去。行业通用的最优解就是滑动窗口:
只保留最近的N条对话作为上下文,更早的历史自动舍弃。既保证了AI能记住近期对话,又不会超出Token限制。
1.3 整体设计思路
用户发送消息
↓
保存用户消息到MySQL
↓
查询当前会话最近N条历史消息
↓
组装消息列表:系统提示 + 历史对话 + 当前问题
↓
调用大模型生成回答
↓
保存AI回答到MySQL
↓
流式返回给前端
二、数据库设计
2.1 表结构设计
我们需要两张核心表:
chat_session:会话表,记录每个会话的基本信息chat_message:消息表,记录每条具体的聊天内容
2.2 执行建表SQL
在你的ai_kefu数据库中执行以下SQL:
-- 会话表
CREATE TABLE `chat_session` (
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键ID',
`session_id` varchar(64) NOT NULL COMMENT '会话唯一标识',
`title` varchar(255) DEFAULT '新对话' COMMENT '会话标题',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
`is_deleted` tinyint NOT NULL DEFAULT '0' COMMENT '是否删除 0-否 1-是',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_session_id` (`session_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='会话表';
-- 消息表
CREATE TABLE `chat_message` (
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键ID',
`session_id` varchar(64) NOT NULL COMMENT '所属会话ID',
`role` varchar(32) NOT NULL COMMENT '消息角色 user/assistant',
`content` text NOT NULL COMMENT '消息内容',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`is_deleted` tinyint NOT NULL DEFAULT '0' COMMENT '是否删除 0-否 1-是',
PRIMARY KEY (`id`),
KEY `idx_session_id` (`session_id`),
KEY `idx_create_time` (`create_time`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='聊天消息表';
字段说明:
session_id:用UUID生成,前端传入,标识唯一会话role:消息角色,user是用户,assistant是AI,和大模型接口要求一致- 统一逻辑删除、创建时间、更新时间,符合企业开发规范
三、后端持久化层开发
3.1 第一步:创建实体类
新建会话实体:src/main/java/org/example/model/entity/ChatSession.java
package org.example.model.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableLogic;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import java.time.LocalDateTime;
@Data
@TableName("chat_session")
public class ChatSession {
@TableId(type = IdType.AUTO)
private Long id;
private String sessionId;
private String title;
private LocalDateTime createTime;
private LocalDateTime updateTime;
@TableLogic
private Integer isDeleted;
}
新建消息实体:src/main/java/org/example/model/entity/ChatMessage.java
package org.example.model.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableLogic;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import java.time.LocalDateTime;
@Data
@TableName("chat_message")
public class ChatMessage {
@TableId(type = IdType.AUTO)
private Long id;
private String sessionId;
private String role;
private String content;
private LocalDateTime createTime;
@TableLogic
private Integer isDeleted;
}
3.2 第二步:创建Mapper接口
新建:src/main/java/org/example/mapper/ChatSessionMapper.java
package org.example.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.example.model.entity.ChatSession;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface ChatSessionMapper extends BaseMapper<ChatSession> {
}
新建:src/main/java/org/example/mapper/ChatMessageMapper.java
package org.example.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.example.model.entity.ChatMessage;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface ChatMessageMapper extends BaseMapper<ChatMessage> {
}
3.3 第三步:会话服务SessionService
新建:src/main/java/org/example/service/SessionService.java
package org.example.service;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import org.example.mapper.ChatSessionMapper;
import org.example.model.entity.ChatSession;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.UUID;
@Service
public class SessionService {
@Autowired
private ChatSessionMapper sessionMapper;
/**
* 创建新会话
*/
public String createSession() {
String sessionId = UUID.randomUUID().toString().replace("-", "");
ChatSession session = new ChatSession();
session.setSessionId(sessionId);
session.setTitle("新对话");
sessionMapper.insert(session);
return sessionId;
}
/**
* 检查会话是否存在
*/
public boolean exists(String sessionId) {
LambdaQueryWrapper<ChatSession> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(ChatSession::getSessionId, sessionId);
return sessionMapper.selectCount(wrapper) > 0;
}
}
3.4 第四步:消息服务MessageService(核心)
这是本章最核心的服务,负责消息保存、历史查询、滑动窗口上下文组装。
新建:src/main/java/org/example/service/MessageService.java
package org.example.service;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import dev.langchain4j.data.message.ChatMessage;
import dev.langchain4j.data.message.UserMessage;
import dev.langchain4j.data.message.AiMessage;
import org.example.mapper.ChatMessageMapper;
import org.example.model.entity.ChatMessage;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
@Service
public class MessageService {
@Autowired
private ChatMessageMapper messageMapper;
/**
* 保存单条消息
*/
public void addMessage(String sessionId, String role, String content) {
ChatMessage message = new ChatMessage();
message.setSessionId(sessionId);
message.setRole(role);
message.setContent(content);
messageMapper.insert(message);
}
/**
* 获取会话的所有历史消息(按时间正序)
*/
public List<ChatMessage> getHistoryMessages(String sessionId) {
LambdaQueryWrapper<ChatMessage> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(ChatMessage::getSessionId, sessionId)
.orderByAsc(ChatMessage::getCreateTime);
return messageMapper.selectList(wrapper);
}
/**
* 构建滑动窗口上下文(转换为LangChain4j的ChatMessage格式)
* @param sessionId 会话ID
* @param limit 保留最近N条消息
*/
public List<ChatMessage> buildSlidingWindowContext(String sessionId, int limit) {
// 1. 查询历史消息
List<ChatMessage> history = getHistoryMessages(sessionId);
// 2. 截取最近N条
int start = Math.max(0, history.size() - limit);
List<ChatMessage> windowMessages = history.subList(start, history.size());
// 3. 转换为LangChain4j的消息格式
List<ChatMessage> context = new ArrayList<>();
for (ChatMessage msg : windowMessages) {
if ("user".equals(msg.getRole())) {
context.add(UserMessage.from(msg.getContent()));
} else if ("assistant".equals(msg.getRole())) {
context.add(AiMessage.from(msg.getContent()));
}
}
return context;
}
}
核心逻辑说明:
buildSlidingWindowContext是滑动窗口的核心实现- 先查出所有历史,再截取最后N条,保证只保留最新的对话
- 把数据库里的消息转换成大模型要求的
UserMessage/AiMessage格式 - 默认建议保留最近10条,平衡记忆能力和Token消耗
四、业务层重构:抽取AiService
之前的聊天逻辑都写在Controller里,现在业务逻辑越来越多,我们按照MVC规范把业务逻辑抽到Service层,为后面第六章的意图识别重构打基础。
4.1 定义AiService接口
新建:src/main/java/org/example/service/AiService.java
package org.example.service;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
public interface AiService {
/**
* 流式生成回答
* @param sessionId 会话ID
* @param message 用户消息
* @param emitter SSE发射器
*/
void streamGenerate(String sessionId, String message, SseEmitter emitter);
}
4.2 实现豆包AI服务
新建:src/main/java/org/example/service/impl/DoubaoAiService.java
package org.example.service.impl;
import dev.langchain4j.data.message.AiMessage;
import dev.langchain4j.data.message.ChatMessage;
import dev.langchain4j.data.message.SystemMessage;
import dev.langchain4j.data.message.UserMessage;
import dev.langchain4j.model.StreamingResponseHandler;
import dev.langchain4j.model.chat.StreamingChatLanguageModel;
import dev.langchain4j.model.output.Response;
import org.example.service.AiService;
import org.example.service.MessageService;
import org.example.service.RagService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Service;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
import java.io.IOException;
import java.util.List;
@Service("doubaoAiService")
public class DoubaoAiService implements AiService {
@Autowired
@Qualifier("doubaoStreamingChatModel")
private StreamingChatLanguageModel streamingChatModel;
@Autowired
private MessageService messageService;
@Autowired
private RagService ragService;
// 滑动窗口大小:保留最近10条消息
private static final int CONTEXT_WINDOW_SIZE = 10;
@Override
public void streamGenerate(String sessionId, String message, SseEmitter emitter) {
// 1. 保存用户消息到数据库
messageService.addMessage(sessionId, "user", message);
// 2. RAG检索知识库
String relevantContent = ragService.retrieveRelevantContent(message);
// 3. 无相关内容,直接返回固定话术,不调用大模型
if (relevantContent.isEmpty()) {
String reply = "抱歉,我没有找到相关信息,请您咨询人工客服。";
try {
emitter.send(reply, MediaType.TEXT_PLAIN);
emitter.send("[DONE]", MediaType.TEXT_PLAIN);
emitter.complete();
// 保存AI回答
messageService.addMessage(sessionId, "assistant", reply);
} catch (IOException e) {
emitter.completeWithError(e);
}
return;
}
// 4. 构建RAG增强后的用户问题
String enhancedQuestion = ragService.buildRagPrompt(message, relevantContent);
// 5. 获取滑动窗口上下文(去掉最后一条,因为刚存的用户消息和enhancedQuestion重复)
List<ChatMessage> contextMessages = messageService.buildSlidingWindowContext(sessionId, CONTEXT_WINDOW_SIZE);
if (!contextMessages.isEmpty() && contextMessages.get(contextMessages.size() - 1) instanceof UserMessage) {
contextMessages.remove(contextMessages.size() - 1);
}
// 6. 组装完整消息列表:系统提示 + 历史上下文 + 增强后的当前问题
contextMessages.add(0, SystemMessage.from("你是专业的AI客服,回答简洁、礼貌、专业。"));
contextMessages.add(UserMessage.from(enhancedQuestion));
// 7. 用于拼接完整的AI回复,最后存库
StringBuilder fullResponse = new StringBuilder();
// 8. 流式生成回答
streamingChatModel.generate(contextMessages, new StreamingResponseHandler<AiMessage>() {
@Override
public void onNext(String token) {
try {
emitter.send(token, MediaType.TEXT_PLAIN);
fullResponse.append(token);
} catch (IOException e) {
emitter.completeWithError(e);
}
}
@Override
public void onComplete(Response<AiMessage> response) {
try {
emitter.send("[DONE]", MediaType.TEXT_PLAIN);
emitter.complete();
// 9. 保存AI回答到数据库
messageService.addMessage(sessionId, "assistant", fullResponse.toString());
} catch (IOException e) {
emitter.completeWithError(e);
}
}
@Override
public void onError(Throwable error) {
emitter.completeWithError(error);
}
});
}
}
关键细节:
- 去重处理:我们刚把用户原话存进了数据库,但传给大模型的是RAG增强后的问题,所以要把最后一条用户原话从上下文中去掉,避免重复
- 系统提示:放在消息列表最前面,大模型最先看到规则
- 完整回复拼接:流式生成是一个字一个字回调的,要用StringBuilder拼完整,最后存库
4.3 改造ChatController
Controller只负责参数校验和请求转发,不再写业务逻辑:
package org.example.controller;
import dev.langchain4j.model.chat.ChatLanguageModel;
import org.example.model.vo.AjaxResult;
import org.example.service.AiService;
import org.example.service.SessionService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
import lombok.Data;
@RestController
@RequestMapping("/api/chat")
public class ChatController {
@Autowired
@Qualifier("doubaoChatModel")
private ChatLanguageModel chatModel;
@Autowired
@Qualifier("doubaoAiService")
private AiService aiService;
@Autowired
private SessionService sessionService;
@Data
public static class ChatRequest {
private String sessionId;
private String message;
}
/**
* 创建新会话
*/
@PostMapping("/session")
public AjaxResult<String> createSession() {
String sessionId = sessionService.createSession();
return AjaxResult.success(sessionId);
}
/**
* SSE流式聊天接口(带上下文+RAG)
*/
@PostMapping(value = "/stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public SseEmitter streamChat(@RequestBody ChatRequest request) {
SseEmitter emitter = new SseEmitter(60000L);
aiService.streamGenerate(request.getSessionId(), request.getMessage(), emitter);
return emitter;
}
/**
* 同步聊天接口(保留用于测试)
*/
@PostMapping("/simple")
public AjaxResult<String> simpleChat(@RequestBody ChatRequest request) {
String response = chatModel.generate(request.getMessage());
return AjaxResult.success(response);
}
}
五、前端改造:支持历史消息加载
5.1 核心改造点
- 页面加载时自动创建会话(如果没有)
- 进入页面拉取当前会话的历史消息并渲染
- 发送消息时带上sessionId
- 刷新页面后历史记录依然存在
5.2 完整改造后的App.vue
<template>
<div class="app">
<div class="chat-container">
<div class="chat-header">
<h1>AI智能客服</h1>
</div>
<div class="chat-content" ref="chatContentRef">
<div
v-for="(msg, index) in messageList"
:key="index"
:class="['message', msg.role]"
>
<div class="message-content">{{ msg.content }}</div>
</div>
</div>
<div class="chat-input">
<el-input
v-model="inputMessage"
placeholder="请输入您的问题..."
@keyup.enter="handleSend"
:disabled="isLoading"
></el-input>
<el-button
type="primary"
@click="handleSend"
:loading="isLoading"
>
发送
</el-button>
</div>
</div>
</div>
</template>
<script setup>
import { ref, nextTick, onMounted } from 'vue'
import { ElMessage } from 'element-plus'
const messageList = ref([])
const inputMessage = ref('')
const isLoading = ref(false)
const chatContentRef = ref(null)
const currentSessionId = ref('')
// 滚动到底部
const scrollToBottom = async () => {
await nextTick()
if (chatContentRef.value) {
chatContentRef.value.scrollTop = chatContentRef.value.scrollHeight
}
}
// 加载历史消息
const loadHistory = async () => {
if (!currentSessionId.value) return
try {
const res = await fetch(`/api/chat/history?sessionId=${currentSessionId.value}`)
const data = await res.json()
if (data.code === 200) {
messageList.value = data.data
await scrollToBottom()
}
} catch (error) {
console.error('加载历史消息失败:', error)
}
}
// 页面加载时初始化会话
onMounted(async () => {
// 从localStorage取会话ID,没有就新建
let sessionId = localStorage.getItem('currentSessionId')
if (!sessionId) {
const res = await fetch('/api/chat/session', { method: 'POST' })
const data = await res.json()
sessionId = data.data
localStorage.setItem('currentSessionId', sessionId)
}
currentSessionId.value = sessionId
// 加载历史消息
await loadHistory()
})
// 发送消息(流式版本)
const handleSend = async () => {
const content = inputMessage.value.trim()
if (!content || isLoading.value) return
inputMessage.value = ''
isLoading.value = true
// 添加用户消息
messageList.value.push({ role: 'user', content })
await scrollToBottom()
// 添加AI空消息占位
const aiIndex = messageList.value.length
messageList.value.push({ role: 'ai', content: '' })
await scrollToBottom()
try {
const response = await fetch('/api/chat/stream', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
sessionId: currentSessionId.value,
message: content
})
})
if (!response.ok || !response.body) {
throw new Error(`请求失败,状态码:${response.status}`)
}
const reader = response.body.getReader()
const decoder = new TextDecoder('utf-8')
let buffer = ''
while (true) {
const { done, value } = await reader.read()
if (done) break
buffer += decoder.decode(value, { stream: true })
if (buffer.includes('[DONE]')) {
const realContent = buffer.replace('[DONE]', '')
messageList.value[aiIndex].content = realContent
buffer = ''
break
} else {
messageList.value[aiIndex].content = buffer
}
await scrollToBottom()
}
if (buffer && buffer !== '[DONE]') {
messageList.value[aiIndex].content = buffer.replace('[DONE]', '')
}
if (!messageList.value[aiIndex].content) {
messageList.value[aiIndex].content = '未收到回复,请重试。'
}
} catch (error) {
console.error('流式请求失败:', error)
messageList.value[aiIndex].content = '抱歉,系统出错了,请稍后再试。'
ElMessage.error('请求失败,请检查网络')
} finally {
isLoading.value = false
await scrollToBottom()
}
}
</script>
<style scoped>
/* 样式和第三章完全一致,直接复用 */
.app {
width: 100vw;
height: 100vh;
background-color: #f5f7fa;
display: flex;
justify-content: center;
align-items: center;
}
.chat-container {
width: 800px;
height: 80vh;
background-color: white;
border-radius: 8px;
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.1);
display: flex;
flex-direction: column;
}
.chat-header {
padding: 20px;
border-bottom: 1px solid #e6e6e6;
text-align: center;
}
.chat-header h1 {
margin: 0;
font-size: 24px;
color: #303133;
}
.chat-content {
flex: 1;
overflow-y: auto;
padding: 20px;
}
.message {
margin-bottom: 16px;
max-width: 70%;
display: flex;
}
.message.user {
margin-left: auto;
justify-content: flex-end;
}
.message.ai {
margin-right: auto;
justify-content: flex-start;
}
.message-content {
padding: 12px 16px;
border-radius: 8px;
line-height: 1.5;
word-break: break-word;
}
.message.user .message-content {
background-color: #409eff;
color: white;
}
.message.ai .message-content {
background-color: #f5f7fa;
color: #303133;
}
.chat-input {
padding: 20px;
border-top: 1px solid #e6e6e6;
display: flex;
gap: 10px;
}
.chat-input .el-input {
flex: 1;
}
</style>
5.3 补充历史消息查询接口
在ChatController中加一个查询历史消息的接口:
/**
* 获取会话历史消息
*/
@GetMapping("/history")
public AjaxResult<List<ChatMessage>> getHistory(@RequestParam String sessionId) {
List<ChatMessage> list = messageService.getHistoryMessages(sessionId);
return AjaxResult.success(list);
}
注意:这里返回的是实体类,生产环境建议转成VO返回,避免把数据库字段全部暴露给前端。
六、完整功能验证
- 启动服务:确保MySQL、Chroma、后端、前端都正常启动
- 刷新页面:打开页面,会自动创建会话,消息列表为空
- 发送第一条消息:“你们产品多少钱?”,AI正常回答
- 发送第二条消息:“专业版有什么功能?”,AI应该能理解你问的是专业版产品的功能,说明上下文生效了
- 刷新页面:刷新浏览器,历史消息依然存在,说明持久化生效了
- 验证RAG:问知识库外的问题,依然返回找不到相关信息
✅ 全部验证通过,多轮对话和持久化功能正常。
七、调优与扩展建议
7.1 滑动窗口大小怎么调?
| 场景 | 建议窗口大小 | 说明 |
|---|---|---|
| 简单问答客服 | 6-8条 | 不需要太长记忆,节省Token |
| 任务型对话(查订单) | 10-15条 | 需要记住多轮追问的信息 |
| 深度咨询场景 | 20-30条 | 需要完整的上下文理解需求 |
原则:在满足业务需求的前提下,窗口越小越好,既省Token又快。
7.2 后续可扩展方向
- 会话列表:左侧加会话列表,支持创建多个会话、切换会话
- 会话标题自动生成:用大模型根据前几句话生成会话标题
- 消息删除:支持删除单条消息、清空整个会话
- Token计数:统计每次请求的Token消耗,做费用统计
八、常见问题排查
问题1:AI记不住之前的对话
- 检查是否传了正确的
sessionId - 检查消息是否成功保存到数据库
- 检查滑动窗口大小是不是设太小了
- 检查是不是RAG增强后的问题把上下文覆盖了
问题2:刷新页面历史消息不显示
- 检查
/api/chat/history接口是否正常返回 - 检查前端
onMounted里是否调用了加载历史的方法 - 检查localStorage里的
currentSessionId是否存在
问题3:提示Token超出限制
- 调小滑动窗口大小
- 调小文档切片大小和Top-K数量
- 检查是不是系统提示写太长了
问题4:消息重复
- 检查是不是没有去掉最后一条用户原话
- 检查保存消息的逻辑是不是执行了两次
九、本章总结
本章我们完成了多轮对话与持久化的核心能力:
- ✅ 完成了数据库设计,建立了会话表和消息表
- ✅ 实现了消息的增删查和持久化存储
- ✅ 基于滑动窗口策略实现了多轮上下文管理
- ✅ 重构了代码架构,分层清晰,Controller只做转发
- ✅ 前端支持历史消息加载,刷新页面记录不丢失
- ✅ RAG和上下文完美结合,既懂知识库又有记忆
现在的系统已经是一个完整可用的智能客服了,有流式体验、有知识库、有记忆、有持久化。下一章我们将进入架构升级阶段,用策略模式+工厂模式实现意图识别,让系统支持任务型对话,具备真正的生产级扩展能力。
下章预告
下一章我们将实现意图识别与任务型对话,用经典的策略模式+工厂模式重构对话架构,支持查询订单、转人工等标准化任务,实现槽位填充和多轮追问,打造可无限扩展的对话系统。
关注我,第一时间收到更新通知!
更多推荐



所有评论(0)