feat(ai): W1-AI-CLOSURE 超级 Sprint — 9 APP 全链路收口 + chat 上下文真激活

Phase 2.3 chat 上下文捕获链路从未真正激活到完整工作:
- 14 处 ai-float-button 补 sourcePage,chat.ts 三分支同步设 pageFilters.contextId
- 后端 page_context 4 层 BUG 修(列名错位 + RLS site_id 未重设)
- xcx_chat filters.pop 破坏 body.page_context 引用 — dict() 浅拷贝隔离
- chat 流式 markdown 实时解析(表格/标题/列表/加粗 + KPI 富卡)
- reference_card KPI 富卡接入 SSE 路径,db 真写入
- 维客线索 source 显示规则:AI 来源用机器人 icon 替代长文字

数据库:
- public.member_retention_clue 加 emoji + runtime_mode + sandbox_instance_id
- biz.ai_run_logs 加 assistant_id + 复合索引
- chk_ai_cache_type CHECK 约束 8 类应用名
- cache_type / app_type 命名统一(app6_note / app7_customer / app8_consolidation)
- 历史 emoji 抽取脚本 44/44 成功

后端 silent failure 修:
- cleanup_service WHERE app_type → cache_type(90 天清理 + 20K 上限重新生效)
- _build_ai_insight 字段错位修复(app4 → app7 + 字段对齐 prompt schema)
- task_manager talkingPoints 改 app5_tactics + tactics 字段
- task_manager aiSuggestion 改取 one_line_summary
- cache_service.CACHE_EXPIRY_DAYS 加 app2a_finance_area
- WS /ws/ai-cache 加 token + JWT + site_id 校验(P0 信息泄露漏洞)
- internal_ai token 改 hmac.compare_digest

工具/文档:
- main.py 加 RotatingFileHandler logs/backend.log + uvicorn /health 过滤
- 新建 utils/clue_category.py(VI 6 类配色 + emoji fallback + source 显示规则)
- 新建 utils/markdown.ts(轻量 md 转 rich-text 解析 + streaming 容错)
- audit + 数据库变更说明 + backlog §七 #14 收口 + #15-#38 残余子任务
- backlog 追加 §十一 App1 参数/MCP/沙箱审计 + §十二 百炼/SQL MCP 主任务线

实地 MCP 走查:14 入口数据层 + 5 代表入口 sourcePage 注入 + customer-detail 全模块 + chat md 渲染 + reference_card 富卡 都已验证。9 项预先 BUG/UX 登记 §七 #29-#38 后续修复。

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Neo
2026-05-06 16:39:07 +08:00
parent c9c2bce101
commit 2dfc926f96
56 changed files with 1983 additions and 278 deletions

View File

@@ -0,0 +1,152 @@
# 2026-05-06 · W1-AI-CLOSURE Schema 修复 + 命名统一
> 关联迁移:[`db/zqyy_app/migrations/20260506__ai_closure_schema_fixes.sql`](../../../db/zqyy_app/migrations/20260506__ai_closure_schema_fixes.sql)
>
> 完整审计:[`docs/audit/changes/2026-05-06__w1_ai_closure_super_sprint.md`](../../audit/changes/2026-05-06__w1_ai_closure_super_sprint.md)
## 变更说明
### 新增列
| 表 | 列 | 类型 | 默认值 | 用途 |
|---|---|---|---|---|
| `public.member_retention_clue` | `emoji` | `varchar(8)` | `''` | 维客线索独立 emoji 字段(App8 prompt 输出 emoji 直接写入,不再嵌 summary 字符串) |
| `public.member_retention_clue` | `runtime_mode` | `varchar(20)` | `'live'` | 运行模式 live/sandbox(与其他 7 张 ai_* 表对齐) |
| `public.member_retention_clue` | `sandbox_instance_id` | `varchar(64)` | `'live'` | sandbox 模式写入隔离实例 ID |
| `biz.ai_run_logs` | `assistant_id` | `bigint NULL` | NULL | App4/App5 这类 (assistant, member) 二元任务的助教 ID,便于失败定位 |
### 新增索引
- `idx_ai_run_logs_assistant_member` ON `biz.ai_run_logs (site_id, assistant_id, member_id, created_at DESC) WHERE assistant_id IS NOT NULL`
### CHECK 约束更新
- `chk_ai_cache_type` 重建:8 类应用名(`app2_finance` / `app2a_finance_area` / `app3_clue` / `app4_analysis` / `app5_tactics` / `app6_note` / `app7_customer` / `app8_consolidation`),与 prompt 文件名 + CacheTypeEnum 完全对齐
### 数据 UPDATE(命名统一)
- `biz.ai_cache.cache_type`:
- `app7_customer_analysis``app7_customer`(42 行)
- `app8_clue_consolidated``app8_consolidation`(72 行)
- `app6_note_analysis``app6_note`(测试库 0 行,生产可能有)
- `biz.ai_run_logs.app_type`:
- `app8_consolidate``app8_consolidation`(123 行)
- `app8_clue_consolidated``app8_consolidation`(测试库 0 行)
- `app6_note_analysis` / `app7_customer_analysis` → 应用名(测试库 0 行)
- `public.member_retention_clue.runtime_mode`:全部 NULL 填 `'live'`(44 行)
### 历史 emoji 回填(独立脚本)
`scripts/ops/backfill_retention_clue_emoji.py` — 把 summary 嵌入的首 emoji 抽到 emoji 列(测试库 44/44 行成功,可重入)。
## 兼容性影响
### ETL 影响
- 无直接影响。ETL 不写 ai_cache / ai_run_logs / member_retention_clue,只是注释里"客户基础信息" → "客户基础" 的字面量调整(`apps/etl/connectors/feiqiu/tasks/dws/member_consumption_task.py` + `member_visit_task.py`)。
### 后端 API 影响
- `customer_service._build_ai_insight`:cache_type 从 `app4_analysis`(错位)改为正确的 `app7_customer`,加 `site_id` 过滤 + 字段对齐 App7Result schema(strategies[].title/content)
- `customer_service._build_retention_clues`:加 `site_id` 过滤 + 字段补齐(detail/source/recorded_by_name/emoji)
- `task_manager.py` aiSuggestion:取 `one_line_summary` 替代不存在的 `app4.summary`
- `task_manager.py` talkingPoints:cache_type `app5_talking_points`(不存在)→ `app5_tactics` + 字段 `tactics[].scenario/script`
- `cleanup_service.py`:`WHERE app_type=%s` BUG 修(应是 `cache_type`)— 90 天清理 + 20K 上限重新生效
- `cache_service.CACHE_EXPIRY_DAYS`:补 `app2a_finance_area: 0`(64 区域组合不再永不过期)
- `dispatcher._write_retention_clue`:emoji 独立写入(不再拼 summary)+ 加 runtime_mode/sandbox_instance_id 五元组隔离
- `xcx_chat`:`ReferenceCard` schema 补 `link/source_page` 字段;`get_messages` SQL 加 `AND role IN ('user','assistant')` 过滤 system 行;`build_reference_card` KPI 富卡接入 SSE 路径
- WS `/ws/ai-cache/{site_id}` + `/ws/ai-alerts/{site_id}`:加 `?token=` query 参数 + JWT 校验 + site_id 一致性校验
### 小程序影响
- `customer-detail.ts`:clues 类型 4 字段 → 6 字段(tag/tagColor/emoji/text/source/desc);`_loadAIInsight` cache_type 改 `app7_customer` + 字段 `s.title/s.content`
- `customer-detail.wxml`:clue-card props 字段对齐 + `wx:if` 空态隐藏 + ai-float-button 补 sourcePage
- `task-detail.ts/wxml/json`:retentionClues tagColor 6 类;talkingPoints 类型 string[] → TacticItem[];整页补 ai-float-button + json 注册组件
- `chat.ts`:三分支(task/customer/coach)补 `sourcePage` + `pageFilters.contextId`,Phase 2.3 链路真正激活
- 14 处 wxml ai-float-button 全部补 sourcePage
### admin-web 影响
- `AIOperations.tsx` `CACHE_TYPE_OPTIONS` 8 类对齐(去除 _analysis / _consolidated 后缀)
- `AIRunLogs.tsx` `RUN_LOG_APP_TYPE_OPTIONS` 删除旧 `app8_consolidate`(数据已 UPDATE 到 `app8_consolidation`)
- `__tests__/adminAiAppTypes.test.ts`:测试断言不再固化"双名共存",改为统一命名
## 回滚策略
迁移文件末尾有完整"回滚参考"块,按以下顺序执行:
```sql
BEGIN;
-- 1. DROP 新约束
ALTER TABLE biz.ai_cache DROP CONSTRAINT IF EXISTS chk_ai_cache_type;
-- 2. UPDATE 命名回滚(注意:旧名 app8_consolidate vs app8_clue_consolidated 已合并,
-- 回滚无法精确还原,只能选其一,以下示例选 ai_cache 旧描述名)
UPDATE biz.ai_run_logs SET app_type = 'app8_consolidate' WHERE app_type = 'app8_consolidation';
UPDATE biz.ai_run_logs SET app_type = 'app7_customer_analysis' WHERE app_type = 'app7_customer';
UPDATE biz.ai_run_logs SET app_type = 'app6_note_analysis' WHERE app_type = 'app6_note';
UPDATE biz.ai_cache SET cache_type = 'app8_clue_consolidated' WHERE cache_type = 'app8_consolidation';
UPDATE biz.ai_cache SET cache_type = 'app7_customer_analysis' WHERE cache_type = 'app7_customer';
UPDATE biz.ai_cache SET cache_type = 'app6_note_analysis' WHERE cache_type = 'app6_note';
-- 3. ADD 旧约束
ALTER TABLE biz.ai_cache ADD CONSTRAINT chk_ai_cache_type CHECK (cache_type IN
('app2_finance','app2a_finance_area','app3_clue','app4_analysis','app5_tactics',
'app6_note_analysis','app7_customer_analysis','app8_clue_consolidated'));
-- 4. DROP 索引 + 列
DROP INDEX IF EXISTS biz.idx_ai_run_logs_assistant_member;
ALTER TABLE biz.ai_run_logs DROP COLUMN IF EXISTS assistant_id;
ALTER TABLE public.member_retention_clue
DROP COLUMN IF EXISTS sandbox_instance_id,
DROP COLUMN IF EXISTS runtime_mode,
DROP COLUMN IF EXISTS emoji;
-- 5. emoji 反向回填(若需要)— 把 emoji 列拼回 summary
UPDATE public.member_retention_clue
SET summary = emoji || ' ' || summary, emoji = '' WHERE emoji != '';
COMMIT;
```
代码侧:`git revert <commit_hash>`
## 验证 SQL(已在测试库 PASS)
```sql
-- 1. 新列存在
SELECT column_name FROM information_schema.columns
WHERE table_schema='public' AND table_name='member_retention_clue'
AND column_name IN ('emoji','runtime_mode','sandbox_instance_id');
-- 预期 3 行
-- 2. assistant_id 列存在
SELECT column_name FROM information_schema.columns
WHERE table_schema='biz' AND table_name='ai_run_logs' AND column_name='assistant_id';
-- 预期 1 行
-- 3. 旧 cache_type 0 残留
SELECT cache_type, count(*) FROM biz.ai_cache
WHERE cache_type IN ('app6_note_analysis','app7_customer_analysis',
'app8_clue_consolidated','app8_consolidate')
GROUP BY 1;
-- 预期 0 行
-- 4. 旧 app_type 0 残留
SELECT app_type, count(*) FROM biz.ai_run_logs
WHERE app_type IN ('app6_note_analysis','app7_customer_analysis',
'app8_consolidate','app8_clue_consolidated')
GROUP BY 1;
-- 预期 0 行
-- 5. emoji 回填覆盖率(回填脚本跑后)
SELECT
count(*) FILTER (WHERE emoji != '') AS filled,
count(*) FILTER (WHERE emoji = '') AS empty,
count(*) AS total
FROM public.member_retention_clue;
-- 预期 filled=44, empty=0, total=44(测试库)
```
## 关联
- 完整审计:`docs/audit/changes/2026-05-06__w1_ai_closure_super_sprint.md`
- backlog 登记:`docs/_overview/architecture-evolution-backlog.md` §七 #14 主体收口 + #15-#28 残余子任务
- 关联表 RLS 迁移(P1-1 public → biz schema):§七 #21 后续 sprint