14 KiB
14 KiB
前端项目架构设计文档 (DESIGN)
任务名称
frontend-vue3
整体架构图
graph TB
subgraph "前端应用 (Vue3)"
A[App.vue] --> B[Vue Router]
B --> C1[Models 模型管理]
B --> C2[KnowledgeBase 知识库]
B --> C3[Conversations 对话列表]
B --> C4[Chat 聊天页]
B --> C5[Agent 执行页]
C1 --> D1[API: models.js]
C2 --> D2[API: kb.js]
C3 --> D3[API: conversations.js]
C4 --> D3
C5 --> D4[API: agent.js]
D1 --> E[Axios Client]
D2 --> E
D3 --> E
D4 --> E
end
E -->|HTTP Request| F[FastAPI Backend<br/>:8000/api/v1]
subgraph "公共层"
G[Error Handler<br/>错误拦截器]
H[Loading State<br/>加载状态]
I[Message Tips<br/>消息提示]
end
E --> G
E --> H
E --> I
style A fill:#e1f5fe
style E fill:#fff3e0
style F fill:#f3e5f5
style G fill:#ffebee
style H fill:#e8f5e9
style I fill:#fff9c4
系统分层设计
1. 展示层 (Views)
职责: 页面渲染、用户交互、状态展示
核心页面:
Models.vue: 模型管理页KnowledgeBase.vue: 知识库管理页Conversations.vue: 对话列表页Chat.vue: 聊天界面Agent.vue: Agent 执行页
页面设计规范:
- 使用 Composition API (
<script setup>) - 表单使用 Element Plus Form 组件
- 数据表格使用 ElTable
- 弹窗使用 ElDialog
2. 服务层 (API)
职责: 封装 HTTP 请求、数据转换、错误处理
API 模块:
// src/api/client.js - Axios 实例
axios.create({
baseURL: import.meta.env.VITE_API_BASE_URL,
timeout: 30000,
headers: { 'Content-Type': 'application/json' }
})
// src/api/models.js - 模型管理
export const modelAPI = {
list(type?: string), // GET /models
create(data), // POST /models
getById(id), // GET /models/:id
update(id, data), // PATCH /models/:id
delete(id) // DELETE /models/:id
}
// src/api/kb.js - 知识库
export const kbAPI = {
list(), // GET /kb
create(data), // POST /kb
getById(id), // GET /kb/:id
delete(id), // DELETE /kb/:id
ingest(id, data), // POST /kb/:id/ingest
query(id, data), // POST /kb/:id/query
getStatus(id) // GET /kb/:id/status
}
// src/api/conversations.js - 对话
export const conversationAPI = {
create(data), // POST /conversations
getById(id), // GET /conversations/:id
delete(id), // DELETE /conversations/:id
getMessages(id, params), // GET /conversations/:id/messages
chat(id, data) // POST /conversations/:id/chat
}
// src/api/agent.js - Agent
export const agentAPI = {
execute(data), // POST /agent/execute
getLogs(agentId, params) // GET /agent/logs/:agent_id
}
3. 路由层 (Router)
职责: 页面导航、路由守卫、参数传递
路由配置:
// src/router/index.js
const routes = [
{
path: '/',
redirect: '/models'
},
{
path: '/models',
name: 'Models',
component: () => import('@/views/Models.vue')
},
{
path: '/knowledge-base',
name: 'KnowledgeBase',
component: () => import('@/views/KnowledgeBase.vue')
},
{
path: '/conversations',
name: 'Conversations',
component: () => import('@/views/Conversations.vue')
},
{
path: '/conversations/:id/chat',
name: 'Chat',
component: () => import('@/views/Chat.vue'),
props: true
},
{
path: '/agent',
name: 'Agent',
component: () => import('@/views/Agent.vue')
}
]
4. 工具层 (Utils)
职责: 通用函数、常量定义、辅助工具
工具模块:
// src/utils/request.js - 请求拦截器
// src/utils/message.js - 消息提示封装
// src/utils/validator.js - 表单验证规则
// src/utils/constants.js - 常量定义
核心组件设计
Component 1: Models.vue (模型管理)
功能模块:
- 模型列表展示(表格)
- 类型筛选(LLM / Embedding)
- 创建模型弹窗
- 编辑/删除操作
- 设置默认模型
数据流:
sequenceDiagram
participant U as User
participant V as Models.vue
participant API as modelAPI
participant B as Backend
U->>V: 访问页面
V->>API: modelAPI.list()
API->>B: GET /models
B-->>API: 模型列表
API-->>V: 渲染表格
U->>V: 点击"创建模型"
V->>V: 打开表单弹窗
U->>V: 填写表单并提交
V->>API: modelAPI.create(data)
API->>B: POST /models
B-->>API: 创建成功
API-->>V: 更新列表
V->>U: 提示成功
核心状态:
const modelList = ref([])
const loading = ref(false)
const dialogVisible = ref(false)
const formData = ref({
name: '',
type: 'llm',
config: {},
is_default: false
})
const typeFilter = ref('')
Component 2: KnowledgeBase.vue (知识库管理)
功能模块:
- 知识库列表
- 创建知识库
- 文档摄取(多文档输入)
- 向量查询
- 状态查看(文档数、索引状态)
- 删除知识库
文档摄取界面:
- 支持动态添加多个文档
- 每个文档包含:title, content, source, metadata
- 选择 embedding 模型
- 提交后显示成功(不追踪状态)
查询界面:
- 输入查询文本
- 选择 k 值(返回数量)
- 展示结果列表(content + score)
核心状态:
const kbList = ref([])
const currentKb = ref(null)
const ingestDialogVisible = ref(false)
const queryDialogVisible = ref(false)
const documents = ref([{ title: '', content: '', source: '', metadata: {} }])
const queryForm = ref({ query: '', k: 3 })
const queryResults = ref([])
Component 3: Conversations.vue (对话列表)
功能模块:
- 会话列表展示
- 创建新会话(user_id + title)
- 跳转到聊天页
- 删除会话
核心状态:
const conversationList = ref([])
const createDialogVisible = ref(false)
const newConversation = ref({ user_id: 1, title: '' })
Component 4: Chat.vue (聊天界面)
功能模块:
- 消息历史展示(分页加载)
- 发送消息
- RAG 开关
- 选择知识库和 LLM
- 显示来源(sources)
分页加载策略:
- 初始加载: limit=50, offset=0
- "加载更多"按钮: offset += 50
- 消息逆序展示(最新在下)
消息展示:
<div class="message-list">
<div v-for="msg in messages" :key="msg.id" :class="msg.role">
<div class="content">{{ msg.content }}</div>
<div v-if="msg.msg_metadata?.sources" class="sources">
来源: {{ msg.msg_metadata.sources }}
</div>
</div>
</div>
核心状态:
const conversationId = ref(route.params.id)
const messages = ref([])
const inputText = ref('')
const useRag = ref(true)
const selectedKbId = ref(null)
const selectedLlm = ref(null)
const pagination = ref({ limit: 50, offset: 0, hasMore: true })
Component 5: Agent.vue (Agent 执行)
功能模块:
- 任务输入
- 选择知识库(可选)
- 选择 LLM
- 执行结果展示
- 工具调用日志(自动展示)
执行流程:
sequenceDiagram
participant U as User
participant V as Agent.vue
participant API as agentAPI
participant B as Backend
U->>V: 输入任务并提交
V->>API: agentAPI.execute(data)
API->>B: POST /agent/execute
B-->>API: { agent_id, output, success }
API-->>V: 展示结果
V->>API: agentAPI.getLogs(agent_id)
API->>B: GET /agent/logs/:agent_id
B-->>API: { tool_calls: [...] }
API-->>V: 展示日志表格
核心状态:
const taskInput = ref('')
const selectedKbId = ref(null)
const selectedLlm = ref(null)
const executionResult = ref(null)
const toolCallLogs = ref([])
const executing = ref(false)
接口契约定义
Request 数据结构
创建模型:
interface CreateModelRequest {
name: string
type: 'llm' | 'embedding'
config: Record<string, any>
is_default?: boolean
}
文档摄取:
interface IngestRequest {
documents: Array<{
content: string
title?: string
source?: string
metadata?: Record<string, any>
}>
embedding_name: string
background?: boolean // 固定为 true
}
发送消息:
interface ChatRequest {
user_input: string
kb_id?: number
llm_name?: string
use_rag?: boolean
}
执行 Agent:
interface AgentExecuteRequest {
task: string
kb_id?: number
llm_name?: string
}
Response 数据结构
模型列表:
interface ModelResponse {
id: number
name: string
type: 'llm' | 'embedding'
config: Record<string, any>
is_default: boolean
status: string
created_at: string
}
查询结果:
interface QueryResponse {
results: Array<{
content: string
metadata: Record<string, any>
score: number
}>
result_count: number
}
消息响应:
interface MessageResponse {
message_id: number
conversation_id: number
role: 'user' | 'assistant'
content: string
metadata?: {
sources?: Array<{ title: string, source: string }>
model?: string
}
}
Agent 日志:
interface ToolCallLog {
id: number
agent_id: string
tool_name: string
call_input: Record<string, any>
call_output: Record<string, any>
created_at: string
}
异常处理策略
HTTP 错误拦截
Axios Response Interceptor:
// src/api/client.js
axios.interceptors.response.use(
response => response.data,
error => {
const { response } = error
if (!response) {
ElMessage.error('网络连接失败,请检查网络')
return Promise.reject(error)
}
const { status, data } = response
const message = data?.detail || '请求失败'
switch (status) {
case 400:
ElMessage.error(`请求错误: ${message}`)
break
case 404:
ElMessage.error(`资源不存在: ${message}`)
break
case 500:
ElMessage.error(`服务器错误: ${message}`)
break
default:
ElMessage.error(`错误 ${status}: ${message}`)
}
return Promise.reject(error)
}
)
表单验证规则
Element Plus Rules:
// src/utils/validator.js
export const modelRules = {
name: [
{ required: true, message: '请输入模型名称', trigger: 'blur' },
{ min: 2, max: 50, message: '长度在 2 到 50 个字符', trigger: 'blur' }
],
type: [
{ required: true, message: '请选择模型类型', trigger: 'change' }
]
}
export const kbRules = {
name: [
{ required: true, message: '请输入知识库名称', trigger: 'blur' }
]
}
export const chatRules = {
user_input: [
{ required: true, message: '请输入消息内容', trigger: 'blur' }
]
}
加载状态管理
统一 Loading 处理:
// 组件内使用
const loading = ref(false)
const fetchData = async () => {
loading.value = true
try {
const data = await modelAPI.list()
// 处理数据
} catch (error) {
// 错误已被拦截器处理
} finally {
loading.value = false
}
}
数据流向图
对话流程数据流
flowchart LR
A[用户输入消息] --> B{是否启用RAG?}
B -->|是| C[选择知识库]
B -->|否| D[直接发送]
C --> E[构造请求]
D --> E
E --> F[POST /conversations/:id/chat]
F --> G{请求成功?}
G -->|是| H[追加消息到列表]
G -->|否| I[显示错误]
H --> J[滚动到底部]
I --> K[用户重试]
Agent 执行数据流
flowchart TB
A[输入任务] --> B[选择配置]
B --> C[POST /agent/execute]
C --> D{执行成功?}
D -->|是| E[显示输出]
D -->|否| F[显示错误]
E --> G[GET /agent/logs/:agent_id]
G --> H[展示工具调用日志]
H --> I[用户查看详情]
布局设计
主布局结构
<!-- App.vue -->
<el-container>
<el-aside width="200px">
<el-menu router>
<el-menu-item index="/models">模型管理</el-menu-item>
<el-menu-item index="/knowledge-base">知识库</el-menu-item>
<el-menu-item index="/conversations">对话</el-menu-item>
<el-menu-item index="/agent">Agent</el-menu-item>
</el-menu>
</el-aside>
<el-main>
<router-view />
</el-main>
</el-container>
聊天页布局
<!-- Chat.vue -->
<el-container direction="vertical">
<el-header>
<div class="chat-config">
<el-switch v-model="useRag" label="启用RAG" />
<el-select v-model="selectedKbId" v-if="useRag">...</el-select>
<el-select v-model="selectedLlm">...</el-select>
</div>
</el-header>
<el-main class="message-area">
<el-button @click="loadMore" v-if="hasMore">加载更多</el-button>
<div v-for="msg in messages" :key="msg.id">...</div>
</el-main>
<el-footer>
<el-input v-model="inputText" @keyup.enter="sendMessage" />
<el-button @click="sendMessage">发送</el-button>
</el-footer>
</el-container>
环境配置
开发环境
# .env.development
VITE_API_BASE_URL=http://localhost:8000/api/v1
生产环境
# .env.production
VITE_API_BASE_URL=https://your-production-domain.com/api/v1
Vite 配置
// vite.config.js
export default defineConfig({
plugins: [vue()],
server: {
port: 5173,
proxy: {
'/api': {
target: 'http://localhost:8000',
changeOrigin: true
}
}
},
resolve: {
alias: {
'@': fileURLToPath(new URL('./src', import.meta.url))
}
}
})
依赖管理
核心依赖
{
"dependencies": {
"vue": "^3.4.0",
"vue-router": "^4.2.5",
"axios": "^1.6.2",
"element-plus": "^2.5.0",
"@element-plus/icons-vue": "^2.3.1"
},
"devDependencies": {
"@vitejs/plugin-vue": "^5.0.0",
"vite": "^5.0.0"
}
}
设计原则总结
- 单一职责: 每个组件只负责一个功能模块
- API 封装: 所有 HTTP 请求集中在
src/api/ - 错误优先: 统一拦截器 + 详细提示
- 用户体验: Loading 状态 + 操作反馈
- 可维护性: 清晰的目录结构 + 模块化设计
架构设计完成,进入任务拆分阶段。