用户创建新对话
关键分析
首先思考,如何创建新对话?
我的思路是,设置变量conv_id和conv_name分别表示对话id和对话名称,当用户创建新对话时,此时的界面中对话信息应为空,也就是说如果conv_id === null||conv_id=== ''
时,同时用户发起第一条消息,表明我们可以创建对话了。
后端给出的创建新对话的接口中有两个参数user_id和conv_name,那么如何生成conv_name呢?
{
"user_id": "",
"conv_name": ""
}
已有的大模型对于用户对话的命名方式有两种,如gpt、智谱清言等会根据用户对话自动分析上下文形成标题,也有文心一言直接选取用户对话的第一句生成标题,当然是给了用户更改标题的权限,这样的好处是方便记忆。
因此这里我选择使用选取用户对话的第一句生成标题,方便用户更改。
至于存储数据,我使用了响应式数据msgList用来存储用户和大模型的对话信息。msgList中有type属性,分别为“user”和“ai”,区分不同发送者。
用户输入并点击发送后,首先判断是否为第一条询问,如果是则调用创建新对话接口,同时将对话名命名为当前这句话,并将这句话加入msgList。
然后将用户的问句作为ai对话接口的参数传入,获取到ai的回答,并将回答作为‘ai’的type传入msgList。
template中循环输出msgList即可。
还有一些细节需要注意:
- 用户发送完询问后将输入栏置空:
value.value = "";
- 设置对话界面自动滚动:
scrollToNew();
- 如果询问栏为空则提示输入内容不能为空!并终止接口
具体实现
实现的代码详见chat.vue。
const msgList = reactive([]);
const AIReplay = async (replay,aitype) => {
var autoReplyMsg = {
content: replay,
type: "ai",
};
msgList.push(autoReplyMsg);
};
目前实现效果如下:
用户历史对话
关键分析
用户历史对话我参考了kimi的设计,将每个对话作为一行el-card,template中循环当前用户从后端查询到的chatHistory,展示每个对话的标题、时间,同时点击详情可以跳转到聊天界面,可以继续进行当前上下文的对话。
这里的设计其实没有什么太大的难点,就是要注意下让对话时间倒序显示。
主要注意的是跳转到聊天界面时,需要和用户创建新对话的判断进行区分。
具体实现
跳转传参我使用了router,通过动态路由匹配传参conv_id和conv_name。
我在chat.vue中添加了onMounted钩子,用于在组件挂载到DOM后立即执行代码。当组件实例被创建并插入到DOM中时,onMounted
钩子会被调用,是进行数据获取、事件监听或直接操作DOM的好时机。
在其中我接受了来自其他界面路由跳转时的传参,实现如下:
conv_id= route.query.conv_id || '';
conv_name = route.query.conv_name || '';
接下来判断 if(conv_id!==null&&conv_name!==null
则加载历史聊天记录进入msgList。
但是在实际运行的时候我发现,当进入到新建对话界面时,用户输出问题后,明明后端已经返回的结果,但是前端无显示,并且数据库报错conv_id为空,因为外键约束无法插入数据库。
这是一个很奇怪的问题,如果用户新建对话的话,正常运行会将conv_name和user_id返回给后端,后端返回生成的conv_id然后继续对话。如果数据库报错conv_id为空的话,说明此时根本就没有调用创建新对话的接口。
查看实现代码,我意识到了在初始化conv_name和conv_id时,我将其设置为null,但是在onMounted中如果route.query为空的话,其实conv_name和conv_id都会变成了‘’
,而我后面是通过是否不等于null进行判断的,所以用户每次进入新建对话界面,其实都进入的是一个conv_id为空的历史记录(其实是不存在的)。
解决方案:
在onMounted方法开始判断一下是否有传参,无传参就退出
if(!route.query.conv_id || !route.query.conv_name||!route.query.user_id){
return ;
}
目前实现效果如下:
还有一个小问题,很多时候ai的回答明明已经在后端显示了,前端却没有加载出来,但是刷新或者从历史记录中重新进入对话,又会看到ai的回答。
查看代码,发现是在接口配置文件authService.js中将axios的timeout:设置为了20000,也就是说时间超过了20前端就不会显示,但是回答已经进入了数据库,所以每次刷新或者从历史记录中重新进入对话其实都是从数据库中加载了回答,所以可以看到。
解决方案:删除掉timeout即可。