Phi-3-mini-128k-instruct前端应用基于Vue3构建智能对话组件最近在捣鼓一个内部工具需要集成一个轻量级的智能对话助手。找了一圈发现微软开源的Phi-3-mini-128k-instruct模型挺合适——推理速度快对硬件要求不高而且上下文长度足够。但问题来了怎么把它优雅地塞进我们的Vue3前端项目里呢直接在前端页面里写一堆调用API的代码不仅难看维护起来也头疼。更好的做法是把它封装成一个独立的、可复用的组件。这样一来哪个页面需要AI对话功能直接引入这个组件就行干净又省事。今天我就来分享一下怎么用Vue3的Composition API一步步把Phi-3-mini模型封装成一个功能完整的智能对话组件。我们会从最基础的模型调用开始一直讲到流式响应、状态管理这些进阶功能让你能快速给自己的Web应用加上AI能力。1. 项目环境与核心思路在开始写代码之前我们先来理清两个关键点项目需要什么环境以及我们打算怎么设计这个组件。1.1 环境准备与项目假设我们假设你已经有一个正在开发的Vue3项目。如果你还没有用Vite快速创建一个是最省事的方法npm create vuelatest my-ai-project创建过程中记得选择TypeScript和Pinia状态管理这对我们后续开发有帮助。项目创建好后安装我们需要的依赖npm install axios # 如果你打算使用更现代的fetch API或者需要处理流式响应也可以考虑安装 ohmyfetch 或直接使用原生的 fetch这里需要明确一点Phi-3-mini模型本身通常运行在后端服务器或云服务上。前端组件不负责运行模型而是通过HTTP请求与后端提供的API进行交互。因此你的后端需要已经部署好模型并暴露了一个可供调用的接口例如/api/chat/completions。本文的重点是前端如何消费这个接口。1.2 组件设计核心思路为什么要把AI对话功能做成组件想象一下如果你的应用里有三个不同的页面都需要对话功能难道你要在每个页面都重复写一遍发送请求、处理响应、展示消息的代码吗这显然不是好主意。我们的设计目标是构建一个SmartChat组件它应该像一块乐高积木拥有以下特点独立封装所有与AI对话相关的逻辑网络请求、状态管理、UI渲染都封装在组件内部。即插即用在任何Vue页面中通过SmartChat /标签就能直接使用。高度可配置可以轻松调整AI角色的设定、对话历史长度、UI样式等。体验流畅支持流式响应让用户看到AI一个字一个字“思考”出来的过程而不是干等。整个组件的架构可以简单理解为一个管理对话状态的Vue组件 一个处理AI通信的Composition函数。接下来我们就从最核心的通信层开始搭建。2. 构建模型通信层与AI模型对话本质上就是向一个特定的URL发送HTTP请求并处理返回的数据。我们将这部分网络逻辑单独抽离出来这样不仅能让组件更清晰也方便未来替换模型或调整API。2.1 创建基础的API调用函数首先我们在src/composables/目录下创建一个usePhi3Chat.ts文件。Composition API的use前缀是一个约定表示这是一个可复用的逻辑函数。// src/composables/usePhi3Chat.ts import { ref } from vue; // 定义消息的类型这是与后端API约定的数据结构 export interface ChatMessage { role: user | assistant | system; // 发送者角色 content: string; // 消息内容 } // 定义调用API的配置项类型 interface ChatCompletionOptions { apiUrl: string; // 后端API地址 model: string; // 使用的模型名称例如 phi-3-mini-128k-instruct temperature?: number; // 温度参数控制回答的随机性 maxTokens?: number; // 回答的最大长度 } /** * 封装Phi-3-mini模型对话能力的Composition函数 * param options 配置选项 * returns 提供聊天相关方法和状态的对象 */ export function usePhi3Chat(options: ChatCompletionOptions) { const { apiUrl, model, temperature 0.7, maxTokens 512 } options; // 核心状态对话消息列表 const messages refChatMessage[]([]); // 状态是否正在加载等待AI响应 const isLoading ref(false); // 状态错误信息 const error refstring | null(null); /** * 发送消息给AI并获取回复非流式一次性返回 * param userInput 用户输入的内容 */ const sendMessage async (userInput: string) { if (!userInput.trim() || isLoading.value) return; // 1. 更新状态和消息列表 isLoading.value true; error.value null; const userMessage: ChatMessage { role: user, content: userInput }; messages.value.push(userMessage); try { // 2. 构建请求体格式需与你的后端API匹配 const requestBody { model, messages: messages.value, // 发送整个历史记录让AI拥有上下文 temperature, max_tokens: maxTokens, stream: false, // 非流式 }; // 3. 发送请求 const response await fetch(apiUrl, { method: POST, headers: { Content-Type: application/json, }, body: JSON.stringify(requestBody), }); if (!response.ok) { throw new Error(API请求失败: ${response.status}); } const data await response.json(); // 4. 处理响应这里需要根据你的后端返回结构调整 // 假设返回格式为 { choices: [{ message: { content: string } }] } const aiResponse data.choices?.[0]?.message?.content || 抱歉我没有理解你的问题。; const aiMessage: ChatMessage { role: assistant, content: aiResponse }; messages.value.push(aiMessage); } catch (err) { // 5. 错误处理 error.value err instanceof Error ? err.message : 未知错误; console.error(对话请求失败:, err); // 可选移除刚才添加的用户消息或者添加一条错误提示消息 // messages.value.pop(); } finally { // 6. 无论成功失败都结束加载状态 isLoading.value false; } }; /** * 清空对话历史 */ const clearMessages () { messages.value []; error.value null; }; // 将内部状态和方法暴露给组件使用 return { messages, isLoading, error, sendMessage, clearMessages, }; }这个函数就是整个对话功能的大脑。它管理着对话记录、加载状态和错误信息并提供了一个sendMessage方法来触发一次完整的AI对话。现在我们可以基于这个大脑来构建UI身体了。3. 实现可复用的对话组件有了处理逻辑的usePhi3Chat创建UI组件就变得非常简单。组件只关心两件事把数据展示出来以及把用户的操作传递给逻辑层。3.1 构建基础的对话界面我们在src/components/目录下创建SmartChat.vue组件。!-- src/components/SmartChat.vue -- template div classsmart-chat-container !-- 标题区域 -- div classchat-header h3Phi-3 智能助手/h3 button clickhandleClear classclear-btn :disabledisLoading 清空对话 /button /div !-- 错误提示 -- div v-iferror classerror-message {{ error }} /div !-- 消息展示区域 -- div refmessagesContainer classmessages-container div v-for(msg, index) in messages :keyindex :class[message-bubble, role-${msg.role}] div classmessage-avatar {{ msg.role user ? 你 : AI }} /div div classmessage-content !-- 使用 v-html 渲染Markdown等格式时需注意XSS安全此处为简单文本 -- {{ msg.content }} /div /div !-- 加载指示器 -- div v-ifisLoading classthinking-indicator AI正在思考... /div /div !-- 输入区域 -- div classinput-area textarea v-modeluserInput keydown.enter.exact.preventhandleSend placeholder输入您的问题... :disabledisLoading rows3 / button clickhandleSend :disabled!userInput.trim() || isLoading classsend-btn 发送 /button /div /div /template script setup langts import { ref, nextTick, watch } from vue; import { usePhi3Chat, type ChatMessage } from /composables/usePhi3Chat; // 定义组件接收的属性使其可配置 interface Props { apiUrl: string; model?: string; systemPrompt?: string; // 系统提示词用于设定AI角色 } const props withDefaults(definePropsProps(), { model: phi-3-mini-128k-instruct, systemPrompt: 你是一个乐于助人的AI助手。, }); // 用户输入的临时状态 const userInput ref(); const messagesContainer refHTMLElement(); // 使用我们封装的逻辑 const { messages, isLoading, error, sendMessage, clearMessages } usePhi3Chat({ apiUrl: props.apiUrl, model: props.model, }); // 初始化系统消息 const initSystemMessage () { if (props.systemPrompt !messages.value.some(m m.role system)) { messages.value.push({ role: system, content: props.systemPrompt }); } }; initSystemMessage(); // 发送消息 const handleSend async () { const input userInput.value.trim(); if (!input) return; await sendMessage(input); userInput.value ; // 清空输入框 // 发送后滚动到消息底部 nextTick(() { if (messagesContainer.value) { messagesContainer.value.scrollTop messagesContainer.value.scrollHeight; } }); }; // 清空对话 const handleClear () { clearMessages(); initSystemMessage(); // 清空后重新添加系统提示 }; // 监听消息变化自动滚动到底部 watch(messages, () { nextTick(() { if (messagesContainer.value) { messagesContainer.value.scrollTop messagesContainer.value.scrollHeight; } }); }, { deep: true }); /script style scoped .smart-chat-container { display: flex; flex-direction: column; height: 600px; border: 1px solid #e4e7ed; border-radius: 8px; background-color: #fafafa; } .chat-header { display: flex; justify-content: space-between; align-items: center; padding: 16px; border-bottom: 1px solid #e4e7ed; background-color: #fff; } .messages-container { flex: 1; overflow-y: auto; padding: 16px; } .message-bubble { display: flex; margin-bottom: 12px; } .message-avatar { flex-shrink: 0; width: 32px; height: 32px; border-radius: 50%; display: flex; align-items: center; justify-content: center; margin-right: 12px; font-size: 12px; font-weight: bold; } .role-user .message-avatar { background-color: #409eff; color: white; } .role-assistant .message-avatar { background-color: #67c23a; color: white; } .role-system .message-avatar { background-color: #909399; color: white; } .message-content { flex: 1; padding: 10px 14px; border-radius: 6px; background-color: white; border: 1px solid #ebeef5; line-height: 1.5; white-space: pre-wrap; /* 保留换行符 */ } .role-user .message-content { background-color: #ecf5ff; border-color: #d9ecff; } .thinking-indicator { padding: 10px; color: #909399; font-style: italic; text-align: center; } .input-area { display: flex; padding: 16px; border-top: 1px solid #e4e7ed; background-color: #fff; } .input-area textarea { flex: 1; padding: 10px; border: 1px solid #dcdfe6; border-radius: 4px; resize: none; font-family: inherit; } .input-area textarea:focus { outline: none; border-color: #409eff; } .send-btn { margin-left: 12px; padding: 0 24px; background-color: #409eff; color: white; border: none; border-radius: 4px; cursor: pointer; } .send-btn:disabled { background-color: #a0cfff; cursor: not-allowed; } .clear-btn { padding: 6px 12px; background-color: #f56c6c; color: white; border: none; border-radius: 4px; cursor: pointer; font-size: 12px; } .clear-btn:disabled { background-color: #fab6b6; cursor: not-allowed; } .error-message { margin: 0 16px; padding: 10px; background-color: #fef0f0; color: #f56c6c; border-radius: 4px; font-size: 14px; } /style现在一个基础可用的智能对话组件就完成了。在任何Vue页面中你都可以像使用普通组件一样使用它template div h1我的AI应用/h1 SmartChat api-urlhttp://your-backend.com/api/chat / /div /template script setup import SmartChat from /components/SmartChat.vue; /script不过现在的体验还有点“卡顿”——用户发送问题后需要等待AI完全生成完所有文字才能看到结果。接下来我们给它加上“流式响应”的能力让回答像打字一样流出来。4. 进阶功能实现流式响应与优化流式响应Streaming Response是提升AI对话体验的关键。它允许服务器一边生成文本一边发送给前端前端则可以实时地将这些片段渲染出来用户无需等待全部完成就能看到部分内容。4.1 升级API函数以支持流式响应我们需要修改usePhi3Chat.ts中的sendMessage函数或者新增一个专门处理流式请求的函数。// 在 usePhi3Chat.ts 中新增一个函数 interface StreamCompletionOptions extends ChatCompletionOptions { onChunk: (chunk: string) void; // 收到数据块时的回调 onDone?: () void; // 流式结束时的回调 } /** * 发送消息并处理流式响应 */ const sendMessageStream async (userInput: string, options: OmitStreamCompletionOptions, apiUrl | model) { const { onChunk, onDone, temperature 0.7, maxTokens 512 } options; if (!userInput.trim() || isLoading.value) return; isLoading.value true; error.value null; const userMessage: ChatMessage { role: user, content: userInput }; messages.value.push(userMessage); // 为AI的回复预先创建一个占位消息 const assistantMessage: ChatMessage { role: assistant, content: }; messages.value.push(assistantMessage); const assistantMessageIndex messages.value.length - 1; try { const requestBody { model, messages: messages.value, temperature, max_tokens: maxTokens, stream: true, // 关键开启流式 }; const response await fetch(apiUrl, { method: POST, headers: { Content-Type: application/json }, body: JSON.stringify(requestBody), }); if (!response.ok) throw new Error(请求失败: ${response.status}); const reader response.body?.getReader(); if (!reader) throw new Error(无法读取响应流); const decoder new TextDecoder(utf-8); let accumulatedText ; while (true) { const { done, value } await reader.read(); if (done) break; const chunk decoder.decode(value); // 处理Server-Sent Events (SSE) 格式每行以 data: 开头 const lines chunk.split(\n).filter(line line.trim() ! ); for (const line of lines) { if (line.startsWith(data: )) { const data line.slice(6); // 去掉 data: 前缀 if (data [DONE]) { if (onDone) onDone(); continue; } try { const parsed JSON.parse(data); const content parsed.choices?.[0]?.delta?.content || ; if (content) { accumulatedText content; // 更新对应的assistant消息内容 messages.value[assistantMessageIndex].content accumulatedText; // 触发回调 onChunk(content); } } catch (e) { console.warn(解析流数据失败:, e, data); } } } } } catch (err) { error.value err instanceof Error ? err.message : 流式请求失败; console.error(流式对话失败:, err); // 如果出错可以移除未完成的AI消息或者标记为错误 // messages.value[assistantMessageIndex].content 请求出错: error.value; } finally { isLoading.value false; } }; // 在 return 语句中暴露这个新函数 return { // ... 其他状态和方法 sendMessageStream, };4.2 在组件中使用流式响应然后我们在SmartChat.vue组件中修改handleSend方法改用流式接口。script setup langts // ... 其他导入和定义 const handleSend async () { const input userInput.value.trim(); if (!input) return; userInput.value ; isLoading.value true; // 注意流式函数内部也会设置这里提前设置以禁用输入框 await sendMessageStream(input, { temperature: 0.7, onChunk: (chunk) { // 每收到一个数据块可以做一些额外处理比如播放音效 console.log(收到数据块:, chunk); }, onDone: () { console.log(流式响应结束); }, }); // 注意流式函数的 finally 会处理 isLoading.value false }; /script现在当用户发送消息时AI的回答就会像流水一样逐字逐句地显示在对话框里体验瞬间就上了一个档次。4.3 状态管理与错误边界增强对于更复杂的应用对话状态可能需要在多个组件间共享。这时我们可以引入Pinia来管理全局的对话状态。同时为组件添加更健壮的错误处理和加载状态反馈也很重要。使用Pinia管理对话会话 创建一个Store来管理多个独立的对话会话方便在不同页面或标签页中切换。增强错误处理 在组件中除了显示错误信息还可以提供重试按钮。在usePhi3Chat函数中可以记录更详细的错误日志并区分网络错误、API错误和解析错误。优化加载状态 除了简单的“正在思考...”文本可以添加一个优雅的加载动画如闪烁的光标让等待过程不那么枯燥。5. 在实际项目中的应用与扩展这个SmartChat组件就像一个乐高底座你可以根据实际需求在上面添加各种“插件”让它变得更强大。场景一客服机器人扩展在组件属性中增加initialMessages用于预加载常见问题FAQ。扩展集成“快捷回复”按钮用户点击即可发送预设问题。用法在客服页面直接引入组件并配置好系统的欢迎语和业务知识。场景二代码助手集成扩展在消息渲染时使用类似highlight.js的库来高亮显示消息中的代码块。扩展添加一个“复制代码”按钮到每个AI生成的代码块旁边。用法在开发工具页面使用系统提示词可以设为“你是一个专业的编程助手擅长Python和JavaScript...”。场景三多模态对话如果后端支持扩展改造ChatMessage接口使其content字段支持数组包含文本和图片URL。扩展在组件中增加图片上传功能并将图片转换为Base64或上传到图床后将URL放入消息中。扩展在消息展示区域增加对图片内容的渲染。用法构建一个能“看图说话”的应用用户上传图片AI描述图片内容或回答相关问题。6. 总结走完这一趟你会发现把像Phi-3-mini这样的AI模型集成到Vue3前端项目里并没有想象中那么复杂。核心思路就是分离关注点用Composition API函数 (usePhi3Chat) 处理所有底层的状态和网络逻辑再用一个Vue组件 (SmartChat) 负责漂亮的UI和用户交互。这样做的好处非常明显。首先你的业务页面会非常干净只需要关心在哪里放置这个聊天窗口。其次所有AI相关的代码都集中在一处无论是以后要换模型、改API还是加新功能比如支持上传文件都只需要改动这个组件和它背后的逻辑函数不会影响到应用的其他部分。今天分享的这个组件版本已经具备了核心功能但肯定还有可以打磨的地方。比如你可以考虑加入对话历史持久化存到LocalStorage或者做一个更漂亮的消息气泡动画。最重要的是这个模式给了你一个坚实的起点你可以基于它快速构建出各种各样有趣的AI增强型Web应用。获取更多AI镜像想探索更多AI镜像和应用场景访问 CSDN星图镜像广场提供丰富的预置镜像覆盖大模型推理、图像生成、视频生成、模型微调等多个领域支持一键部署。