Phi-3-Mini-128K网络应用开发:基于Vue3构建智能问答管理后台
Phi-3-Mini-128K网络应用开发:基于Vue3构建智能问答管理后台
1. 引言
想象一下,你部署了一个功能强大的Phi-3-Mini-128K模型,它能流畅地进行智能对话、解答问题。但很快你会发现,对话记录散落在各处,精心设计的Prompt模板难以复用,历史问答也无法有效追溯和分析。这就像拥有一个聪明的助手,却没有给他配备记事本和文件柜。
这正是我们今天要解决的问题。本文将带你一步步构建一个专门为Phi-3-Mini-128K模型设计的智能问答管理后台。这个后台系统将基于现代前端框架Vue 3,它不仅能让对话变得井井有条,还能显著提升你使用大模型的效率和体验。
我们将聚焦于如何用Vue 3的组合式API来优雅地管理应用状态,如何封装与后端模型API的交互,以及如何打造一个既美观又实用的响应式界面。无论你是前端开发者,还是对AI应用集成感兴趣的工程师,这篇文章都将提供一套可直接落地的解决方案。你会发现,为AI能力搭建一个管理门户,并没有想象中那么复杂。
2. 项目核心功能与架构设计
在动手写代码之前,我们先明确这个管理后台需要做什么,以及整个系统如何组织。这能帮助我们避免在开发过程中迷失方向。
2.1 核心功能模块
我们的管理后台主要围绕三个核心实体展开:问答记录、Prompt模板和对话历史。
- 问答记录管理:这是系统的核心。每次用户与Phi-3-Mini-128K模型的交互都会被记录下来。我们需要能够查看、搜索、筛选这些记录,例如按时间、按对话主题或按模型响应的质量进行过滤。理想情况下,还可以对重要的问答进行标记或分类。
- Prompt模板库:好的Prompt是获得优质AI回复的关键。这个模块允许用户创建、保存和复用常用的Prompt模板。比如,你可以有一个“代码审查”模板、一个“创意写作”模板或一个“数据总结”模板。点击即可应用,省去重复输入的麻烦。
- 对话历史与上下文:大模型对话的魅力在于连续性。这个功能需要展示完整的对话线程,清晰地分辨用户提问和模型回复。更重要的是,它需要支持从历史中选择某条记录,以其为上下文开启新一轮对话,让交流更有连贯性。
2.2 前端技术选型与架构思路
为了实现上述功能,并确保良好的开发体验和最终性能,我们选择以下技术栈:
- Vue 3:作为主要框架,我们看重其组合式API带来的逻辑组织灵活性。与Options API相比,组合式API让我们能更自由地将相关的数据、计算属性和方法聚合在一起,这对于管理复杂的对话状态和网络请求非常有利。
- Pinia:这是Vue官方的状态管理库。我们将使用Pinia来集中管理那些需要在多个组件间共享的数据,例如当前用户信息、全局的对话列表、Prompt模板集合等。这比通过组件层层传递属性要清晰和高效得多。
- Element Plus:一个基于Vue 3的桌面端UI组件库。它提供了丰富的、高质量的预制组件(如表格、表单、对话框、导航菜单),能让我们快速搭建出专业且一致的后台界面,把主要精力放在业务逻辑而非基础样式上。
- Axios:用于处理所有与后端API的通信。我们将用它来封装对Phi-3-Mini-128K模型服务端的调用,以及问答记录的增删改查请求。
整体的数据流向可以这样理解:用户在Vue组件中进行操作(如发送问题)→ 组件调用封装好的Axios服务 → 服务请求后端API → 后端与Phi-3-Mini模型交互并返回结果 → 响应数据更新Pinia状态 → 状态变化驱动组件视图更新。这是一个清晰的前后端分离架构。
3. 开发环境搭建与项目初始化
工欲善其事,必先利其器。让我们从创建一个干净的Vue 3项目开始。
3.1 创建Vue 3项目
首先,确保你的本地环境已经安装了Node.js。然后,打开终端,运行以下命令来创建一个新的Vue项目。这里我们使用Vite作为构建工具,因为它能提供极快的启动和热更新速度。
# 使用 npm npm create vue@latest phi3-management-dashboard # 或使用 yarn yarn create vue@latest phi3-management-dashboard在创建过程中,命令行会提示你进行一些选择。对于本项目,我们建议进行如下配置:
Add TypeScript?->Yes(类型检查有助于大型项目维护)Add JSX Support?->NoAdd Vue Router for Single Page Application?->Yes(用于页面路由)Add Pinia for state management?->Yes(状态管理,我们需要的)Add Vitest for Unit Testing?->No(可选,本文不涉及)Add an End-to-End Testing Solution?->NoAdd ESLint for code quality?->Yes(保持代码规范)
项目创建完成后,进入项目目录并安装依赖:
cd phi3-management-dashboard npm install3.2 安装必要的依赖库
除了创建项目时选择的,我们还需要手动安装UI库和HTTP客户端。
# 安装 Element Plus 及其图标库 npm install element-plus @element-plus/icons-vue # 安装 Axios npm install axios3.3 配置Element Plus和Axios
安装好后,需要进行简单配置。在src/main.ts文件中,引入并注册Element Plus。
import { createApp } from 'vue' import { createPinia } from 'pinia' import ElementPlus from 'element-plus' import 'element-plus/dist/index.css' import * as ElementPlusIconsVue from '@element-plus/icons-vue' import App from './App.vue' import router from './router' const app = createApp(App) // 注册所有Element Plus图标 for (const [key, component] of Object.entries(ElementPlusIconsVue)) { app.component(key, component) } app.use(createPinia()) app.use(router) app.use(ElementPlus) // 使用Element Plus app.mount('#app')接下来,在src目录下创建一个utils文件夹,并在其中创建request.ts文件,用于配置Axios实例。这样可以统一设置请求基础URL、超时时间和拦截器。
// src/utils/request.ts import axios from 'axios' // 创建axios实例,这里假设你的后端API运行在本地3000端口,Phi-3模型服务在另一个端口 const service = axios.create({ baseURL: 'http://localhost:3000/api', // 你的后端API地址 timeout: 30000, // 超时时间设为30秒,因为AI生成可能需要时间 }) // 请求拦截器 service.interceptors.request.use( (config) => { // 在发送请求前可以做一些事情,比如添加token // const token = localStorage.getItem('token') // if (token) { // config.headers.Authorization = `Bearer ${token}` // } return config }, (error) => { return Promise.reject(error) } ) // 响应拦截器 service.interceptors.response.use( (response) => { // 对响应数据做统一处理 return response.data }, (error) => { // 对响应错误做统一处理 console.error('API请求错误:', error) return Promise.reject(error) } ) export default service至此,项目的基础骨架和核心依赖就搭建好了。你可以运行npm run dev启动开发服务器,看到一个默认的Vue页面。
4. 核心功能实现
现在进入最有趣的部分——实现具体的功能。我们将按照功能模块来组织代码。
4.1 使用Pinia管理应用状态
状态管理是复杂应用的核心。我们在src/stores目录下创建几个Store来分别管理不同的数据。
首先,创建管理问答记录的Store:src/stores/chatStore.ts
import { defineStore } from 'pinia' import { ref } from 'vue' import type { ChatRecord } from '@/types/chat' // 假设我们定义了类型 export const useChatStore = defineStore('chat', () => { // 状态 const chatList = ref<ChatRecord[]>([]) // 所有问答记录 const currentChat = ref<ChatRecord | null>(null) // 当前选中的对话 const isLoading = ref(false) // 操作 const fetchChatList = async (params?: any) => { isLoading.value = true try { // 这里会调用我们后面封装的API服务 // const res = await chatApi.getList(params) // chatList.value = res.data // 模拟数据 chatList.value = [ { id: '1', question: '如何学习Vue 3?', answer: '建议从官方文档开始...', timestamp: new Date() }, { id: '2', question: 'Phi-3模型的特点?', answer: 'Phi-3-Mini是一个轻量级...', timestamp: new Date() }, ] } catch (error) { console.error('获取对话列表失败:', error) } finally { isLoading.value = false } } const addChatRecord = (record: ChatRecord) => { chatList.value.unshift(record) // 新的记录放在最前面 } const clearCurrentChat = () => { currentChat.value = null } return { chatList, currentChat, isLoading, fetchChatList, addChatRecord, clearCurrentChat, } })类似地,可以创建promptStore.ts来管理Prompt模板。使用Pinia的组合式Store写法,让相关的状态和逻辑紧密地组织在一起,非常清晰。
4.2 封装与Phi-3模型API的交互
为了保持代码的整洁和可维护性,我们将所有与后端API的通信封装在独立的服务模块中。创建src/services目录。
首先,创建模型交互服务:src/services/phi3Service.ts
import request from '@/utils/request' // 发送消息给Phi-3模型 export const sendMessageToPhi3 = async (prompt: string, history?: Array<{role: string, content: string}>) => { // 这里需要根据你实际部署的Phi-3模型API的接口规范来调整参数 const payload = { model: 'phi-3-mini-128k', // 或其他标识 messages: [ ...(history || []), { role: 'user', content: prompt } ], stream: false, // 假设为非流式响应 // 可以添加其他参数,如 temperature, max_tokens等 } try { const response = await request.post('/v1/chat/completions', payload) // 假设是OpenAI兼容的API return response.choices[0]?.message?.content || '' } catch (error) { console.error('调用Phi-3模型API失败:', error) throw new Error('模型服务暂时不可用') } }然后,创建管理后台数据的服务:src/services/chatApi.ts
import request from '@/utils/request' import type { ChatRecord } from '@/types/chat' // 获取问答记录列表 export const getChatList = (params: { page?: number; size?: number; keyword?: string }) => { return request.get('/chat/records', { params }) } // 创建新的问答记录 export const createChatRecord = (data: Partial<ChatRecord>) => { return request.post('/chat/records', data) } // 删除问答记录 export const deleteChatRecord = (id: string) => { return request.delete(`/chat/records/${id}`) }这样封装之后,在组件或Store中调用API就变得非常简洁和统一,也便于未来接口变更时集中修改。
4.3 构建响应式管理界面
界面是用户直接交互的地方。我们使用Element Plus组件来快速构建。这里以问答记录列表页面为例。
创建src/views/ChatListView.vue:
<template> <div class="chat-management-container"> <el-card class="box-card"> <template #header> <div class="card-header"> <span>问答记录管理</span> <div> <el-input v-model="searchKeyword" placeholder="搜索问题或答案..." style="width: 240px; margin-right: 10px;" @keyup.enter="handleSearch" > <template #prefix> <el-icon><Search /></el-icon> </template> </el-input> <el-button type="primary" :icon="Refresh" @click="refreshList">刷新</el-button> </div> </div> </template> <el-table v-loading="chatStore.isLoading" :data="filteredChatList" style="width: 100%"> <el-table-column prop="id" label="ID" width="80" /> <el-table-column prop="question" label="用户问题" min-width="200" show-overflow-tooltip /> <el-table-column prop="answer" label="模型回复" min-width="300" show-overflow-tooltip /> <el-table-column prop="timestamp" label="时间" width="180"> <template #default="scope"> {{ formatDate(scope.row.timestamp) }} </template> </el-table-column> <el-table-column label="操作" width="150" fixed="right"> <template #default="scope"> <el-button link type="primary" size="small" @click="viewDetail(scope.row)">查看</el-button> <el-button link type="danger" size="small" @click="handleDelete(scope.row.id)">删除</el-button> </template> </el-table-column> </el-table> <!-- 分页组件 --> <div class="pagination-container"> <el-pagination v-model:current-page="currentPage" v-model:page-size="pageSize" :page-sizes="[10, 20, 50]" :total="total" layout="total, sizes, prev, pager, next, jumper" @size-change="handleSizeChange" @current-change="handleCurrentChange" /> </div> </el-card> <!-- 查看详情的对话框 --> <el-dialog v-model="detailDialogVisible" title="对话详情" width="60%"> <div v-if="currentDetail"> <p><strong>问题:</strong>{{ currentDetail.question }}</p> <el-divider /> <p><strong>回复:</strong></p> <div class="answer-content">{{ currentDetail.answer }}</div> </div> </el-dialog> </div> </template> <script setup lang="ts"> import { ref, computed, onMounted } from 'vue' import { Search, Refresh } from '@element-plus/icons-vue' import { useChatStore } from '@/stores/chatStore' import { ElMessage, ElMessageBox } from 'element-plus' const chatStore = useChatStore() const searchKeyword = ref('') const currentPage = ref(1) const pageSize = ref(10) const total = ref(0) const detailDialogVisible = ref(false) const currentDetail = ref(null) // 计算属性:根据关键词过滤列表 const filteredChatList = computed(() => { if (!searchKeyword.value) return chatStore.chatList const keyword = searchKeyword.value.toLowerCase() return chatStore.chatList.filter(item => item.question.toLowerCase().includes(keyword) || item.answer.toLowerCase().includes(keyword) ) }) onMounted(() => { loadChatList() }) const loadChatList = () => { chatStore.fetchChatList({ page: currentPage.value, size: pageSize.value }).then(() => { // 这里可以根据后端返回的数据更新total // total.value = res.total total.value = chatStore.chatList.length // 模拟 }) } const handleSearch = () => { currentPage.value = 1 loadChatList() } const refreshList = () => { loadChatList() ElMessage.success('列表已刷新') } const handleSizeChange = (val: number) => { pageSize.value = val loadChatList() } const handleCurrentChange = (val: number) => { currentPage.value = val loadChatList() } const viewDetail = (record) => { currentDetail.value = record detailDialogVisible.value = true } const handleDelete = (id: string) => { ElMessageBox.confirm('确定要删除这条记录吗?', '提示', { confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning', }).then(() => { // 调用删除API // await deleteChatRecord(id) ElMessage.success('删除成功') loadChatList() // 重新加载列表 }).catch(() => { // 用户取消 }) } const formatDate = (date: Date) => { return new Date(date).toLocaleString() } </script> <style scoped> .card-header { display: flex; justify-content: space-between; align-items: center; } .pagination-container { margin-top: 20px; display: flex; justify-content: flex-end; } .answer-content { white-space: pre-wrap; background-color: #f5f7fa; padding: 12px; border-radius: 4px; max-height: 400px; overflow-y: auto; } </style>这个组件展示了如何使用Element Plus的el-card、el-table、el-pagination等组件快速搭建一个功能齐全的管理页面,包括搜索、分页、查看和删除操作。通过v-loading绑定Pinia Store中的加载状态,用户体验更加流畅。
5. 功能集成与优化
将各个模块连接起来,并添加一些提升体验的细节。
5.1 集成对话界面
管理后台除了管理历史,也应该能发起新的对话。我们可以在侧边栏或顶部导航添加一个“新建对话”入口,链接到一个新的对话页面ChatView.vue。
在这个页面中,主要包含:
- 一个消息列表区域,展示对话历史。
- 一个输入框区域,用于输入问题。
- 一个发送按钮,点击后调用我们封装好的
sendMessageToPhi3服务。 - 发送时,将用户问题立即添加到本地消息列表,并显示一个加载状态。
- 收到模型回复后,更新消息列表,并自动将本次完整的问答记录通过
chatApi.createChatRecord保存到后台数据库。
这个过程完美体现了Vue的响应式特性:用户操作触发方法调用 → 方法更新Pinia状态或调用服务 → 状态变化自动更新视图。
5.2 实现Prompt模板的快速应用
在对话输入框附近,可以添加一个下拉选择器或按钮,用于选择已保存的Prompt模板。当用户选择一个模板后,自动将模板内容填充到输入框中,用户可以在其基础上进行修改。这极大地提升了常用任务的效率。
5.3 响应式设计与用户体验
- 布局响应式:使用Element Plus的栅格系统 (
el-row,el-col) 或CSS Flex/Grid,确保管理界面在桌面、平板等不同屏幕尺寸下都能良好显示。 - 操作反馈:在所有网络请求(发送消息、保存、删除)时,使用Element Plus的
ElLoading或按钮加载状态给予用户即时反馈。成功或失败时,使用ElMessage弹出提示。 - 错误处理:在Axios拦截器和各个API调用中做好错误捕获和用户友好的提示,例如“网络连接失败”、“模型服务繁忙,请稍后再试”。
6. 总结
走完整个开发流程,你会发现基于Vue 3构建这样一个AI模型管理后台,思路是清晰且高效的。Vue 3的组合式API让我们能够以非常直观的方式组织复杂的对话逻辑和状态;Pinia提供了跨组件共享状态的优雅方案;而Element Plus则像是一套强大的积木,让我们能快速搭建出专业的前端界面。
这个项目不仅仅是一个管理工具,它更是一个将AI能力产品化的桥梁。通过这个后台,非技术用户也能方便地查阅历史、使用模板,从而更专注于利用Phi-3-Mini-128K模型的能力去创造价值。你可以在此基础上继续扩展,比如加入对话情感分析、回复质量评分、团队协作共享模板等功能。
开发过程中,关键是将业务逻辑(与模型交互、数据管理)与UI展示逻辑清晰地分离。多思考组件的复用性,例如一个MessageBubble组件既可以用于展示用户提问,也可以展示模型回复。最后,始终从用户体验出发,思考如何让操作更少、反馈更及时、信息更清晰。
获取更多AI镜像
想探索更多AI镜像和应用场景?访问 CSDN星图镜像广场,提供丰富的预置镜像,覆盖大模型推理、图像生成、视频生成、模型微调等多个领域,支持一键部署。
