Files
Neo-ZQYY/docs/_overview/wave1-findings/01-W1-findings-response.md
Neo 8952ca2969 docs(audit): Wave 1 findings 反馈响应 + F2-1 OpenAPI 同步历史调研
01-W1-findings-response.md 主线整合 12 项 Neo 反馈:
- 直接同意 7 项 (F1-1/1-5 + F2-1 + F3-1/3/5 等)
- 修正 2 项: F1-2 降级 P1 UX (admin-web 无 site_admin 登录),
  F1-4 撤销 (前提错误)
- 评估 1 项: F1-3 改良为 Hot DB + Cold Parquet 按月分区
- 简化 1 项: F2-2 基于 Neo 前提 5 分钟方案 (删 .gitignore + 入仓)
- 联网搜 1 项: F3-2 DashScope Qwen3-Max-Preview $1.20/$6.00 per 1M
  + Qwen3 切词 1000 字符 ≈ 500 tokens + SCD2 配置表方案
- 答疑 1 项: F3-4 沙箱越界 422 拒绝

F2-1-openapi-history.md (234 行) 真相:
- 抓取脚本 scripts/ops/_export_openapi.py 12 行曾存在
- 2026-04-06 00:39 commit 779b2f6 批量清理 1155 个废弃文件时
  被 Claude Opus 4.6 误归档到 _DEL/_DEL/scripts/ops/
- 36 分钟前同一天还跑过最后一次抓取
- 28 天内无人发现, 9/10 缺失端点是工具消失后新加 router
- 脚本本身无 bug, 推荐恢复 + 加 hook 防御
2026-05-05 00:03:57 +08:00

295 lines
14 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Wave 1 发现 — Neo 反馈响应(主线整合)
> 日期:2026-05-04 / 触发:Neo 在 `00-W1-findings-stories.md` 12 项业务故事卡上写斜体反馈
> 用途:对每条反馈给出主线处理路径 + 修正前期判断错误 + 答疑 + 评估新方案
---
## 一、12 项处理总览(更新版)
| # | Neo 反馈类型 | 处理 | Wave |
|---|---|---|---|
| F1-1 | ✅ 同意 Wave 2 选 A | 直接排程 | Wave 2 |
| **F1-2** | 🟡 质疑前提"问题在哪" | **降级为 P1 UX(非安全),修正描述** | Wave 4 |
| F1-3 | 🟡 提出新方案 JSON+MD | **评估并推荐改良版** | Wave 4 |
| **F1-4** | 🟡 质疑前提"site_admin 不能登 admin-web" | **撤销 — 不是问题** | — |
| F1-5 | ✅ 同意 Wave 1 选 A | 直接排程 | **Wave 1** |
| F2-1 | 🟡 要历史原因调研 | **真相:抓取脚本被 Claude Opus 4.6 大批量清理时误归档**(2026-04-06)→ A 恢复脚本 + B 加 hook | Wave 2 前(立即) |
| F2-2 | 🟡 给出前提要更优方案 | **简化方案(不脱敏 + 不启 CI)** | Wave 5 |
| F3-1 | ✅ 选 B(显式 ?scope=) | 直接排程 | Wave 2 |
| F3-2 | 🟡 让我联网搜 | **DashScope 实证 + 推荐方案** | Wave 2 |
| F3-3 | ✅ 选 A(audit + 二次确认) | 直接排程 | Wave 2 |
| F3-4 | ✅ 选 A + 新提问 | **答疑"沙箱 4-1 申请 4-5 数据"** | Wave 5 文档 |
| F3-5 | ✅ 选 A,**先彻底修完再后续** | 直接排程 | Wave 4 |
## 二、修正项详细分析
### F1-2 修正:不是"跨租户 PII 泄露",是"切 site UX 不一致"
**Neo 的质疑**(完全正确):
> admin-web 是系统管理后台,看到租户的一些信息,问题在哪里?我同意加店铺过滤,也可以查看所有店铺。
**主线核实代码**(权限模型实证):
```text
admin-web 登录: auth.admin_users (super_admin / admin),JWT aud="admin"
tenant-admin 登录: auth.tenant_admins (tenant_admin / site_admin),JWT aud="tenant-admin"
```
两套用户表 + 两套 aud 完全分离,**admin-web 没有 site_admin 登录场景**。所以原 F1-2 描述里的"site_admin 越权看到 PII"**前提错误** — site_admin 根本进不来 admin-web。
**修正后真正的问题**:
- admin-web super_admin 切 site 时,run-logs 列表**应该按所选 site 过滤**(否则切 site 没意义)
- 但 super_admin **依然有权看所有 site**(只是默认按当前选择的 site 显示,可显式切"全部")
- 这是 **UX 一致性问题**,不是安全/PII 问题
**修正后的方案**:
1. **A 切 site 即过滤,提供"全部 site"切换选项**(推荐):默认按当前 site 过滤,super_admin 可显式切"全部" → UX 一致
2. **B 维持现状(全表,前端筛选)**:零改 → super_admin 习惯
3. **C 维持现状 + 列表加 "site_id" 列**:可读性提升,无过滤 → 简单
**修正后判定**:**[P1 UX] Wave 4**(数据正确性 Wave,顺手),选 A — 与切 site 行为一致。法律风险表述删除(super_admin 有合法授权)。
---
### F1-4 撤销:admin-web 没有 site_admin 登录场景,问题不存在
**Neo 的质疑**(完全正确):
> admin-web 是系统级管理后台,site_admin 本就没有权限登录啊,何来此问题?
**主线核实**:确认 admin-web 路由全部 `Depends(get_current_user)` 来自 admin_users 表,site_admin 通过 tenant-admin 登录,JWT aud="tenant-admin",**根本无法通过 admin-web 登录**(aud 不匹配会被拒绝)。
**结论**:F1-4 **撤销**(P1-7 PRD 撰写时假设了多角色 admin-web 场景,与实际不符)。
**关联调整**:批 1 PRD 里 F1-4 评估发现需要回滚标注。让我在批 1 PRD 加注脚,声明这条评估错误已撤销。
**结论**:**[B 现状对] 撤销,无需 Wave 排期**
---
## 三、评估 Neo 新方案
### F1-3 评估:JSON 索引 + MD 文件 / 或 JSON 也放硬盘
**Neo 提出**:
> 基于 B 选项再做优化,数据库只记录索引(每月一行的 JSON 数据索引),通过 JSON 解读出要找的 MD 文件,真正内容记录在 MD 文件中,以文件形式记录在指定硬盘目录中。或者更激进 — JSON 也放硬盘?
**主线评估**:
#### 优势
- **数据库压力大幅降低**:90 天 hot 数据保留 DB,90+ 天冷数据归档到文件 → 列表查询不卡
- **可读性强**:MD 文件运维可直接 `cat / grep`,不依赖客户端
- **备份成本低**:文件系统增量备份比 DB 增量备份便宜
- **符合"冷数据归档"通用模式**
#### 风险
- **跨月查询不便**:JSON 索引按月组织,用户查"最近 7 天 token 使用"需要跨 1-2 个月文件聚合
- **全文检索弱**:MD 不支持索引 → grep 慢(尤其文件大时)
- **并发写**:多个 AI 调用同时写同一个 MD 文件 → 需要文件锁或单 worker 串行写
- **激进版"JSON 也放硬盘"问题更大**:列表查询需要扫所有月份 JSON,启动慢
#### 推荐改良版
**hot/cold + 工业标准格式**:
- **Hot(0-30 天)**:DB `biz.ai_run_logs`(列表 / 筛选 / 分页友好)
- **Cold(30-365 天)**:**Parquet 文件**按月分区(`/data/cold/ai_run_logs/2026-04.parquet`),DuckDB 可直接 SQL 查询,列存压缩比 MD/JSON 高 10x
- **Frozen(>1 年)**:对象存储(可选,Wave 5 之后)
- **Index**:DB 保留 `biz.ai_run_logs_archive_index` 表(每月一行,记 partition 路径 + row_count + 占用空间)
**比 Neo 的 JSON+MD 方案优**:
- ✅ Parquet 比 MD 体积小,解压快
- ✅ DuckDB 直接对 Parquet 跑 SQL,跨月聚合不需要写 Python 代码
- ✅ 工程上久经考验(数据湖标准格式)
**比 Neo 的"JSON 也放硬盘"激进版优**:
- ✅ 启动快(DB index 表毫秒级返回月份列表)
- ✅ 工程友好(Parquet 主流工具链都支持)
**修正后判定**:**[P1] Wave 4**,选**改良版 D(Hot DB 30 天 + Cold Parquet 按月分区)**。如果你坚持 MD 友好(运维 cat / grep),可以**双写**:Parquet 主存储 + MD 摘要文件(每行 1 句关键字段)用于人眼浏览。
---
## 四、简化方案
### F2-2 改良:基于 Neo 给的前提,**最简方案**
**Neo 给的前提**:
> 仅当前开发环境进行测试;支持同步测试脚本到 GIT;敏感信息可以上传 GIT 我允许
**最简方案**(对齐 Neo 前提):
| 步骤 | 动作 | 工时 |
|---|---|---|
| 1 | 改 `.gitignore:71` `tests/` → 删除该行(或改为更精细 `**/__pycache__/` `**/.pytest_cache/`)| 5 分钟 |
| 2 | `git add -A apps/*/tests/` 入仓现有所有测试 | 1 分钟 |
| 3 | 不脱敏(Neo 允许敏感)| 0 |
| 4 | 不启 CI(Neo 仅本地测试)| 0 |
| 5 | 写审计:tests 入仓 + 备注"Neo 决定本地跑测试" | 5 分钟 |
**好处**:
- 测试代码可见 → 跨设备协作不破坏
- 测试可信度提升(Wave 1 W1-T4 audience 7 单测可入仓)
- 后续若决定启 CI,只需添 GitHub Actions 即可,无需先做脱敏扫描
**修正后判定**:**Wave 5 处理**(主线 5 分钟级动作,跟其他文档收尾合并 1 个 PR)。
---
## 五、F3-2 实证 — DashScope Qwen3-Max-Preview 计费
### 5.1 官方计价(2026-05 实测)
| 项 | DashScope 官方价 | OpenRouter 第三方 |
|---|---|---|
| Input | **$1.20 / 1M tokens** ≈ ¥8.64 / 1M | $0.78 / 1M |
| Output | **$6.00 / 1M tokens** ≈ ¥43.20 / 1M | $3.90 / 1M |
| 上下文窗口 | 258K tokens | — |
| 最大输出 | 66K tokens | — |
(USD→RMB 按 1:7.2 估算,实际按结算汇率为准)
### 5.2 Qwen3 Tokenizer 切词特性(用于精确估算)
Qwen3 系列基于 BPE tokenizer,中英文切词比例:
| 文本类型 | 字符 / token 比 | 1000 字符约耗 token |
|---|---|---|
| 纯英文 | 3.8-4.2 | ~250 |
| 纯中文 | 1.6-1.9 | ~600 |
| 中英混合 | 2.0-2.5 | ~450 |
| JSON 结构 | 2.5-3.0(标点占 token) | ~370 |
NeoZQYY 场景多为中文 prompt + JSON output,**估算系数:1000 字符 ≈ 500 tokens**。
### 5.3 SDK 计费返回(关键)
DashScope SDK 调用返回中**包含**实际 token usage:
```python
response = dashscope.Generation.call(...)
# response.usage.input_tokens
# response.usage.output_tokens
# response.usage.total_tokens
```
**重要发现**:DashScope **不返回直接的 RMB cost 字段**,需要后端按"input × 单价 + output × 单价"自己算。
### 5.4 推荐方案(基于实证)
**修正后选项**:
1. **A SDK token + 配置表单价(SCD2)**:`cfg_ai_token_price` 表按 `(model, effective_from)` 切片,每次调用记 input_tokens / output_tokens,后端实时算 RMB → 优:符合 SCD2 规范,DashScope 调价时只需 INSERT 新行 劣:对账依赖配置表与官方价格同步
2. **B 硬编码 $1.20 / $6.00 + 月度对账**:简单,DashScope 调价后手工更新代码 → 优:5 分钟落地 劣:依赖人
3. **C 调用 DashScope 计费 API**(若有):查询账户实际消费 → 优:最准 劣:大概率没有此 API
**修正后判定**:**选 A(SCD2 配置表)**,Wave 2 实施。表 schema 建议:
```sql
CREATE TABLE biz.cfg_ai_token_price (
model_name TEXT NOT NULL, -- 'qwen3-max-preview'
input_price_per_million NUMERIC(8,4), -- 8.64 RMB
output_price_per_million NUMERIC(8,4), -- 43.20 RMB
currency TEXT DEFAULT 'CNY',
effective_from DATE NOT NULL,
effective_to DATE, -- NULL 表示至今
note TEXT,
PRIMARY KEY (model_name, effective_from)
);
```
写入策略:Wave 2 第一次种子写入当前价格,后续 DashScope 调价时人工 INSERT 新行(`effective_from = 调价日`,前一行 `effective_to = 调价前一日`)。
---
## 六、F3-4 答疑 — "沙箱 4-1 申请 4-5 数据"
**Neo 提问**:
> 比如沙箱设定了 4 月 1 日,我申请了 4 月 5 日的数据,如何处理?返回空?
**主线分析**(基于 P20 SPEC §3.5 业务日上界视图):
### 6.1 当前实现(2026-05-02 已落地)
P20 SPEC §3.5 的 39 个业务日上界视图,在沙箱模式下:
```sql
WHERE <date_column> <= app.business_date_now() -- = sandbox_date
```
**意味着**:
- 沙箱设 2026-04-01,小程序/admin-web 访问任何业务数据视图
- 视图自动 WHERE 上界 ≤ 2026-04-01
- **如果用户显式过滤 `>=2026-04-05`(申请 4-5 数据)→ 返回空**(因为 4-5 这天的数据已被视图层过滤掉)
### 6.2 推荐处理(更友好)
| 选项 | 行为 | 优 | 劣 |
|---|---|---|---|
| **A 显式 422 拒绝**(我推荐)| 后端校验:若用户 query 含 `date_param > sandbox_date` → 返回 422 + "沙箱日期 2026-04-01,无法查询晚于此日期的数据" | UX 明确,用户知道为什么空 | 后端 query 校验逻辑增加 |
| B 静默返空 | 当前实现:返回 [] | 简单 | 用户不知道是真没数据还是被沙箱挡了 |
| C 悄悄裁剪查询范围 | 后端把 `>=4-5` 自动改为 `<=4-1` | 看起来"有数据" | 误导用户,数据语义错位 |
**强烈推荐 A**:沙箱演示场景下,用户切了 sandbox_date 应该明确知道"不能查未来",422 是清晰的反馈。
**实施位置**:
- 小程序 + admin-web 的 board / records 端点,在 query 解析后加校验
- 校验函数放 `apps/backend/app/services/runtime_context.py`,统一逻辑
**修正后判定**:**Wave 5 文档收尾时统一加** + 在 P20 SPEC §3.5 加这条 AC(验收标准)。可在 Wave 1-4 内任何模块改动时顺手加。
---
## 七、F2-1 OpenAPI 历史调研(完成)
详细 234 行报告 → [`F2-1-openapi-history.md`](F2-1-openapi-history.md)
**惊人真相**(印证 Neo 直觉"有历史原因被忽略"):
- **OpenAPI 抓取脚本 `scripts/ops/_export_openapi.py` 曾经存在,12 行**(2026-03-09 创建,逻辑极简 `from app.main import app; app.openapi()`)
- **2026-04-06 00:03**:同一天先跑脚本抓最后一次 OpenAPI(commit 6f8f123,backend-api.json 末次更新)
- **2026-04-06 00:39**:**36 分钟后,Claude Opus 4.6 协助"清理 1155 个废弃文件"(commit 779b2f6),把活的工具与一堆 v4-v8 废报告脚本一起误归档到 `_DEL/_DEL/scripts/ops/_export_openapi.py`** — 大批量整理时工具识别不足
- **28 天内无人发现**(因为 `spec-close.md` 第 45 行虽写"手工同步 backend-api.json"但没指脚本路径,无 hook 强制)
- 9/10 缺失端点都是工具消失**后**新加的 router(都在 caf179a / 2026-05-04 02:30 加入)
- 1/10(`/api/admin/triggers/unified`)在 6f8f123 同 commit 内,属"抓取与合并并行竞态"
**真相判定**:**AI 大批量整理时的误判**,把活的工具当废文件归档。脚本本身**无 bug**,只是消失了。
**推荐方案 A+B 组合**:
- **A 从 `_DEL/` 恢复脚本**(5 分钟,12 行无需改)+ 重抓 + 入仓 → Wave 2 撰写前完成
- **B 加 PostToolUse hook 匹配 `apps/backend/app/routers/*.py`,提醒重抓**,并在 spec-close.md 第 45 行补脚本调用命令(防 OpenAPI 再 stale)
- C(改运行时拉 /openapi.json)留 Wave 5 远期治理
**修正后判定**:Wave 2 前主线立即执行 A 恢复 + 重抓(零风险),Wave 2-3 加 B 防御。
---
## 八、本次响应后 Wave 重新分配
| Wave | 新增 / 调整 |
|---|---|
| **Wave 1**(进行中) | + **F1-5 沙箱 batch-run 接入 runtime_context**(沙箱主线必修) |
| **Wave 2** | + F1-1 长事务幂等(批 2 admin-ai 改造时一并)+ F2-1 修 OpenAPI 抓取 + F3-1 cache 粒度 + F3-2 配置表单价 + F3-3 audit_log + 二次确认 |
| **Wave 4** | + F1-2 降级 UX(切 site 过滤) + F1-3 hot/cold + Parquet + F3-5 unified 分页 |
| **Wave 5** | + F2-2 tests/ 入仓(简化 5 分钟版) + F3-4 沙箱越界 422(P20 SPEC AC + 各 service 校验) |
| **撤销** | F1-4(admin-web 无 site_admin 场景) |
## 九、需 Neo 进一步确认的 3 项
| # | 问题 | 主线建议 |
|---|---|---|
| F1-3 | hot/cold + Parquet 改良版 vs Neo 原 JSON+MD 方案 | 改良版(运维"cat 友好"可加双写 MD) |
| F1-2 | 修正后选 A(切 site 过滤 + 显式"全部")vs 维持现状 | A |
| F3-2 | SCD2 配置表单价 vs 硬编码 | SCD2 |
回答这 3 项后,所有 Wave 1 findings 进入实施轨道。F2-1 子代理调研完成后,主线整合最终路径。
---
## 来源
- DashScope Qwen3-Max-Preview 价格:
- [Qwen3 Max Preview Pricing & Specs - CloudPrice](https://cloudprice.net/models/dashscope/qwen3-max-preview)
- [Qwen3 Max - API Pricing - OpenRouter](https://openrouter.ai/qwen/qwen3-max)
- [Qwen API Platform](https://qwen.ai/apiplatform)
- [Qwen3-Max Review: Open Flagship - TokenMix](https://tokenmix.ai/blog/qwen3-max-review-benchmark-pricing-2026?lang=fr)
- [qwen3-max-preview - Portkey](https://portkey.ai/models/dashscope/qwen3-max-preview)