docs(audit): Wave 1 发现待 Neo 拍板 12 项业务故事卡
Wave 1 Day 1-4 实施过程中挖出的问题, 按 04a/b/c 业务故事卡风格呈现: - 7 字段 (关联/背景/逻辑/影响/选项/判定) - 12 项分 3 组: P0 评估发现×5 / 项目治理×2 / 业务语义×5 - 每条带 Wave 分配建议, 不评估工时 第一组 P0 评估发现 (W1-T7 PRD 撰写挖出): - F1-1 批量 AI 长事务无幂等 (重复扣费风险) - F1-2 run-logs PII 跨租户泄露 (个保法风险, 建议 Wave 1 修) - F1-3 batch_id 生命周期未管理 (数据库膨胀) - F1-4 triggers/unified 权限过松 (跨租户可见) - F1-5 沙箱 batch-run 未读 runtime_context (沙箱主线必修) 第二组 项目治理: - F2-1 OpenAPI 与代码不同步 (建议 Wave 2 提前修脚本) - F2-2 tests/ .gitignore 排除 (建议 Wave 5 入仓 + 启 CI) 第三组 业务语义待 Neo 答: - F3-1 cache invalidate 粒度 - F3-2 AI budget 单价来源 - F3-3 手动触发 audit + 二次确认 - F3-4 sandbox_date 边界 - F3-5 unified 分页
This commit is contained in:
445
docs/_overview/wave1-findings/00-W1-findings-stories.md
Normal file
445
docs/_overview/wave1-findings/00-W1-findings-stories.md
Normal file
@@ -0,0 +1,445 @@
|
|||||||
|
# Wave 1 发现待 Neo 拍板 — 业务故事卡
|
||||||
|
|
||||||
|
> 日期:2026-05-04 / 来源:Wave 1 Day 1-4 实施过程中挖出的问题
|
||||||
|
> 用途:对齐 04a/b/c-feedback 风格,以业务上下文呈现,让 Neo 可读可判可拍
|
||||||
|
> 12 项总览 → P0 评估发现×5 / 项目治理×2 / 业务语义×5
|
||||||
|
> 注:本轮**不评估工时**,Neo 拍判定后主线自动安排到合适 Wave
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 第一组 P0 评估发现(5 项)
|
||||||
|
|
||||||
|
### F1-1. 批量 AI 任务 长事务无幂等(可能重复扣费)
|
||||||
|
|
||||||
|
**关联页面/接口**:
|
||||||
|
- admin-web 路由:`/ai/operations`(AI 手动操作页)
|
||||||
|
- 后端端点:`POST /api/admin/ai/batch-run` + `POST /api/admin/ai/batch-run/confirm`
|
||||||
|
- 后端代码:`apps/backend/app/routers/admin_ai.py`(批量任务创建/确认)
|
||||||
|
- 数据库:`biz.ai_run_logs`、`biz.ai_run_costs`(token 计费)
|
||||||
|
- 影响 AI 应用:全部 8 个 APP(因为批量入口通用)
|
||||||
|
|
||||||
|
**业务背景**:
|
||||||
|
admin-web `/ai/operations` 提供"批量触发"按钮,运维一次点击可以为多个 site 或多组合发起 AI 任务(典型场景:8 区域财务洞察并发跑)。后端做法:先 `POST /batch-run` 创建批次返回 batch_id,再 `POST /batch-run/confirm` 触发实际 dashscope 调用。
|
||||||
|
|
||||||
|
**问题描述**:
|
||||||
|
- 整个流程**一个事务包了"创建批次 + 调 N 个 AI"**
|
||||||
|
- 如果调用 dashscope 第 5 个时进程崩溃,前 4 次的 token 已经扣了(dashscope 计费),但事务回滚 → `ai_run_costs` 没记录这 4 次费用
|
||||||
|
- 运维不知道实际花了多少 → **预算追踪失真**
|
||||||
|
- 重启后又重跑一遍,可能再扣 4 次费用 → **重复扣费**
|
||||||
|
|
||||||
|
**业务联系**:
|
||||||
|
- 上游:运维点"批量触发"按钮
|
||||||
|
- 下游:AI cache 写入 / `ai_run_costs` 计费 / 触发器统计
|
||||||
|
- 影响范围:每次批量 AI 任务都有这个风险,门店数 × 应用数越多风险越大
|
||||||
|
|
||||||
|
**修改影响**:
|
||||||
|
- 数据层:`ai_run_costs` 写入策略改为"单次调用即写,不等批次完成"
|
||||||
|
- 接口层:`/batch-run/confirm` 改为异步队列消费 + 单次幂等
|
||||||
|
- 展示层:admin-web `/ai/run-logs` 可能需要"部分完成"状态
|
||||||
|
|
||||||
|
**推荐选项**:
|
||||||
|
1. **A 拆事务 + 幂等 token**:每次 AI 调用前生成 idempotency_key,失败重启可幂等跳过 → 优:正确 劣:改造面广
|
||||||
|
2. **B 同步 → 异步队列**:`/batch-run` 入队,worker 单次消费,数据库做唯一约束防重 → 优:架构清晰 劣:引入队列基建
|
||||||
|
3. **C 仅打 audit log,不改架构**:崩溃后人工对账 → 优:最快 劣:依赖人,不可持续
|
||||||
|
|
||||||
|
**建议判定**:**[D Bug] 升级到 Wave 2(随 P1-7 admin API PRD 批 2)**,选 A(因为 Wave 2 已经会改 admin-ai 路由)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### F1-2. AI 运行日志 PII 跨租户泄露(super_admin 看到其他门店用户名)
|
||||||
|
|
||||||
|
**关联页面/接口**:
|
||||||
|
- admin-web 路由:`/logs/ai-run-logs`
|
||||||
|
- 后端端点:`GET /api/admin/ai/run-logs`、`GET /api/admin/ai/run-logs/{log_id}`
|
||||||
|
- 数据库:`biz.ai_run_logs`(含 user_id、site_id、prompt、response 字段)
|
||||||
|
- 关联 Wave 0 决策:P0-5 致命 1+2 已修,但本项是新发现的另一处租户隔离 Bug
|
||||||
|
|
||||||
|
**业务背景**:
|
||||||
|
admin-web AI 运行日志页给运维看 AI 调用历史(谁触发的、prompt 是什么、token 用了多少、有没有失败)。其中 prompt 字段会**包含用户姓名 / 客户姓名 / 助教姓名**(因为 prompt 模板把这些注入了)。
|
||||||
|
|
||||||
|
**问题描述**:
|
||||||
|
- 端点未加 `WHERE site_id = current_setting('app.current_site_id')` RLS 过滤
|
||||||
|
- super_admin 一旦切到任何 site,**能看到所有 site 的日志**(包括 prompt 里的客户姓名 / 手机号尾号 / 助教姓名)
|
||||||
|
- 多租户上线后:租户 A 的运维查日志可能误看到租户 B 的客户 PII
|
||||||
|
|
||||||
|
**业务联系**:
|
||||||
|
- 上游:AI 调用产生日志写入 `biz.ai_run_logs`
|
||||||
|
- 下游:admin-web `/logs/ai-run-logs` 列表 / 详情
|
||||||
|
- 法律影响:多租户场景下涉嫌"未授权访问个人信息",违反《个人信息保护法》第 51 条(责任主体的访问控制义务)
|
||||||
|
|
||||||
|
**修改影响**:
|
||||||
|
- 数据层:`biz.ai_run_logs` 加 RLS 策略(已有 site_id 列)
|
||||||
|
- 接口层:`run-logs` 端点加 `SET LOCAL app.current_site_id`
|
||||||
|
- 展示层:super_admin 跨 site 时显式切站才能看到对应日志(符合最小授权)
|
||||||
|
- 兜底:勿动 prompt 字段(脱敏成本高)
|
||||||
|
|
||||||
|
**业务联系**(进一步):
|
||||||
|
- 与 P0-5 致命 1+2 同类(都是租户隔离),Wave 1 Day 1 已修 4 处 fdw_etl,这是第 5 处遗漏
|
||||||
|
- 与 P0-7 沙箱无直接冲突(沙箱影响时间不影响 site 隔离)
|
||||||
|
|
||||||
|
**推荐选项**:
|
||||||
|
1. **A 加 RLS + 强制选 site**:类似 admin-runtime-context 行为,super_admin 必须先选 site → 优:最干净 劣:UX 多一步
|
||||||
|
2. **B 后端 query 强制带 site_id 参数**:不依赖 GUC,显式入参 → 优:简单 劣:跟全局一致性脱钩
|
||||||
|
3. **C 仅记 audit log,不限制访问**:运维知情同意 → 优:零改 劣:法律风险高
|
||||||
|
|
||||||
|
**建议判定**:**[D Bug 安全] 升级到 Wave 1(本 Wave 内修)**,选 A(与 admin-runtime-context UX 一致)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### F1-3. AI 批次 batch_id 生命周期未管理(数据库永久膨胀)
|
||||||
|
|
||||||
|
**关联页面/接口**:
|
||||||
|
- admin-web 路由:`/ai/operations` + `/logs/ai-run-logs`
|
||||||
|
- 后端端点:`POST /api/admin/ai/batch-run` + `POST /confirm`
|
||||||
|
- 数据库:`biz.ai_run_logs`(每次批次产生 N 行)、`biz.ai_run_costs`、可能还有 `biz.ai_batches`(批次主表)
|
||||||
|
|
||||||
|
**业务背景**:
|
||||||
|
每次批量 AI 任务产生一个 batch_id,关联一组 ai_run_logs。一年下来如果每周跑一次 8 区域财务洞察预热,会产生 52 × 8 = 416 行 ai_run_logs(还没算其他 APP)。生产环境跑 1-2 年后,这张表会膨胀到几十万行。
|
||||||
|
|
||||||
|
**问题描述**:
|
||||||
|
- `biz.ai_run_logs` **没有 retention / archive 策略**
|
||||||
|
- 也没有 `cleanup_ai_run_logs_after_days` 类似配置
|
||||||
|
- admin-web 的 run-logs 列表查询可能从"快"变"慢"
|
||||||
|
- 数据库备份大小持续上涨
|
||||||
|
|
||||||
|
**业务联系**:
|
||||||
|
- 上游:每个 AI 调用都写 1 行
|
||||||
|
- 下游:列表查询 / 计费报表 / 异常排查
|
||||||
|
- 与沙箱关联:沙箱模式下也会写 run_logs 但带 `runtime_mode='sandbox'`,如果不区分清理会丢沙箱回放数据
|
||||||
|
|
||||||
|
**修改影响**:
|
||||||
|
- 数据层:加 `cleanup_ai_run_logs.py` 定期任务(保留 N 天 / 归档前 M 天到对象存储)
|
||||||
|
- 接口层:可选加 `GET /api/admin/ai/run-logs/archive` 查归档
|
||||||
|
- 展示层:admin-web 列表注明"最近 N 天",超出范围跳归档查询
|
||||||
|
|
||||||
|
**推荐选项**:
|
||||||
|
1. **A 简单 retention**:cron 每日删 90 天前的 live 日志,sandbox 日志保留 7 天 → 优:简单 劣:历史不可查
|
||||||
|
2. **B retention + 冷归档**:90 天后归档到对象存储(parquet / 或 cold table),admin-web 提供"查归档"入口 → 优:历史可查 劣:基建复杂
|
||||||
|
3. **C 仅打监控告警**:数据库行数 > 阈值时报警,不自动清理 → 优:零改 劣:被动响应
|
||||||
|
|
||||||
|
**建议判定**:**[P1] 留 Wave 4**(DWS / 数据正确性 Wave),选 A 起步,B 后续观察
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### F1-4. triggers/unified 权限过松(任意 admin 看全局)
|
||||||
|
|
||||||
|
**关联页面/接口**:
|
||||||
|
- admin-web 路由:`/triggers`(触发器统一管理页)
|
||||||
|
- 后端端点:`GET /api/admin/triggers/unified`(P1-6 调研发现的第 3 个 API)
|
||||||
|
- 数据库:跨表只读聚合(`biz.trigger_jobs` + ai_trigger_jobs + scheduled_tasks)
|
||||||
|
|
||||||
|
**业务背景**:
|
||||||
|
admin-web `/triggers` 顶部按 Tab 展示业务触发器 / AI 触发器,底部有个"统一视图"显示跨类型聚合。这个聚合数据来自 `/admin/triggers/unified` 端点。
|
||||||
|
|
||||||
|
**问题描述**:
|
||||||
|
- 该端点只校验 `admin` 角色,**不校验 super_admin**
|
||||||
|
- 任何 admin(含 site_admin)登录后能看**所有 site 的触发器**(因为 unified 是聚合视图,不按 site 过滤)
|
||||||
|
- 多租户后:租户 A 的 site_admin 能看到租户 B 的触发器名称 / cron / status
|
||||||
|
|
||||||
|
**业务联系**:
|
||||||
|
- 上游:运维查"全局调度状态"
|
||||||
|
- 下游:仅展示,不直接动数据
|
||||||
|
- 与 P1-6 关联:P1-6 已规划"扩展 /trigger-jobs PATCH + 业务触发器禁用守卫 + 保留 unified",**本项是 unified 的额外权限收紧**(原 P1-6 没覆盖)
|
||||||
|
|
||||||
|
**修改影响**:
|
||||||
|
- 接口层:`/admin/triggers/unified` 加 `super_admin` 守卫,或加 site_id 过滤
|
||||||
|
- 展示层:site_admin 看不到"统一视图" Tab(仅 super_admin 可见)
|
||||||
|
|
||||||
|
**推荐选项**:
|
||||||
|
1. **A super_admin 才可见**:unified 是跨 site 的全局视图,符合 super_admin 职责 → 优:语义清晰 劣:site_admin 失去全局视图
|
||||||
|
2. **B 按当前 site_id 过滤**:站内 unified 视图 → 优:site_admin 仍可用 劣:与 unified 跨表语义冲突
|
||||||
|
3. **C 维持现状,文档警告**:零改 劣:租户隔离失效
|
||||||
|
|
||||||
|
**建议判定**:**[D Bug 安全] 升级到 Wave 2**(随 P1-6 合并修),选 A
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### F1-5. 沙箱模式下批量 AI 任务未校验 runtime_context(切沙箱后仍按真实时钟跑)
|
||||||
|
|
||||||
|
**关联页面/接口**:
|
||||||
|
- admin-web 路由:`/ai/operations`(批量任务入口)
|
||||||
|
- 后端端点:`POST /api/admin/ai/batch-run`、`POST /api/admin/ai/run/{app_type}`
|
||||||
|
- runtime context:`biz.site_runtime_context`(P20 SPEC 主表)
|
||||||
|
- 影响 AI 应用:全部 8 APP
|
||||||
|
|
||||||
|
**业务背景**:
|
||||||
|
P20 沙箱设计:切 sandbox 后,所有"读取业务数据"的查询应该按 sandbox_date 切片(看到历史日期当时的数据)。批量 AI 任务的 prompt 模板会嵌入"current_time" / "ref_date" 等字段,沙箱模式下这些应该是 sandbox_date,而不是真实今天。
|
||||||
|
|
||||||
|
**问题描述**:
|
||||||
|
- `/api/admin/ai/batch-run` 创建批次时**没读 site_runtime_context**
|
||||||
|
- prompt 模板里的"current_time"被设成 `datetime.now()`(真实今天)
|
||||||
|
- 切沙箱回放 2026-03-01 时,AI 看到的"今天"还是真实日期(如 2026-05-04)
|
||||||
|
- AI 输出的洞察文案会说"截至今天 5 月 4 日,本月营收..." → **跟沙箱数据(3 月)语义错位**
|
||||||
|
|
||||||
|
**业务联系**:
|
||||||
|
- 上游:运维切 sandbox + 触发批量预热
|
||||||
|
- 下游:AI 输出文案语义错位 → 沙箱演示效果失真
|
||||||
|
- 与 P0-3 看板沙箱关联:P0-3 已修小程序看板,但 AI 入口还没接(本项)
|
||||||
|
- 与 P20 SPEC §10.2 关联:AI 提示词矩阵标了 X 已接入,但**这是 prompt builder 接入,不是 admin-web 触发入口接入**
|
||||||
|
|
||||||
|
**修改影响**:
|
||||||
|
- 接口层:`batch-run` / `run/{app_type}` 在创建任务前 `get_runtime_context(site_id)`,把 sandbox_date 传给 prompt builder
|
||||||
|
- 数据层:`ai_run_logs.runtime_mode` + `sandbox_instance_id` 列(P20 已规划)
|
||||||
|
- 展示层:admin-web `/ai/operations` 顶部条带显示当前 sandbox_date(如非 live)
|
||||||
|
|
||||||
|
**推荐选项**:
|
||||||
|
1. **A 完整接入沙箱**:batch-run 读 runtime_context 传给 prompt + 写 ai_run_logs.runtime_mode → 优:沙箱主线达标 劣:Wave 1 工作量略增
|
||||||
|
2. **B 只 block 沙箱模式**:sandbox 时 batch-run 直接 422 拒绝(不允许沙箱跑批量)→ 优:简单 劣:沙箱演示功能受限
|
||||||
|
3. **C 留 Wave 2 处理**:Wave 1 不修,P20 §10.2 标"待补" → 优:不阻塞 Wave 1 劣:沙箱主线收口延后
|
||||||
|
|
||||||
|
**建议判定**:**[D Bug 沙箱主线] 升级到 Wave 1**(P0-7 沙箱收口主线,本 Wave 必修),选 A
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 第二组 项目治理(2 项)
|
||||||
|
|
||||||
|
### F2-1. OpenAPI 与代码不同步(本批 23 端点中 10 个不在 backend-api.json)
|
||||||
|
|
||||||
|
**关联页面/接口**:
|
||||||
|
- 项目级:`docs/contracts/openapi/backend-api.json`(OpenAPI 静态导出)
|
||||||
|
- 抓取脚本:`tools/codex/...` 或类似(具体位置 Wave 1 没查到)
|
||||||
|
- 影响:全部 admin-web API PRD 工作流
|
||||||
|
|
||||||
|
**业务背景**:
|
||||||
|
admin-web API PRD(P1-7)的工作流是"自动生成 + 人工细化",依赖 backend-api.json 作为权威源。批 1 撰写时发现:**本批 23 端点中 10 个不在 OpenAPI 文件**,意味着每批 PRD 都可能漏 30%+ 端点。
|
||||||
|
|
||||||
|
**问题描述**:
|
||||||
|
- `backend-api.json` 缺以下端点:
|
||||||
|
- 全部 `/api/admin/runtime-context/*`(5 个)
|
||||||
|
- `/api/admin/triggers/unified`(1 个)
|
||||||
|
- `admin_ai` 后期扩展 4 个端点(`/run/{app_type}`、`/triggers` GET&PATCH、`/prewarm/progress`、`/trigger-event`)
|
||||||
|
- 推测原因:OpenAPI 静态导出脚本未含某些后注册的 router,或抓取时服务未启动全部 router
|
||||||
|
- backend-api.json 上次更新时间:Wave 0 迁移期(2026-04-XX)?
|
||||||
|
|
||||||
|
**业务联系**:
|
||||||
|
- 上游:后端 router 改动 → 应触发 OpenAPI 重抓
|
||||||
|
- 下游:admin-web 前端类型生成 / API PRD / 文档同步
|
||||||
|
- Wave 关联:批 2-5 会持续依赖 OpenAPI,如果不修,Wave 2-4 可能各漏 10-20%
|
||||||
|
|
||||||
|
**修改影响**:
|
||||||
|
- 工程层:修抓取脚本(确保启动包含所有 router 的服务)
|
||||||
|
- 文档层:`backend-api.json` 重新生成 + 入仓
|
||||||
|
- 工作流:加 git pre-commit / CI 钩子检查 OpenAPI 与代码同步(可选)
|
||||||
|
|
||||||
|
**推荐选项**:
|
||||||
|
1. **A Wave 2 提前修**(我推荐):批 2 撰写前先修脚本,避免后续批漏端点 → 优:止血最早 劣:Wave 2 多一项准备工作
|
||||||
|
2. **B Wave 5 文档收尾时统一修**:5 批合并时一次校核 + 修补 → 优:跟收尾节奏一致 劣:批 2-5 各漏端点时需手工补
|
||||||
|
3. **C 不修脚本,人工对照代码补 PRD**:每批主线手动 grep router 补漏 → 优:零基建 劣:容易漏 + 持续工作量
|
||||||
|
|
||||||
|
**建议判定**:**[C 待补] Wave 2 提前修**,选 A
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### F2-2. tests/ 不入仓(.gitignore:71 排除测试代码)
|
||||||
|
|
||||||
|
**关联页面/接口**:
|
||||||
|
- 项目级:`.gitignore:71` `tests/`
|
||||||
|
- 影响范围:**全部子项目**(backend / etl / miniprogram / admin-web / tenant-admin)
|
||||||
|
- Wave 1 实例:W1-T4 audience 7 单测 + W1-T5 db_viewer 测试改动留本地
|
||||||
|
|
||||||
|
**业务背景**:
|
||||||
|
Wave 1 Day 4 跑测试时发现:`apps/backend/tests/` 全部测试文件**从未被 tracked**(`git ls-files apps/backend/tests/test_auth_jwt.py` 返回空)。原因:`.gitignore:71` 写了 `tests/` 排除全部测试目录。
|
||||||
|
|
||||||
|
**问题描述**:
|
||||||
|
- 测试代码不入仓 → CI 没法跑测试 → 测试可能 rot
|
||||||
|
- 各子项目测试只在本地 → 不同开发者机器测试覆盖不一
|
||||||
|
- Wave 1 我加的 jwt audience 7 单测(75% 覆盖 W1-T4)留本地,无法保护后续 Wave 不破坏 audience 行为
|
||||||
|
- 这是项目级早期决策(可能是为了避开 venv / pytest_cache 误入仓)
|
||||||
|
|
||||||
|
**业务联系**:
|
||||||
|
- 上游:开发写测试(本地有效)
|
||||||
|
- 下游:CI / 后续 Wave / 跨设备协作(都受影响)
|
||||||
|
- 与 P1-13 tasks.md 撒谎对照:tasks.md 撒谎是"声明做了实际没做",tests 不入仓是"做了别人看不到" — 两者都侵蚀测试可信度
|
||||||
|
|
||||||
|
**修改影响**:
|
||||||
|
- `.gitignore:71` 改为更精细的排除(如 `**/__pycache__/` `**/.pytest_cache/` `**/*.egg-info/`)
|
||||||
|
- 全部子项目测试目录入仓
|
||||||
|
- CI 配置(可选,Wave 5 决议)
|
||||||
|
- 注意:某些 tests 内可能含 fixtures / 测试库 dump,需要先脱敏
|
||||||
|
|
||||||
|
**推荐选项**:
|
||||||
|
1. **A 改 .gitignore + 入仓 + Wave 5 启 CI**(我推荐):彻底解决,CI 反馈 → 优:长期投资 劣:需要先扫一遍现有 tests/ 是否含敏感数据
|
||||||
|
2. **B 改 .gitignore + 入仓 + 不启 CI**:先解决"代码可见",CI 后续再说 → 优:渐进 劣:仍依赖人工跑
|
||||||
|
3. **C 维持现状**:接受 tests rot 风险 → 优:零改 劣:Wave 5 收尾时无法证明测试通过
|
||||||
|
|
||||||
|
**建议判定**:**[B 现状对 待优化] Wave 5 处理**,选 A(需先脱敏扫描)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 第三组 批 1 PRD 业务语义待答(5 项)
|
||||||
|
|
||||||
|
### F3-1. AI cache 失效粒度
|
||||||
|
|
||||||
|
**关联**:`POST /api/admin/ai/cache/invalidate`、`biz.ai_cache` 表
|
||||||
|
|
||||||
|
**业务背景**:
|
||||||
|
admin-web 提供"清空 AI 缓存"按钮,运维在 prompt 调优后清缓存让下次调用拿新版本。当前实现按 cache_key 整张表清,**可能误伤所有 site 的缓存**。
|
||||||
|
|
||||||
|
**冲突逻辑**:
|
||||||
|
- 设计意图:某 site 的 prompt 改了 → 只清该 site 的缓存
|
||||||
|
- 实际实现:`DELETE FROM biz.ai_cache WHERE cache_key LIKE ?`,不带 site_id 过滤
|
||||||
|
|
||||||
|
**业务联系**:
|
||||||
|
- 上游:运维改 prompt 后清缓存
|
||||||
|
- 下游:其他 site 缓存被误清 → 下次访问触发 AI 调用 → token 浪费
|
||||||
|
|
||||||
|
**修改影响**:
|
||||||
|
- 接口层:加 `site_id` 参数(可选,不传则当前 site)
|
||||||
|
- 数据层:无 schema 变更
|
||||||
|
- 展示层:admin-web 按钮加 site 选择器
|
||||||
|
|
||||||
|
**推荐选项**:
|
||||||
|
1. **A 按当前 site 清**:默认清当前 super_admin 选的 site → 优:安全 劣:运维需先切 site
|
||||||
|
2. **B 显式 ?scope=site|tenant|all**:运维选粒度,默认 site → 优:灵活 劣:UI 多按钮
|
||||||
|
3. **C 维持现状(全表清)**:简单粗暴 → 优:简单 劣:误伤
|
||||||
|
|
||||||
|
**建议判定**:**待 Neo 拍板**,我倾向 **B**(灵活 + 默认安全)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### F3-2. AI budget 单价是估算还是实际计费?
|
||||||
|
|
||||||
|
**关联**:`GET /api/admin/ai/budget`、DashScope 计费
|
||||||
|
|
||||||
|
**业务背景**:
|
||||||
|
admin-web 显示"本月 AI 预算 X 元 / 已用 Y 元"。Y 来自 `biz.ai_run_costs` 累计 token × 单价。但单价配置存哪里?DashScope 实际单价(qwen-max / qwen-turbo)是浮动的还是固定的?
|
||||||
|
|
||||||
|
**冲突逻辑**:
|
||||||
|
- 配置层:`config.AI_TOKEN_PRICE_*`(代码里硬编码?cfg 表?)
|
||||||
|
- 计费层:DashScope API 返回 token usage,后端按单价转元
|
||||||
|
- 实际值:DashScope 控制台账单(权威)
|
||||||
|
|
||||||
|
**业务联系**:
|
||||||
|
- 上游:每次 AI 调用记 token
|
||||||
|
- 下游:budget 展示 / 上线前预算估算 / 超额告警
|
||||||
|
- 上线门槛:P11 上线时 budget 准确性是验收点之一
|
||||||
|
|
||||||
|
**修改影响**:
|
||||||
|
- 配置层:加 `cfg_ai_token_price`(SCD2 表,按 model + 时间切片)
|
||||||
|
- 接口层:budget 端点改读 cfg 表
|
||||||
|
- 对账:每月与 DashScope 账单对账,误差 > 5% 报警
|
||||||
|
|
||||||
|
**推荐选项**:
|
||||||
|
1. **A 配置表 + 月度对账**:正确性最高 → 优:可审计 劣:对账自动化成本
|
||||||
|
2. **B 硬编码单价**:维持简单 → 优:0 改 劣:DashScope 调价后失准
|
||||||
|
3. **C 用 DashScope SDK 返回的实际计费值**:如果 SDK 返回 cost_yuan 字段,直接用 → 优:最准 劣:依赖 SDK
|
||||||
|
|
||||||
|
**建议判定**:**待 Neo 答** — 你知道 DashScope SDK 是返 token 还是 cost,这影响选项
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### F3-3. 手动触发 AI 调用是否记 audit_log + 二次确认?
|
||||||
|
|
||||||
|
**关联**:`POST /api/admin/ai/run/{app_type}`、admin-web `/ai/operations`
|
||||||
|
|
||||||
|
**业务背景**:
|
||||||
|
admin-web 提供"手动触发某 APP"按钮,运维点了直接调 dashscope。区别于 P0-6 clearAllTasks(那是清数据),这个是"花钱 + 不可撤销 + 可能产生 PII 写入"。
|
||||||
|
|
||||||
|
**冲突逻辑**:
|
||||||
|
- 当前:点击直接调用,不记 audit
|
||||||
|
- 期望:符合"可追溯 + 可控制成本"原则,应该记日志 + 弹窗确认
|
||||||
|
|
||||||
|
**业务联系**:
|
||||||
|
- 上游:运维或测试人员手动触发
|
||||||
|
- 下游:dashscope 扣费 + 日志写入 + 可能影响小程序展示
|
||||||
|
- 与 P0-6 对照:P0-6 是清空(数据风险),F3-3 是调用(费用 + PII)
|
||||||
|
|
||||||
|
**修改影响**:
|
||||||
|
- 接口层:`/run/{app_type}` 加 audit_log 写入(`biz.admin_audit_log`,含 user / params / timestamp)
|
||||||
|
- 展示层:admin-web 加二次确认弹窗(可选关闭)
|
||||||
|
|
||||||
|
**推荐选项**:
|
||||||
|
1. **A 加 audit_log + 二次确认**(我推荐):标准做法 → 优:可追溯 + 防误操作 劣:UX 多一步
|
||||||
|
2. **B 仅加 audit_log,不加二次确认**:运维信任度高 → 优:UX 流畅 劣:误点风险
|
||||||
|
3. **C 维持现状**:零改 → 优:简单 劣:无追溯
|
||||||
|
|
||||||
|
**建议判定**:**待 Neo 答**,我倾向 A
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### F3-4. runtime-context 切 sandbox 的"未来日期"边界
|
||||||
|
|
||||||
|
**关联**:`PATCH /api/admin/runtime-context`、admin-web `/settings/runtime-context`
|
||||||
|
|
||||||
|
**业务背景**:
|
||||||
|
admin-web 沙箱页可以切 sandbox_date。当前代码校验:
|
||||||
|
```python
|
||||||
|
if sandbox_date > date.today():
|
||||||
|
raise HTTPException(422, "沙箱日期不能晚于今天")
|
||||||
|
```
|
||||||
|
|
||||||
|
**冲突逻辑**:
|
||||||
|
- 设计:沙箱是"回放历史",上限 = today
|
||||||
|
- 但 P20 SPEC §3.1 没明确写"sandbox_date 是否可以等于 today"
|
||||||
|
- 边界:`> today` 拒绝,`= today` 允许(当前实现)
|
||||||
|
- 问题:`= today` 时沙箱跟 live 几乎一致,有意义吗?
|
||||||
|
|
||||||
|
**业务联系**:
|
||||||
|
- 上游:运维切 sandbox 用于演示 / 调试
|
||||||
|
- 下游:全部读 sandbox_date 的逻辑
|
||||||
|
|
||||||
|
**修改影响**:
|
||||||
|
- P20 SPEC §3.1 文字补充
|
||||||
|
- 后端校验逻辑(若需收紧)
|
||||||
|
- admin-web 日期选择器 maxDate 配置
|
||||||
|
|
||||||
|
**推荐选项**:
|
||||||
|
1. **A 维持现状(`> today` 拒绝,`= today` 允许)**:今天也算合法回放(数据齐全)→ 优:零改 劣:沙箱意义弱
|
||||||
|
2. **B 严格 `< today`**:沙箱必须是历史 → 优:语义清晰 劣:今天演示需切 live
|
||||||
|
3. **C 提示 + 允许**:`= today` 时 admin-web 提示"今天数据可能不完整(实时更新中)" → 优:体验好 劣:UI 多一项
|
||||||
|
|
||||||
|
**建议判定**:**待 Neo 答**(产品决策),我倾向 A 或 C
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### F3-5. triggers/unified 是否需要分页
|
||||||
|
|
||||||
|
**关联**:`GET /api/admin/triggers/unified`、admin-web `/triggers`
|
||||||
|
|
||||||
|
**业务背景**:
|
||||||
|
unified 端点跨表聚合返回所有触发器(含 biz / ai 两类)。当前**全表返回,不分页**。
|
||||||
|
|
||||||
|
**冲突逻辑**:
|
||||||
|
- 当前实现:全量返回(预计 < 100 条)
|
||||||
|
- 真实场景:门店多 + AI 触发器多 → 100-500 条
|
||||||
|
- 极端场景:多租户 + 50 门店 × 10 触发器 = 500 条
|
||||||
|
- 单 row JSON 估 200 bytes → 100KB(可接受)~ 1MB(慢)~ 10MB(慢且占带宽)
|
||||||
|
|
||||||
|
**业务联系**:
|
||||||
|
- 上游:运维浏览触发器
|
||||||
|
- 下游:列表渲染 / 筛选
|
||||||
|
|
||||||
|
**修改影响**:
|
||||||
|
- 接口层:加 `?page=&page_size=`
|
||||||
|
- 展示层:admin-web 列表加分页器
|
||||||
|
- 跟 P1-6 关联:P1-6 合并方案保留 unified,可顺带加分页
|
||||||
|
|
||||||
|
**推荐选项**:
|
||||||
|
1. **A 加分页(默认 page_size=50)**:标准做法 → 优:任意规模可承受 劣:Wave 2 改造时多一项
|
||||||
|
2. **B 全量 + 前端分页**:后端简单 → 优:简单 劣:1000+ 触发器时仍卡
|
||||||
|
3. **C 维持现状**:门店少时无问题 → 优:零改 劣:多租户后必爆
|
||||||
|
|
||||||
|
**建议判定**:**待 Neo 答**,我倾向 A,**留 Wave 4**(数据/性能 Wave),不阻塞 Wave 2 P1-6 合并主线
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 总览决策清单
|
||||||
|
|
||||||
|
| # | 简述 | 我的建议 | Wave |
|
||||||
|
|---|---|---|---|
|
||||||
|
| F1-1 | 批量 AI 长事务无幂等 | D Bug 选 A | Wave 2 |
|
||||||
|
| F1-2 | run-logs PII 跨租户泄露 | D Bug 选 A | **Wave 1** |
|
||||||
|
| F1-3 | batch_id 数据库膨胀 | P1 选 A | Wave 4 |
|
||||||
|
| F1-4 | triggers/unified 权限过松 | D Bug 选 A | Wave 2 |
|
||||||
|
| F1-5 | 沙箱 batch-run 未读 runtime | D Bug 选 A | **Wave 1** |
|
||||||
|
| F2-1 | OpenAPI 不同步 | C 待补 选 A | Wave 2 提前修 |
|
||||||
|
| F2-2 | tests/ 不入仓 | B 待优化 选 A | Wave 5 |
|
||||||
|
| F3-1 | AI cache 失效粒度 | 待 Neo 答 (B 倾向) | Wave 2 |
|
||||||
|
| F3-2 | AI budget 单价来源 | 待 Neo 答 | Wave 2-4 |
|
||||||
|
| F3-3 | 手动触发 audit + 确认 | 待 Neo 答 (A 倾向) | Wave 2 |
|
||||||
|
| F3-4 | sandbox_date 边界 | 待 Neo 答 (A/C 倾向) | Wave 5 文档 |
|
||||||
|
| F3-5 | unified 分页 | 待 Neo 答 (A 倾向) | Wave 4 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
> 等 Neo 在每条卡上写斜体反馈(类似 04a/b/c),主线接斜体即按判定走 Wave。
|
||||||
Reference in New Issue
Block a user