模块 1 · 问数入口

问数工作台 vs 分析对话
两套门,别走错

数据分析 Agent 走平台鉴权(get_platform_auth_context), 和对话线不同——不要带 X-Agent-Type。 像进档案室查账:只出示工牌(JWT),不用说明「我是来聊天还是来问数」。

前端两条菜单

问数工作台

/app/analytics/query → AnalystQueryPage
真·NL→SQL:表格 + SQL + 解读,调 POST /api/analyst/chat

分析对话(占位)

/app/analytics/chat → AnalystChatShell
横幅写明「轻量对话占位;查数请用问数工作台」——还没接独立问数图

指挥 AI 改功能时: 要表格和 SQL 就改 问数 REST(analyst.py) / NL→SQL 编排(analyst_agent.py); 要聊天体验才碰 analytics/chat 占位页,别和问数混成一个接口。
customer self 域: 客户 token 进问数 → domain=self, SQL 安全校验(sql_guard.py)强制 SQL 带本人 customer_id;免责声明追加 CUSTOMER_AI_RISK_NOTE。

后端「四件套」路由

全部挂在 问数 REST(app/api/analyst.py),前缀 /api/analyst:

POST /chat
问数主入口 → AnalystAgent.run()
GET /dashboard
智能看板卡片(按角色返回 metrics)
POST /assets
口径/样例沉淀(仅 analyst 角色可写)
GET /ops/metrics
运营指标:查询总量 / 被拦截次数

群聊:分析员点「提问」之后

STAFF-20001 在问数工作台输入「客户总数是多少」——简化版:

前端 · analyst.ts
await apiFetch('/api/analyst/chat', {
  method: 'POST',
  token,  // Bearer JWT only
  body: JSON.stringify({ question, session_id }),
})
白话

问数和读持仓一样:登录令牌够了,不用声明 Agent 类型。

后端用角色(analyst/advisor/customer…)决定能查哪片数据域,不靠 X-Agent-Type。

问数工作台调 POST /api/analyst/chat,请求头要带 X-Agent-Type: analyst 吗?

模块 2 · NL→SQL 管线

一句话怎么变成
可审计的表格?

NL→SQL 编排(app/service/analyst_agent.py)把用户问题拆成可测试步骤。 像翻译官 + 安检 + 记账员:先弄清指标含义,再生成 SQL,层层校验后才执行,最后解读并留痕。

七步流水线(动画)

点击「下一步」看 NL→SQL 在代码里怎么走:

📖消歧 dict
🤖LLM 生成
🛡SQL 安全校验
🗄execute
✓guardrail

校验失败 → 403 或 degrade(只给表格不给瞎编解读)

SQL 安全校验(sql_guard.py)在拦什么

只读白名单

拒绝 INSERT/UPDATE/DELETE 等写操作关键字

表白名单

core_* + risk_alert 等,超出即 403

行级归属

customer 域强制 customer_id = 本人;advisor 只能名下客户

粒度控制

ops 聚合域禁止下钻到单个客户明细

CODE · NL→SQL 编排(analyst_agent.py).run
amb = self._detect_ambiguity(question)
if amb: return self._clarify(amb, trace_id)
sql_text, usage = self._generate_sql(...)
vres = validate(sql_text, domain, scope)  # sql_guard
exec_result = self.repo.execute_readonly(sql_text)
answer, guard = self._generate_verified_answer(...)
self._persist(...)  # analytics_query_log
白话

指标说不清就先反问,不瞎猜 SQL。

生成后必须过 SQL 安全校验(sql_guard.py),执行只走只读连接。

AI 解读若数字对不上表,降级为「请看表格」;全程写入查询日志备查。

数字护栏(guardrail)校验失败时,用户还能看到查询结果表格吗?

模块 3 · Agent 与页面

AnalystAgent.run
+ 看板与资产 API

问数主脑是 AnalystAgent.run;页面侧 AnalystQueryPage(P2) 同时拉 GET /dashboard 卡片和 POST /chat 结果。 口径沉淀走 POST /assets(仅 analyst 角色可写)。

AnalystAgent.run 要点

鉴权与域

assert_analyst_query_access → domain;resolve_analyst_scope → customer_id 白名单。

消歧与生成

_detect_ambiguity → clarify;否则 模板填参 或 _generate_sql。

执行与缓存

validate → execute_readonly;Redis 权限指纹 + SQL + 表世代;写交易/预警/L3 后 bump。

解读与留痕

_generate_verified_answer + guardrail;_persist 写 analytics_query_log。

返回体 AnalystResponse: answer + table + sql + meta(exec_ms, template_hit, cache_hit, cost_est)+ disclaimer + status(success / degrade / clarify)。

dashboard / assets API

GET /dashboard

按角色返回 cards 文案 + metrics:analyst 全库统计、customer 本人持仓、advisor 名下 AUM 等。

POST /assets

kind=dict/few_shot/template;仅 analyst 角色,写入口径资产表。

GET /ops/metrics

运营侧查询总量与被拦截次数,与 dashboard 卡片分离。

CODE · AnalystQueryPage.tsx(P2)
useEffect(() => getAnalystDashboard(token))
setDashMetrics(d.metrics)
setDashCards(d.cards)
// 用户点查询
const resp = await postAnalystChat(token, question)
setResult(resp)  // Table + Collapse(SQL)
白话

进页面先拉看板 MetricCard,数字来自 /dashboard 不是 chat。

提问才走 AnalystAgent.run,结果区展示解读、表格、可折叠 SQL。

apiFetch 只带 token,与 risk.ts 的 X-Agent-Type 头无关。

理财师 STAFF-10086 能调用 POST /api/analyst/assets 沉淀口径吗?

模块 4 · 红线与排障

问数只统计不处置
还有几个常见空结果坑

问数 Agent 能出表格和解读,但不能关预警、不能当投顾、不能改画像。 另外「消歧总空」「改占位 Chat 没用」是 vibe coder 高频踩坑——这模块专门讲清。

问数三条红线(合规 §4.3)

①

不处置预警 — 不能调用 POST /api/risk/alerts/{id}/handle;台账只读统计。

②

不当投顾 — 输出带免责声明;禁止生成可执行买卖/处置指令。

③

不改 L0/L1 画像 — SQL 只读;customer 域仅 self 趋势统计,不下钻他人。

和风控对话线的区别: 风控 Chat 用 RISK_TOOL_REGISTRY 只读查台账;问数走 /api/analyst/chat + SQL 安全校验(sql_guard.py),不要混接口。

排障:消歧为什么总空?

口径字典在 MySQL,本机须执行一次 scripts/agent/seed-analyst-metric-dict.sql。没灌种子 → NL2SQL 消歧永远不命中,不是 LLM 「坏了」。

占位页 vs 真问数
AnalystChatShell  → 占位 UI,未接问数 API
AnalystQueryPage  → POST /api/analyst/chat
改 Shell 不会出 SQL 表格
白话

菜单里两个「分析」入口可能让人改错文件——问数逻辑在 NL→SQL 编排(analyst_agent.py) + 问数页。

Redis 问数缓存键含权限指纹,别只 hash SQL 字符串,否则可能串权。

问数 401,AI 建议给 analyst.ts 加 X-Agent-Type: analyst,你怎么回?

模块 5 · D-06

模板填参 + 结果缓存
写侧 bump 失效

问数两层「快路径」:① 同形态问题命中 published 模板,跳过 LLM 写 SQL(template_service.py) ② 执行结果进 Redis,键里带表世代——交易/预警/L3 写后 bump,不等 TTL 过期。

两层 D-06 各管什么

层入口答辩句
模板缓存 match_template → 填 :days 等 「客户+总数」→ template_hit: true,省 LLM 写 SQL
结果缓存 cache_service.py 权限指纹 + SQL hash + 世代 「同 SQL 同权限」秒回;写侧 bump 后旧键自然 miss
写侧失效 analyst_cache_invalidate 交易/预警/L3 后 「演示里刚模拟交易,问数不会还显示旧汇总」
前端问数页会展示 模板命中 / 结果缓存 标签(读 meta.template_hit · cache_hit)。
analyst_agent 决策顺序(简化)
clarify? → return suggestions
template match? → fill SQL → validate → execute
else LLM generate_sql → validate → execute (cache?)
guardrail → persist analytics_query_log
白话

像预制菜 + 冰箱贴保质期:常问题型直接拿模板填天数;跑完的结果贴冰箱,但 Core 有新交易就撕掉旧标签(bump)。

种子:scripts/agent/seed-analyst-query-templates.sql · 一键演示:prepare_all.ps1。

群聊:模板 vs LLM vs 缓存

AI 说「问数缓存就是 Redis TTL 10 分钟」,你怎么纠正?