# Smart Support -- 系统架构文档 > 版本: 1.0 | 日期: 2026-03-29 | 状态: APPROVED (CEO + ENG CLEARED) > > **配套文档:** 分阶段任务清单、检查点标准、风险登记册详见 [DEVELOPMENT-PLAN.md](DEVELOPMENT-PLAN.md) --- ## 目录 1. [系统概览](#1-系统概览) 2. [组件职责分解](#2-组件职责分解) 3. [数据流图](#3-数据流图) 4. [技术栈决策](#4-技术栈决策) 5. [数据库设计](#5-数据库设计) 6. [API 设计](#6-api-设计) 7. [Agent 系统架构](#7-agent-系统架构) 8. [MCP 集成层](#8-mcp-集成层) 9. [安全架构](#9-安全架构) 10. [部署架构](#10-部署架构) 11. [架构决策记录 (ADR)](#11-架构决策记录-adr) 12. [非功能性需求](#12-非功能性需求) --- ## 1. 系统概览 ### 1.1 定位 Smart Support 是一个 AI 客服**行动层框架**。它不替代 Zendesk/Intercom 等现有客服平台, 而是补全这些平台缺失的能力 -- 让 AI 能直接调用内部系统完成操作(查订单、取消订单、发优惠券等)。 现有客服工具的自动化率卡在 20-30%,原因是它们只能回答问题,无法执行操作。 Smart Support 通过 MCP 协议连接内部系统,将自动化率提升到 60%+。 ### 1.2 核心价值 "粘贴你的 API,获得一个能执行真实操作的智能客服。" ### 1.3 目标用户 中型电商公司(日均 500-5000 订单,5-20 名客服)的客户体验负责人。 ### 1.4 高层架构图 ``` +------------------+ | React Chat UI | (前端: 聊天 / 回放 / 分析仪表盘) +--------+---------+ | WebSocket (双向, 流式) v +--------+---------+ | FastAPI Server | (HTTP + WebSocket 入口) | main.py | +--------+---------+ | v +--------+---------+ | Context Manager |<--- PostgresSaver (会话状态持久化) | (LangGraph State)| langgraph-checkpoint-postgres +--------+---------+ | v +--------+--------------------+ | LangGraph Supervisor | (Agent 编排 + 意图路由) | langgraph-supervisor v1.1 | +--------+--------------------+ | +----+----+----+----+ | | | | | v v v v v Agent Agent Agent Agent Fallback A B C D Agent | | | | v v v v MCP MCP @tool CLI Tools Tools Wrapper | | | | v v v v +-------------------------------+ | 客户内部系统 | | (Shopify, REST API, gRPC 等) | +-------------------------------+ 横切关注点: +-------------------------------------------+ | interrupt() -- 写操作人工确认 (HITL) | | callbacks.py -- Token 用量 / 成本统计 | | analytics/ -- 解决率 / Agent 使用率 | | replay/ -- 对话回放 API | | openapi/ -- OpenAPI 自动发现 + SSRF 防护 | +-------------------------------------------+ ``` ### 1.5 项目结构 ``` smart-support/ ├── backend/ │ ├── app/ │ │ ├── main.py # FastAPI + WebSocket 入口 │ │ ├── graph.py # LangGraph Supervisor 配置 │ │ ├── agents/ # Agent 定义 + 工具绑定 │ │ ├── registry.py # YAML Agent 注册表加载器 │ │ ├── openapi/ # OpenAPI 解析 + MCP 服务器生成 │ │ │ ├── parser.py # OpenAPI 3.0 规范解析 │ │ │ ├── ssrf.py # SSRF 防护 (独立工具) │ │ │ ├── classifier.py # LLM 端点分类 │ │ │ └── generator.py # MCP 服务器代码生成 │ │ ├── replay/ # 对话回放 API │ │ ├── analytics/ # 数据分析查询 + API │ │ └── callbacks.py # Token 用量统计 Callback │ ├── agents.yaml # Agent 注册表配置 │ ├── templates/ # 垂直行业模板 │ │ ├── e-commerce.yaml │ │ ├── saas.yaml │ │ └── fintech.yaml │ └── tests/ ├── frontend/ # React 聊天 UI + 回放 + 仪表盘 ├── docker-compose.yml # PostgreSQL + 应用 └── pyproject.toml ``` --- ## 2. 组件职责分解 ### 2.1 前端层 (React) | 组件 | 职责 | |------|------| | Chat UI | 多轮对话界面, 流式 token 显示, interrupt 确认/拒绝交互 | | Replay UI | 分步时间线回放 Agent 决策过程、工具调用和返回结果 | | Analytics Dashboard | 解决率、Agent 使用率、升级率、对话成本等指标可视化 | | OpenAPI Import UI | 粘贴 URL, 查看导入进度, 审核 LLM 分类结果 | ### 2.2 API 层 (FastAPI) | 模块 | 职责 | |------|------| | main.py | 应用入口, WebSocket 端点, 静态文件服务 | | WebSocket Handler | 双向通信: 接收用户消息, 流式返回 token, 处理 interrupt 响应 | ### 2.3 Agent 编排层 (LangGraph) | 模块 | 职责 | |------|------| | graph.py | 初始化 LangGraph Supervisor, 绑定 Agent + 工具 + Checkpointer | | registry.py | 从 YAML 文件加载 Agent 定义, 验证配置合法性 | | agents/ | Agent 定义: 每个 Agent 拥有独立的 system prompt, 工具集, 权限边界 | ### 2.4 工具集成层 | 模块 | 职责 | |------|------| | MCP Tools | 通过 `langchain-mcp-adapters` (MultiServerMCPClient) 连接 MCP 服务器 | | @tool 装饰器 | 直接 REST/GraphQL HTTP 调用, 无 MCP 开销 | | CLI Wrappers | 包装现有 CLI 工具 (Shopify CLI, AWS CLI 等), 解析 stdout/stderr | ### 2.5 持久化层 | 模块 | 职责 | |------|------| | PostgresSaver | LangGraph checkpoint 持久化, 支持回放和分析查询 | | PostgreSQL | 存储会话状态、对话历史、分析数据 | ### 2.6 横切模块 | 模块 | 职责 | |------|------| | callbacks.py | LangChain callback, 统计每次对话的 token 用量和成本 | | openapi/ | OpenAPI 规范解析 + SSRF 防护 + LLM 端点分类 + MCP 服务器生成 | | replay/ | 自定义分页 API, 查询 checkpointer 状态历史 | | analytics/ | 聚合查询: 解决率、Agent 使用率、升级率、成本 | --- ## 3. 数据流图 ### 3.1 消息处理流程 ``` 用户输入消息 | v [1] WebSocket 接收消息 | v [2] 构造 LangGraph input (thread_id + message) | v [3] Supervisor 收到消息 | +---> [3a] 意图分类 (LLM structured output) | v [4] 路由到目标 Agent (基于 Agent 描述匹配) | +---> [4a] 无匹配 -> Fallback Agent -> 通用回复或澄清提问 | v [5] Agent 执行 | +---> [5a] 读操作: 直接调用 MCP Tool -> 返回结果 | +---> [5b] 写操作: 触发 interrupt() -> 进入中断流程 (见 3.2) | v [6] Agent 生成回复 | v [7] astream_events() 流式推送 token | v [8] WebSocket 发送给客户端 | v [9] PostgresSaver 自动保存 checkpoint ``` ### 3.2 中断 (Interrupt) 流程 ``` Agent 调用写操作工具 | v [1] interrupt() 暂停图执行 | v [2] 服务端通过 WebSocket 发送确认提示 {type: "interrupt", action: "cancel_order", params: {...}} | v [3] 客户端显示确认对话框 | +---> [3a] 用户批准 | | | v | [4a] Command(resume=True) 恢复图执行 | | | v | [5a] 工具执行操作 -> 返回结果 -> 流式回复 | +---> [3b] 用户拒绝 | | | v | [4b] Command(resume=False) 恢复图执行 | | | v | [5b] Agent 确认未执行 -> 流式回复 | +---> [3c] 30 分钟无响应 (TTL 超时) | v [4c] 自动取消 + 重新评估当前状态 | v [5c] 向用户发送过期通知 + 提供重试选项 会话 TTL 规则 (来源: design-doc.md): - 会话采用 30 分钟滑动窗口 TTL, 每次用户消息重置计时器 - 待审批的 interrupt 延长会话 TTL, 直到用户响应或 interrupt 超时 - interrupt 自身有独立的 30 分钟固定 TTL (超时自动取消) - WebSocket 断线重连时, 服务端重新发送未完成的 interrupt 提示 ``` ### 3.3 OpenAPI 导入流程 ``` 运维人员粘贴 OpenAPI 规范 URL | v [1] SSRF 防护检查 | +---> 私有 IP / DNS 重绑定 -> 拒绝并返回错误 | v [2] 获取 OpenAPI 规范文件 | v [3] openapi-spec-validator 验证规范格式 | +---> 格式错误 -> 返回清晰的错误信息 | v [4] 解析所有端点 (路径 + 方法 + 参数 + Schema) | v [5] LLM 自动分类 (异步后台任务) | - 读/写分类 | - 客户参数识别 | - Agent 分组建议 | (通过 WebSocket 推送进度) | v [6] 运维人员审核分类结果 | - 修正错误分类 | - 调整 Agent 分组 | v [7] 生成 MCP 服务器 + Agent YAML 配置 | v [8] 新工具注册到 Agent Registry | v [9] 立即可在聊天中使用 ``` --- ## 4. 技术栈决策 | 组件 | 选型 | 理由 | |------|------|------| | 语言 | Python 3.11+ | LangGraph/LangChain 生态首选语言, Agent 开发最成熟 | | Web 框架 | FastAPI | 原生 async, WebSocket 支持, 性能优秀 | | Agent 编排 | LangGraph v1.1 + langgraph-supervisor | 内置 supervisor 模式, 中间件支持, 不重复造轮子 | | MCP 集成 | langchain-mcp-adapters | MultiServerMCPClient 管理多 MCP 连接 | | 本地工具 | LangChain @tool 装饰器 | 简单 Python 函数即工具, 无 MCP 开销 | | 状态持久化 | PostgresSaver (langgraph-checkpoint-postgres v3.0.5) | 从第一天起用 PostgreSQL, 支持回放/分析查询 | | 数据库 | PostgreSQL 16 | 成熟稳定, jsonb 支持, checkpoint 兼容 | | LLM | Claude Sonnet 4.6 (默认) | 通过 LangChain BaseChatModel 抽象, 支持切换 OpenAI/Google | | 流式输出 | FastAPI WebSocket + astream_events() | 双向通信, 内置流式支持 | | 前端 | React | 组件化, 生态成熟, 满足 Chat/Replay/Dashboard 三个 UI 面 | | 部署 | Docker Compose | 单命令启动, PostgreSQL + 应用 | | OpenAPI 验证 | openapi-spec-validator | 成熟的 Python OpenAPI 规范验证库 | | 测试 | pytest + FastAPI TestClient | Python 测试标准, 支持 async | ### LLM 提供商抽象 ``` 环境变量: LLM_PROVIDER=anthropic # anthropic | openai | google LLM_MODEL=claude-sonnet-4-6-20250514 代码路径: LangChain BaseChatModel -> ChatAnthropic / ChatOpenAI / ChatGoogleGenerativeAI ``` 不构建自定义 wrapper。使用 LangChain 内置的提供商抽象。 --- ## 5. 数据库设计 ### 5.1 设计原则 - Phase 1 锁定 schema, 确保 Phase 4 分析/回放无需迁移 - PostgresSaver 管理 checkpoint 表 (由 langgraph-checkpoint-postgres 自动创建) - 分析数据通过查询 checkpoint 表 + 自定义表聚合 ### 5.2 表结构 #### checkpoints (由 PostgresSaver 自动管理) ```sql -- langgraph-checkpoint-postgres 自动创建的表 -- 存储 LangGraph 图的每一步状态快照 CREATE TABLE checkpoints ( thread_id TEXT NOT NULL, checkpoint_ns TEXT NOT NULL DEFAULT '', checkpoint_id TEXT NOT NULL, parent_checkpoint_id TEXT, type TEXT, checkpoint JSONB NOT NULL, -- 图状态快照 metadata JSONB DEFAULT '{}', -- 包含 step, source, writes 等 created_at TIMESTAMPTZ DEFAULT NOW(), PRIMARY KEY (thread_id, checkpoint_ns, checkpoint_id) ); -- checkpoint 写入记录 CREATE TABLE checkpoint_writes ( thread_id TEXT NOT NULL, checkpoint_ns TEXT NOT NULL DEFAULT '', checkpoint_id TEXT NOT NULL, task_id TEXT NOT NULL, idx INTEGER NOT NULL, channel TEXT NOT NULL, type TEXT, blob BYTEA NOT NULL, PRIMARY KEY (thread_id, checkpoint_ns, checkpoint_id, task_id, idx) ); ``` #### conversations (自定义 - 对话元数据) ```sql CREATE TABLE conversations ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), thread_id TEXT UNIQUE NOT NULL, -- 关联 checkpoints.thread_id customer_id TEXT, -- 客户标识 (可选) status TEXT NOT NULL DEFAULT 'active', -- active | resolved | escalated resolution_type TEXT, -- auto | human | escalated started_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), ended_at TIMESTAMPTZ, turn_count INTEGER DEFAULT 0, agents_used TEXT[] DEFAULT '{}', -- 使用过的 Agent 名称 total_tokens INTEGER DEFAULT 0, total_cost_usd NUMERIC(10,6) DEFAULT 0, escalation_url TEXT, -- Webhook 升级目标 URL metadata JSONB DEFAULT '{}' ); CREATE INDEX idx_conversations_status ON conversations(status); CREATE INDEX idx_conversations_started_at ON conversations(started_at); ``` #### interrupts (自定义 - 中断审批记录) ```sql CREATE TABLE interrupts ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), thread_id TEXT NOT NULL REFERENCES conversations(thread_id), agent_name TEXT NOT NULL, action TEXT NOT NULL, -- cancel_order, apply_discount 等 parameters JSONB NOT NULL, status TEXT NOT NULL DEFAULT 'pending', -- pending | approved | rejected | expired created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), resolved_at TIMESTAMPTZ, resolved_by TEXT, -- customer | operator | system(ttl) ttl_expires_at TIMESTAMPTZ NOT NULL -- created_at + 30 min ); CREATE INDEX idx_interrupts_status ON interrupts(status); CREATE INDEX idx_interrupts_ttl ON interrupts(ttl_expires_at) WHERE status = 'pending'; ``` #### analytics_events (自定义 - 分析事件流) ```sql CREATE TABLE analytics_events ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), thread_id TEXT NOT NULL, event_type TEXT NOT NULL, -- message | tool_call | interrupt | escalation | resolution agent_name TEXT, tool_name TEXT, tokens_used INTEGER DEFAULT 0, cost_usd NUMERIC(10,6) DEFAULT 0, duration_ms INTEGER, success BOOLEAN, error_message TEXT, metadata JSONB DEFAULT '{}', created_at TIMESTAMPTZ NOT NULL DEFAULT NOW() ); CREATE INDEX idx_analytics_thread ON analytics_events(thread_id); CREATE INDEX idx_analytics_type ON analytics_events(event_type); CREATE INDEX idx_analytics_created ON analytics_events(created_at); ``` ### 5.3 分析查询示例 ```sql -- 解决率 (成功工具调用 + 无升级) SELECT COUNT(*) FILTER (WHERE resolution_type = 'auto') * 100.0 / COUNT(*) AS resolution_rate FROM conversations WHERE started_at >= NOW() - INTERVAL '7 days'; -- Agent 使用率 SELECT unnest(agents_used) AS agent, COUNT(*) AS usage_count FROM conversations WHERE started_at >= NOW() - INTERVAL '7 days' GROUP BY agent ORDER BY usage_count DESC; -- 成本/对话 SELECT AVG(total_cost_usd) AS avg_cost FROM conversations WHERE ended_at IS NOT NULL AND started_at >= NOW() - INTERVAL '7 days'; ``` --- ## 6. API 设计 ### 6.1 WebSocket 协议 **端点:** `ws://host:8000/ws` #### 客户端 -> 服务端 ```json // 发送消息 { "type": "message", "thread_id": "conv-abc-123", "content": "取消订单 #1042" } // 中断响应 (批准) { "type": "interrupt_response", "thread_id": "conv-abc-123", "interrupt_id": "int-xyz-789", "approved": true } // 中断响应 (拒绝) { "type": "interrupt_response", "thread_id": "conv-abc-123", "interrupt_id": "int-xyz-789", "approved": false } ``` #### 服务端 -> 客户端 ```json // 流式 token { "type": "token", "thread_id": "conv-abc-123", "content": "正在" } // 中断提示 (需要人工确认) { "type": "interrupt", "thread_id": "conv-abc-123", "interrupt_id": "int-xyz-789", "agent": "order_actions", "action": "cancel_order", "params": {"order_id": "1042"}, "description": "取消订单 #1042", "ttl_seconds": 1800 } // 工具调用通知 (UI 展示用) { "type": "tool_call", "thread_id": "conv-abc-123", "agent": "order_lookup", "tool": "get_order_status", "params": {"order_id": "1042"} } // 工具结果 { "type": "tool_result", "thread_id": "conv-abc-123", "tool": "get_order_status", "result": {"status": "shipped", "tracking": "SF1234567"} } // 消息完成 { "type": "message_complete", "thread_id": "conv-abc-123" } // 错误 { "type": "error", "thread_id": "conv-abc-123", "message": "LLM 服务暂时不可用,请稍后重试" } // OpenAPI 导入进度 { "type": "import_progress", "task_id": "imp-456", "stage": "classifying", "progress": 0.6, "message": "正在分类端点 (18/30)..." } ``` #### 重连机制 WebSocket 断线后客户端重连: 1. 客户端使用相同 `thread_id` 重新连接 2. 服务端从 PostgresSaver 恢复会话状态 3. 如有未完成的 interrupt, 重新发送 interrupt 提示 ### 6.2 REST 端点 | 方法 | 路径 | 描述 | |------|------|------| | GET | `/` | 聊天 UI 静态页面 | | GET | `/api/replay/{thread_id}` | 对话回放 (分页) | | GET | `/api/replay/{thread_id}?page=2&per_page=20` | 回放分页参数 | | GET | `/api/analytics` | 分析仪表盘数据 | | GET | `/api/analytics?range=7d` | 指定时间范围 | | POST | `/api/openapi/import` | 提交 OpenAPI 规范 URL | | GET | `/api/openapi/import/{task_id}` | 查询导入任务状态 | | POST | `/api/openapi/import/{task_id}/review` | 提交分类审核结果 | ### 6.3 回放 API 响应格式 ```json { "success": true, "data": { "thread_id": "conv-abc-123", "total_steps": 42, "page": 1, "per_page": 20, "steps": [ { "step": 1, "type": "user_message", "content": "查一下订单 1042 的状态", "timestamp": "2026-03-29T10:00:00Z" }, { "step": 2, "type": "supervisor_routing", "target_agent": "order_lookup", "reasoning": "用户询问订单状态,路由到 order_lookup agent", "timestamp": "2026-03-29T10:00:01Z" }, { "step": 3, "type": "tool_call", "agent": "order_lookup", "tool": "get_order_status", "params": {"order_id": "1042"}, "result": {"status": "shipped"}, "duration_ms": 230, "timestamp": "2026-03-29T10:00:02Z" }, { "step": 4, "type": "agent_response", "agent": "order_lookup", "content": "订单 #1042 已发货...", "tokens": 87, "timestamp": "2026-03-29T10:00:03Z" } ] }, "error": null } ``` ### 6.4 分析 API 响应格式 ```json { "success": true, "data": { "range": "7d", "total_conversations": 342, "resolution_rate": 0.62, "escalation_rate": 0.15, "avg_turns_per_conversation": 4.2, "avg_cost_per_conversation_usd": 0.034, "agent_usage": [ {"agent": "order_lookup", "count": 198, "percentage": 0.42}, {"agent": "order_actions", "count": 89, "percentage": 0.19}, {"agent": "discount", "count": 55, "percentage": 0.12} ], "interrupt_stats": { "total": 89, "approved": 71, "rejected": 12, "expired": 6 } }, "error": null } ``` --- ## 7. Agent 系统架构 ### 7.1 Agent 注册表 (YAML 驱动) 所有 Agent 通过 `agents.yaml` 声明式配置: ```yaml agents: - name: order_lookup description: "查询订单状态、物流信息、订单详情" permission: read # read 操作不触发 interrupt personality: tone: professional greeting: "您好,我来帮您查询订单信息。" escalation_message: "这个问题需要人工客服介入,我来帮您转接。" tools: - get_order_status - get_tracking_info - get_order_details - name: order_actions description: "取消订单、修改订单、变更收货地址" permission: write # write 操作触发 interrupt personality: tone: careful greeting: "我可以帮您处理订单变更,所有操作都会先经过您的确认。" tools: - cancel_order - modify_order - update_shipping_address - name: discount description: "发放优惠券、应用折扣码、退款处理" permission: write personality: tone: friendly tools: - apply_discount - generate_coupon - process_refund ``` ### 7.2 注册表加载 (registry.py) ``` 启动时: [1] 读取 agents.yaml [2] 验证配置合法性 (名称唯一, 工具存在, 权限合法) - 失败: 清晰的错误信息 (文件名 + 行号) [3] 为每个 Agent 构建 LangGraph node: - 绑定 system prompt (含 personality 配置) - 绑定工具集 - 设置权限边界 [4] 注册到 Supervisor ``` ### 7.3 Supervisor 路由 ``` +---> order_lookup (读) | 用户消息 --> Supervisor --> order_actions (写 + interrupt) (意图分类) | +---> discount (写 + interrupt) | +---> fallback_agent (兜底) ``` 路由机制: - Supervisor 使用 LLM structured output 进行意图分类 - 基于 Agent 的 `description` 字段选择最佳匹配 - 多意图请求 (如 "取消订单并给我优惠券") 由 Supervisor 顺序编排 - 歧义意图 -> 澄清提问 - 无匹配 -> Fallback Agent 处理 ### 7.4 Fallback Agent 专门处理路由失败的通用 Agent: - 捕获所有未匹配的意图 - 提供通用帮助信息 - 引导用户更清晰地表述需求 - 记录未匹配事件供后续分析路由准确率 ### 7.5 垂直行业模板 预置 YAML 模板, 新客户 5 分钟内上手: | 模板 | 包含的 Agent | 典型工具 | |------|-------------|---------| | e-commerce.yaml | order_lookup, order_actions, discount, shipping | 订单查询, 取消, 退款, 物流追踪 | | saas.yaml | account, billing, support | 账户管理, 订阅变更, 工单创建 | | fintech.yaml | account_inquiry, transaction, dispute | 余额查询, 交易记录, 争议处理 | --- ## 8. MCP 集成层 ### 8.1 三层工具架构 ``` LangChain Tool Interface (统一 Python 函数) | +--- [1] MCP Tools (langchain-mcp-adapters) | 用于复杂/有状态的集成 | 通过 MCP 协议 (stdio/SSE) 连接 | +--- [2] Direct API Tools (@tool 装饰器) | 简单 REST/GraphQL 调用 | 无 MCP 开销 | +--- [3] CLI Wrappers (@tool 装饰器) 包装 CLI 工具 (Shopify CLI, AWS CLI) 解析 stdout/stderr ``` 关键设计: LangChain 工具就是 Python 函数加描述 -- 后端实现细节对 Agent 透明。 不构建自定义工具基类。 ### 8.2 MCP 连接管理 ```python # 使用 langchain-mcp-adapters 的 MultiServerMCPClient # 每个 MCP 服务器独立连接 mcp_config = { "shopify": { "command": "python", "args": ["mcp_servers/shopify_server.py"], "transport": "stdio" }, "internal_api": { "url": "http://mcp-server:3000/sse", "transport": "sse" } } ``` ### 8.3 OpenAPI 自动生成 MCP 服务器 Phase 3 核心功能: ``` OpenAPI 3.0 规范 | v [解析] 提取端点 + 参数 + Schema | v [分类] LLM 自动分类: - read / write - 客户参数 (order_id, customer_id 等) - Agent 分组建议 | v [审核] 运维人员修正分类结果 | v [生成] 输出: - MCP 服务器代码 (包装每个端点) - agents.yaml 配置 (Agent 定义 + 工具绑定) | v [注册] 热加载到 Agent Registry ``` ### 8.4 工具故障处理 | 故障类型 | 处理策略 | |---------|---------| | 瞬时错误 (超时/限流) | 指数退避重试 (最多 3 次) | | 认证失败 | 立即升级到人工, 不重试 | | API 不可用 | 向用户显示错误 + 升级 | | 响应格式异常 | 记录日志 + 返回友好错误消息 | --- ## 9. 安全架构 ### 9.1 安全层次 ``` +---------------------------------------+ | [L1] 输入验证 | | - WebSocket 消息格式校验 | | - 消息长度限制 | | - Agent YAML 配置验证 | +---------------------------------------+ | [L2] SSRF 防护 (openapi/ssrf.py) | | - 屏蔽私有 IP (10.x, 172.16-31.x, | | 192.168.x, 127.x, 169.254.x) | | - DNS 重绑定攻击防护 | | - URL 白名单 (可选) | +---------------------------------------+ | [L3] HITL 人工确认 | | - 所有写操作需要人工批准 | | - 30 分钟 TTL, 过期自动取消 | | - 重新评估当前状态后才允许重试 | +---------------------------------------+ | [L4] 权限隔离 | | - 每个 Agent 只能访问配置的工具集 | | - read Agent 无法调用 write 工具 | | - 工具级别的参数校验 | +---------------------------------------+ | [L5] 操作审计 | | - 每个操作记录: Agent, 参数, 结果, | | 时间戳, 操作 ID | | - 全量存入 PostgreSQL | | - 支持回放和事后审查 | +---------------------------------------+ ``` ### 9.2 SSRF 防护详细设计 `openapi/ssrf.py` 作为独立工具模块: ``` URL 输入 | v [1] 解析 URL -> 提取 hostname | v [2] DNS 解析 hostname -> IP 地址 | v [3] 检查 IP 是否为私有地址: - 10.0.0.0/8 - 172.16.0.0/12 - 192.168.0.0/16 - 127.0.0.0/8 - 169.254.0.0/16 - ::1, fe80::/10 - 0.0.0.0 | +---> 私有 IP -> 拒绝, 返回明确错误 | v [4] 发起 HTTP 请求 | v [5] 二次检查: 验证实际连接的 IP (防 DNS 重绑定) | +---> 重绑定到私有 IP -> 中断连接, 返回错误 | v [6] 返回响应内容 ``` ### 9.3 未来安全增强 (NOT in scope, 记录于 TODOS.md) - API Key 认证 (WebSocket 连接) - Session-based 认证 (Dashboard/Replay/Import) - 全端点限流 - 多租户隔离 --- ## 10. 部署架构 ### 10.1 Docker Compose ``` +-------------------------------------------+ | Docker Compose | | | | +----------------+ +------------------+ | | | smart-support | | PostgreSQL 16 | | | | (Python App) | | | | | | | | - checkpoints | | | | FastAPI :8000 +--> - conversations | | | | React (static) | | - interrupts | | | | | | - analytics | | | +----------------+ +------------------+ | | | +-------------------------------------------+ ``` ```yaml # docker-compose.yml (概念) version: "3.9" services: app: build: . ports: - "8000:8000" environment: - DATABASE_URL=postgresql://user:pass@db:5432/smartsupport - LLM_PROVIDER=anthropic - LLM_MODEL=claude-sonnet-4-6-20250514 - ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY} depends_on: - db db: image: postgres:16 environment: - POSTGRES_DB=smartsupport - POSTGRES_USER=user - POSTGRES_PASSWORD=pass volumes: - pgdata:/var/lib/postgresql/data ports: - "5432:5432" volumes: pgdata: ``` ### 10.2 环境变量 | 变量 | 用途 | 默认值 | |------|------|--------| | DATABASE_URL | PostgreSQL 连接串 | (必填) | | LLM_PROVIDER | LLM 提供商 | anthropic | | LLM_MODEL | 模型名称 | claude-sonnet-4-6-20250514 | | ANTHROPIC_API_KEY | Anthropic API Key | (必填) | | OPENAI_API_KEY | OpenAI API Key | (可选) | | INTERRUPT_TTL_MINUTES | 中断超时时间 | 30 | | WEBHOOK_RETRY_MAX | Webhook 最大重试次数 | 3 | ### 10.3 单命令启动 ```bash # 设置 API Key export ANTHROPIC_API_KEY=sk-xxx # 启动 docker compose up # 访问 open http://localhost:8000 ``` --- ## 11. 架构决策记录 (ADR) ### ADR-001: 使用 LangGraph Supervisor 进行多 Agent 编排 **背景:** 需要将不同类型的客服请求路由到不同的专业 Agent。 **决策:** 使用 `langgraph-supervisor` v1.1 内置的 supervisor 模式。 **正面影响:** - 不同 Agent 拥有独立的权限边界和工具集 - 不同操作有不同的失败模式和安全检查 - 框架内置, 无需自建编排逻辑 - 支持中间件扩展 **负面影响:** - 每次请求增加一轮 LLM 调用 (supervisor 路由), 延迟 8-15 秒 - 原型阶段可能过度工程化 (Cross-Model Review 的质疑) **缓解措施:** 全程流式输出 token, 降低用户感知延迟。 **替代方案:** - 单 Agent + 工具路由: 更简单, 但权限隔离困难 - 关键词路由: 无 LLM 开销, 但准确率低 **状态:** ACCEPTED (创始人明确选择, 理由: 不同操作需要不同权限边界) --- ### ADR-002: 从第一天使用 PostgresSaver 而非 InMemorySaver **背景:** LangGraph 提供多种 checkpointer 实现。InMemorySaver 更简单, PostgresSaver 需要数据库。 **决策:** 从 Phase 1 起使用 PostgresSaver + Docker Compose。 **正面影响:** - Phase 4 的回放和分析功能可直接查询, 无需迁移 - 进程重启不丢失会话状态 - 数据 schema 从第一天锁定 **负面影响:** - 开发环境需要运行 PostgreSQL (通过 Docker Compose 缓解) - 比 InMemorySaver 配置更复杂 **替代方案:** - InMemorySaver: 更简单但重启丢数据, 迁移到 PostgresSaver 并非简单配置切换 - SQLiteSaver: 中间方案, 但最终仍需迁移 **状态:** ACCEPTED --- ### ADR-003: WebSocket 双向通信而非 HTTP 轮询 **背景:** 需要支持流式 token 输出 + interrupt 交互。 **决策:** FastAPI WebSocket + LangGraph astream_events()。 **正面影响:** - 真正的双向通信: 服务端推送 token, 客户端发送 interrupt 响应 - 延迟低, 无轮询开销 - LangGraph 原生支持 astream_events() **负面影响:** - WebSocket 连接管理复杂 (断线重连, 状态恢复) - 不如 REST 易于调试 **替代方案:** - SSE + REST: 服务端推送用 SSE, 客户端操作用 REST。更简单但需两个通道。 - HTTP 轮询: 最简单但延迟高, 不适合流式场景。 **状态:** ACCEPTED --- ### ADR-004: YAML 声明式 Agent 注册表 **背景:** 需要让客户和运维人员能配置 Agent, 而不需要修改代码。 **决策:** Agent 定义通过 YAML 文件配置, 启动时加载验证。 **正面影响:** - 非开发人员可配置 Agent - 新增 Agent = 新增配置条目 + 连接工具 - 支持垂直行业模板 (预置 YAML) - 配置可版本控制 **负面影响:** - YAML 配置错误的调试体验不如代码 - 需要构建配置验证逻辑 **缓解措施:** 启动时严格验证, 错误信息包含文件名和行号。 **状态:** ACCEPTED --- ### ADR-005: LangGraph interrupt() 实现 HITL 而非自定义审批系统 **背景:** 写操作需要人工确认。 **决策:** 使用 LangGraph 内置的 `interrupt()` 函数暂停图执行, 等待用户响应。 **正面影响:** - 框架内置, 无需自建状态机 - 与 checkpointer 深度集成, 中断状态自动持久化 - 使用 `Command(resume=value)` 恢复, API 简洁 **负面影响:** - 需要自建 TTL 超时逻辑 (30 分钟) - 需要处理 WebSocket 断线后重发 interrupt **状态:** ACCEPTED --- ### ADR-006: OpenAPI 规范解析 + LLM 分类 + 人工审核三步流程 **背景:** 核心卖点 "粘贴 API, 获得客服" 需要自动理解 API 端点。 **决策:** 三步流程: 解析规范 -> LLM 自动分类 -> 人工审核修正。 **正面影响:** - 自动化程度高: LLM 处理大部分分类工作 - 人工审核兜底: 防止 LLM 错误分类导致安全问题 (将读操作误分为写) - 异步执行 + WebSocket 进度推送, 不阻塞聊天 **负面影响:** - LLM 分类成本 (大型 API 可能有 100+ 端点) - 分类准确率取决于 API 文档质量 **替代方案:** - 纯手动配置: 安全但耗时 - 纯 LLM 自动化: 快但有安全风险 **状态:** ACCEPTED --- ### ADR-007: SSRF 防护作为独立工具模块 **背景:** OpenAPI URL 导入功能允许用户提供任意 URL, 存在 SSRF 风险。 **决策:** 构建独立的 `openapi/ssrf.py` 工具, 屏蔽私有 IP + DNS 重绑定。 **正面影响:** - 独立模块, 可在 Phase 3 前并行开发 - 可复用于其他需要外部 URL 访问的场景 - 双重检查 (DNS 解析前 + 实际连接后) **状态:** ACCEPTED --- ## 12. 非功能性需求 ### 12.1 性能 | 指标 | 目标 | 备注 | |------|------|------| | 首 token 延迟 | < 2 秒 | Supervisor 路由 + Agent 启动 | | 端到端响应时间 | 8-15 秒 | 含 LLM 调用, 通过流式输出缓解感知延迟 | | MCP 工具调用 | < 5 秒 | 取决于下游 API | | 回放 API | < 500ms | 分页查询, 每页 20 步 | | 分析查询 | < 2 秒 | PostgreSQL 聚合查询 + 索引 | | WebSocket 消息 | < 100ms | 本地处理延迟 | ### 12.2 可扩展性 **当前阶段 (原型):** - 单实例部署, Docker Compose - 单租户架构 - 预期负载: 并发 10-50 个对话 **扩展路线图:** | 阶段 | 用户规模 | 架构变更 | |------|---------|---------| | 原型 | 1-10 并发 | 单实例 Docker Compose | | 首个客户 | 10-50 并发 | 增加连接池, 优化查询 | | 多客户 | 50-200 并发 | 多租户隔离, 水平扩展应用层 | | 规模化 | 200+ 并发 | Redis 缓存层, 读写分离, CDN 静态资源 | ### 12.3 可靠性 | 故障场景 | 处理策略 | |---------|---------| | LLM API 超时/限流 | 向用户返回错误消息, 建议重试 | | MCP 工具调用失败 | 升级到人工 + 显示错误消息 | | PostgreSQL 连接断开 | try/except 包裹图调用, 返回用户友好错误 | | WebSocket 断线 | 客户端自动重连, 服务端恢复状态 | | 中断 TTL 过期 | 自动取消 + 提供重试选项 | | Webhook 目标不可达 | 指数退避重试 (最多 3 次) + 记录日志 | | 无效 YAML 配置 | 启动时检查, 清晰报错 (文件+行号) | ### 12.4 可测试性 | 测试类型 | 覆盖范围 | 工具 | |---------|---------|------| | 单元测试 | Agent 逻辑, SSRF 防护, YAML 解析 | pytest | | 集成测试 | WebSocket 消息流, 图调用, Checkpoint | pytest + FastAPI TestClient | | E2E 测试 | 6 个关键流程 (见下) | pytest | **6 个 E2E 关键流程:** 1. 快乐路径: 查询订单状态 -> 获得答案 2. 取消+批准: 取消订单 -> interrupt -> 批准 -> 确认取消 3. 取消+拒绝: 取消订单 -> interrupt -> 拒绝 -> 未执行 4. 多轮上下文: "查订单1042" -> "取消那个" -> 正确解析指代 5. OpenAPI 导入: 粘贴规范 URL -> 工具生成 -> 聊天中使用新工具 6. 对话回放: 选择已完成对话 -> 分步回放正确渲染 **覆盖率目标:** 80%+ ### 12.5 成本控制 | 措施 | 说明 | |------|------| | Prompt 缓存 | 从第一天启用 (Phase 1 任务 1.4.5), 减少重复 system prompt 的 LLM 成本。来源: TODOS.md 设计变更 | | Token 用量统计 | LangChain callback 记录每次对话的 token 消耗 | | 成本/对话指标 | 在分析仪表盘展示, 支持优化决策 | | LLM 提供商可切换 | 可根据成本/性能在 Claude/GPT/Gemini 间切换 | ### 12.6 NOT in Scope > 完整的范围外事项清单见 [DEVELOPMENT-PLAN.md 范围外事项](DEVELOPMENT-PLAN.md#范围外事项-来自工程评审)。 > 关键排除项: 认证/授权、多租户、CI/CD、限流、Zendesk/Intercom 集成、移动端、i18n、计费。 --- ## 附录: 关键参考文档 | 文档 | 路径 | 内容 | |------|------|------| | **开发计划** | [`docs/DEVELOPMENT-PLAN.md`](DEVELOPMENT-PLAN.md) | 分阶段任务清单、检查点标准、风险登记册、并行化策略 | | 项目概览 | `README.md` | 技术栈, 项目结构, 快速开始 | | CEO 计划 | `ceo-plan.md` | 产品愿景, 范围决策, 6 项扩展 | | 设计文档 | `design-doc.md` | 问题定义, 约束, 方案选择 | | 工程评审 | `eng-review-plan.md` | 架构决策, 测试策略, 失败模式 | | 测试计划 | `eng-review-test-plan.md` | 测试路径, 边界情况, E2E 流程 | | 待办事项 | `TODOS.md` | 延迟项, 设计变更记录 |