
Phi-3-Mini-128K前端应用Vue3项目集成AI对话组件全指南1. 引言最近在捣鼓一些AI小应用发现微软开源的Phi-3-Mini-128K这个模型挺有意思的。它虽然是个“小”模型但在很多对话和理解任务上表现不错关键是资源占用相对友好很适合我们前端开发者自己拿来玩玩或者集成到自己的项目里。如果你正好有个Vue3项目想给它加个智能对话的功能比如做个客服助手、内容生成工具或者就是个聊天机器人那这篇文章就是为你准备的。我会手把手带你从零开始把一个能跟Phi-3-Mini-128K模型“说话”的组件集成到你的Vue3应用里。整个过程不复杂核心就是两件事一是学会怎么通过API跟后端的模型服务“打招呼”二是在Vue3里把这些逻辑封装成一个漂亮、好用、还能复用的对话组件。我会把每一步的代码都贴出来你跟着做就行。咱们的目标是看完这篇文章你就能在自己的项目里跑起来一个AI对话功能。2. 环境准备与项目搭建在开始写代码之前咱们得先把“舞台”搭好。这里假设你已经有一个能提供Phi-3-Mini-128K模型API的后端服务。这个服务可以用各种方式搭建比如使用一些流行的模型推理框架。关键是它需要提供一个我们可以用HTTP请求去调用的接口。2.1 前端项目初始化如果你还没有Vue3项目用下面这条命令快速创建一个。这里我推荐使用Vite因为它速度快配置简单。npm create vuelatest my-ai-chat-project创建过程中你可以根据提示选择需要的特性。对于这个教程我们主要需要Vue Router可选如果你的应用有多个页面Pinia状态管理可选但推荐用于管理对话历史当然还有TypeScript能让我们的代码更健壮。项目创建好后进入目录并安装依赖cd my-ai-chat-project npm install2.2 安装必要的依赖我们需要安装几个关键的库来帮助我们处理网络请求和用户界面。npm install axios npm install vueuse/coreaxios: 一个非常好用的HTTP客户端我们将用它来调用后端的模型API。vueuse/core: 一个Vue3组合式API的工具集合里面有很多现成好用的函数比如我们待会儿会用到的useFetch或自定义的可组合函数来简化流式请求的处理。安装完成后你可以先运行npm run dev启动开发服务器确保项目基础环境没问题。3. 核心概念如何与AI模型对话在动手写组件之前咱们先花几分钟搞清楚前端是怎么和背后的AI模型“沟通”的。这样后面写代码时你就能明白每一行是在干什么。你可以把这个过程想象成点外卖你下单发送请求你在外卖APP前端里选好菜品输入对话内容点击下单。餐厅处理模型推理餐厅后端AI服务接到订单开始炒菜模型计算生成回答。有些菜快有些菜慢。骑手送餐接收响应骑手网络把做好的菜送给你。如果是大餐可能会分成几个盒子陆续送来流式响应。你开吃展示结果你收到菜开始享用前端渲染出AI的回答。在我们的技术实现里最关键的是第1步和第3步。发送请求我们需要告诉后端服务“嘿这是用户说的话prompt请让Phi-3-Mini模型回复一下。” 这个信息通常通过一个HTTP POST请求发送数据格式一般是JSON比如{“messages”: [{“role”: “user”, “content”: “你好”}]}。接收响应这里有两种方式普通响应模型一次性生成全部回答然后打包成一个完整的JSON返回给你。就像外卖骑手等所有菜都做好了一次性全部送上门。流式响应SSE模型每生成一小段文字比如一个词或一句话就立刻把这一段发送给前端。前端就像在看一个打字机实时打印出回答体验更好。这就像是骑手先送上来开胃菜然后汤最后主菜让你不用干等着。这篇教程里我们会重点实现流式响应因为它能带来更即时的交互体验。我们会用axios来发送请求并处理这种持续返回的数据流。4. 构建可复用的AI对话组件好了理论基础有了现在开始敲代码。我们要创建一个名叫AiChat.vue的组件它将是咱们AI对话功能的核心。4.1 组件基础结构与状态首先我们来定义这个组件需要哪些“状态”。在Vue3的script setup语法里我们可以用ref和reactive来声明。template div classai-chat-container !-- 对话消息列表 -- div classmessages div v-for(msg, index) in messages :keyindex :class[message, msg.role] div classavatar{{ msg.role user ? 你 : AI }}/div div classcontent{{ msg.content }}/div /div !-- 如果正在生成显示一个加载指示器 -- div v-ifisGenerating classmessage assistant div classavatarAI/div div classcontent typing-indicator{{ currentResponse }}/div /div /div !-- 输入区域 -- div classinput-area textarea v-modeluserInput placeholder输入你想问的问题... keydown.enter.exact.preventsendMessage rows3 /textarea button clicksendMessage :disabledisGenerating || !userInput.trim() {{ isGenerating ? 生成中... : 发送 }} /button /div /div /template script setup langts import { ref, reactive } from vue // 定义消息类型 interface ChatMessage { role: user | assistant content: string } // 状态定义 const userInput ref() // 用户输入框的内容 const messages reactiveChatMessage[]([ // 对话历史记录 { role: assistant, content: 你好我是基于Phi-3-Mini模型的助手有什么可以帮你的 } ]) const isGenerating ref(false) // 是否正在生成回复 const currentResponse ref() // 用于流式接收的当前回复内容 /script style scoped .ai-chat-container { display: flex; flex-direction: column; height: 600px; border: 1px solid #e0e0e0; border-radius: 8px; overflow: hidden; } .messages { flex: 1; overflow-y: auto; padding: 16px; } .message { display: flex; margin-bottom: 12px; } .message.user { justify-content: flex-end; } .avatar { width: 32px; height: 32px; border-radius: 50%; background-color: #f0f0f0; display: flex; align-items: center; justify-content: center; margin-right: 8px; flex-shrink: 0; } .message.user .avatar { background-color: #007bff; color: white; margin-right: 0; margin-left: 8px; } .content { padding: 10px 14px; border-radius: 18px; max-width: 70%; background-color: #f5f5f5; } .message.user .content { background-color: #007bff; color: white; } .input-area { display: flex; border-top: 1px solid #e0e0e0; padding: 12px; } .input-area textarea { flex: 1; padding: 10px; border: 1px solid #ccc; border-radius: 4px; resize: none; font-family: inherit; } .input-area button { margin-left: 12px; padding: 10px 20px; background-color: #007bff; color: white; border: none; border-radius: 4px; cursor: pointer; } .input-area button:disabled { background-color: #cccccc; cursor: not-allowed; } .typing-indicator::after { content: ▋; animation: blink 1s infinite; } keyframes blink { 0%, 100% { opacity: 1; } 50% { opacity: 0; } } /style上面这段代码搭建了组件的静态界面和基础状态。现在它还不能真正和AI对话但已经有了聊天窗口的样子。4.2 实现流式API调用接下来是最关键的一步实现sendMessage函数让它能调用后端API并以流式方式接收数据。我们将创建一个独立的工具函数来处理流式请求。在src/utils/目录下创建一个streamApi.ts文件// src/utils/streamApi.ts import axios, { AxiosResponse } from axios // 定义API配置接口根据你的后端调整 interface ApiConfig { baseURL: string endpoint: string model: string } // 默认配置你需要替换成自己后端的地址 const defaultConfig: ApiConfig { baseURL: http://localhost:8000, // 你的后端服务地址 endpoint: /v1/chat/completions, // 常见的开放API格式端点 model: phi-3-mini-128k-instruct // 模型名称根据后端实际部署调整 } /** * 发送消息并处理流式响应 * param messages 对话历史消息 * param onChunk 接收到数据块时的回调函数 * param config 可选的API配置 * returns 一个Promise用于处理完成或错误 */ export async function sendStreamMessage( messages: Array{ role: string; content: string }, onChunk: (chunk: string, isFinished: boolean) void, config: PartialApiConfig {} ): Promisevoid { const finalConfig { ...defaultConfig, ...config } const apiUrl ${finalConfig.baseURL}${finalConfig.endpoint} try { const response await axios({ method: post, url: apiUrl, data: { model: finalConfig.model, messages: messages, stream: true, // 关键开启流式输出 max_tokens: 512 // 控制生成的最大长度 }, responseType: stream // 关键指定响应类型为流 }) // 处理流式数据 const reader response.data.getReader() const decoder new TextDecoder(utf-8) let buffer while (true) { const { done, value } await reader.read() if (done) { onChunk(, true) // 流结束 break } buffer decoder.decode(value, { stream: true }) const lines buffer.split(\n) buffer lines.pop() || // 最后一行可能不完整放回缓冲区 for (const line of lines) { if (line.trim() ) continue if (line.startsWith(data: )) { const data line.slice(6) // 去掉 data: 前缀 if (data [DONE]) { onChunk(, true) return } try { const parsed JSON.parse(data) const chunkContent parsed.choices?.[0]?.delta?.content || if (chunkContent) { onChunk(chunkContent, false) } } catch (e) { console.error(解析流数据出错:, e, 原始数据:, data) } } } } } catch (error) { console.error(调用API失败:, error) throw error // 将错误抛给组件处理 } }这个工具函数做了几件事构造一个符合常见AI API格式的请求体并指定stream: true。使用axios以流的形式 (responseType: stream) 接收响应。不断读取数据流按\n分割并解析每一条data:开头的有效数据。将解析出的文本片段 (chunkContent) 通过回调函数onChunk实时传递出去。4.3 在组件中集成对话逻辑现在回到我们的AiChat.vue组件完善它的逻辑部分。script setup langts import { ref, reactive } from vue import { sendStreamMessage } from /utils/streamApi // 导入我们刚写的工具函数 // ... 之前的接口和状态定义保持不变 ... // 发送消息的核心函数 const sendMessage async () { const inputText userInput.value.trim() if (!inputText || isGenerating.value) return // 1. 将用户输入添加到消息列表 messages.push({ role: user, content: inputText }) userInput.value // 清空输入框 // 2. 准备开始接收AI回复 isGenerating.value true currentResponse.value // 清空当前响应 // 在消息列表中添加一个占位符消息用于显示流式内容 const assistantMessageIndex messages.length messages.push({ role: assistant, content: }) try { // 3. 调用流式API函数 await sendStreamMessage( // 发送整个对话历史给模型让它有上下文 messages.slice(0, -1).map(m ({ role: m.role, content: m.content })), // 定义接收到数据块时的回调 (chunk, isFinished) { if (chunk) { // 不断追加接收到的文本片段 currentResponse.value chunk // 实时更新消息列表中最后一条即AI的消息内容 messages[assistantMessageIndex].content currentResponse.value } if (isFinished) { // 生成结束 isGenerating.value false currentResponse.value // 可选在这里可以做一些结束后的处理比如保存对话历史 } } ) } catch (error) { // 错误处理 console.error(对话失败:, error) isGenerating.value false messages[assistantMessageIndex].content 抱歉对话出错了请稍后再试。 currentResponse.value } } /script至此一个具备基础对话功能的Vue3组件就完成了。你可以运行项目在输入框里打字然后看到AI模型一个字一个字地把回复“打”出来。5. 功能增强与最佳实践基础功能跑通了但要让这个组件真正好用、健壮我们还需要给它加点儿“料”。下面是一些实用的增强功能和最佳实践。5.1 对话历史管理目前对话历史只是存在组件的内存里页面一刷新就没了。我们可以用Pinia或Vuex来管理全局状态或者利用浏览器的本地存储 (localStorage) 做一个简单的持久化。这里展示一个使用Pinia的简单例子创建一个Store (src/stores/chat.ts)import { defineStore } from pinia import { ref } from vue export const useChatStore defineStore(chat, () { const chatHistory refArray{ role: string; content: string }([]) function addMessage(role: string, content: string) { chatHistory.value.push({ role, content }) // 可选保存到localStorage localStorage.setItem(ai_chat_history, JSON.stringify(chatHistory.value)) } function loadHistory() { const saved localStorage.getItem(ai_chat_history) if (saved) { chatHistory.value JSON.parse(saved) } } function clearHistory() { chatHistory.value [] localStorage.removeItem(ai_chat_history) } return { chatHistory, addMessage, loadHistory, clearHistory } })在组件中使用这个Storescript setup langts import { useChatStore } from /stores/chat const chatStore useChatStore() // 在组件挂载时加载历史记录 import { onMounted } from vue onMounted(() { chatStore.loadHistory() }) // 发送消息时将消息存入store // 在sendMessage函数中成功发送和接收后调用 chatStore.addMessage /script5.2 用户体验优化自动滚动当新消息出现或AI正在输出时自动将视图滚动到底部。可以使用Vue的nextTick配合DOM操作或者使用vueuse/core中的useScroll等函数。支持Markdown渲染如果AI的回复包含Markdown格式如代码块、列表我们可以集成一个Markdown渲染器如marked或markdown-it来美化显示。停止生成按钮在流式生成过程中提供一个按钮让用户可以中断请求。这需要在sendStreamMessage函数中支持取消令牌 (AbortController)。输入框自适应高度让输入框能随内容自动增高提升输入体验。5.3 错误处理与加载状态我们已经有了基本的错误处理。可以进一步优化网络错误提示区分网络错误、服务器错误、模型生成错误等给用户更友好的提示。重试机制对于某些临时性错误可以提供“重试”按钮。更美观的加载指示器除了文字可以使用旋转图标或骨架屏。5.4 性能与可维护性API配置抽离不要将API地址、模型名称等硬编码在组件或工具函数里。应该放在环境变量 (.env文件) 或专门的配置文件中。请求防抖与节流避免用户快速连续点击发送按钮导致意外问题。组件拆分如果AiChat.vue变得太大可以考虑将消息列表 (MessageList.vue)、输入框 (ChatInput.vue) 拆分为独立的子组件使结构更清晰。6. 总结走完这一趟你应该已经成功地在你的Vue3项目里集成进了一个能跟Phi-3-Mini-128K模型对话的智能组件了。从最开始的搭建环境、理解前后端交互原理到一步步写出处理流式响应的核心代码最后再给它加上状态管理、错误处理这些让应用更健壮的“配件”整个过程其实就是在解决一个个具体的小问题。这个组件现在是一个很好的起点。你可以根据自己项目的实际需求轻松地给它“化妆”或者“升级”。比如把它包装成一个浮动在页面角落的客服机器人或者集成到内容管理后台作为一个写作助手。关键是把后端API的地址和参数调对剩下的界面和交互就完全是你熟悉的Vue3开发领域了。AI能力的前端集成核心思路就是“请求-响应-展示”。流式响应让这个过程变得生动起来。希望这个指南能帮你打开思路不仅仅是集成一个对话功能更能把它作为一种模式应用到其他需要与AI服务交互的场景中去。代码都在上面了动手试试遇到问题多看看控制台的报错信息祝你开发顺利获取更多AI镜像想探索更多AI镜像和应用场景访问 CSDN星图镜像广场提供丰富的预置镜像覆盖大模型推理、图像生成、视频生成、模型微调等多个领域支持一键部署。