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:
152
docs/database/changes/2026-05-06__ai_closure_schema_fixes.md
Normal file
152
docs/database/changes/2026-05-06__ai_closure_schema_fixes.md
Normal 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
|
||||
Reference in New Issue
Block a user