Phi-3 Forest Laboratory 一键部署教程:基于Vue3的前端可视化界面快速搭建
Phi-3 Forest Laboratory 一键部署教程:基于Vue3的前端可视化界面快速搭建
你是不是也对那些炫酷的AI模型演示界面感到好奇?想自己动手,快速搭建一个既能调用强大模型,又拥有漂亮交互界面的应用?今天,我们就来搞定这件事。
我们将使用星图GPU平台,一键部署最近备受关注的轻量级模型——Phi-3 Forest Laboratory,然后用时下流行的Vue 3框架,亲手打造一个属于你自己的模型管理前端界面。整个过程就像搭积木,即使你前端经验不算特别丰富,跟着步骤走也能轻松完成。最终,你会得到一个可以输入问题、看到模型思考过程、并展示生成结果的可交互应用。
1. 环境准备与模型部署
在开始写前端代码之前,我们得先把“大脑”——也就是AI模型——给准备好。这一步我们选择在星图GPU平台完成,因为它提供了预置的模型镜像,能让我们省去复杂的环境配置和模型下载过程。
1.1 创建GPU实例
首先,你需要登录星图GPU平台。在控制台找到“创建实例”或类似的入口。
关键步骤在这里:
- 选择镜像:在镜像市场或选择镜像的环节,搜索“Phi-3 Forest Laboratory”。平台通常提供了预配置好所有依赖的镜像,直接选中它。
- 选择硬件:Phi-3模型家族以“小身材,大智慧”著称,对算力要求相对友好。根据你的需求,选择一款合适的GPU型号即可,比如NVIDIA的T4或更高级别的卡,都能获得不错的推理速度。
- 配置存储与网络:确保系统盘空间足够(建议50GB以上),网络配置通常保持默认即可,系统会分配一个公网IP用于访问。
- 创建并启动:确认配置,点击创建。几分钟后,你的实例就会处于“运行中”状态。
1.2 获取模型API访问地址
实例启动后,最重要的事情是找到模型的API服务地址。
- 进入实例详情页,找到“访问方式”或“服务地址”。通常,预置镜像会将模型服务启动在某个特定端口(例如
7860或8000)。 - 你会看到一个链接,格式类似于
http://<你的实例公网IP>:<端口号>。这个链接就是我们前端将要对话的后端地址,请记下来。 - 为了确保服务正常,你可以直接在浏览器中打开这个链接。如果能看到一个简单的API文档页面(如Swagger UI)或者模型的基本信息页面,就说明模型服务已经成功运行了。
至此,我们的“大脑”已经在线,随时准备接收指令。接下来,就是为它建造一个美观好用的“控制台”。
2. 初始化Vue 3前端项目
现在,我们把注意力转回本地开发环境。我们将使用Vite和Vue 3来快速搭建项目骨架,这是目前最流畅的Vue开发体验。
打开你的终端,执行以下命令来创建项目:
npm create vue@latest phi3-forest-lab-frontend执行命令后,你会看到一个交互式的选项菜单。对于本项目,我们做如下选择:
Add TypeScript?:Yes。TypeScript能提供更好的类型提示,减少错误。Add JSX Support?:No。Add Vue Router for Single Page Application?:No。我们这个单页应用暂时不需要路由。Add Pinia for state management?:Yes。状态管理能让我们的数据流动更清晰。Add Vitest for Unit Testing?:No。Add an End-to-End Testing Solution?:No。Add ESLint for code quality?:Yes。保持代码规范。
选择完成后,进入项目目录并安装依赖:
cd phi3-forest-lab-frontend npm install项目创建好后,我们还需要安装两个关键的库:
axios:用于向后端模型API发送HTTP请求。element-plus:一个基于Vue 3的桌面端UI组件库,能让我们快速构建出美观的界面。
npm install axios element-plus安装完成后,你可以运行npm run dev来启动开发服务器,在浏览器中访问http://localhost:5173就能看到默认的Vue欢迎页面了。
3. 构建模型交互核心组件
项目架子搭好了,我们来创建应用的核心——一个能与Phi-3模型对话的界面。我们将创建一个ModelChat.vue组件。
首先,在src/components目录下创建这个文件。这个组件主要包含三部分:输入区、对话记录展示区和控制区。
3.1 组件模板与样式
我们先来搭建基础的HTML结构和样式。这里会用到Element Plus的输入框、按钮和卡片组件。
<!-- src/components/ModelChat.vue --> <template> <div class="model-chat-container"> <el-card class="chat-box"> <template #header> <div class="card-header"> <span>🤖 Phi-3 Forest Laboratory 对话</span> <el-tag type="success">在线</el-tag> </div> </template> <!-- 对话历史展示区域 --> <div class="message-list" ref="messageListRef"> <div v-for="(msg, index) in messages" :key="index" :class="['message-item', msg.role]"> <div class="avatar">{{ msg.role === 'user' ? '你' : 'AI' }}</div> <div class="bubble"> <div v-if="msg.role === 'assistant' && msg.isStreaming" class="streaming-indicator"> <span class="dot"></span><span class="dot"></span><span class="dot"></span> 思考中... </div> <div v-else class="content">{{ msg.content }}</div> </div> </div> </div> <!-- 输入与控制区域 --> <div class="input-area"> <el-input v-model="userInput" type="textarea" :rows="3" placeholder="请输入你想问Phi-3模型的问题..." @keydown.enter.exact.prevent="handleSend" :disabled="isLoading" ></el-input> <div class="action-bar"> <el-button type="primary" :loading="isLoading" @click="handleSend" :disabled="!userInput.trim()"> 发送 </el-button> <el-button @click="clearHistory">清空记录</el-button> <div class="config"> <el-tooltip content="最大生成长度" placement="top"> <el-input-number v-model="config.maxTokens" :min="10" :max="2000" :step="50" size="small" /> </el-tooltip> </div> </div> </div> </el-card> </div> </template> <script setup lang="ts"> // 逻辑部分将在下一步编写 </script> <style scoped> .model-chat-container { max-width: 800px; margin: 20px auto; padding: 20px; } .chat-box { height: 70vh; display: flex; flex-direction: column; } .card-header { display: flex; justify-content: space-between; align-items: center; } .message-list { flex: 1; overflow-y: auto; padding: 15px; border: 1px solid #eee; border-radius: 8px; margin-bottom: 20px; background-color: #fafafa; } .message-item { display: flex; margin-bottom: 15px; } .message-item.user { flex-direction: row-reverse; } .message-item.user .bubble { background-color: #95ec69; } .message-item.assistant .bubble { background-color: #ffffff; border: 1px solid #e4e7ed; } .avatar { width: 36px; height: 36px; border-radius: 50%; background-color: #409eff; color: white; display: flex; align-items: center; justify-content: center; margin: 0 10px; flex-shrink: 0; } .bubble { max-width: 70%; padding: 10px 15px; border-radius: 18px; word-break: break-word; } .input-area { margin-top: auto; } .action-bar { display: flex; justify-content: space-between; align-items: center; margin-top: 10px; } .config { display: flex; gap: 10px; } .streaming-indicator { color: #909399; font-style: italic; } .dot { display: inline-block; width: 8px; height: 8px; border-radius: 50%; background-color: #909399; margin: 0 2px; animation: blink 1.4s infinite both; } .dot:nth-child(2) { animation-delay: 0.2s; } .dot:nth-child(3) { animation-delay: 0.4s; } @keyframes blink { 0%, 80%, 100% { opacity: 0; } 40% { opacity: 1; } } </style>3.2 组件逻辑与API调用
现在,我们来填充组件的逻辑。我们需要管理对话历史、用户输入、加载状态,并实现发送消息和调用后端API的功能。
<script setup lang="ts"> import { ref, reactive, nextTick } from 'vue' import axios from 'axios' import type { AxiosResponse } from 'axios' // 定义消息类型 interface ChatMessage { role: 'user' | 'assistant' content: string isStreaming?: boolean } // 响应式数据 const userInput = ref('') const isLoading = ref(false) const messageListRef = ref<HTMLElement>() // 用于自动滚动 const messages = reactive<ChatMessage[]>([ { role: 'assistant', content: '你好!我是Phi-3 Forest Laboratory。有什么可以帮你的吗?' } ]) // 模型配置 const config = reactive({ apiBaseUrl: import.meta.env.VITE_MODEL_API_URL || 'http://你的实例IP:端口', // 从环境变量读取 maxTokens: 500, temperature: 0.7 }) // 发送消息 const handleSend = async () => { const question = userInput.value.trim() if (!question || isLoading.value) return // 添加用户消息 messages.push({ role: 'user', content: question }) userInput.value = '' // 添加一个“思考中”的AI消息占位符 const assistantMessageIndex = messages.length messages.push({ role: 'assistant', content: '', isStreaming: true }) isLoading.value = true scrollToBottom() try { // 构造请求体,具体格式需根据Phi-3 Forest Laboratory的API文档调整 const requestBody = { messages: messages.slice(0, -1).map(m => ({ role: m.role, content: m.content })), // 发送历史消息 max_tokens: config.maxTokens, temperature: config.temperature, stream: false // 这里先使用非流式,更简单稳定 } const response: AxiosResponse<{ choices: Array<{ message: { content: string } }> }> = await axios.post( `${config.apiBaseUrl}/v1/chat/completions`, // API端点路径可能不同,请根据实际文档调整 requestBody, { headers: { 'Content-Type': 'application/json' } } ) // 更新AI消息内容,移除“思考中”状态 const aiResponse = response.data.choices[0]?.message?.content || '(模型未返回有效内容)' messages[assistantMessageIndex] = { role: 'assistant', content: aiResponse, isStreaming: false } } catch (error) { console.error('调用模型API失败:', error) messages[assistantMessageIndex] = { role: 'assistant', content: `抱歉,请求出错:${error instanceof Error ? error.message : '未知错误'}`, isStreaming: false } } finally { isLoading.value = false scrollToBottom() } } // 清空对话历史 const clearHistory = () => { messages.splice(0, messages.length) messages.push({ role: 'assistant', content: '对话历史已清空。我们可以重新开始了。' }) } // 自动滚动到最新消息 const scrollToBottom = () => { nextTick(() => { if (messageListRef.value) { messageListRef.value.scrollTop = messageListRef.value.scrollHeight } }) } </script>关键点说明:
- API地址:
config.apiBaseUrl需要替换成你在第一步获取到的模型服务地址。最佳实践是将其放在项目根目录的.env.development和.env.production环境变量文件中,例如VITE_MODEL_API_URL=http://your-instance-ip:port。 - 请求格式:示例中的请求体格式是模仿OpenAI ChatCompletion API的。你需要根据Phi-3 Forest Laboratory镜像实际提供的API文档进行调整。可能的端点路径是
/chat,/generate或/api/v1/generate等,请求体字段也可能不同。 - 错误处理:我们简单捕获了错误并展示给用户,在实际生产中可能需要更细致的处理。
4. 集成组件与界面优化
核心组件已经完成,现在我们需要把它放到主页面中,并做一些优化,比如配置环境变量。
4.1 在主页面中引入组件
修改src/App.vue文件,使其简洁地承载我们的聊天组件。
<!-- src/App.vue --> <template> <div id="app"> <header class="app-header"> <h1>🌲 Phi-3 Forest Laboratory 交互实验室</h1> <p class="subtitle">轻量级模型,强大的对话体验 | 基于Vue 3构建</p> </header> <main class="app-main"> <ModelChat /> </main> <footer class="app-footer"> <p>模型服务运行于星图GPU平台 | 前端界面使用 Vue 3 + Element Plus 开发</p> </footer> </div> </template> <script setup lang="ts"> import ModelChat from './components/ModelChat.vue' </script> <style> * { margin: 0; padding: 0; box-sizing: border-box; } body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, sans-serif; background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%); min-height: 100vh; } #app { min-height: 100vh; display: flex; flex-direction: column; } .app-header { text-align: center; padding: 2rem 1rem; background-color: rgba(255, 255, 255, 0.9); box-shadow: 0 2px 12px rgba(0, 0, 0, 0.1); } .app-header h1 { color: #2c3e50; margin-bottom: 0.5rem; } .subtitle { color: #7f8c8d; font-size: 1.1rem; } .app-main { flex: 1; padding: 1rem; } .app-footer { text-align: center; padding: 1rem; color: #95a5a6; font-size: 0.9rem; background-color: rgba(255, 255, 255, 0.8); } </style>4.2 配置Element Plus与环境变量
为了让Element Plus组件库正常工作,我们需要在src/main.ts中进行全局引入。
// src/main.ts import { createApp } from 'vue' import App from './App.vue' // 引入Element Plus及其样式 import ElementPlus from 'element-plus' import 'element-plus/dist/index.css' const app = createApp(App) app.use(ElementPlus) app.mount('#app')接下来,在项目根目录创建.env.development文件,用于设置开发环境的后端API地址。
# .env.development VITE_MODEL_API_URL=http://你的实例公网IP:端口号请务必将你的实例公网IP:端口号替换为你在第一步记下的实际地址。
4.3 运行与测试
现在,所有部分都已就绪。在终端中运行:
npm run dev打开浏览器访问http://localhost:5173,你应该能看到一个完整的聊天界面。尝试在输入框中提问,比如“用简单的语言解释一下量子计算”,然后点击发送。如果一切配置正确,前端会将问题发送到你部署在星图GPU上的Phi-3模型,并将模型的回复展示在界面上。
5. 总结
走完这个教程,你会发现,将一个强大的AI模型与一个现代化的前端界面连接起来,并没有想象中那么复杂。核心其实就是两步:让模型服务跑起来,以及构建一个能与之对话的网页。
星图GPU平台的一键部署功能,为我们解决了模型环境这个最棘手的部分。而Vue 3的响应式特性和丰富的生态(如Element Plus),让我们能像拼装乐高一样,快速搭建出交互流畅、界面美观的前端应用。你在这个教程里完成的,不仅仅是一个演示界面,更是一个可以扩展的脚手架。你可以在此基础上,增加更多功能,比如对话历史保存、多种模型参数调节、支持流式输出以获得更快的响应体验,或者将UI打造得更加个性化。
动手尝试的过程,就是最好的学习。不妨现在就基于这个基础,添加一个“模型切换”功能,或者试试用不同的CSS框架改变一下界面风格。当你看到自己的想法通过代码变成现实,并与AI模型产生互动时,那种感觉是非常棒的。
获取更多AI镜像
想探索更多AI镜像和应用场景?访问 CSDN星图镜像广场,提供丰富的预置镜像,覆盖大模型推理、图像生成、视频生成、模型微调等多个领域,支持一键部署。
