--- created: "2026-03-29" type: project status: 未开始 parent: "[[Smart Support]]" phase: 4 timeline: "第 6-7 周" --- # Phase 4:分析 + 回放 ## 目标 让客户看到 AI 客服的 ROI。对话回放让客户信任系统(看到 AI 为什么做了某个决定),分析仪表盘用数据证明价值(自动解决了多少问题、省了多少成本)。这个阶段结束时,Smart Support 是一个完整可演示的产品。 ## 前置条件 - [[Smart Support/Phase 1 - 核心框架]] 完成(PostgresSaver 已持久化所有 checkpoint 数据) - [[Smart Support/Phase 3 - OpenAPI 自动发现]] 完成(有真实工具调用数据可分析) - Token 用量统计回调已运行(Phase 1 实现) ## 阶段产出 - 对话回放页面:逐步展示 Agent 的决策过程 - 分析仪表盘:解决率、Agent 使用率、升级率、每对话成本 - 数据驱动的 ROI 证明能力 ## 集成检查点 第 7 周末验证: 1. 完成几轮对话后,打开回放页面 → 看到完整决策时间线 2. 分析仪表盘显示正确的解决率和成本数据 3. 零数据状态(新部署)→ 仪表盘显示空状态引导 4. 200+ 轮对话的回放 → 分页正常,不卡顿 --- ## 任务清单 ### 1. 对话回放 API - [ ] 端点 `GET /api/conversations` → 对话列表(分页) - 返回:`thread_id`, 开始时间, 消息数, 最终状态(resolved/escalated/abandoned) - 支持筛选:按状态、按日期范围、按 agent - 分页参数:`page`, `page_size`(默认 20) - [ ] 端点 `GET /api/replay/{thread_id}` → 单个对话的回放数据(分页) - 查询 PostgresSaver checkpoint 表,按 checkpoint_id 排序 - 每个 checkpoint 解析为结构化时间线事件: ```json { "thread_id": "uuid", "total_steps": 15, "page": 1, "page_size": 50, "events": [ { "step": 1, "timestamp": "2026-04-10T14:30:00Z", "type": "user_message", "content": "查询订单 1042 的状态" }, { "step": 2, "timestamp": "2026-04-10T14:30:01Z", "type": "routing", "agent": "order_lookup", "reasoning": "用户请求查询订单状态" }, { "step": 3, "timestamp": "2026-04-10T14:30:02Z", "type": "tool_call", "agent": "order_lookup", "tool": "get_order_status", "input": {"order_id": "1042"}, "output": {"status": "shipped", "tracking": "SF1234567"}, "duration_ms": 230 }, { "step": 4, "timestamp": "2026-04-10T14:30:03Z", "type": "agent_response", "agent": "order_lookup", "content": "您的订单 1042 已发货,运单号 SF1234567", "tokens": {"input": 450, "output": 35} } ] } ``` - 分页:`page` + `page_size` 控制每页 events 数量 - thread 不存在 → 404 - [ ] 端点 `GET /api/replay/{thread_id}/summary` → 对话摘要 - 总步骤数、涉及的 agents、工具调用次数、总 token 用量、总耗时、最终状态 ### 2. 对话回放 UI - [ ] 对话列表页: - 表格显示所有对话(时间、消息数、状态、涉及 agent) - 状态标签:🟢 已解决 / 🟡 已升级 / ⚫ 已放弃 - 点击进入回放详情 - [ ] 回放详情页: - 左侧:原始聊天记录(用户消息 + AI 回复) - 右侧:决策时间线(路由决策、工具调用、参数、返回值、耗时) - 时间线高亮: - 工具调用 → 蓝色 - interrupt 确认 → 黄色 - 错误/升级 → 红色 - 每个步骤可展开查看详细信息(工具输入输出、token 用量) - 支持键盘导航(上/下箭头逐步浏览) - [ ] 长对话分页加载(滚动加载或分页按钮) ### 3. 分析数据查询 - [ ] 数据来源:PostgresSaver checkpoint 表 + token 用量表 - [ ] 核心指标计算: **解决率** ```sql -- resolved = 至少一次成功工具调用 且 未触发升级 resolved_count / total_conversations * 100 ``` **Agent 使用率** ```sql -- 每个 agent 被路由到的次数占总路由次数的百分比 SELECT agent_name, COUNT(*) * 100.0 / total_routes AS usage_pct ``` **升级率** ```sql -- 触发 webhook 升级的对话占总对话的百分比 escalated_count / total_conversations * 100 ``` **每对话成本** ```sql -- 基于 token 用量计算 SELECT thread_id, SUM(input_tokens) * input_price + SUM(output_tokens) * output_price AS cost ``` **对话量趋势** ```sql -- 按天/周/月聚合对话数量 SELECT DATE(created_at) AS date, COUNT(DISTINCT thread_id) AS conversations GROUP BY date ORDER BY date ``` - [ ] 时间范围筛选:今天 / 7 天 / 30 天 / 自定义 - [ ] 所有查询加索引优化(checkpoint 表的 thread_id + timestamp) ### 4. 分析仪表盘 API - [ ] 端点 `GET /api/analytics/overview` → 概览数据 ```json { "period": "last_7_days", "total_conversations": 142, "resolution_rate": 73.2, "escalation_rate": 12.7, "avg_cost_per_conversation": 0.045, "total_cost": 6.39, "avg_messages_per_conversation": 4.2, "avg_resolution_time_seconds": 45 } ``` - [ ] 端点 `GET /api/analytics/agents` → Agent 使用分布 ```json { "agents": [ {"name": "order_lookup", "usage_pct": 45.3, "resolution_rate": 89.1}, {"name": "order_actions", "usage_pct": 30.2, "resolution_rate": 72.5}, {"name": "discount", "usage_pct": 15.8, "resolution_rate": 65.0}, {"name": "fallback", "usage_pct": 8.7, "resolution_rate": 20.0} ] } ``` - [ ] 端点 `GET /api/analytics/trend` → 对话量趋势(按日) - [ ] 端点 `GET /api/analytics/costs` → 成本趋势 + 按 agent 成本分布 - [ ] 所有端点支持 `period` 参数(`today`, `7d`, `30d`, `custom`) ### 5. 分析仪表盘 UI - [ ] 概览卡片(顶部): - 解决率(百分比 + 趋势箭头) - 总对话数 - 升级率 - 平均成本/对话 - [ ] Agent 使用分布(饼图或条形图) - [ ] 对话量趋势(折线图,按日) - [ ] 成本趋势(折线图,按日) - [ ] 零数据状态: - 没有对话数据时,显示引导页面:「开始你的第一次对话,数据将在这里展示」 - 卡片显示 "—" 而非 0 或 NaN ### 6. 对话状态判定 - [ ] 实现对话最终状态判定逻辑: - **resolved**:至少一次成功工具调用 + 未触发升级 webhook - **escalated**:触发了升级 webhook - **abandoned**:最后一条消息是用户发送的,且超过 30 分钟无后续(session TTL 过期) - [ ] 状态写入 checkpoint metadata 或独立表 - [ ] 状态判定在对话结束时(WebSocket 断开 或 TTL 过期)异步执行 ### 7. 测试 - [ ] **回放 API 测试:** 有效 thread_id → 返回结构化时间线 - [ ] **回放 API 测试:** 不存在的 thread_id → 404 - [ ] **回放 API 测试:** 大对话(200+ 步骤)→ 分页正常 - [ ] **回放 API 测试:** 时间线事件类型覆盖(user_message, routing, tool_call, agent_response, interrupt, error) - [ ] **分析 API 测试:** overview 返回正确的解决率计算 - [ ] **分析 API 测试:** agent 使用分布百分比之和 = 100% - [ ] **分析 API 测试:** 成本计算准确(基于 token 用量 × 价格) - [ ] **分析 API 测试:** 时间范围筛选正确 - [ ] **零数据测试:** 无对话 → 所有指标返回合理默认值(非 NaN/null) - [ ] **状态判定测试:** 成功工具调用 + 无升级 → resolved - [ ] **状态判定测试:** 触发 webhook → escalated - [ ] **状态判定测试:** 用户最后发言 + 超时 → abandoned - [ ] **E2E 测试:** 完成对话 → 回放页面正确展示 → 仪表盘数据更新 ## 技术要点 | 功能 | 实现方式 | 说明 | |------|---------|------| | 回放数据 | PostgresSaver checkpoint 表查询 | 按 thread_id + checkpoint_id 排序 | | 分页 | OFFSET/LIMIT 或 cursor-based | 大数据量用 cursor | | 图表 | Recharts 或 Chart.js | React 图表库 | | 索引 | checkpoint 表加 thread_id + created_at 索引 | 保证查询性能 | | 状态判定 | 异步任务 | WebSocket 断开或 TTL 到期时触发 | ## 风险与缓解 | 风险 | 影响 | 缓解措施 | |------|------|---------| | Checkpoint 数据格式变化 | 回放解析失败 | 版本化 checkpoint 格式,解析失败降级显示原始数据 | | 大量对话数据查询慢 | 仪表盘加载慢 | 加索引 + 预聚合热门查询(物化视图) | | 解决率定义不准确 | 指标误导 | 可配置定义,后续加入客户满意度信号 | | Token 价格变化 | 成本计算不准 | 价格配置化,支持不同模型不同价格 | ## Related - [[Smart Support/Phase 3 - OpenAPI 自动发现]] - [[Smart Support/Phase 5 - 打磨 + 演示]] - [[Smart Support]]