langchain-learning-kit/docs/frontend-vue3/DESIGN_frontend-vue3.md

667 lines
14 KiB
Markdown
Raw Normal View History

# 前端项目架构设计文档 (DESIGN)
## 任务名称
frontend-vue3
## 整体架构图
```mermaid
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 模块**:
```javascript
// 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)
**职责**: 页面导航、路由守卫、参数传递
**路由配置**:
```javascript
// 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)
**职责**: 通用函数、常量定义、辅助工具
**工具模块**:
```javascript
// src/utils/request.js - 请求拦截器
// src/utils/message.js - 消息提示封装
// src/utils/validator.js - 表单验证规则
// src/utils/constants.js - 常量定义
```
## 核心组件设计
### Component 1: Models.vue (模型管理)
**功能模块**:
1. 模型列表展示(表格)
2. 类型筛选LLM / Embedding
3. 创建模型弹窗
4. 编辑/删除操作
5. 设置默认模型
**数据流**:
```mermaid
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: 提示成功
```
**核心状态**:
```javascript
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 (知识库管理)
**功能模块**:
1. 知识库列表
2. 创建知识库
3. 文档摄取(多文档输入)
4. 向量查询
5. 状态查看(文档数、索引状态)
6. 删除知识库
**文档摄取界面**:
- 支持动态添加多个文档
- 每个文档包含title, content, source, metadata
- 选择 embedding 模型
- 提交后显示成功(不追踪状态)
**查询界面**:
- 输入查询文本
- 选择 k 值(返回数量)
- 展示结果列表content + score
**核心状态**:
```javascript
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 (对话列表)
**功能模块**:
1. 会话列表展示
2. 创建新会话user_id + title
3. 跳转到聊天页
4. 删除会话
**核心状态**:
```javascript
const conversationList = ref([])
const createDialogVisible = ref(false)
const newConversation = ref({ user_id: 1, title: '' })
```
### Component 4: Chat.vue (聊天界面)
**功能模块**:
1. 消息历史展示(分页加载)
2. 发送消息
3. RAG 开关
4. 选择知识库和 LLM
5. 显示来源sources
**分页加载策略**:
- 初始加载: limit=50, offset=0
- "加载更多"按钮: offset += 50
- 消息逆序展示(最新在下)
**消息展示**:
```vue
<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>
```
**核心状态**:
```javascript
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 执行)
**功能模块**:
1. 任务输入
2. 选择知识库(可选)
3. 选择 LLM
4. 执行结果展示
5. 工具调用日志(自动展示)
**执行流程**:
```mermaid
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: 展示日志表格
```
**核心状态**:
```javascript
const taskInput = ref('')
const selectedKbId = ref(null)
const selectedLlm = ref(null)
const executionResult = ref(null)
const toolCallLogs = ref([])
const executing = ref(false)
```
## 接口契约定义
### Request 数据结构
**创建模型**:
```typescript
interface CreateModelRequest {
name: string
type: 'llm' | 'embedding'
config: Record<string, any>
is_default?: boolean
}
```
**文档摄取**:
```typescript
interface IngestRequest {
documents: Array<{
content: string
title?: string
source?: string
metadata?: Record<string, any>
}>
embedding_name: string
background?: boolean // 固定为 true
}
```
**发送消息**:
```typescript
interface ChatRequest {
user_input: string
kb_id?: number
llm_name?: string
use_rag?: boolean
}
```
**执行 Agent**:
```typescript
interface AgentExecuteRequest {
task: string
kb_id?: number
llm_name?: string
}
```
### Response 数据结构
**模型列表**:
```typescript
interface ModelResponse {
id: number
name: string
type: 'llm' | 'embedding'
config: Record<string, any>
is_default: boolean
status: string
created_at: string
}
```
**查询结果**:
```typescript
interface QueryResponse {
results: Array<{
content: string
metadata: Record<string, any>
score: number
}>
result_count: number
}
```
**消息响应**:
```typescript
interface MessageResponse {
message_id: number
conversation_id: number
role: 'user' | 'assistant'
content: string
metadata?: {
sources?: Array<{ title: string, source: string }>
model?: string
}
}
```
**Agent 日志**:
```typescript
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**:
```javascript
// 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**:
```javascript
// 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 处理**:
```javascript
// 组件内使用
const loading = ref(false)
const fetchData = async () => {
loading.value = true
try {
const data = await modelAPI.list()
// 处理数据
} catch (error) {
// 错误已被拦截器处理
} finally {
loading.value = false
}
}
```
## 数据流向图
### 对话流程数据流
```mermaid
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 执行数据流
```mermaid
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[用户查看详情]
```
## 布局设计
### 主布局结构
```vue
<!-- 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>
```
### 聊天页布局
```vue
<!-- 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>
```
## 环境配置
### 开发环境
```bash
# .env.development
VITE_API_BASE_URL=http://localhost:8000/api/v1
```
### 生产环境
```bash
# .env.production
VITE_API_BASE_URL=https://your-production-domain.com/api/v1
```
### Vite 配置
```javascript
// 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))
}
}
})
```
## 依赖管理
### 核心依赖
```json
{
"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"
}
}
```
## 设计原则总结
1. **单一职责**: 每个组件只负责一个功能模块
2. **API 封装**: 所有 HTTP 请求集中在 `src/api/`
3. **错误优先**: 统一拦截器 + 详细提示
4. **用户体验**: Loading 状态 + 操作反馈
5. **可维护性**: 清晰的目录结构 + 模块化设计
---
**架构设计完成,进入任务拆分阶段。**