# 企业级 AI 客户端平台 — 架构方案文档

> 文档编号：2026-06-19-002
> 创建日期：2026-06-19
> 状态：方案评审中（含独立批判性分析）

---

## 目录

1. [背景与动机](#1-背景与动机)
2. [产品定位](#2-产品定位)
3. [架构总览](#3-架构总览)
4. [客户端设计](#4-客户端设计)
5. [服务端设计](#5-服务端设计)
6. [权限模型](#6-权限模型)
7. [终端安全](#7-终端安全)
8. [LLM 调用链路](#8-llm-调用链路)
9. [数据同步](#9-数据同步)
10. [认证体系](#10-认证体系)
11. [数据库设计](#11-数据库设计)
12. [安全模型](#12-安全模型)
13. [实施路线图](#13-实施路线图)
14. [独立批判性分析](#14-独立批判性分析)
15. [方案优化与补全](#15-方案优化与补全)
16. [待决策问题](#16-待决策问题)

---

## 1. 背景与动机

### 1.1 当前架构

Fischer AgentKit 当前是纯本地运行架构：

- **CLI/Tauri 桌面端**：启动本地 Python sidecar（`127.0.0.1:{随机端口}`），前端通过 Tauri IPC 获取端口后连接本地 sidecar
- **LLM 配置**：API Key 存储在本地 `agentkit.yaml` + `.env` 中
- **认证**：仅全局 API Key + `clients.yaml` 多客户端 key，无用户系统
- **数据**：会话存储在本地 SQLite，情景记忆在 PostgreSQL（可选）
- **终端**：PTY 在本地 sidecar 中执行

### 1.2 改造动机

- **企业统一管理 LLM Key 和成本**：当前 Key 散落在各客户端，无法统一管控
- **团队知识共享**：企业代码规范、最佳实践需要集中管理、团队共享
- **权限与审计**：企业需要控制谁能使用终端等高危功能，需要审计操作记录
- **多用户支持**：当前无用户概念，无法区分不同用户的会话和操作

### 1.3 对标产品

| 产品 | 客户端 | 服务端 | 差异 |
|------|--------|--------|------|
| Trae | 桌面 IDE + AI Agent | 云端模型 | 个人工具 |
| Cursor | VS Code Fork + AI | 云端模型 + Team | 团队工具 |
| Codex CLI | 终端 CLI | 云端模型 | 命令行助手 |
| **AgentKit 目标** | **桌面客户端 + AI Agent** | **企业平台（权限/KB/审计）** | **企业级 AI 开发平台** |

**核心差异化**：代码不离开客户端 + Agent 本地执行 + 企业级治理

---

## 2. 产品定位

### 2.1 定位声明

> 企业级 AI 开发客户端平台：客户端是开发者的 AI 工作台（类似 Trae），服务端是企业共享资源与治理中心。

### 2.2 双买家模型

| 维度 | 开发者视角 | 企业 IT 视角 |
|------|----------|------------|
| 核心价值 | AI 辅助编程、本地执行 | 统一 Key 管理、成本管控、审计合规 |
| 部署模式 | 本地优先 | 集中部署 |
| 成功指标 | 任务完成率、响应延迟 | 合规率、成本可控 |

### 2.3 差异化竞争力

1. **代码不离开客户端**（真差异化）：Cursor/Trae 的 codebase indexing 在云端，AgentKit 的代码上下文完全本地
2. **专家团/董事会模式**（已有优势）：当前代码库已有的多 Agent 协作模式，Cursor/Trae 完全没有
3. **企业级治理**（目标差异化）：统一 LLM Key、成本管控、权限审计

---

## 3. 架构总览

### 3.1 双进程模型

```
┌─────────────────────────────────────────────────────────────┐
│                    企业服务端（云端/内网）                     │
│                                                             │
│  ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐       │
│  │ LLM 网关  │ │ 用户权限  │ │ 审计日志  │ │ 知识库    │       │
│  │ (统一Key) │ │ 中心     │ │ 中心     │ │ 中心     │       │
│  └──────────┘ └──────────┘ └──────────┘ └──────────┘       │
│                                                             │
│  PostgreSQL + pgvector  │  Redis  │  MinIO(可选)            │
└───────────────────────┬─────────────────────────────────────┘
                        │ HTTPS API + WSS
                        │ (认证: JWT + API Key)
                        │
          ┌─────────────┼─────────────┐
          │             │             │
          ▼             ▼             ▼
  ┌──────────────┐ ┌──────────┐ ┌──────────────┐
  │ Tauri 桌面    │ │ Tauri    │ │ Web 管理台   │
  │ 客户端        │ │ 客户端   │ │ (浏览器)     │
  │              │ │          │ │              │
  │ ┌──────────┐ │ └──────────┘ │ 用户管理     │
  │ │本地Sidecar│ │              │ 审计查看     │
  │ │(Python)  │ │              │ 配置管理     │
  │ │├Agent引擎│ │              └──────────────┘
  │ │├终端     │ │
  │ │├文件操作 │ │
  │ │├会话存储 │ │
  │ │└LLM代理  │ │
  │ └──────────┘ │
  │ ┌──────────┐ │
  │ │前端(Vue3)│ │
  │ └──────────┘ │
  └──────────────┘
```

### 3.2 核心设计原则

| 原则 | 说明 | 价值定位 |
|------|------|---------|
| LLM Key 不下发客户端 | 客户端通过服务端 LLM 网关间接调用 | **成本管控 + 审计**（非安全边界） |
| 代码不离开客户端 | Agent 在客户端本地执行，文件操作在本地 | **安全 + 隐私**（真差异化） |
| 终端在客户端本地执行 | 服务端不提供终端功能 | **安全**（服务端无 RCE 风险） |
| 离线降级 | 服务端不可用时客户端部分可用 | **可用性** |

### 3.3 关键架构决策

| 决策点 | 选择 | 理由 |
|--------|------|------|
| 终端位置 | 本地 Python sidecar（非 Tauri Rust） | 避免用 Rust 重写 PTY + 命令分类逻辑 |
| LLM 调用模式 | 服务端代理（非客户端直连） | 统一 Key 管理 + 用量审计 + 成本管控 |
| 配置同步 | 轮询 + ETag（非 WebSocket 推送） | 配置变更频率低（周/月级），轮询足够 |
| 会话历史 | 本地 SQLite（不上传服务端） | 隐私保护 + 减少网络传输 |
| 服务端部署 | 支持内网私有化部署 | 跨地域公网延迟不可接受 |

---

## 4. 客户端设计

### 4.1 客户端分层

```
Tauri 桌面客户端
├── UI 层（Vue 3 + Ant Design Vue）
│   ├── 对话界面（Chat）
│   ├── 工作台界面（Workspace）
│   ├── 终端界面（Terminal）
│   ├── 知识库浏览（KB Browser）
│   ├── 工作流管理（Workflow）
│   └── 设置中心（Settings）
│
├── 本地服务层（Python Sidecar - FastAPI）
│   ├── /local/agent        — 本地 Agent 执行引擎
│   ├── /local/terminal     — 本地终端（PTY）
│   ├── /local/files        — 本地文件操作
│   ├── /local/session      — 本地会话存储（SQLite）
│   ├── /local/cache        — 本地 LLM 语义缓存
│   └── /local/llm-proxy    — LLM 代理（转发到服务端网关）
│
├── 远程通信层
│   ├── /api/auth/*         — 认证（登录/JWT）
│   ├── /api/llm/chat       — LLM 代理（通过服务端网关）
│   ├── /api/kb/*           — 知识库查询
│   ├── /api/config/*       — 配置同步
│   └── /api/audit/*        — 审计日志上报
│
└── 本地存储
    ├── SQLite（会话、缓存、配置快照）
    ├── 文件系统（代码、项目）
    └── 内存（Agent 运行时状态）
```

### 4.2 客户端保留的模块

| 当前模块 | 客户端角色 | 改造 |
|---------|----------|------|
| `core/` Agent 引擎 | 本地执行 | LLM 改为 RemoteLLMProvider |
| `tools/` 工具 | 本地执行 | 不变 |
| `chat/` 会话 | 本地 SQLite | 增加 owner_id |
| `routes/terminal.py` | 本地终端 | 增加权限检查 + 审计上报 |
| `llm/gateway.py` | 改为代理模式 | 新增 RemoteLLMProvider |
| `skills/` | 本地 + 远程同步 | 增加从服务端同步 |
| `experts/` | 本地 + 远程配置 | 增加从服务端同步 |

### 4.3 客户端启动流程

```
1. Tauri 启动 → 启动本地 Python Sidecar（127.0.0.1:{随机端口}）
2. 前端加载 → 检查本地是否有登录凭证（JWT）
   ├─ 有且有效 → 直接进入主界面
   ├─ 有但过期 → 尝试 refresh → 成功则进入，失败则跳转登录
   └─ 无 → 跳转登录页
3. 登录成功后：
   a. 从服务端拉取用户信息 + 权限
   b. 从服务端同步配置（技能/Agent/工作流，带 ETag 增量）
   c. 初始化本地 Agent 引擎（使用 RemoteLLMProvider）
4. 进入主界面，用户开始工作
```

### 4.4 离线降级策略

| 功能 | 服务端可用 | 服务端不可用 |
|------|----------|------------|
| 对话 | 通过服务端 LLM 网关 | 用户自填 Key 直连 LLM（降级模式） |
| 终端 | 本地执行 + 审计上报 | 本地执行 + 审计暂存本地 |
| 文件操作 | 本地执行 | 本地执行 |
| 知识库 | 实时查询服务端 | 使用本地缓存的 KB 元数据 + 已缓存文档 |
| 工作流模板 | 实时拉取 | 使用本地缓存版本 |
| 配置同步 | 轮询更新 | 使用本地缓存 |

---

## 5. 服务端设计

### 5.1 服务端模块（按必要性排序）

| 优先级 | 模块 | 职责 | V1 是否必须 |
|--------|------|------|------------|
| P0 | LLM 网关 | 统一 Key、用量统计、成本管控、语义缓存、限流 | ✅ 必须 |
| P0 | 用户认证 | JWT 签发/验证、用户 CRUD | ✅ 必须 |
| P1 | 审计日志 | LLM 调用审计 + 危险命令审计 | ✅ 应该 |
| P1 | 知识库 | pgvector 语义搜索、团队隔离 | ✅ 应该 |
| P2 | 配置同步 API | 技能/Agent/工作流配置分发 | 可延后 |
| P3 | Web 管理台 | 用户管理、审计查看、配置管理 | 可延后 |
| P3 | 技能市场 | 技能发布与分发 | 可延后 |
| P3 | 工作流模板中心 | 工作流 CRUD、版本控制 | 可延后 |
| P4 | 生产系统集成 | CI/CD Webhook | 暴露 Webhook 即可 |

### 5.2 服务端 API

```
认证 API：
  POST   /api/v1/auth/login            — 登录
  POST   /api/v1/auth/refresh          — 刷新 token
  POST   /api/v1/auth/logout           — 登出
  GET    /api/v1/auth/me               — 当前用户信息

LLM 网关 API：
  POST   /api/v1/llm/chat              — LLM 对话（非流式）
  POST   /api/v1/llm/chat/stream       — LLM 流式对话（SSE）

知识库 API：
  GET    /api/v1/kb/search             — 知识库搜索
  GET    /api/v1/kb/documents/{id}     — 获取文档

配置同步 API：
  GET    /api/v1/config/version        — 配置版本号
  GET    /api/v1/config/skills         — 技能配置
  GET    /api/v1/config/agents         — Agent 配置
  GET    /api/v1/config/workflows      — 工作流模板

审计 API：
  POST   /api/v1/audit/terminal        — 终端审计上报
  GET    /api/v1/usage/me              — 我的用量

管理 API（需 admin 权限）：
  GET    /api/v1/admin/users           — 用户管理
  POST   /api/v1/admin/users           — 创建用户
  GET    /api/v1/admin/audit/terminal  — 终端审计日志
  GET    /api/v1/admin/usage           — 全局用量统计
```

---

## 6. 权限模型

### 6.1 三级角色 + 权限位

```
角色：
  member    — 普通用户（对话 + 只读 KB + 工作流执行）
  operator  — 高级用户（+ 终端 + KB 写入）
  admin     — 管理员（+ 用户管理 + 系统配置）
```

### 6.2 权限位定义

```python
class Permission(str, Enum):
    CHAT = "chat"                              # 对话
    KB_QUERY = "kb.query"                      # 知识库查询
    KB_WRITE = "kb.write"                      # 知识库写入
    WORKFLOW_EXECUTE = "workflow.execute"      # 工作流执行
    TERMINAL_LOCAL_USE = "terminal.local.use"  # 本地终端使用
    TERMINAL_SERVER_USE = "terminal.server.use"  # 服务端终端使用
    TERMINAL_WHITELIST_MANAGE = "terminal.whitelist.manage"  # 白名单管理
    USER_MANAGE = "user.manage"                # 用户管理
    SYSTEM_CONFIG = "system.config"            # 系统配置

ROLE_PERMISSIONS: dict[str, set[Permission]] = {
    "member": {Permission.CHAT, Permission.KB_QUERY, Permission.WORKFLOW_EXECUTE},
    "operator": {
        Permission.CHAT, Permission.KB_QUERY, Permission.KB_WRITE,
        Permission.WORKFLOW_EXECUTE, Permission.TERMINAL_LOCAL_USE,
    },
    "admin": set(Permission),  # 含 TERMINAL_SERVER_USE + TERMINAL_WHITELIST_MANAGE
}
```

### 6.3 终端授权

终端分为**本地终端**和**服务端终端**两种模式，授权要求不同：

| 终端模式 | 执行位置 | 所需权限 | 额外要求 | 审计策略 |
|---------|---------|---------|---------|---------|
| 本地终端 | 客户端本地 sidecar | `TERMINAL_LOCAL_USE` | `is_terminal_authorized=True` | 仅危险命令上报 |
| 服务端终端 | 服务端 PTY | `TERMINAL_SERVER_USE` | `is_server_terminal_authorized=True` | **全量命令记录** |

**授权流程**：
1. `operator` 角色默认有本地终端权限，admin 可单独授予 `is_terminal_authorized`
2. 服务端终端权限仅 `admin` 角色默认拥有，或 admin 单独授予 `is_server_terminal_authorized`
3. 两种终端权限独立控制，互不影响

---

## 7. 终端安全

### 7.1 三层防护

```
第一层：角色门禁
  → 用户必须有 TERMINAL_USE 权限 + is_terminal_authorized=True
  → member 角色看不到终端入口

第二层：命令分类
  → 安全命令（ls, cat, pwd...）：有权限即可执行
  → 危险命令（rm, sudo, chmod...）：每次必须人工确认
  → 未知命令：默认视为危险

第三层：人工确认（每次）
  → 危险命令每次执行都弹出确认对话框
  → 不提供"加入白名单"选项（危险命令不持久化白名单）
  → 必须勾选"我已了解风险"才能确认
```

### 7.2 终端审计

| 审计内容 | 上报策略 |
|---------|---------|
| 危险命令 + 确认结果 | 实时上报服务端 |
| 普通命令流水 | **不上报**（隐私保护，全量监控是 MDM/EDR 的职责） |
| 命令输出内容 | **不上报**（可能含敏感信息） |
| 审计元数据 | 命令文本、风险等级、是否确认、exit_code、时间戳 |

### 7.3 BYOD 模式

- 支持纯本地模式（不接入企业服务端），此时无审计上报
- 企业配发设备接入服务端时，审计上报自动启用

---

## 8. LLM 调用链路

### 8.1 调用流程

```
客户端 Agent → 本地 LLM Proxy → 服务端 LLM 网关 → LLM Provider API
                                ↑
                          JWT 认证
                          用量统计
                          成本管控
                          语义缓存（共享）
                          限流/熔断
```

### 8.2 RemoteLLMProvider

```python
class RemoteLLMProvider(LLMProvider):
    """通过服务端 LLM 网关调用 LLM，不在本地存储 API Key"""

    def __init__(self, server_url: str, auth_token_provider: Callable[[], str]):
        self._server_url = server_url
        self._auth_token_provider = auth_token_provider

    async def chat(self, request: LLMRequest) -> LLMResponse:
        response = await httpx.post(
            f"{self._server_url}/api/v1/llm/chat",
            json=request.model_dump(),
            headers={"Authorization": f"Bearer {self._auth_token_provider()}"},
        )
        return LLMResponse(**response.json())

    async def chat_stream(self, request: LLMRequest):
        async with httpx.AsyncClient() as client:
            async with client.stream(
                "POST",
                f"{self._server_url}/api/v1/llm/chat/stream",
                json=request.model_dump(),
                headers={"Authorization": f"Bearer {self._auth_token_provider()}"},
            ) as response:
                async for line in response.aiter_lines():
                    if line.startswith("data: "):
                        yield StreamChunk(**json.loads(line[6:]))
```

### 8.3 流式 fallback 语义

**已知风险**：当前 `gateway.py` 的"首 chunk 前失败则 fallback"在单进程内简单，跨网络后客户端可能已收到部分 chunk，fallback 会导致内容跳变。

**处理方案**：
- 服务端网关在首 chunk 前完成 fallback（当前逻辑不变）
- 首 chunk 后不再 fallback，直接返回错误
- 客户端收到错误后自行决定是否重试

### 8.4 延迟分析

| 部署拓扑 | RTT | 50 次 LLM 调用额外延迟 | 可接受性 |
|---------|-----|---------------------|---------|
| 同内网 | 5ms | +0.5s | ✅ 完全可接受 |
| 跨城内网 | 30ms | +3s | ✅ 可接受 |
| 跨地域公网 | 150ms | +15s | ⚠️ 开始难受 |
| 跨国公网 | 300ms | +30s | ❌ 不可接受 |

**结论**：服务端必须支持**内网私有化部署**。跨地域公网部署需要客户端支持直连降级。

### 8.5 内网 LLM 支持

服务端 LLM 网关支持接入内网 LLM 服务：
- vLLM（OpenAI 兼容 API）
- Ollama（OpenAI 兼容 API）
- 当前 `ProviderConfig` 已是 OpenAI 兼容格式，只需配置 `base_url` 指向内网地址

---

## 9. 数据同步

### 9.1 同步策略（简化版）

| 数据类型 | 方向 | 策略 |
|---------|------|------|
| 用户信息/权限 | 服务端 → 客户端 | 登录时拉取 + JWT 续期时刷新 |
| 技能/Agent/工作流配置 | 服务端 → 客户端 | 启动时全量拉取 + 每 5 分钟轮询版本号 |
| 知识库 | 服务端 → 客户端 | 实时查询 + 元数据本地缓存 + LRU 文档缓存 |
| 会话历史 | 纯本地 | 不上传服务端 |
| 终端审计 | 客户端 → 服务端 | 危险命令实时上报 + 离线暂存 |
| LLM 用量 | 服务端记录 | 通过 LLM 网关自动记录 |

### 9.2 配置同步实现（轮询版）

```python
# 客户端配置同步引擎
class ConfigSync:
    async def sync(self):
        # 1. 检查版本号
        remote_version = await api.get("/api/v1/config/version")
        if remote_version == local_version:
            return  # 无变更

        # 2. 全量拉取（配置数据量小，无需增量）
        skills = await api.get("/api/v1/config/skills")
        agents = await api.get("/api/v1/config/agents")
        workflows = await api.get("/api/v1/config/workflows")

        # 3. 更新本地缓存
        await update_local_cache(skills, agents, workflows)
        local_version = remote_version

    async def start_polling(self):
        while True:
            await asyncio.sleep(300)  # 5 分钟
            try:
                await self.sync()
            except Exception:
                pass  # 离线时静默失败
```

### 9.3 知识库缓存策略

| 层级 | 缓存内容 | 大小 | 策略 |
|------|---------|------|------|
| L1 | KB 文档列表 + 标题 + 摘要 | ~100KB | 启动时全量拉取 |
| L2 | 最近使用的 N 篇文档全文 | ~10MB | LRU 缓存，按需拉取 |
| L3 | 实时查询 | - | L1/L2 miss 时请求服务端 |

---

## 10. 认证体系

### 10.1 双轨认证

```
请求进入
  │
  ├─ 有 Authorization: Bearer <jwt> ?
  │   └─ 是 → 验证 JWT → 提取 user_id → request.state.current_user = User
  │
  ├─ 有 X-API-Key: <key> ?
  │   └─ 是 → 查 user_api_keys 表 → 识别 user_id → request.state.current_user = User
  │           （兼容旧 clients.yaml → 识别为 system client）
  │
  └─ 无凭证
      ├─ dev mode → 放行
      └─ 生产 → 401
```

### 10.2 JWT 流程

```
登录：POST /api/v1/auth/login {username, password}
  → 验证密码 (bcrypt)
  → 生成 access_token (15min) + refresh_token (7d)
  → 返回 {access_token, refresh_token, user}

刷新：POST /api/v1/auth/refresh {refresh_token}
  → 生成新 access_token

登出：POST /api/v1/auth/logout
  → 撤销 refresh_token
```

### 10.3 SSO/OIDC（V2 规划）

V1 支持本地账号认证。V2 预留 OIDC 接口，支持：
- Azure AD / Microsoft Entra ID
- Google Workspace
- 企业自建 OIDC Provider

---

## 11. 数据库设计

### 11.1 服务端 PostgreSQL

```sql
-- 用户表
CREATE TABLE users (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    username VARCHAR(64) UNIQUE NOT NULL,
    email VARCHAR(255) UNIQUE NOT NULL,
    password_hash VARCHAR(255) NOT NULL,
    role VARCHAR(32) NOT NULL DEFAULT 'member',
    is_active BOOLEAN NOT NULL DEFAULT TRUE,
    is_terminal_authorized BOOLEAN NOT NULL DEFAULT FALSE,
    created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    last_login_at TIMESTAMPTZ,
    created_by UUID REFERENCES users(id),
    tenant_id UUID,  -- 多租户预留
    metadata JSONB DEFAULT '{}'
);

-- 用户 API Key 表
CREATE TABLE user_api_keys (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
    key_hash VARCHAR(255) UNIQUE NOT NULL,
    key_prefix VARCHAR(16) NOT NULL,
    name VARCHAR(64),
    created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    last_used_at TIMESTAMPTZ,
    expires_at TIMESTAMPTZ,
    is_revoked BOOLEAN NOT NULL DEFAULT FALSE
);

-- 终端审计日志
CREATE TABLE terminal_audit_logs (
    id BIGSERIAL PRIMARY KEY,
    user_id UUID NOT NULL REFERENCES users(id),
    session_id VARCHAR(64) NOT NULL,
    command TEXT NOT NULL,
    command_hash VARCHAR(64) NOT NULL,
    risk_level VARCHAR(16) NOT NULL,
    was_confirmed BOOLEAN NOT NULL DEFAULT FALSE,
    was_approved BOOLEAN NOT NULL DEFAULT FALSE,
    executed_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    exit_code INTEGER,
    client_ip INET,
    metadata JSONB DEFAULT '{}'
);

-- LLM 用量记录
CREATE TABLE llm_usage_records (
    id BIGSERIAL PRIMARY KEY,
    user_id UUID NOT NULL REFERENCES users(id),
    provider VARCHAR(64) NOT NULL,
    model VARCHAR(128) NOT NULL,
    input_tokens INTEGER NOT NULL,
    output_tokens INTEGER NOT NULL,
    cost_cents INTEGER NOT NULL DEFAULT 0,
    latency_ms INTEGER,
    created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    metadata JSONB DEFAULT '{}'
);

-- 用户会话（JWT refresh token）
CREATE TABLE user_sessions (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
    refresh_token_hash VARCHAR(255) UNIQUE NOT NULL,
    device_info JSONB DEFAULT '{}',
    created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    expires_at TIMESTAMPTZ NOT NULL,
    revoked_at TIMESTAMPTZ
);
```

### 11.2 客户端 SQLite（现有 + 改造）

```sql
-- conversations 表增加 owner_id
ALTER TABLE conversations ADD COLUMN owner_id TEXT;
CREATE INDEX idx_conversations_owner ON conversations(owner_id);
```

### 11.3 多租户预留

所有服务端表预留 `tenant_id` 字段，V1 默认为 NULL（单租户），V2 启用多租户隔离。

---

## 12. 安全模型

### 12.1 分层安全

```
Layer 1: 网络安全
  ├─ 客户端 ↔ 服务端：HTTPS + WSS
  ├─ 服务端 ↔ LLM：HTTPS
  └─ 服务端内部：VPC 隔离

Layer 2: 认证安全
  ├─ 用户登录：用户名+密码（bcrypt, rounds=12）→ JWT
  ├─ 客户端认证：JWT（access 15min + refresh 7d）
  ├─ 系统集成：API Key（SHA256 hash 存储）
  └─ 登录限流：5次/分钟

Layer 3: 权限安全
  ├─ RBAC 三级角色 + 权限位
  ├─ 路由级权限检查
  ├─ 数据级隔离（owner_id / tenant_id）
  └─ 终端双重授权

Layer 4: 操作安全
  ├─ 终端命令三层防护
  ├─ 危险命令不持久化白名单
  ├─ 审计日志（只追加，不可篡改）
  └─ 敏感操作二次确认

Layer 5: 数据安全
  ├─ LLM API Key 不下发客户端
  ├─ 代码文件不离开客户端
  ├─ 会话历史不上传服务端
  └─ 审计日志脱敏
```

### 12.2 LLM 成本配额

```python
# 按 user/tenant 设月度 token 配额
class UsageQuota:
    monthly_token_limit: int       # 月度 token 上限
    monthly_cost_limit_cents: int  # 月度费用上限（分）
    current_tokens: int            # 当月已用
    current_cost_cents: int        # 当月已花费

# 服务端 LLM 网关检查
async def check_quota(user: User) -> bool:
    quota = await get_user_quota(user.id)
    if quota.current_tokens >= quota.monthly_token_limit:
        raise HTTPException(429, "Monthly token quota exceeded")
    if quota.current_cost_cents >= quota.monthly_cost_limit_cents:
        raise HTTPException(429, "Monthly cost quota exceeded")
    return True
```

---

## 13. 实施路线图

### 13.1 修订后的路线图（含 Phase 0）

| 阶段 | 内容 | 周期 | 交付物 |
|------|------|------|--------|
| **Phase 0** | 包拆分 + 双进程骨架 | 3 周 | `agentkit_core` / `agentkit_client` / `agentkit_server` 三包分离，双进程能跑 |
| **Phase 1** | LLM 网关 + 基础认证 | 3 周 | RemoteLLMProvider + 服务端 LLM 网关 + JWT 登录 |
| **Phase 2** | 权限 + 审计 + 配置同步 | 3 周 | 3 级 RBAC + 审计日志 + 配置轮询同步 |
| **Phase 3** | KB 共享 + 离线降级 | 3 周 | 团队 KB + KB 缓存 + 离线降级 |
| **Phase 4+** | Web 管理台 / SSO / 代码索引 | 延后 | 按需推进 |

### 13.2 Phase 0：包拆分（前置必做）

当前 `server/` 包是单体，没有客户端/服务端边界。Phase 0 需要拆分为：

```
agentkit/
├── core/           — Agent 引擎（客户端服务端共用）
│   ├── base.py
│   ├── react.py
│   ├── rewoo.py
│   └── ...
├── llm/            — LLM 抽象层（共用）
│   ├── protocol.py
│   ├── gateway.py
│   ├── config.py
│   └── remote_provider.py  — 新增
├── tools/          — 工具（共用）
├── client/         — 客户端特有
│   ├── sidecar.py  — 本地 sidecar 入口
│   ├── terminal.py — 终端（从 server/routes/ 迁移）
│   ├── files.py    — 文件操作
│   ├── session.py  — 本地会话存储
│   ├── sync.py     — 配置同步引擎
│   └── audit.py    — 审计上报
├── server/         — 服务端特有
│   ├── app.py      — 服务端 FastAPI app
│   ├── auth/       — 认证授权
│   ├── llm_gateway/— LLM 网关
│   ├── kb/         — 知识库
│   ├── audit/      — 审计
│   └── admin/      — 管理 API
└── shared/         — 共享模型/配置
    ├── models.py
    └── config.py
```

### 13.3 Phase 1 验证目标

Phase 1 结束时验证核心价值：
- 企业统一管 LLM Key
- 审计 LLM 用量
- 客户端通过服务端代理调用 LLM

**如果 Phase 1 后企业不买单，停止后续投入。**

### 13.4 替代方案：最小验证（4 周）

如果不确定企业是否买单，可先做最小方案：

- 服务端只做 LLM Key 代理 + 用量统计
- 用户认证用 API Key（沿用现有 `clients.yaml`）
- 无 RBAC、无 KB 共享
- 4 周可交付，用最小代价验证企业需求

---

## 14. 独立批判性分析

> 以下为独立架构师以全新视角对方案的批判性分析，未经修改。

### 14.1 需求目标分析

#### 14.1.1 产品定位不清晰

方案把两个**买家不同、成功指标不同、部署模式不同**的产品缝在一起：

| 维度 | 开发者生产力工具 | 企业治理平台 |
|------|----------------|------------|
| 买家 | 开发者个人 | IT/安全/采购 |
| 核心指标 | 任务完成率、延迟 | 合规率、成本可控 |
| 部署 | 本地优先 | 集中部署 |

方案同时声称要"类似 Trae/Cursor"又要"企业级平台"，但**没有回答：先卖给谁？谁拍板？谁付钱？**

#### 14.1.2 差异化竞争力

真正的差异化只有一条半：
- **"代码不离开客户端"**：真差异化（Cursor/Trae 的 codebase indexing 在云端）
- **"Agent 本地执行"**：半个差异化（Cursor 的 Agent 也是本地跑的）

方案漏掉了真正能拉开差距的能力：当前代码库已有的**专家团/董事会模式**，这是 Cursor/Trae 完全没有的。

#### 14.1.3 遗漏的核心场景

1. **代码库语义索引**：Cursor 的核心竞争力，方案完全没提
2. **IDE 集成**：开发者活在 VS Code/JetBrains 里，纯桌面端推广阻力大
3. **离线/内网 LLM**：很多企业不能用云端 LLM
4. **多设备会话**：方案内部矛盾（会话纯本地 vs 多设备）
5. **模型评估/回归**：企业上线 Agent 前要评估

### 14.2 必要性分析

#### 14.2.1 服务端模块必要性

| 模块 | 必要性 | 理由 |
|------|--------|------|
| LLM 网关 | 必须 | "企业级"唯一不可替代的价值 |
| 用户认证 | 必须 | 但 V1 只需 3 级 |
| 审计日志 | 应该 | 范围要收窄（只审计 LLM 调用 + 危险命令） |
| 知识库 | 应该 | 可用现有 KB + pgvector 先跑 |
| 工作流模板 | 可延后 | YAML + Git 够用 |
| 技能市场 | 可延后 | 分发不是瓶颈 |
| 生产系统集成 | 不要做 | 暴露 Webhook 够用 |
| Web 管理台 | 可延后 | V1 用 CLI 管理 |

**结论**：V1 服务端只需要 3 个模块——LLM 网关 + 用户认证 + 审计日志。

#### 14.2.2 LLM 代理模式利弊

代理模式的真实成本（被低估）：
- ReAct/专家团循环是**多轮串行 LLM 调用**，一个复杂任务可能 20-50 次
- 跨地域公网（RTT 150ms+）会明显劣化体验

代理模式的真实收益（被高估）：
- "Key 不下发"保护的是**企业的 LLM 预算和用量审计**，不是真正的安全边界
- Tauri 桌面端跑在用户机器上，用户能 dump 内存、抓包

**建议**：代理模式保留，但明确其价值是成本管控 + 审计，不是安全。服务端必须支持内网部署。

#### 14.2.3 四级 RBAC 过度设计

- `viewer`：谁会用 AI 编程工具只看不操作？想象出来的角色
- `user vs power`：终端和 KB 管理是两个不相关的权限，不该绑成一个等级

企业真实需求是**权限点**，不是角色等级。建议 3 级 + 权限位。

#### 14.2.4 终端审计隐私

| 场景 | 审计合理性 |
|------|-----------|
| 企业配发电脑 | 合理 |
| BYOD | 不合理 |
| 危险命令确认记录 | 合理 |
| 全量命令流水 | 过度（是监控不是审计） |

建议：只上报危险命令 + 确认结果，不上报普通命令流水。

### 14.3 架构设计合理性

#### 14.3.1 配置同步 WebSocket 过度设计

配置变更频率是**周/月级**，用 WebSocket 推送是杀鸡用牛刀。更简单的方案：启动时全量拉取 + 每 5 分钟轮询版本号 + 手动刷新。

#### 14.3.2 会话历史纯本地 vs 企业审计矛盾

如果企业要审计"员工用 AI 做了什么"，会话历史是最核心的审计对象。纯本地意味着审计员看不到。

**建议**：拆成三层：
1. 完整对话内容：本地（默认）
2. 操作摘要：上报服务端（脱敏后）
3. LLM 调用元数据：服务端网关天然就有

#### 14.3.3 知识库不缓存导致离线断裂

KB 完全不缓存意味着离线时 Agent 无法引用任何团队知识，与"离线降级"原则矛盾。

**建议**：元数据缓存 + LRU 文档缓存 + 离线时只用已缓存文档。

#### 14.3.4 当前代码库适配性

FastAPI + Vue3 + Tauri 技术栈没问题，但当前 `server/` 包是单体，没有客户端/服务端边界。需要一次彻底的包结构重组（Phase 0）。

### 14.4 改造方案可行性

#### 14.4.1 最大技术风险：终端的位置

当前 `terminal.py` 的 PTY 跑在 Python sidecar 里。改造后：
- 路线 A（移到 Tauri Rust）：风险太高，用 Rust 重写 PTY + 命令分类
- 路线 B（保留本地 Python sidecar）：**唯一现实路线**，终端逻辑不动

#### 14.4.2 RemoteLLMProvider 改造

`LLMProvider` 是干净的 ABC，RemoteLLMProvider 本身 2-3 天。难点在服务端流式透传 + 取消传播 + fallback 语义，1-2 周。

#### 14.4.3 路线图评估

"每阶段 2-3 周，共 8-12 周"——**不现实**，因为：
1. Phase 0（包拆分）没算进去
2. 用户系统从零开始
3. 终端位置决策未明确
4. 双进程调试环境搭建

**现实评估**：12 周（4 阶段 × 3 周），假设 1-2 个全职开发。

#### 14.4.4 "看似简单实则困难"的改造点

1. 流式 fallback 语义跨网络后的重新定义
2. `_verify_api_key` 的全局替换（十几个路由文件）
3. `clients.yaml` 兼容（两套认证并存）
4. Tauri sidecar 的远程地址注入
5. 专家团/董事会的 LLM 调用放大延迟

### 14.5 改进建议汇总

#### 架构简化
1. 砍掉 WebSocket 配置同步，用轮询 + ETag
2. 砍掉 viewer 角色，RBAC 改 3 级 + 权限位
3. 砍掉"生产系统集成"模块，暴露 Webhook
4. 砍掉"技能市场"和"工作流模板中心"，YAML + Git 够用
5. 明确双进程模型

#### 遗漏的考虑
| 遗漏项 | 重要性 | 建议 |
|--------|--------|------|
| 多租户隔离 | 高 | 所有表预留 tenant_id |
| SSO/OIDC | 高 | V2 支持，V1 预留接口 |
| 数据备份/DR | 高 | PG 备份 + Redis 持久化 |
| 客户端升级 | 中 | Tauri updater 机制 |
| LLM 成本配额 | 中 | 按 user/tenant 设月度限额 |
| 代码库语义索引 | 高 | "类 Cursor"的必修课 |
| API 版本化 | 低 | 客户端和服务端独立发版后需要 |

#### 替代方案

**替代方案 B（推荐起步）：服务端只做 LLM 代理**
- 砍掉 KB/工作流/技能市场/用户系统
- 服务端只做 LLM Key 代理 + 用量统计
- 用户认证用 API Key（沿用 clients.yaml）
- 4 周可交付，用最小代价验证企业需求

---

## 15. 方案优化与补全

基于独立分析，对原方案进行以下优化：

### 15.1 已采纳的优化

| 原方案 | 优化后 | 依据 |
|--------|--------|------|
| 四级 RBAC (viewer/user/power/admin) | 三级 + 权限位 (member/operator/admin) | §14.2.3 |
| WebSocket 配置同步 | 轮询 + ETag | §14.3.1 |
| 9 个服务端模块并列 | V1 只做 3 个（LLM 网关 + 认证 + 审计） | §14.2.1 |
| 终端全量审计上报 | 只上报危险命令 | §14.2.4 |
| 知识库不缓存 | 元数据 + LRU 文档缓存 | §14.3.3 |
| 8-12 周路线图 | 12 周（含 Phase 0 包拆分） | §14.4.3 |
| 会话历史纯本地 | 三层拆分（内容本地 + 摘要上报 + 元数据服务端） | §14.3.2 |

### 15.2 新增补全

| 补全项 | 说明 |
|--------|------|
| Phase 0 包拆分 | 前置必做，3 周 |
| 多租户预留 | 所有表预留 tenant_id |
| SSO/OIDC 接口预留 | V1 本地账号，V2 OIDC |
| LLM 成本配额 | 按 user/tenant 月度限额 |
| 内网 LLM 支持 | vLLM/Ollama 接入 |
| 客户端直连降级 | 服务端不可用时用户自填 Key 直连 LLM |
| 代码库语义索引 | 列入 Phase 4+ 规划 |
| 数据备份策略 | PG 备份 + Redis 持久化 |

### 15.3 替代方案：最小验证路径

如果不确定企业是否买单，建议先走 4 周最小方案：

```
Week 1-2: 服务端 LLM Key 代理 + 用量统计
Week 3:   客户端 RemoteLLMProvider
Week 4:   联调 + 企业试用
```

验证核心假设：**企业是否真需要统一管 LLM Key 和成本**。

验证通过后，再按 Phase 0 → Phase 3 推进完整方案。

---

## 16. 待决策问题

以下问题需要明确后才能进入实施：

### 16.1 必须先回答的三个问题

| # | 问题 | 影响 |
|---|------|------|
| 1 | **卖给谁？** 开发者个人还是企业 IT？ | 决定优先级和功能取舍 |
| 2 | **服务端部署在哪？** 内网还是公网？ | 决定代理模式是否可接受 |
| 3 | **终端跑在哪？** Tauri Rust 还是本地 sidecar？ | 决定 Phase 0 工作量 |

### 16.2 架构决策

| # | 问题 | 选项 | 建议 |
|---|------|------|------|
| 4 | 先做完整方案还是最小验证？ | 完整 12 周 vs 最小 4 周 | 先做最小验证 |
| 5 | 是否支持 BYOD？ | 支持/不支持 | 支持（纯本地模式） |
| 6 | 会话内容是否上传？ | 纯本地/摘要上报/全量上传 | 摘要上报 |
| 7 | V1 是否支持 SSO？ | 支持/不支持/预留接口 | 预留接口 |
| 8 | 代码库语义索引何时做？ | Phase 3/Phase 4+/不做 | Phase 4+ |

---

## 附录 A：当前代码库关键文件索引

| 模块 | 文件 | 改造角色 |
|------|------|---------|
| LLM 协议 | `src/agentkit/llm/protocol.py` | 共用，新增 RemoteLLMProvider |
| LLM 网关 | `src/agentkit/llm/gateway.py` | 服务端保留，客户端改代理 |
| LLM 配置 | `src/agentkit/llm/config.py` | 服务端 |
| Agent 引擎 | `src/agentkit/core/` | 客户端本地执行 |
| 工具 | `src/agentkit/tools/` | 客户端本地执行 |
| 终端 | `src/agentkit/server/routes/terminal.py` | 迁移到 `client/terminal.py` |
| 会话存储 | `src/agentkit/chat/sqlite_conversation_store.py` | 客户端，增加 owner_id |
| 鉴权中间件 | `src/agentkit/server/middleware.py` | 服务端，改造为 JWT + API Key 双轨 |
| 客户端配置 | `src/agentkit/server/client_config.py` | 服务端，迁移到 DB |
| 服务端配置 | `src/agentkit/server/config.py` | 服务端，增加 auth 配置 |
| 专家团 | `src/agentkit/experts/` | 客户端，配置从服务端同步 |
| 知识库 | `src/agentkit/memory/knowledge_base.py` | 服务端 |
| 情景记忆 | `src/agentkit/memory/episodic.py` | 服务端，增加 user_id |
| Tauri sidecar | `src-tauri/src/sidecar.rs` | 客户端，增加远程地址注入 |
| Tauri 配置 | `src-tauri/tauri.conf.json` | 客户端，放宽 CSP |
| 前端 API | `src/agentkit/server/frontend/src/api/base.ts` | 客户端，支持双目标 |
| 前端 Chat | `src/agentkit/server/frontend/src/stores/chat.ts` | 客户端，WS 附加 token |

## 附录 B：术语表

| 术语 | 定义 |
|------|------|
| Sidecar | Tauri 启动的本地 Python 进程，提供本地 API |
| LLM 网关 | 服务端统一管理 LLM API Key 和用量的代理服务 |
| RemoteLLMProvider | 客户端通过服务端 LLM 网关调用 LLM 的 Provider 实现 |
| RBAC | 基于角色的访问控制 |
| 权限位 | 独立于角色的细粒度权限（如 terminal.local.use） |
| BYOD | Bring Your Own Device，自带设备 |
| 离线降级 | 服务端不可用时客户端部分功能仍可使用 |
| 本地终端 | 在客户端本地机器上执行命令的终端模式 |
| 服务端终端 | 在服务端机器上执行命令的终端模式，需 admin 授权 |
| 危险命令 | 可能影响系统安全的终端命令（rm/sudo/chmod 等） |
| 内置白名单 | 系统级不可修改的安全命令列表（_SAFE_COMMAND_PREFIXES） |
| 全局白名单 | admin 管理的命令白名单，仅服务端终端适用 |
| 用户白名单 | 用户自管的命令白名单，仅本地终端适用 |
| 会话白名单 | 临时白名单，当前会话有效，会话结束清除 |
| 黑名单 | admin 管理的禁止命令列表，所有终端模式生效，优先级最高 |
| 终端审批 | 服务端终端中非白名单危险命令需 admin 实时批准的机制 |
| 审计日志 | 记录用户操作的不可篡改日志 |
| 租户 | 多租户架构中的隔离单位（企业/团队） |

---

*文档结束。待决策问题明确后进入实施阶段。*
