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

14 KiB
Raw Blame History

前端项目架构设计文档 (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 (模型管理)

功能模块:

  1. 模型列表展示(表格)
  2. 类型筛选LLM / Embedding
  3. 创建模型弹窗
  4. 编辑/删除操作
  5. 设置默认模型

数据流:

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 (知识库管理)

功能模块:

  1. 知识库列表
  2. 创建知识库
  3. 文档摄取(多文档输入)
  4. 向量查询
  5. 状态查看(文档数、索引状态)
  6. 删除知识库

文档摄取界面:

  • 支持动态添加多个文档
  • 每个文档包含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 (对话列表)

功能模块:

  1. 会话列表展示
  2. 创建新会话user_id + title
  3. 跳转到聊天页
  4. 删除会话

核心状态:

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
  • 消息逆序展示(最新在下)

消息展示:

<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 执行)

功能模块:

  1. 任务输入
  2. 选择知识库(可选)
  3. 选择 LLM
  4. 执行结果展示
  5. 工具调用日志(自动展示)

执行流程:

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"
  }
}

设计原则总结

  1. 单一职责: 每个组件只负责一个功能模块
  2. API 封装: 所有 HTTP 请求集中在 src/api/
  3. 错误优先: 统一拦截器 + 详细提示
  4. 用户体验: Loading 状态 + 操作反馈
  5. 可维护性: 清晰的目录结构 + 模块化设计

架构设计完成,进入任务拆分阶段。