Ai智能体四:互动式 AI 谈天助手:前端实现

打印 上一主题 下一主题

主题 897|帖子 897|积分 2691

在当代 web 应用中,集成智能对话功能已经成为提升用户体验的紧张本领之一。本文将介绍如何通过 Vue 3Element Plus 构建一个高效的 AI 谈天助手界面,并详细讲解其实现原理和功能。
1. 整体架构

该谈天界面分为 左侧边栏右侧内容地区,实现了清楚的布局布局,左侧边栏用于展示历史会话和各种功能开关,右侧内容地区用于展示当前会话的消息和输入框。系统支持以下功能:


  • 向量存储:可以开启以便访问已上传的文档内容。
  • Agent功能:允许开启主动化代理功能来加强对话本领。
  • 数据分析:用户可以上传文件举行分析,并在分析后通过向量存储访问文件内容。
此外,用户还可以检察和管理历史会话、快速开始对话、上传文件等。

2. 左侧边栏:管理与功能开关

左侧边栏包罗了三个主要部分:


  • Logo与新建会话:展示应用的Logo并支持创建新会话。
  • 功能开关:包括启用向量存储、启用Agent功能以及文件上传举行数据分析的功能开关。这些功能开关都使用了 Element Plus 的 el-switch 组件,允许用户根据需求自由开关。
  • 历史消息列表:展示用户的历史会话,用户可以点击每个会话检察之前的消息,并删除不需要的会话。

  1. <div class="sidebar">
  2.   <div class="sidebar-header">
  3.     <div class="logo">
  4.       <img src="@/assets/logo.png" alt="Logo" class="logo-img" />
  5.       <span class="logo-text">AI助手</span>
  6.     </div>
  7.     <el-icon class="new-chat-icon" @click="resetChat"><Plus /></el-icon>
  8.   </div>
  9.   <div class="feature-toggles">
  10.     <div class="toggle-item">
  11.       <div class="toggle-label-group">
  12.         <span class="toggle-label">向量存储</span>
  13.         <el-tooltip content="开启后可以访问已上传的文档内容" placement="right">
  14.           <el-icon class="info-icon"><InfoFilled /></el-icon>
  15.         </el-tooltip>
  16.       </div>
  17.       <el-switch v-model="enableVectorStore" size="small" active-color="#409EFF" />
  18.     </div>
  19.     <div class="toggle-item">
  20.       <span class="toggle-label">Agent</span>
  21.       <el-switch v-model="enableAgent" size="small" active-color="#409EFF" />
  22.     </div>
  23.     <div class="toggle-item">
  24.       <div class="toggle-label-group">
  25.         <span class="toggle-label">数据分析</span>
  26.         <el-tooltip content="上传文件进行分析,分析后可通过向量存储访问文件内容" placement="right">
  27.           <el-icon class="info-icon"><InfoFilled /></el-icon>
  28.         </el-tooltip>
  29.       </div>
  30.       <el-upload :auto-upload="false" :show-file-list="false" :on-change="handleEmbedding" class="upload-btn-small">
  31.         <el-button size="small" type="primary" :loading="uploadLoading">上传文件</el-button>
  32.       </el-upload>
  33.     </div>
  34.   </div>
  35.   <div class="history-list">
  36.     <div class="history-group">
  37.       <div class="group-title">历史消息</div>
  38.       <div v-for="item in conversations" :key="item.sessionId" class="chat-item" :class="{ active: currentSessionId === item.sessionId }" @click="selectSession(item)">
  39.         <div class="chat-item-content">
  40.           <el-icon><ChatLineRound /></el-icon>
  41.           <span class="chat-title">{{ item.name }}</span>
  42.           <span class="chat-time">{{ formatTime(item.createdTime) }}</span>
  43.         </div>
  44.         <div class="chat-item-actions">
  45.           <el-icon class="delete-icon" @click.stop="handleDelete(item.sessionId)"><Delete /></el-icon>
  46.         </div>
  47.       </div>
  48.     </div>
  49.   </div>
  50. </div>
复制代码
3. 右侧内容区:展示与互动

右侧内容区展示当前选定会话的详细内容。用户可以看到与 AI 的对话消息,并通过输入框举行新一轮的对话。输入框支持文本输入和文件上传,允许用户在对话中插入文件或直接输入消息内容。
初始页面

当用户没有选择当前会话时,右侧内容地区会表现接待信息和热门问答,供用户快速开始与 AI 的对话。
  1. <template v-if="!currentSessionId">
  2.   <div class="welcome-page">
  3.     <h1 class="welcome-title">欢迎使用小鹏AI助手</h1>
  4.     <div class="history-qa" v-if="randomHistoryMessages.length > 0">
  5.       <h2>热门问答</h2>
  6.       <div class="qa-list">
  7.         <div v-for="(qa, index) in randomHistoryMessages" :key="index" class="qa-item" @click="quickStart(qa.question)">
  8.           <div class="qa-question">
  9.             <el-icon><ChatLineRound /></el-icon>
  10.             <span>{{ qa.question }}</span>
  11.           </div>
  12.           <div class="qa-answer">{{ qa.answer }}</div>
  13.         </div>
  14.       </div>
  15.     </div>
  16.   </div>
  17. </template>
复制代码
对话消息区

当用户选择了某个会话后,右侧内容区展示历史对话消息,包括用户和 AI 的消息内容。每条消息根据消息类型(如用户消息、AI 回复、系统消息)举行不同的样式渲染。

  1. <template v-else>
  2.   <div class="chat-messages" ref="messagesContainer">
  3.     <div v-for="message in messages" :key="message.id" class="message-item" :class="message.type.toLowerCase()">
  4.       <div class="message-content">{{ message.textContent }}</div>
  5.       <div class="message-time">{{ formatMessageTime(message.createdTime) }}</div>
  6.     </div>
  7.   </div>
  8. </template>
复制代码
输入与发送

输入框允许用户在对话框内输入消息,并支持文件上传。点击发送按钮后,消息将被发送到后端,并实时表现在谈天界面。
  1. <div class="input-section">
  2.   <div class="input-wrapper">
  3.     <el-input v-model="inputMessage" :placeholder="currentSessionId ? '继续对话...' : '给 AI助手 发送消息'" class="message-input" size="large" @keyup.enter="sendMessage">
  4.       <template #suffix>
  5.         <div class="input-actions">
  6.           <el-upload ref="uploadRef" :auto-upload="false" :show-file-list="false" :on-change="handleFileChange" class="upload-btn">
  7.             <el-icon :size="20"><Paperclip /></el-icon>
  8.           </el-upload>
  9.           <div class="send-btn" :class="{ active: canSend }" @click="sendMessage">
  10.             <el-icon><Position /></el-icon>
  11.           </div>
  12.         </div>
  13.       </template>
  14.     </el-input>
  15.   </div>
  16.   <div v-if="selectedFile" class="selected-file">
  17.     <el-tag closable @close="clearFile" class="file-tag">{{ selectedFile.name }}</el-tag>
  18.   </div>
  19. </div>
复制代码
4. 数据与状态管理

在代码中,使用了 Vue 3 的 Composition API 来管理组件的状态和相应式数据。这里的状态主要包括用户输入、当前会话、消息内容、文件上传等。
  1. const inputMessage = ref('') // 用户输入的消息
  2. const conversations = ref([]) // 历史会话列表
  3. const currentSessionId = ref(null) // 当前选中的会话 ID
  4. const messages = ref([]) // 当前会话的消息内容
  5. const loading = ref(false) // 加载状态,用于发送消息时显示加载效果
  6. const messagesContainer = ref(null) // 对话内容容器,用于滚动
  7. const uploadRef = ref(null) // 文件上传组件的引用
  8. const selectedFile = ref(null) // 已选择的文件
  9. const eventSource = ref(null) // 用于 SSE 连接的对象
  10. const enableVectorStore = ref(false) // 启用向量存储
  11. const enableAgent = ref(false) // 启用 Agent
  12. const uploadLoading = ref(false) // 文件上传时的加载状态
  13. const randomHistoryMessages = ref([]) // 随机历史消息
复制代码
5. 历史会话与消息获取

  1. const getConversations = async () => {
  2.   try {
  3.     const res = await request({
  4.       url: '/api/ai/getConversationById',
  5.       method: 'get',
  6.       params: { userId: userStore.userId }
  7.     })
  8.     if (res.success) {
  9.       conversations.value = res.data
  10.       // 获取随机历史消息
  11.       await getRandomHistoryMessages()
  12.     } else {
  13.       ElMessage.error('获取历史会话失败')
  14.     }
  15.   } catch (error) {
  16.     console.error('获取历史会话出错:', error)
  17.     ElMessage.error('获取历史会话失败')
  18.   }
  19. }
复制代码
getConversations 方法用于从后端获取历史会话记录,并通过 conversations 状态更新会话列表。假如获取失败,会表现错误信息。
同理,getMessages 用来获取特定会话的消息内容。
6. 消息发送与文件上传

  1. const sendMessage = async () => {
  2.   if ((!inputMessage.value.trim() && !selectedFile.value) || loading.value) return
  3.   
  4.   const content = inputMessage.value.trim()
  5.   inputMessage.value = '' // 立即清空输入框
  6.   loading.value = true
  7.   try {
  8.     let sessionId = currentSessionId.value
  9.    
  10.     // 如果没有当前会话,创建新会话
  11.     if (!sessionId) {
  12.       sessionId = await createConversation(content || selectedFile.value?.name)
  13.       if (!sessionId) {
  14.         loading.value = false
  15.         return
  16.       }
  17.       currentSessionId.value = sessionId
  18.       await getConversations()
  19.     }
  20.     // 准备消息数据
  21.     const chatMessage = {
  22.       sessionId: sessionId.toString(),
  23.       textContent: content,
  24.       type: 'user',
  25.       medias: [],
  26.       userId: userStore.userId
  27.     }
  28.     // 添加用户消息到界面
  29.     messages.value.push({
  30.       ...chatMessage,
  31.       createdTime: new Date().toISOString()
  32.     })
  33.     // 添加AI消息占位
  34.     const aiMessage = {
  35.       id: (Date.now() + 1).toString(),
  36.       type: 'assistant',
  37.       textContent: '',
  38.       createdTime: new Date().toISOString(),
  39.       sessionId: sessionId.toString()
  40.     }
  41.     messages.value.push(aiMessage)
  42.     await scrollToBottom()
  43.     // 创建 FormData
  44.     const formData = new FormData()
  45.     formData.append('input', JSON.stringify({
  46.       message: chatMessage,
  47.       params: {
  48.         enableVectorStore: enableVectorStore.value,
  49.         enableAgent: enableAgent.value
  50.       }
  51.     }))
  52.    
  53.     // 如果有文件,添加到 FormData
  54.     if (selectedFile.value) {
  55.       formData.append('file', selectedFile.value)
  56.     }
  57.     // 关闭之前的连接
  58.     if (eventSource.value) {
  59.       eventSource.value.close()
  60.     }
  61.     // 创建 SSE 连接
  62.     eventSource.value = new SSE('/api/ai/chat', {
  63.       headers: {
  64.         Accept: 'text/event-stream',
  65.       },
  66.       method: 'POST',
  67.       payload: formData
  68.     })
  69.     eventSource.value.onmessage = (event) => {
  70.       try {
  71.         const responseData = JSON.parse(event.data)
  72.         const finishReason = responseData.result?.metadata?.finishReason
  73.         if (responseData.result?.output?.content) {
  74.           aiMessage.textContent += responseData.result.output.content
  75.           messages.value = [...messages.value]
  76.           scrollToBottom()
  77.         }
  78.         if (finishReason && finishReason.toLowerCase() === 'stop') {
  79.           // 保存ai的回复消息
  80.           saveMessage({
  81.             sessionId: aiMessage.sessionId,
  82.             textContent: aiMessage.textContent,
  83.             type: aiMessage.type,
  84.             medias: []
  85.           })
  86.           loading.value = false
  87.           clearFile()
  88.           eventSource.value?.close()
  89.         }
  90.       } catch (error) {
  91.         console.error('解析消息出错:', error)
  92.       }
  93.     }
  94.     eventSource.value.onerror = (error) => {
  95.       console.error('SSE 连接错误:', error)
  96.       if (aiMessage.textContent === '') {
  97.         aiMessage.textContent = '消息处理出错,请重试'
  98.       }
  99.       eventSource.value?.close()
  100.       loading.value = false
  101.       clearFile()
  102.     }
  103.     eventSource.value.onopen = () => {
  104.       console.log('SSE 连接已建立')
  105.     }
  106.   } catch (error) {
  107.     console.error('发送消息失败:', error)
  108.     ElMessage.error('发送消息失败,请重试')
  109.     loading.value = false
  110.   }
  111. }
复制代码
sendMessage 是处理惩罚用户输入的核心函数。当用户输入消息并点击发送时,函数起首判断是否有有用的输入(文本或文件),然后处理惩罚消息的发送,包括创建新的会话、更新界面上的消息以及建立与后端的 SSE(服务器推送事件)连接。
SSE 连接的目标是吸收 AI 模型的相应,并实时更新对话内容。AI 回复的内容通过 onmessage 事件监听器获取,并动态地将其添加到消息列表中。
7. 文件上传与处理惩罚

  1. const handleEmbedding = async (file) => {
  2.   uploadLoading.value = true
  3.   try {
  4.     const formData = new FormData()
  5.     formData.append('file', file.raw)
  6.    
  7.     const res = await request({
  8.       url: '/api/ai/embedding',
  9.       method: 'post',
  10.       data: formData,
  11.       headers: {
  12.         'Content-Type': 'multipart/form-data'
  13.       }
  14.     })
  15.    
  16.     if (res.success) {
  17.       ElMessage.success('文件分析成功,现在可以开启向量存储来访问文件内容')
  18.       // 自动开启向量存储
  19.       enableVectorStore.value = true
  20.     } else {
  21.       ElMessage.error('文件分析失败')
  22.     }
  23.   } catch (error) {
  24.     console.error('文件分析失败:', error)
  25.     ElMessage.error('文件分析失败')
  26.   } finally {
  27.     uploadLoading.value = false
  28.   }
  29. }
复制代码
handleEmbedding 方法处理惩罚文件上传。文件被上传到后端举行分析,分析后开启向量存储功能,用户可以通过此功能访问文件中的内容。上传过程使用了 FormData 来处理惩罚文件数据,并通过 multipart/form-data 请求头发送。
8. 滚动和视图更新

  1. const scrollToBottom = async () => {
  2.   await nextTick()
  3.   if (messagesContainer.value) {
  4.     const scrollOptions = {
  5.       top: messagesContainer.value.scrollHeight,
  6.       behavior: 'smooth'
  7.     }
  8.     messagesContainer.value.scrollTo(scrollOptions)
  9.   }
  10. }
复制代码
scrollToBottom 方法确保消息列表在更新后主动滚动到最新消息。这是通过访问 messagesContainer 的 DOM 节点,并设置其 scrollTop 属性实现的。
总结

本系统使用 Vue 3Element Plus 实现了一个高效、互动性强的 AI 谈天助手界面。通过集成历史消息管理、文件上传、向量存储等功能,提供了一个全面的对话平台,满意了用户在一样平常工作中的各种需求。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

缠丝猫

金牌会员
这个人很懒什么都没写!
快速回复 返回顶部 返回列表