Files
smart-support/docs/ARCHITECTURE.md
Yaojia Wang f0699436c5 refactor: engineering improvements -- API versioning, structured logging, Alembic, error standardization, test coverage
- API versioning: all REST endpoints prefixed with /api/v1/
- Structured logging: replaced stdlib logging with structlog (console/JSON modes)
- Alembic migrations: versioned DB schema with initial migration
- Error standardization: global exception handlers for consistent envelope format
- Interrupt cleanup: asyncio background task for expired interrupt removal
- Integration tests: +30 tests (analytics, replay, openapi, error, session APIs)
- Frontend tests: +57 tests (all components, pages, useWebSocket hook)
- Backend: 557 tests, 89.75% coverage | Frontend: 80 tests, 16 test files
2026-04-06 23:19:29 +02:00

37 KiB

Smart Support -- 系统架构文档

版本: 1.0 | 日期: 2026-03-29 | 状态: APPROVED (CEO + ENG CLEARED)

配套文档: 分阶段任务清单、检查点标准、风险登记册详见 DEVELOPMENT-PLAN.md


目录

  1. 系统概览
  2. 组件职责分解
  3. 数据流图
  4. 技术栈决策
  5. 数据库设计
  6. API 设计
  7. Agent 系统架构
  8. MCP 集成层
  9. 安全架构
  10. 部署架构
  11. 架构决策记录 (ADR)
  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 0.0.30+|
+--------+--------------------+
         |
    +----+----+----+----+
    |    |    |    |    |
    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 构建
│   │   ├── graph_context.py     # GraphContext: 图 + 分类器 + 注册表的类型化封装
│   │   ├── ws_handler.py        # WebSocket 消息分发 + 速率限制
│   │   ├── ws_context.py        # WebSocketContext: WS 依赖包
│   │   ├── auth.py              # API Key 认证中间件
│   │   ├── api_utils.py         # 共享 API 响应工具 (envelope)
│   │   ├── 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 端点, 静态文件服务
auth.py API Key 认证: 管理端点通过 X-API-Key header, WebSocket 通过 ?token= query param
ws_handler.py 双向通信: 接收用户消息, 流式返回 token, 处理 interrupt 响应
graph_context.py 类型化封装: 将编译后的图与分类器、注册表绑定, 替代猴子补丁
ws_context.py 依赖包: 将 WebSocket 处理所需的 9 个依赖打包为单一不可变对象
api_utils.py 共享响应格式: 统一的 envelope() 函数

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 1.x + 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';

sessions (自定义 - 会话状态持久化)

-- 用于多 worker 部署的 PostgreSQL 会话状态管理
-- PgSessionManager 使用此表替代内存中的 dict
CREATE TABLE sessions (
    thread_id               TEXT PRIMARY KEY,
    last_activity           TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    has_pending_interrupt   BOOLEAN NOT NULL DEFAULT FALSE,
    created_at              TIMESTAMPTZ NOT NULL DEFAULT NOW()
);

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 断线后客户端重连:

  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 响应格式

{
    "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 关键流程:

  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 范围外事项。 关键排除项: 认证/授权、多租户、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 延迟项, 设计变更记录