- CLAUDE.md with phase execution workflow and project conventions - ARCHITECTURE.md with system design, data flow, and component breakdown - DEVELOPMENT-PLAN.md with detailed 5-phase task breakdown
36 KiB
Smart Support -- 系统架构文档
版本: 1.0 | 日期: 2026-03-29 | 状态: APPROVED (CEO + ENG CLEARED)
配套文档: 分阶段任务清单、检查点标准、风险登记册详见 DEVELOPMENT-PLAN.md
目录
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 自动管理)
-- 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 (自定义 - 对话元数据)
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 (自定义 - 中断审批记录)
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 (自定义 - 分析事件流)
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 分析查询示例
-- 解决率 (成功工具调用 + 无升级)
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
客户端 -> 服务端
// 发送消息
{
"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
}
服务端 -> 客户端
// 流式 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 断线后客户端重连:
- 客户端使用相同
thread_id重新连接 - 服务端从 PostgresSaver 恢复会话状态
- 如有未完成的 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 响应格式
{
"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 响应格式
{
"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 声明式配置:
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 连接管理
# 使用 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 | |
| +----------------+ +------------------+ |
| |
+-------------------------------------------+
# 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 单命令启动
# 设置 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 关键流程:
- 快乐路径: 查询订单状态 -> 获得答案
- 取消+批准: 取消订单 -> interrupt -> 批准 -> 确认取消
- 取消+拒绝: 取消订单 -> interrupt -> 拒绝 -> 未执行
- 多轮上下文: "查订单1042" -> "取消那个" -> 正确解析指代
- OpenAPI 导入: 粘贴规范 URL -> 工具生成 -> 聊天中使用新工具
- 对话回放: 选择已完成对话 -> 分步回放正确渲染
覆盖率目标: 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 范围外事项。 关键排除项: 认证/授权、多租户、CI/CD、限流、Zendesk/Intercom 集成、移动端、i18n、计费。
附录: 关键参考文档
| 文档 | 路径 | 内容 |
|---|---|---|
| 开发计划 | docs/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 |
延迟项, 设计变更记录 |