245 lines
9.6 KiB
Markdown
245 lines
9.6 KiB
Markdown
# 问数结果 · 分析助手(文字 + 可选图表)设计
|
||||
|
|
|
|||
|
|
**日期:** 2026-09-12
|
|||
|
|
**状态:** 已实现(2026-09-12)
|
|||
|
|
**关联需求:** D-12 子集(问数页可视化)· 不实现 D-09 多轮 · NL2SQL 路径不变
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 1. 目标与边界
|
|||
|
|
|
|||
|
|
### 1.1 用户故事
|
|||
|
|
|
|||
|
|
分析员在问数工作台先 NL 问数得到表格;再打开**分析助手对话框**,用自然语言说明要什么——可能只要文字解读、只要可视化、或两者都要。大模型根据**当前表格结构 + 用户分析提示词**决定输出形态与图表类型(如时间序列 → 折线图),前端渲染可简单互动的图表。
|
|||
|
|
|
|||
|
|
### 1.2 必须覆盖的三类意图(分析阶段)
|
|||
|
|
|
|||
|
|
| 用户意图 | 问数阶段 | 分析对话框提交后 |
|
|||
|
|
| --- | --- | --- |
|
|||
|
|
| **只要表** | 点「问数」即可 | **不打开**分析助手;无额外 LLM |
|
|||
|
|
| **只要分析(文字)** | 先有表 | `answer` 有内容,`chart=null` |
|
|||
|
|
| **只要图** | 先有表 | `chart` 有 spec,`answer` 可为空或一句说明 |
|
|||
|
|
| **要表 + 分析(文+图)** | 先有表 | `answer` + `chart` 同时存在 |
|
|||
|
|
|
|||
|
|
「表」指问数 SQL 执行结果表格,始终在问数成功后展示;分析助手**不再执行 SQL**。
|
|||
|
|
|
|||
|
|
### 1.3 明确不做(本期)
|
|||
|
|
|
|||
|
|
- D-09 多轮追问 / Redis 会话上下文
|
|||
|
|
- 在主问数输入框里混合「画图」语句(避免误走 NL2SQL)
|
|||
|
|
- 把 chart spec 沉淀为 D-11 资产
|
|||
|
|
- D-12 看板卡片钻取进对话
|
|||
|
|
- 替换行情详情页 `ProductNavChartPanel`(Chart.js)
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 2. 交互设计
|
|||
|
|
|
|||
|
|
### 2.1 问数工作台(`AnalystQueryPage.tsx`)
|
|||
|
|
|
|||
|
|
1. **主输入 +「问数」**:行为不变,`POST /api/analyst/chat`,`interpret=false`。
|
|||
|
|
2. 成功/降级且有行时展示结果表格、SQL、溯源/转人工等现有能力。
|
|||
|
|
3. 原「分析该数据」单按钮 → **「分析助手…」**,打开 **Modal**(或小 Drawer):
|
|||
|
|
- 说明:基于**本轮问数问题 + 下方表格**,不会重新查库。
|
|||
|
|
- **分析提示词**(必填):多行输入。
|
|||
|
|
- Placeholder 示例轮换:
|
|||
|
|
- 「用三句话总结这张表的关键结论」
|
|||
|
|
- 「把各产品按日期的净值趋势画成可悬停折线图」
|
|||
|
|
- 「总结要点并画一张适合这张表的图」
|
|||
|
|
- 主按钮:**「生成分析」**(loading 态)。
|
|||
|
|
4. 提交后在表格下方新增区域(可与原「数据解读」Card 合并):
|
|||
|
|
- 有 `answer` → 文字 Card + disclaimer
|
|||
|
|
- 有 `chart` → `ChartCard` + `AnalystResultChartPanel`
|
|||
|
|
5. **新开一轮问数**时清空分析结果(与现清空 interpret 一致)。
|
|||
|
|
6. **门禁**:`status ∈ {success, degrade}` 且 `table.rows.length > 0` 才可打开对话框;否则 Tooltip 提示先问数。
|
|||
|
|
|
|||
|
|
### 2.2 与「只要表」的关系
|
|||
|
|
|
|||
|
|
用户若仅需表格:**问数后不点分析助手**即可。无需新开关;顶栏 Alert 可补一句「需要解读或图表时点分析助手」。
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 3. API 契约
|
|||
|
|
|
|||
|
|
### 3.1 新增:`POST /api/analyst/analyze`
|
|||
|
|
|
|||
|
|
| 项 | 说明 |
|
|||
|
|
| --- | --- |
|
|||
|
|
| 鉴权 | 与 `/api/analyst/chat` 相同(`deps` + analyst RBAC) |
|
|||
|
|
| 请求 | 扩展问数快照 + 分析提示词 |
|
|||
|
|
|
|||
|
|
```json
|
|||
|
|
{
|
|||
|
|
"question": "上轮 NL 问数原问题",
|
|||
|
|
"analysis_prompt": "用户在对画框里输入的内容",
|
|||
|
|
"status": "success",
|
|||
|
|
"answer": "",
|
|||
|
|
"table": { "columns": [], "rows": [] },
|
|||
|
|
"sql": "...",
|
|||
|
|
"trace_id": "trace-...",
|
|||
|
|
"meta": { "row_count": 0, "...": "..." }
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
| 字段 | 约束 |
|
|||
|
|
| --- | --- |
|
|||
|
|
| `question` | 上轮问数问题(审计关联) |
|
|||
|
|
| `analysis_prompt` | 1~2000 字,trim 后非空 |
|
|||
|
|
| 其余 | 与现有 `InterpretRequest` 一致,客户端提交当前页快照 |
|
|||
|
|
|
|||
|
|
### 3.2 响应:扩展 `AnalystResponse`
|
|||
|
|
|
|||
|
|
新增字段:
|
|||
|
|
|
|||
|
|
```typescript
|
|||
|
|
type ChartType = 'line' | 'bar' | 'column' | 'pie' | 'none'
|
|||
|
|
|
|||
|
|
type ChartSpec = {
|
|||
|
|
chart_type: ChartType
|
|||
|
|
title: string
|
|||
|
|
reason?: string // 模型简述为何选此图型(展示在 subtitle)
|
|||
|
|
x_field: string | null // pie 可为 null
|
|||
|
|
y_fields: string[] // 一个或多个度量列
|
|||
|
|
series_field?: string | null // 多系列折线/柱:系列维度列
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// AnalystResponse 增:
|
|||
|
|
analysis_kind: 'text' | 'chart' | 'both' | 'none'
|
|||
|
|
chart: ChartSpec | null
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
| `analysis_kind` | 条件 |
|
|||
|
|
| --- | --- |
|
|||
|
|
| `text` | 有 `answer`,无有效 chart |
|
|||
|
|
| `chart` | 有 chart,`answer` 可选短说明 |
|
|||
|
|
| `both` | 同时有 |
|
|||
|
|
| `none` | 失败/空(应极少;降级时用 `degrade` + message) |
|
|||
|
|
|
|||
|
|
`table` / `sql` / `meta`:原样回传客户端快照(便于前端不丢上下文);**不**因分析而改表数据。
|
|||
|
|
|
|||
|
|
### 3.3 保留 `/api/analyst/interpret`
|
|||
|
|
|
|||
|
|
- 一期实现 **`/analyze` 后前端改走 analyze**;`/interpret` 保留兼容旧客户端,内部可委托 analyze(`analysis_prompt` 默认「请解读下表」)或标记 deprecated(文档说明即可)。
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 4. 后端逻辑(`analyst_agent.py`)
|
|||
|
|
|
|||
|
|
### 4.1 新入口 `analyze(req, auth) -> AnalystResponse`
|
|||
|
|
|
|||
|
|
1. 权限:`assert_analyst_query_access`(与 interpret 相同)。
|
|||
|
|
2. 终态 `clarify/deny/error/escalate`:echo 快照,不调用 LLM。
|
|||
|
|
3. 空表:返回 `degrade`,`answer` 说明无数据可分析。
|
|||
|
|
4. 行数上限:**500 行**(可配置 `ANALYST_CHART_MAX_ROWS`);超出 → `degrade`,提示缩小问数范围或加条件。
|
|||
|
|
5. 构造 LLM 消息:
|
|||
|
|
- System:角色 + JSON schema + 图表类型选择规则 + 禁止编造未出现在表中的数值。
|
|||
|
|
- User:`question`、`analysis_prompt`、列名、行数、`data_as_of`、SQL 一行、表格 **前 30 行 TSV**(超 30 行注明「仅展示样本」)。
|
|||
|
|
6. LLM 输出 **单 JSON 对象**(temperature=0);解析失败 → 重试 1 次 → 仍失败 `escalate` / `INTERPRET_ERROR`。
|
|||
|
|
7. **`validate_chart_spec(spec, columns, row_count)`**(新模块或 `analyst_chart.py`):
|
|||
|
|
- `chart_type` 枚举内;`none` 表示不要图。
|
|||
|
|
- 所有 field 名 ∈ `columns`(大小写按列名精确匹配)。
|
|||
|
|
- `y_fields` 非空(当 chart 非 none);pie 时 `y_fields.length === 1` 且需 `series_field` 或 x 为分类。
|
|||
|
|
- 可选:检测 x 列是否日期型(≥80% 可 parse)→ 建议 line + time,但 **最终 chart_type 以模型为准**,校验只拦非法映射。
|
|||
|
|
8. 文字解读:若用户 prompt 明显要文字或 `both`,生成/保留 `answer`;若仅要图,`answer` 可为 `reason` 一句。
|
|||
|
|
9. **数字护栏**:若 `answer` 含数字,复用现有 `_generate_verified_answer` / guard 逻辑(与 interpret 同路径);纯 chart 无 prose 数字可跳过 guard。
|
|||
|
|
10. 审计:`log_audit(event_type=analyst_analyze, input_summary={question, analysis_prompt, analysis_kind, chart_type}, trace_id)`。
|
|||
|
|
|
|||
|
|
### 4.2 LLM 图表选择规则(写入 system prompt 摘要)
|
|||
|
|
|
|||
|
|
- 日期/时间 + 数值 → 优先 **line**
|
|||
|
|
- 分类 + 单指标比较 → **bar** / **column**
|
|||
|
|
- 占比、少量分类 → **pie**(分类 ≤ 12)
|
|||
|
|
- 无法安全绑列 → `chart_type: none` + `answer` 解释需要什么列
|
|||
|
|
- 用户明确说「不要图」→ `none`
|
|||
|
|
- 用户明确图型时优先遵从,若与数据结构严重不符则在 `reason` 说明并选最接近合法类型
|
|||
|
|
|
|||
|
|
### 4.3 NL2SQL
|
|||
|
|
|
|||
|
|
**不修改** `run()` 的模板/LLM SQL 生成、sql_guard、execute 路径。
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 5. 前端实现
|
|||
|
|
|
|||
|
|
### 5.1 API 层(`web/src/api/analyst.ts`)
|
|||
|
|
|
|||
|
|
- `postAnalystAnalyze(token, payload)` → `AnalystChatResponse & { analysis_kind, chart }`
|
|||
|
|
- 类型 `ChartSpec` 与后端一致
|
|||
|
|
|
|||
|
|
### 5.2 组件
|
|||
|
|
|
|||
|
|
| 中文职责 | 路径 |
|
|||
|
|
| --- | --- |
|
|||
|
|
| 分析助手 Modal + 结果区编排 | `AnalystQueryPage.tsx` |
|
|||
|
|
| ChartSpec + table → @ant-design/charts | `AnalystResultChartPanel.tsx` |
|
|||
|
|
| 列类型推断、长表 series pivot | `web/src/utils/analystChartSpec.ts` |
|
|||
|
|
|
|||
|
|
### 5.3 图表库
|
|||
|
|
|
|||
|
|
- 使用 **`@ant-design/charts`**(与 Dashboard 一致)。
|
|||
|
|
- 互动:**tooltip**、**legend** 切换;折线支持 smooth optional。
|
|||
|
|
- x 为日期字符串时:parse 为 Date,axis formatter `MM-DD` / 自动。
|
|||
|
|
|
|||
|
|
### 5.4 布局规则
|
|||
|
|
|
|||
|
|
| `analysis_kind` | UI |
|
|||
|
|
| --- | --- |
|
|||
|
|
| `text` | 仅文字 Card |
|
|||
|
|
| `chart` | 仅 ChartCard(title + reason) |
|
|||
|
|
| `both` | 文字在上或左,图在下(移动端纵向堆叠) |
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 6. 错误与降级
|
|||
|
|
|
|||
|
|
| 场景 | status | 用户可见 |
|
|||
|
|
| --- | --- | --- |
|
|||
|
|
| chart spec 校验失败 | `degrade` | 文字说明 + 仍展示原表 |
|
|||
|
|
| LLM JSON 无效 | `escalate` | 与现 interpret 失败一致 |
|
|||
|
|
| 行数 > 500 | `degrade` | 提示缩小问数 |
|
|||
|
|
| 客户角色 self 域 | 同 interpret | disclaimer + AI 风险提示 |
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 7. 测试计划
|
|||
|
|
|
|||
|
|
### 7.1 pytest
|
|||
|
|
|
|||
|
|
- `validate_chart_spec`:合法 line / 非法列名 / pie 多 y / 空 y
|
|||
|
|
- `analyze()` mock LLM:返回 text-only、chart-only、both;guard 误杀 degrade
|
|||
|
|
- API route 401/403 与 chat 一致
|
|||
|
|
|
|||
|
|
### 7.2 Vitest
|
|||
|
|
|
|||
|
|
- `analystChartSpec.ts`:宽表 pivot、日期 x、多 series
|
|||
|
|
- 可选:Panel smoke render with fixture table
|
|||
|
|
|
|||
|
|
### 7.3 手工 Demo
|
|||
|
|
|
|||
|
|
1. 问数:「各产品最近 30 天每日净值」→ 表有 `nav_date` + `nav` + `product_name`
|
|||
|
|
2. 分析助手:「画趋势折线,按产品分系列」→ 多系列折线 + tooltip
|
|||
|
|
3. 「三句话总结」→ 仅文字
|
|||
|
|
4. 「总结并画图」→ both
|
|||
|
|
5. 问数后不点助手 → 仅表
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 8. 文档与 memory(实现后)
|
|||
|
|
|
|||
|
|
- `MEMORY.md` / `FRAMEWORK.md`:问数页增 analyze + chart
|
|||
|
|
- `docs/答辩/DEMO-SOP-问数.md`:加 30 秒可视化演示步骤
|
|||
|
|
- `REQUIREMENTS`:可挂 D-12 子能力脚注(非完整看板钻取)
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 9. Spec 自检(2026-09-12)
|
|||
|
|
|
|||
|
|
- [x] 无 TBD 占位
|
|||
|
|
- [x] 「只要表」= 不问 analyze,与「只要图/文/both」不矛盾
|
|||
|
|
- [x] NL2SQL 与 analyze 边界清晰
|
|||
|
|
- [x] 单迭代可交付(API + agent + 页 + 校验 + 测试)
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
**请评审本文件。** 确认或修改后回复,将进入 **writing-plans** 生成实现任务清单(仍不自动 commit,除非你要求)。
|