- Introduced `analyst_auth_adapter.py` for managing authentication context and access control for the data analysis agent. - Added new API endpoints in `analyst.py` for chat, dashboard, asset management, and metrics, utilizing the new authentication context. - Created Pydantic models in `analyst_schemas.py` for request and response structures, ensuring consistent data handling. - Updated SQL guard logic in `sql_guard.py` to enforce access restrictions based on user roles and contexts. - Implemented migration scripts for new database tables related to the data analysis agent, enhancing data management capabilities. - Removed legacy authentication code from `auth.py`, streamlining the authentication process. This update significantly enhances the data analysis capabilities, providing a robust framework for querying and managing data securely.
19 KiB
数据分析 Agent · 从 data-analysis-agent-work 合并说明
日期:2026-09-09
远程分支:xinghuo/data-analysis-agent-work@b19a241(zyi)
原则:只增不盖;与merger同路径冲突时 以 merger 为准(不覆盖main.py/deps.py/chat.py/agent_service.py宿主骨架)。
状态:接缝设计稿(未 merge、未接线)
0. 一句话定位
数据分析 Agent = NL2SQL 只读查数线:自然语言 → 安全 SQL → 表格 + 解读 + 留痕;与宿主 通用对话线(agent_service 轻提示词 Chat)是两条不同产物,接缝上 并行挂载,不替换 agent_service.py。
1. 合并什么(新增文件清单)
从 data-analysis-agent-work 整包迁入(无冲突或仅新增路径):
| 类别 | 路径 | 作用 |
|---|---|---|
| 路由 | app/api/analyst.py |
/api/analyst/chat · /dashboard · /assets · /ops/metrics |
| 编排 | app/service/analyst_agent.py |
LangGraph StateGraph(input_guard → … → audit_persist) |
| 安全 | app/service/sql_guard.py guardrail.py |
五层 SQL 白名单 + 数字护栏 |
| 支撑 | analytics_repo.py dict_service.py cache_service.py schema_meta.py |
留痕/口径/缓存/元数据 |
| LLM | app/service/llm.py |
DeepSeek 封装(分析线专用;与 merger embedding.py 不冲突) |
| 模型 | app/model/schemas/analyst.py |
输出四件套 Pydantic(answer/table/sql/meta) |
| 实体 | app/model/entities/analytics.py(若远程有) |
analytics_* ORM 映射 |
| 工具 | app/tool/sql_tool.py(若远程有) |
只读执行 + 行数/超时 |
| 文档 | 数据分析Agent架构说明书.md 数据分析Agent开发清单.md 03-mysql-analyst专用.sql |
架构 / 任务 / DDL |
| 测试 | 见 §6 | 重命名后迁入,避免覆盖 merger 730 基线 |
不迁入(冲突 · 保留 merger):
| 文件 | 原因 |
|---|---|
app/main.py |
AL-09 全量宿主入口;仅 追加 include_router(analyst_router) |
app/api/deps.py |
merger 完整 JWT + 归属 + 矩阵(382 行);远程版被砍成 22 行 stub |
app/api/chat.py app/service/agent_service.py |
四 Agent 通用对话线已集成 |
app/config/settings.py |
merger 含 risk/customer/visitor/JWT 全量;追加 analyst_* 字段 |
app/model/schemas.py |
保留单文件;分析专用模型放 schemas/analyst.py 子模块(见 §3.3) |
app/repository/core_ro.py |
merger 版含 check_suitability 等平台能力;分析线只 调用 不覆盖 |
app/utils/auth.py |
远程第二套鉴权(subject_id);废弃,改 deps 适配(§4) |
docs/memory/*、前端 web/、风控/平台 API |
全部保留 merger |
2. 数据库(需手工执行)
2.1 已有(merger canonical)
docs/项目框架设计/表设计/02-mysql-agent专用.sql 已含 analytics_query_log(NL2SQL 留痕)。merger entities.py 已有 ORM。无需重复建表(若本机已跑过 02 迁移)。
2.2 新增(分析资产三表 · D-07 / D-11)
远程 03-mysql-analyst专用.sql → 独立迁移脚本(不修改 canonical 02 文件):
scripts/agent/migrate-analyst-d07-d11.sql
→ analytics_metric_dict 口径字典
→ analytics_few_shot 问题→SQL 示例
→ analytics_query_template 参数化模板
执行时机:merge 代码后、跑分析线单测/E2E 前;库 jinrong_agent,前置 01/02 共用底座已存在。
2.3 Redis
复用 merger Docker Redis(:6380)。分析线 cache_service 使用独立 key 前缀(对齐 02-redis-keys.md 分析段);与客服/游客/风控 key 命名空间隔离,同实例不冲突。
3. S3 接缝设计(推荐方案 · 待你确认 §8)
3.1 入口策略:并行双轨(推荐)
| 入口 | 行为 | 前端页面 |
|---|---|---|
POST /api/analyst/chat |
AnalystAgent.run() → JSON 四件套(table/sql/meta/disclaimer) |
/app/analytics/query 问数工作台(主链路) |
GET /api/analyst/dashboard |
D-12 智能看数卡片 | /app/analyst/home 可接 metrics |
POST /api/analyst/assets |
D-11 资产沉淀 | 问数页「沉淀」按钮(P1 后做) |
GET /api/analyst/ops/metrics |
N-08 运营指标 | 分析员运维面板(P1 后做) |
POST /api/chat + X-Agent-Type: analyst |
仍走 agent_service(轻量对话 stub,无 NL2SQL) |
/app/analytics/chat 分析对话(可选保留) |
为何不把 analyst 并入 chat.py 分流?
- 问数响应含 结构化 table + sql + meta,与 Chat 的
{reply, has_disclaimer}形状不同;硬塞进 SSE 需额外 adapter 层,收益低。 - 架构说明书 §13 已定义独立
/api/analyst/*;与客服 S2「customer 分流」场景不同(customer 仍是 Chat 形态)。
main.py 改动(唯一宿主挂载点):
from app.api.analyst import router as analyst_router
app.include_router(analyst_router) # prefix 已在 analyst.py 内:/api/analyst
3.2 鉴权接缝:deps 为准 + 薄适配
远程代码混用 deps.get_auth_context 与 utils.auth.AuthContext(字段 subject_id vs actor_id)。合并后:
- 删除迁入后的
app/utils/auth.py(或保留空 shim 仅 re-export,最终删除)。 - 新增
app/api/analyst_auth_adapter.py(或扩展现有auth_adapter.py):
def analyst_auth_from_deps(ctx: deps.AuthContext) -> AnalystAuthView:
"""deps.AuthContext → 分析线内部视图(仅字段映射,不做权限判定)。"""
return AnalystAuthView(
subject_id=ctx.actor_id, # 分析 repo 写 staff_id 用
roles=ctx.roles,
permissions=ctx.permissions,
token_type=ctx.token_type,
trace_id=current_trace() or "",
agent_type="analyst",
)
analyst.py路由层:auth = Depends(get_auth_context)→ 适配后传入AnalystAgent.run()。- 远程
assert_analyst_access(auth)逻辑 迁入deps.py或app/utils/authz.py(与assert_customer_access并列),使用actor_id命名。 X-Agent-Type:/api/analyst/*不要求该头(路由即 analyst);JWT 矩阵仍校验 token 角色含analyst/compliance/advisor/risk_officer/ops(按远程assert_analyst_access域划分)。
3.3 Schemas:单文件 + 子模块共存
merger 当前 app/model/schemas.py 为单文件。合并策略:
- 新增
app/model/schemas/analyst.py(仅分析请求/响应)。 - 不把
schemas.py改成包(避免大面积 import 破坏);analyst.py路由from app.model.schemas.analyst import ...直引子路径。 - 若 Python 包解析与
schemas.py文件冲突:将子模块改为app/model/analyst_schemas.py(备选,merge 时二选一)。
3.4 settings 追加字段(合并冲突手工解)
在 merger settings.py 追加(取自远程,命名对齐架构说明书):
| 字段 | 用途 |
|---|---|
analyst_llm_model / analyst_llm_timeout |
DeepSeek NL2SQL |
analyst_sql_row_limit / analyst_sql_timeout_sec |
只读执行上限 |
analyst_cache_ttl_sec |
结果缓存 |
analyst_rate_limit_per_min |
问数限流 |
.env.example 同步追加;不覆盖 merger 已有 jwt_* / risk_* / customer_*。
3.5 requirements
merger 已有 langgraph;追加 sqlglot>=25.0.0(远程新增,SQL AST 白名单硬依赖)。
3.6 core_ro / llm 边界
| 模块 | 策略 |
|---|---|
core_ro.py |
保留 merger;analytics_repo.execute_readonly 走 agent 库只读账号 或现有 Core RO 连接,不 duplicate |
llm.py |
迁入分析线专用;AnalystAgent 只 import 此模块 |
input_guard |
分析线复用 merger app/service/input_guard.py(限流/注入),不保留远程 duplicate utils/input_guard.py |
4. 前端接缝(merger 现状 → 目标)
4.1 现状
| 路由 | 现状 |
|---|---|
/app/analytics/query |
PlaceholderPage(待 NL2SQL) |
/app/analytics/chat |
ChatPanel + agentType="analyst" → POST /api/chat/stream(无 table/sql) |
4.2 推荐(与 §3.1 双轨一致)
| 页面 | API | UI |
|---|---|---|
问数工作台 /app/analytics/query |
POST /api/analyst/chat |
新问题组件:AnalystQueryPanel(问题框 + 解读 + Ant Design Table + SQL 折叠 + disclaimer) |
分析对话 /app/analytics/chat |
暂保留 ChatPanel SSE |
页顶 Banner 提示:「复杂查数请用问数工作台」;P2 可下线或改跳转 |
新增 web/src/api/analyst.ts:
export interface AnalystChatResponse {
answer: string
table: { columns: string[]; rows: unknown[][] }
sql: string
meta: { exec_ms: number; row_count: number; cache_hit: boolean; data_as_of?: string }
disclaimer: string
status: string
trace_id?: string
}
不强行让 ChatPanel 解析四件套(职责分离)。
4.3 首页看板
AnalystMarketDashboard QuickAction「问数工作台」已指向 /app/analytics/query;merge 后可接 GET /api/analyst/dashboard 填充 metrics 卡片(P1)。
5. 需求 ID 对齐
| 需求 | 合并后落点 | 阶段 |
|---|---|---|
| D-01~D-04 | analyst_agent + sql_guard + analytics_query_log |
merge 后冒烟 |
| D-05 | 多表只读聚合(sql_generate 节点) | 同上 |
| D-06 | cache_service 双层缓存 |
同上 |
| D-07 | dict_service + analytics_metric_dict |
需跑 migrate-analyst |
| D-08~D-10 | 拒答分支 + guardrail |
同上 |
| D-11 | /assets + 三资产表 |
migrate + 前端 P1 |
| D-12 | /dashboard |
首页 P1 |
| N-01~N-08 | 见架构说明书 §4/§6 | 单测覆盖 |
6. 测试策略
6.1 远程测试文件 → 迁入命名
| 远程 | 迁入后 | 说明 |
|---|---|---|
tests/test_agent.py |
tests/test_wave6_analyst_agent.py |
改 import:utils.auth → analyst_auth_adapter |
tests/test_sql_guard.py |
tests/test_wave6_sql_guard.py |
无冲突,直迁 |
tests/test_guardrail.py |
tests/test_wave6_guardrail.py |
若 merger 已有同名则合并 case |
tests/test_dict_service.py |
tests/test_wave6_dict_service.py |
|
tests/test_cache_service.py |
tests/test_wave6_cache_service.py |
merger 已有 test_cache_service.py → 合并或 rename 远程 |
tests/test_llm.py |
tests/test_wave6_analyst_llm.py |
避免与 merger LLM 测试混淆 |
6.2 基线要求
- merge + 适配完成后:
python -m pytest→ 730 + N passed(N = 新增 wave6 条数)。 - 禁止恢复远程对
deps.py/conftest.py的删减。
6.3 冒烟清单(Scope B · merge 后)
POST /api/analyst/chat+ analyst JWT → 200 + 四件套 JSON- 顾问 token 查他人客户 → 403 +
analytics_query_log(blocked) GET /api/analyst/dashboard→ 200 + cards- 前端
/app/analytics/query真跑一条问数 POST /api/chat/stream+ analyst → 仍 200(stub 未回归)
7. Merge 操作顺序(执行 SOP · 尚未做)
1. git worktree add ../JinRong-analyst-merge data-analysis-agent-work # 可选隔离
2. git merge data-analysis-agent-work --no-commit # 或 cherry-pick 新增文件
3. 冲突文件按 §1「不迁入」表逐项保留 merger 版
4. 手工:analyst_auth_adapter · main.py include_router · settings 追加 · requirements sqlglot
5. 删除 utils/auth.py · 改 analyst.py / analyst_agent.py import
6. 执行 scripts/agent/migrate-analyst-d07-d11.sql
7. pytest 全绿 → 前端 AnalystQueryPanel → Scope B 冒烟
8. 更新 docs/memory/TODO.md · FRAMEWORK 实现状态表
8. 已确认决策(2026-09-09)
| # | 决策 | 结论 |
|---|---|---|
| Q1 | 问数主入口 | A · 独立 POST /api/analyst/chat;各角色在权限范围内自助查数(见 §9) |
| Q2 | /app/analytics/chat |
A · 保留 ChatPanel stub + Banner 引导问数工作台 |
| Q3 | schemas 路径 | A · app/model/schemas/analyst.py(merge 时若与 schemas.py 包冲突再改 analyst_schemas.py) |
| Q4 | merge 方式 | A · git merge + 手工解冲突(默认,未单独拍板则按此执行) |
9. 鉴权接缝 · 角色矩阵与缺口(merge 前必须补)
9.1 目标口径(产品确认)
每个 Demo 账户角色都能调用数据分析模块,只看其权限范围内的数据。
前端已把「数据分析」放进 客户 / 理财师 / 风控 的 sharedPlatform 菜单(/app/analytics/query);后端远程实现 尚未覆盖 customer,且与 merger deps 矩阵不一致,需在 S3 接缝一并补齐。
9.2 目标角色 × 数据域(接缝后应达到)
Demo 仅四角色:customer · advisor · analyst · risk_officer(无 compliance / risk_manager 独立账号;问数线不单独开域)。
| 角色 | token | 数据域 key | 可见范围 | sql_guard 要点 |
|---|---|---|---|---|
| customer | customer | self(待实现) |
仅本人可读数据(持仓/流水/风评/净值趋势等) | 禁止其他 CUST-*;强制 customer_id = auth.customer_id;见 §9.6 产品约束 |
| advisor | staff | assigned |
名下客户(core_customer_advisor active) |
远程已有;inject_ownership + 白名单 |
| analyst | staff | full |
全量 + 敏感列策略 | 远程已有 |
| risk_officer | staff | risk |
预警台账全量 + 客户/持仓/交易只读(脱敏列) | 远程已有;表域评估见 §9.7 |
| ops | staff | aggregate |
无客户维度,仅聚合 | 远程已有(非 Demo 角色,预留) |
9.6 客户问数 · 产品约束(2026-09-09 确认)
客户走 self 域,能力边界如下(merge 时在 analyst_agent 解读阶段 + prompt 硬约束):
| 项 | 口径 |
|---|---|
| 允许 | 基于本人可读数据的趋势总结、分布描述、数量统计(如持仓结构、近 N 日交易笔数、盈亏区间描述) |
| 禁止 | 投资建议、收益承诺、买卖时点、产品推荐;命中则拒答或降级为「仅展示表格」 |
| 尾部声明 | 在标准 disclaimer 之外,客户域回复追加:「AI 分析有风险,仅供参考。」 |
| 留痕 | analytics_query_log.actor_id = customer_id,actor_role = customer |
实现落点(merge 时):
scope_resolve:token_type=customer→domain=self,scope=[auth.customer_id]sql_guard.validate(domain=self):同 advisor 归属逻辑,但 scope 固定单人_generate_sql/ANSWER_SYSTEM:注入「仅描述数据、不给建议」answer_compose:domain=self时 append 客户专用尾注
9.7 风控专员 · 表域是否够用(2026-09-09 评估)
结论:够用。 风控 Agent 自动监督触发的结果(规则引擎出单、AML 命中、适当性拦截、集中度/代理人行为链等)权威落点都在现有白名单内;风控专员问数线是只读查台账与关联上下文,不通过 NL2SQL 触发处置或扫名单。
| 监督链路(风控 Agent / 引擎) | 落库/可读表 | 已在 sql_guard 白名单 |
|---|---|---|
| RISK-001~006 交易/持仓规则 | risk_alert(alert_type + payload/triggered_rules) |
✅ |
| RISK-006 集中度 | risk_alert(pattern + payload.alert_subtype=concentration)+ core_holding |
✅ |
| RISK-008 代理人行为链 | risk_alert(pattern + payload.alert_subtype=agent_behavior) |
✅ |
| AML 命中 | risk_alert(alert_type=aml)+ customer_profile_l3 标记 |
✅ |
| 适当性拦截 | risk_alert(alert_type=suitability) |
✅ |
| 客户监测画像 | customer_profile_l1/l2/l3 |
✅ |
| 关联上下文(客户/持仓/交易/产品) | core_* 系列 |
✅ |
刻意不进白名单(安全/职责分离):
| 表 | 原因 |
|---|---|
risk_aml_list |
反洗钱名单本体;命中结果已在 risk_alert,不应 NL2SQL 直查名单库 |
audit_log |
审计明细走 HTTP/专用 Tool;问数以 risk_alert 聚合即可 |
可选 P2(非阻塞 merge): 若后续要问「适当性判定历史通过率/明细」,可把 risk_suitability_log 加入白名单并在 risk 域只读;当前 Demo 问数(待处理预警数、按类型分布、客户监测分布)不依赖该表。
域规则: 维持远程 domain=risk(台账全量 + 白名单内客户只读),不新增 compliance/manager 域;与现网风控 Demo(STAFF-30001 · risk_officer)一致即可。
9.3 远程 vs merger 缺口清单
| 缺口 | 远程现状 | merger 现状 | 接缝动作(记入 TODO) |
|---|---|---|---|
| G1 customer 不可用 | assert_analyst_access 要求 token_type=staff |
平台 G-01 支持 customer 本人 | 新增域 self;/api/analyst/* 入口允许 customer token |
| G2 sql_guard 无 self 域 | 仅 full/assigned/risk/aggregate | — | validate() + inject_ownership() 支持 self + 强制 customer_id = auth.customer_id |
| G3 双套 AuthContext | subject_id + utils/auth.py |
actor_id + deps.py |
§3.2 适配器;废弃 remote auth |
| G4 入口矩阵分裂 | /api/analyst/* 用 ANALYST_ROLES 四角色 |
AGENT_ACCESS_MATRIX["analyst"] 仅 analyst+compliance(对话线) |
问数路由 不走 chat 矩阵;assert_analyst_query_access() 覆盖 customer/advisor/analyst/risk_officer |
| — | — | 已关闭:无 compliance/risk_manager Demo 角色,不单独开域 | |
| G6 留痕 actor | staff_id=subject_id |
customer 无 staff_id | analytics_query_log 写 actor_id + actor_role;customer 写 customer_id |
| G7 前端 PermissionGate | — | 问数页仍 Placeholder | 403 展示可查范围引导(对齐 AnalystResponse.suggestions) |
| G8 客户解读约束 | 远程无 customer 域 | — | §9.6:趋势总结 · 禁建议 · 尾部「AI 分析有风险」 |
9.4 推荐实现落点(merge 时)
app/utils/authz.py # 新增 assert_analyst_query_access(auth) → domain
app/service/sql_guard.py # 扩展 domain=self(§9.6 客户约束)
app/api/analyst.py # Depends(get_auth_context) → assert_analyst_query_access → adapter → agent.run
app/api/analyst_auth_adapter.py # actor_id ↔ 分析线内部视图(可选 NamedTuple)
/api/chat analyst stub 仍走现有 AGENT_ACCESS_MATRIX(analyst/compliance 轻聊,Demo 无 compliance 账号);与问数线 刻意分离。
10. 修订记录
| 日期 | 说明 |
|---|---|
| 2026-09-09 | 初稿:远程 b19a241 审阅 + merger 730 基线对照;未 merge |
| 2026-09-09 | Q1=A Q2=A;§9 鉴权缺口与角色矩阵(含 customer self 域待实现) |
| 2026-09-09 | 产品拍板:§9.6 客户趋势总结/禁建议/AI 风险尾注;§9.7 risk 表域够用;关闭 G5 |