---
created: 2026-03-29
type: reference
project: "[[Smart Support]]"
source: docs/ARCHITECTURE.md
tags:
  - architecture
  - ai-agent
  - multi-agent
  - langgraph
  - websocket
  - postgresql
  - mcp
  - security
  - ssrf
  - deployment
  - docker
  - api-design
  - database-schema
  - adr
  - hitl
  - interrupt
  - openapi
---

# 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` | 延迟项, 设计变更记录 |
