# 企业级 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 → 提取 user_id → request.state.current_user = User │ ├─ 有 X-API-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 实时批准的机制 | | 审计日志 | 记录用户操作的不可篡改日志 | | 租户 | 多租户架构中的隔离单位(企业/团队) | --- *文档结束。待决策问题明确后进入实施阶段。*