Files
Neo-ZQYY/docs/ai/ai_apps_feature_acceptance_spec.md
Neo caf179a5da feat: 2026-04-15~05-02 累积变更基线 — AI 重构 + Runtime Context + DWS 修复
涵盖(每条对应已存的审计记录):
- AI 模块拆分:apps/backend/app/ai/apps -> prompts/(8 个 APP + app2a 派生)
  audit: 2026-04-20__ai-module-complete.md
- admin-web AI 管理套件:AIDashboard / AIOperations / AIRunLogs / AITriggers / TriggerManager
  audit: 2026-04-21__admin-web-ai-management-suite.md
- App2 财务洞察 prompt v3 -> v5.1 + 小程序 AI 接入(chat / board-finance)
  audit: 2026-04-22__app2_prompt_v5_1_and_miniprogram_ai_insight.md
- App2 prewarm 全过滤器 + AI 触发器 cron reschedule
  audit: 2026-04-21__app2-finance-prewarm-all-filters.md
  migration: 20260420_ai_trigger_jobs_and_app2_prewarm.sql / 20260421_app2_prewarm_cron_reschedule.sql
- AppType 联合类型对齐 + adminAiAppTypes.test.ts
  audit: 2026-04-30__admin_web_ai_app_type_alignment.md
- DashScope tokens_used 提取修复
  audit: 2026-04-30__backend_dashscope_tokens_used_extraction.md
- App3 线索完整详情 prompt
  audit: 2026-05-01__backend_app3_full_detail_prompt.md
- Runtime Context 沙箱(5-1~5-2 主线):
  - 后端 schema/service + admin_runtime_context / xcx_runtime_clock 两个 router
  - admin-web RuntimeContext.tsx + miniprogram runtime-clock.ts
  - migration: 20260501__runtime_context_sandbox.sql
  - tools/db/verify_admin_web_sandbox.py + verify_sandbox_end_to_end.py
  - database/changes: 7 份 sandbox_* 验证报告
- 飞球 DWS 修复:finance_area_daily 区域汇总 + task_engine 调整
  + RLS 视图业务日上界(migration 20260502 + scripts/ops/gen_rls_business_date_migration.py)

合规:
- .gitignore 启用 tmp/ 排除
- 不入仓:apps/etl/connectors/feiqiu/.env(API_TOKEN secret,本地修改保留)

待验证清单:
- docs/audit/changes/2026-05-04__cumulative_baseline_pending_verification.md
  每个主题的功能完整性 / 上线验证几乎都未收口,按优先级 P0~P3 逐一处理
2026-05-04 02:30:19 +08:00

62 KiB
Raw Blame History

AI 应用 APP1-APP8 功能需求 - 目的检查文档

版本2026-04-22 (v4全景资料集成8 维度并行调研 — 生产端 / DB schema / 合规 / MCP / admin-web / 后端路由服务 / 历史审计 / P2/P3 外围) 适用范围:apps/backend/app/ai/ + apps/admin-web/src/pages/AI* + apps/miniprogram/miniprogram/ + apps/demo-miniprogram/miniprogram/UI 标杆) PRD 来源:


0. 总体原则

  • demo-miniprogram 是样式标杆(假数据驱动),生产 miniprogram 所有 AI 字段的排版/配色必须与 demo 一致。
  • 前端不直接调用 DashScope,所有 AI 产物统一通过 ai_cache 或 SSE 流式接口消费。
  • 后端统一响应包装 {code:0, data:...}snake_case 自动转驼峰。
  • cache miss 时前端必须清空陈旧数据
  • 告警统一走 WebSocket /ws/ai-alerts/{site_id}

0.1 PRD 硬性数字约束(权威表)

所有数字来自 docs/prd/ 精读,代码实现与此不符时以 PRD 为准并回退排查。

维度 约束值 PRD 来源
应用总数 8APP1-APP8 AI 需求 2
财务洞察组合 8 时间 × 9 区域 = 72 AI 需求 2
线索分类APP3 3 个枚举 P5 / ai-app-prompts
线索分类APP6/8 6 个枚举 P5 / ai-app-prompts
单条线索 summary ≤ 20 字(简短可作标题) AI 需求 2
单条线索 detail ≤ 120 字 AI 需求 2
APP7 summary 长度 200-400 字,开头定性短句 ≤ 25 字 ai-app-prompts
APP8 clues 数组 ≤ 5 条 ai-app-prompts
APP5 tactics 数组 2-4 条,每条 script ≤ 150 字 AI 需求 2
APP6 clues 数组 0-10 条(按价值排序) AI 需求 2
APP3 clues 数组 1-5 条(按价值排序) AI 需求 2
System prompt 上限APP2-8 ≤ 8000 字 NS2
System prompt 上限APP1 ≤ 4000 字 NS2
page_context 上限 ≤ 2000 字 NS2 / page_context.py:21
Token 日预算 100,000 P14
Token 月预算 2,000,000 P14
APP1 限流 每用户每分钟 10 次 P14
APP2-8 限流 每门店每小时 100 次 P14
熔断阈值 连续 5 次失败 OPEN P14
熔断恢复窗口 60 秒 P14
关系流失预警 停止消费 > 14 天 ai-app-prompts
高粘性阈值 月内消费 ≥ 3 次 ai-app-prompts
学习型阈值 助教费占比 > 40% ai-app-prompts
当期 < 7 天 必须降权表述("初步观察"/"不足为据" ai-app-prompts
财务洞察加载 < 2 秒(秒级缓存读取) P5
对话复用窗口customer/coach/finance 3 天 P5
对话复用窗口task 无时限 P5
对话复用general 始终新建 P5

0.2 跨 APP 编排链路PRD 定义)

消费事件链(严格串行)

新结算单 → APP3 [await]
         → APP8 [await]
         → APP7 [await](读 APP8 最新缓存)
         如含助教:
           → APP4 [await](读 APP8 最新缓存)
             → APP5

约束APP7 与 APP4 均依赖 APP8 完成,确保上游线索就绪。

备注事件链

备注提交 → APP6 [await] → APP8 [await]

任务分配链

任务分配priority_recall / high_priority_recall
  → APP4 [await](读 APP8 已有缓存miss 时 prompt 标注"暂无历史线索"
    → APP5

约束:任务链不等 APP8 执行,直接读缓存;消费链则必须等 APP8 完成才能执行 APP4/APP7。


1. APP1 聊天(流式对话 + 屏幕上下文)

功能目的PRD

店员/助教在小程序里与 AI 自由对话("这个客户最近消费变多了为什么"),支持 10 种页面入口带上下文,后端将上下文结构化为 ≤2000 字中文文本注入 prompt。PRD 参考AI 需求 2 表首行 / P5 AC1 / NS2 page_context 设计。

后端请求 schemaschemas.py:16-23

ChatStreamRequest {
  message: str                 # 用户本轮输入
  source_page: str | None      # 来源页面标识10 种枚举)
  page_context: dict | None    # 看板筛选参数timeDimension/areaFilter/dimension/typeFilter/projectFilter
  screen_content: str | None   # 「屏幕可见内容」快照(前端采集,后端原样拼入 prompt← 当前 gap
}

10 种 sourcePagepage_context.py:24-36

task-detail / customer-detail / coach-detail / board-finance / board-customer / board-coach / performance / my-profile / task-list / customer-service-records

前端消费链路

阶段 文件与行号 处理
浮按钮属性 ai-float-button.ts:29-40 接收 sourcePage + pageFiltersJSON 对象)
浮按钮跳转 ai-float-button.ts:44-72 URL 编码 JSON 到 querystring
Chat 解析 chat.ts:234-259 JSON.parse(decodeURIComponent(options.pageFilters)) ; 回退支持旧单键 timeDimension/areaFilter/dimension/typeFilter/projectFilter
SSE 发送 chat.ts:487-490 body: {chatId, content, sourcePage, pageContext}
后端文本化 page_context.py:39-96 build_page_text(source_page, context_id, site_id, filters) → 结构化中文 ≤2000 字
后端注入 xcx_chat.py 将文本拼入 DashScope biz_params 或作为前置 system message

screen_content「屏幕可见内容」特性待实现 / 文档化 gap

  • schema 已预留字段(schemas.py:22
  • 预期采集方式(需前端实现):浮按钮 onTap 时通过 triggerEvent 让父页面回传 this.data 中可见区块的快照(如 board-finance 当前展示的 overview/recharge/revenue/... 卡片数据),序列化为短 JSON 字符串
  • 后端消费:原样拼入 prompt 系统消息(与 page_context 互补page_context 是后端查 DB 的结果screen_content 是前端用户真实看到的数字)
  • 验证:两者都存在时,提示词顺序应为 screen_content用户视角 → page_context权威数据,避免矛盾时以 page_context 为准

各 sourcePage 携带字段示例

sourcePage pageFilters 字段 后端 page_context 输出要点
board-finance timeDimension + areaFilter 汇总笔数 / 总营收 / 笔均 / 优惠率 / 储值活跃度
board-customer dimension + typeFilter Top 10 客户 + 排序维度 + 客群分类
board-coach dimension + projectFilter + timeDimension Top 10 助教 + 服务统计 + 技能筛选
performance / task-list / my-profile timeDimension 个人绩效 / 任务列表 / 本人统计
task-detail / customer-detail / coach-detail 无 filterscontextId 单实体详情
customer-service-records 服务记录流水

Demo 参考UI 形态)

  • chat.ts:消息气泡、引用卡片 referenceCard、逐字追加动效
  • 进入路径:浮按钮仅在 board-*(看板类)与详情页挂载,距底部 200rpxTabBar 页)/ 120rpx非 TabBar

验证点(细化)

  • 财务看板任意 time/area 切换 → 点浮按钮 → 后端日志可见 build_page_text("board-finance", ...) 返回文本含当前切换维度的数字
  • 小程序 SSE body 中 pageContext 为 JSON 对象而非字符串(后端解析不失败)
  • pageFilters 为空对象时不追加 URL 参数,走 general 对话
  • SSE ArrayBuffer 跨 chunk 的 UTF-8 中文字符无乱码(构造「当前营业」被 2 chunk 切分的测试)
  • done 事件 conversation_id 返回后,下一轮发送透传该 id 保持多轮
  • ai_run_logs.tokens_usedusage.models 嵌套结构累加(非顶层 tokens
  • 网络中断重试上限 2 次2/4/8s 指数退避)
  • screen_content gap 待确认:若本次上线计划启用,需在浮按钮 onTap 内实现页面快照 triggerEvent否则保留 schema 占位并在文档注明"暂不采集"

2. APP2 财务洞察72 组合预热)

功能目的

日结完成后预生成 8 时间维度 × 9 区域 = 72 组 财务洞察,命中看板时直接从 ai_cache 秒级读取。

返回 schemaschemas.py:93-104

App2Result { insights: [ { seq: int, title: str, body: str } ] }

System Prompt 核心规则(ai_system_prompt_by_app.md:41-93

  • 收入构成三口径不可互换:发生额 / 成交收入 / 现金流入
  • 5 类优惠拆解必须落到细分(团购/会员折扣/手动/赠送卡/分摊)
  • 警戒线:优惠率 >30% / 助教薪酬占比 >40% / 充值占现金流入 25-40% 为健康
  • seq=11/12 固定为板块 F「综合健康度」置顶作为"本期总结"
  • 当期 <7 天需降权表述
  • 现金流出为 0 视为录入异常并高亮

target_id 拼装(dispatcher.py _app2_target_id

${TIME_MAP[selectedTime]}__${selectedArea},例:this_month__hallA / last_quarter__all

TIME_MAPmonth→this_monthlastMonth→last_monthweek→this_weekquarter→this_quarterhalf6→last_6_months 9 区域all / hall / hallA / hallB / hallC / vip / snooker / mahjong / ktv

cache_type 切分2026-04-23

  • area='all' 的 8 组 → app2_finance
  • area!='all' 的 64 组 → app2a_finance_area

Demo 展示形态(关键 UI 规范

参考 board-finance.wxml:208-221

┌─ AI 智能洞察(机器人 SVG 图标 + 标题)
│  优惠率Top团购(5.2%) / 大客户(3.1%) / 赠送卡(2.5%)
│  差异最大:酒水(+18%) / 台桌(-5%) / 包厢(+12%)
│  建议关注:充值高但消耗低,会员活跃度需提升
└─

字段到 UI 映射

字段 Demo 类 渲染规范
title .ai-insight-dim 灰色引导词(如"优惠率Top"/"差异最大:"),结尾带中文冒号
body .ai-insight-line 主体 白色正文,含数字与百分比
强调片段 .ai-insight-underline 关键词下划线(需 AI 用 **...** 标记,前端轻量 Markdown 渲染)
顺序 seq 升序 seq=11/12 置顶为"本期总结"
图标 /assets/icons/ai-robot.svg 必须 SVG禁止 emoji 替代demo 已标注 CHANGE 2026-03-12

验证点(细化)

  • 72 组 target_id 全覆盖SQLSELECT DISTINCT target_id FROM biz.ai_cache WHERE cache_type IN ('app2_finance','app2a_finance_area') AND site_id=? AND created_at::date=CURRENT_DATE 应 = 72
  • seq=11/12 在前端必定置顶(不按数字大小升序,需特判)
  • title 结尾是中文冒号(便于 dim 样式识别)
  • body**xxx** 标记被轻量 Markdown 渲染为 underline/加粗
  • area 切换后旧 aiInsights/aiInsightSummary/aiInsightDetails 立即清空
  • cache miss 时显示占位文案("正在生成洞察,请稍后"),不复用上次数据
  • 现金流出为 0 时 AI 输出能明确指出录入异常(单元测试固化该场景)
  • 当期 <7 天的时间维度(本周 this_week 若当前周三)返回文本含降权用词("初步观察"/"不足为据"等)
  • Prompt KEY_TRANSLATIONSapp2_finance_prompt.py:24-64)无缺字段
  • 熔断/限流后剩余组跳过,ai_cache.result_json 不写 NULL

3. APP3 维客线索消费端3 分类)

功能目的

消费结算后从 DWS 消费记录提取 3 类线索(客户基础/消费习惯/玩法偏好),providers 固定「系统」。

返回 schemaschemas.py:71-78, 107-110

App3Result { clues: [ ClueItem { category, summary, detail, emoji } ] }

System Prompt 核心规则(ai_system_prompt_by_app.md:97-125

  • category 严格 3 选 1App3CategoryEnum
  • 必须输出"下一步做什么"的行动导向,不得仅描述现象
  • 消费结构解读:酒水 >40% = 休闲社交客;台费 >80% = 硬核客;助教费 >30% = 学习进阶客

前端消费(经 App8 合并后落 member_retention_clue

App3 结果不直接渲染,由 App8 读 ai_cache 整合 → 写库 → 前端查客户详情时显示。

Demo 展示形态customer-detail

参考 customer-detail.ts:99-145 + customer-detail.wxml:92-110

维客线索  [AI 徽章]
┌─ [客户/基础]  🎂 生日 3月15日 · VIP会员 · 注册2年      By:系统
├─ [消费/习惯]  🌙 常来夜场 · 月均4-5次                  By:系统
├─ [消费/习惯]  💰 高客单价                              By:系统
│     近60天场均消费 ¥420高于门店均值 ¥180...
└─ ...

字段到 UI 映射

schema 字段 Demo clue-card 属性 UI 规范
category tag 两行显示(如"客户\n基础"),配色 categoryColor 按类映射primary/success/purple/warning/pink/error
summary title(含 emoji 前缀) 主文案,短句 30 字内
emoji 拼在 title 开头 1 个 emoji类别相关
detail content(展开区) 50-100 字展开,可折叠
providersApp8 合并后补) source"By:系统"/"By:小燕" 区分系统推断 vs 备注来源

验证点(细化)

  • category ∈ {客户基础, 消费习惯, 玩法偏好}Pydantic 校验 + 前端 categoryColor 映射无 undefined
  • emoji 字段必填且为单个 emoji正则 /^[\p{Emoji}]+$/u
  • summary 以行动导向动词结尾("可推..."/"建议..."/"需..."),非纯描述(单测)
  • detail 长度 ≥ 50 字且 ≤ 150 字
  • DWS 取数失败时降级到 _default_member_data,不导致 dispatcher 崩溃
  • Prompt 超 4000 字后仍保留完整消费明细2026-05-01 合成 100 条完整明细真实调用成功返回
  • 团购客无储值卡与储值会员识别正确AI 不得混淆)

4. APP4 关系分析(任务链首)

功能目的

助教参与消费 / 任务分配时,分析助教-会员关系,产出任务描述、行动建议、一句话总结。

返回 schemaschemas.py:113-118

App4Result {
  task_description: str        # 任务详述
  action_suggestions: [str]    # 行动建议数组
  one_line_summary: str        # 一句话总结
}

System Prompt 核心规则(ai_system_prompt_by_app.md:129-156

  • 输出必含:关系评级(紧密/一般/疏远)+ 核心原因 + 风险/机会点
  • 粘性阈值:月内 ≥3 次 = 高粘性;激励课占比 >40% = 学习型
  • 停止消费 >14 天 = 流失预警

Demo 展示形态task-detail 顶部 Banner

参考 task-detail.ts:107-112

关系等级:很好    heartScore=8.5
banner 背景根据 taskType 动态切换 SVG

字段到 UI 映射

schema 字段 Demo 字段 位置
one_line_summary Banner 下方副标题 任务详情头部一行显示
task_description detail.taskTypeLabel + 主内容 任务卡片主体
action_suggestions[] 勾选式建议列表 可勾选动作项,完成后记录

验证点(细化)

  • action_suggestions 为非空 list每条 ≤ 40 字
  • one_line_summary 含关系评级词("紧密/一般/疏远"+ 数字
  • target_id = ${assistant_id}_${member_id},供 App5 复用
  • 助教数据缺失时 warnings 数组被填充,前端显示降级提示
  • Prompt 截断 _MAX_PROMPT_LEN=8000 保留关键关联字段

5. APP5 话术参考(任务链尾)

功能目的

基于 App4 的 one_line_summary 与关系评级,生成针对性微信/当面沟通话术

返回 schemaschemas.py:121-131

App5Result { tactics: [ { scenario: str, script: str } ] }

System Prompt 核心规则(ai_system_prompt_by_app.md:160-192

  • 微信私信:口语、短、配 1 个 emoji不群发感
  • 当面沟通:引导式提问 > 直接推销
  • 会员分层:新客试听 / 成长储值 / 核心赛事邀请 / 流失限时券
  • 助教不能:打任意折扣 / 承诺 KOL 免费陪打
  • 输出需标注:适用场景 + 建议发送时段 + 预期反应

Demo 展示形态task-detail 话术区)

参考 task-detail.ts:79-86

话术参考
┌─ 王哥您好,好久不见!最近店里新到了几张国际标准斯诺克球桌... [复制]
├─ 王哥,最近忙吗?这周末我们有个老客户专属球友交流赛...    [复制]
└─ ... 共 5 条

字段到 UI 映射

schema 字段 Demo talkingPoints 规范
scenario 折叠区 hint"适用:流失预警"/"适用:储值召回" 小字副标题,可选展示
script 话术主体 长按可复制,配置 copiedIndex 高亮反馈

验证点(细化)

  • tactics 长度 3-5 条Prompt 约束)
  • script 含会员姓名或称呼(如"王哥"),非模板化空话
  • script 含 0-1 个 emojiemoji_count <= 1
  • 不出现"8折/免费/KOL/抽成"等越权用词(黑词正则拦截)
  • App4 失败时 App5 跳过(串行依赖,context.app4_result is None 不执行)
  • 长按复制触发 wx.setClipboardData 成功
  • target_id 与 App4 一致(${assistant_id}_${member_id}

6. APP6 备注分析(备注链首)

功能目的

助教提交备注后打分 1-10 并提取 6 类线索providers = 备注提交人真实姓名。

返回 schemaschemas.py:134-138

App6Result {
  score: int (ge=1, le=10)
  clues: [ ClueItem { category, summary, detail, emoji } ]
}

System Prompt 核心规则(ai_system_prompt_by_app.md:196-220

  • 忠于备注原文,不延伸推测
  • 8 维度归一到 6 分类App6CategoryEnum
  • 一条备注可对应多个维度
  • 情感倾向(正面/中性/负面)影响后续助教触达开场白
  • 标注备注是谁写的、何时写的(时效性)

前端消费

App6 结果不直接渲染,经 App8 整合后写入 member_retention_clue 表,前端查客户详情展示(source 字段显示"By:小燕")。

Demo 展示形态(客户详情「维客线索」中显示 6 类

参考 customer-detail.ts:99-145 7 条 clue 分布:

  • 客户基础 × 1生日/VIP
  • 消费习惯 × 2夜场/高客单)
  • 玩法偏好 × 1中式斯诺克
  • 促销偏好 × 1酒水套餐敏感
  • 社交关系 × 1固定球搭子
  • 重要反馈 × 1斯诺克走位需求

验证点(细化)

  • score ∈ [1,10] 严格校验Pydantic ge=1, le=10
  • category ∈ {客户基础, 消费习惯, 玩法偏好, 促销偏好, 社交关系, 重要反馈}
  • providers 为备注提交人姓名(非"系统"、非空)
  • 备注原文中的专有名词("李哥"、"阿杰"、"A12号台")保留在 detail
  • 备注 >8000 字时截断但保留情感词与动作词
  • score 写入 ai_cache.score 列(非 result_json.score,供后端排序)

7. APP7 客户分析(消费链尾,客户画像)

功能目的

消费链 App8 完成后串行触发,综合消费 + 备注 + 助教关系输出 200-400 字客户画像

返回 schemaschemas.py:141-152

App7Result {
  strategies: [ { title: str, content: str } ]
  summary: str                     # 画像主文
}

System Prompt 核心规则(ai_system_prompt_by_app.md:224-257

  • 开头一句话定性"工作日晚间的上班族硬核玩家"
  • 中段数字:消费结构、频次、客单、助教绑定
  • 结尾助教可动 1-2 条建议
  • 避免评判语言("消费低"改为"客单 60 元偏低于店均 120 元"
  • 必须标注数据时间窗"近 30 天 / 近 90 天"
  • 备注信息需带【来源:人名,请甄别真实性】标注(app7_customer_prompt.py:64-77

Demo 展示形态customer-detail 头部)

参考 customer-detail.ts:91-98 + customer-detail.wxml:69-90

🤖 AI 智能洞察
┌─ summary高价值 VIP 客户,月均到店 4-5 次,偏好夜场中式台球,
│          近期对斯诺克产生兴趣。社交属性强...
│  当前推荐策略
│  ■ [green]  最后到店距今 12 天,超出理想间隔 7 天,建议...
│  ■ [amber]  客户提到想练斯诺克走位,可推荐专项课程包...
│  ■ [pink]   社交属性强,可邀请参加球友赛事活动...
└─

字段到 UI 映射

schema 字段 Demo 字段 UI 规范
summary aiInsight.summary 开头白色正文段
strategies[].title 策略颜色 color 枚举前缀 不直接渲染配色由前端枚举轮转green/amber/pink/...
strategies[].content strategy-text 策略卡片主体,每条独立配色条
末条样式 .strategy-item-last 最后一条无下边距

注意demo 中 strategies[].color 是前端枚举色green/amber/pinkAI 返回的 title 字段应映射为 emoji/关键词而非颜色。实际代码中前端按 index % 6 循环配色(不依赖 AI

验证点(细化)

  • summary 开头为"定性短句"(句号前 ≤ 25 字)
  • summary 包含时间窗关键词("近 30 天"/"近 90 天"/"近 60 天"
  • summary 长度 200-400 字
  • strategies 长度 2-3 条,前端展示最多 2 条demo 规范 index < length - 1 为非末,index === length - 1 为末条)
  • 评判性词("差"/"低"/"糟糕"出现时应改写为对比表述AI 侧或后处理)
  • 备注来源标注【来源xxx请甄别真实性】进入 prompt
  • 消费链中断时 App7 跳过App8 未完成不执行)
  • cache_type = app7_customer_analysistarget_id = member_id

8. APP8 线索整合(写库幂等)

功能目的

整合 App3 + App6 全部线索 → 去重、统一 6 分类、providers 逗号拼接 → 幂等写 member_retention_clue

返回 schemaschemas.py:80-88, 155-158

App8Result {
  clues: [ ConsolidatedClueItem {
    category: str       # 6 选 1
    summary: str        # 30 字内行动导向
    detail: str         # 50-100 字展开
    emoji: str          # 分类对应 emoji
    providers: str      # "消费数据" / "店员小燕备注" / 逗号拼接多源
  } ]
}

System Prompt 核心规则(ai_system_prompt_by_app.md:261-290

  • 冲突处理:备注原文 > 行为推断
  • summary 禁放数字(数字放 detail
  • 线索排序:可直接动作 > 身体偏好 > 长期画像
  • 不超过 5 条(助教可读性)
  • 不输出泛化建议("请关心会员"禁止)

落库规则(dispatcher.py _write_retention_clue

BEGIN;
  DELETE FROM app.member_retention_clue
   WHERE site_id=:site AND member_id=:member AND source IN ('ai_consumption','ai_note');
  INSERT INTO app.member_retention_clue (...) VALUES (...);
COMMIT;
  • source 取值:消费链触发 → ai_consumption;备注链触发 → ai_note
  • source='manual'(人工线索)绝不删除

Demo 展示形态7 条线索在客户详情,见 App3 表格)

验证点(细化)

  • DELETE + INSERT 在同一事务内pg_stat_activity 查询验证)
  • 写入后 source='manual' 行数不变(跨事务前后 count 一致)
  • 同日多次消费 / 多条备注不会让线索重复累积(幂等)
  • providers 多源拼接去重(如"消费数据、备注-小燕",不出现"消费数据、消费数据"
  • summary 不含数字(正则 /[0-9%¥]/ 不匹配)
  • detail 含数字(与 summary 分工)
  • clues 数组长度 ≤ 5
  • 排序:可直接动作类("推课"/"约时段")在前
  • App3 或 App6 任一失败时App8 仍执行但对应 _clues=[]
  • cache_type = app8_clue_consolidated

9. 管理后台通用检查

AIDashboard总览

  • AIDashboard.tsx:120 getDashboard()
  • 字段:today_calls / today_success_rate / today_tokens / today_avg_latency_ms / trend_7d
  • 数值与 biz.ai_run_logs 聚合一致
  • trend_7d 折线图日期连续无跳点,单位 ms / % / 次
  • 切换 site_id 过滤器后数字按租户隔离

AIOperations手动执行

  • AIOperations.tsx:67 runApp(appType, {site_id, member_id}) 支持 App3-App8App1/App2 不适用)
  • AIOperations.tsx:146 triggerEvent({event_type, site_id, member_id, is_forced}) 越过去重
  • is_forced=true 可绕过当日去重,ai_trigger_jobs 新增一行
  • 返回 job_idtag 色 processing → success/failed
  • WebSocket /ws/ai-alerts/{site_id} 接收 circuit_open / rate_limited / budget_exceeded / timeout / failed

TriggerManager调度配置

  • TriggerManager.tsx:183 updateTriggerConfig(id, {cron_expression, interval_seconds})
  • cron 表达式校验通过才允许保存
  • 改动后调度器热加载Scheduler lifespan 监听 meta.scheduled_tasks

10. 跨切面共性验证

维度 检查项 验证方法
RunLog 状态转换 pending→running→success|failed|timeout|circuit_open|rate_limited|budget_exceeded SELECT status, COUNT(*) FROM biz.ai_run_logs GROUP BY status 覆盖 8 种
RunLog success 必有非零 tokens_used SELECT COUNT(*) FROM biz.ai_run_logs WHERE status='success' AND tokens_used=0 = 0
RunLog request_prompt ≤ 8000 字符 SELECT MAX(length(request_prompt)) ≤ 8000
熔断 连续失败 N 次后 OPEN 人工注入失败 → 第 N+1 次日志 status=circuit_open无真实 DashScope 调用
限流 分钟/日双维度 压测超限后 rate_limited 数突增,tokens_used IS NULL
预算 不足时记 budget_exceeded:<reason> 人为把日预算调至 0 → 下一次调用日志为 budget_exceeded:daily_budget_used_up
缓存 result_json IS NULL 行数应为 0 SELECT COUNT(*) FROM biz.ai_cache WHERE result_json IS NULL = 0
缓存 cache_type 枚举齐全 SELECT DISTINCT cache_type 仅含 CacheTypeEnum 的 8 个值
去重 (event_type, member_id, site_id, date) 当天仅 1 次 SELECT event_type, member_id, COUNT(*)is_forced=false 时均为 1
多租户 SET LOCAL app.current_site_id tcpdump / SQL trace 首条 SQL 为 SET LOCAL
告警推送 WebSocket 接收 5 类 alert_type 前端 devtools Network → WS 抓包校验
前端降级 cache miss 清空旧数据 切换 area → DevTools Inspect aiInsights 短暂为 []
前端降级 SSE 断流重试 chrome network throttle offline → 看 retry 2/4/8s
Demo 对齐 字段 → UI 映射一致 并排截图 miniprogram vs demo-miniprogram 像素对齐

11. 自测清单(按角色)

前端工程师

客户详情页

  • App7 summary 开头为定性短句且含时间窗
  • App7 strategies 末条 CSS 类 .strategy-item-last 生效(无下边距)
  • App3/6/8 合并后 clues 配色按 categoryColor 映射无 undefined
  • clue-card source 字段区分 "By:系统" vs "By:{人名}"
  • 展开折叠 detail 动画无抖动

任务详情页

  • App4 one_line_summary 在 banner 副标题
  • App5 tactics[].script 长按复制成功 + toast 反馈
  • action_suggestions 勾选持久化到本地

财务看板

  • 9 区域 × 5 时间 = 45 种组合切换AI 洞察正确加载或清空
  • title 以冒号结尾、.ai-insight-dim 灰色样式生效
  • **xxx** 渲染为 underline
  • seq=11/12 置顶"本期总结"
  • 图标为 SVG/assets/icons/ai-robot.svg),非 emoji

聊天页

  • 浮按钮从 6 类页面进入都正确带 sourcePage + pageFilters
  • SSE 中文无乱码
  • done 事件替换临时消息 ID
  • 历史会话 / 客户 3 天内复用 / 任务复用 / 看板新建 四种入口均工作
  • 重试 2/4/8s 指数退避
  • 待确认)若实现 screen_content,浮按钮 triggerEvent 采集页面快照

后端工程师

  • ai_run_logs 状态分布覆盖 8 种
  • ai_cache 无 NULL result_json
  • member_retention_clue source='manual' 跨天数量稳定
  • 日结后 2 分钟内 72 组缓存写入完整
  • SET LOCAL app.current_site_id 每次查询都执行
  • Pydantic schema 对 score/enum/长度的约束不被绕过

测试工程师

  • demo-miniprogram 与 miniprogram 的客户详情 / 任务详情 / 财务看板像素级对齐
  • customer-detail 页 AI 区块三部分互不干扰summary / strategies / clues
  • 小程序热启动后 AI 洞察不闪烁旧数据
  • admin-web Dashboard 与数据库聚合一致性

附录 A已废弃 / 已迁移

  • apps/backend/app/ai/apps/app1_chat.py ~ app8_consolidation.py 已删除,逻辑统一内聚至 dispatcher.py
  • APP2 于 2026-04-23 拆分为 app2_finance (8 组) + app2a_finance_area (64 组)

附录 B已知 Gap / 待办PRD 对齐清单)

来源:docs/prd/ 精读对照当前代码与本 spec 的差异。

ID Gap PRD 来源 当前状态 建议
G1 APP1 screen_content 采集(浮按钮 onTap triggerEvent 采集页面快照) AI 需求 2 / NS2 schema 已预留 schemas.py:22chat.ts:487 未传 浮按钮 triggerEvent('snapshot') → 父页返回 setData 可见子集 → URL/SSE body 透传;后端按 "screen_content → page_context" 顺序拼 prompt
G2 APP2 72 组预热完整性校验 P14 调度器 代码已拆 8+64 组,缺自动化核对 日结后 T+10min 执行 SELECT DISTINCT target_id FROM biz.ai_cache WHERE cache_type IN ('app2_finance','app2a_finance_area') AND site_id=? AND created_at::date=CURRENT_DATE,应 = 72不足告警
G3 APP3 summary 行动导向动词约束 ai-app-prompts prompt 未强化动词要求 prompt 增加"必须以可推/建议/需/可约/可邀 等动词结尾";后端 schema 加正则;单测固化
G4 APP4 target_id 拼装规则文档化 P5 代码实现 {assistant_id}_{member_id} 但文档未固定分隔符 正式文档化分隔符为单下划线,拦截 assistant_id/member_id 含下划线的场景
G5 APP5 scenario 前端消费规范 AI 需求 2 schema 定义但 UI 未约定 约定 task-detail 话术区 scenario 作为折叠 hint"适用:流失预警"),空字符串时不展示
G6 APP6 score 驱动前端 UI ai-app-prompts 后端已写 ai_cache.score 列但前端未消费 备注列表按 score 降序排列score ≥ 8 高亮、≤ 3 置灰
G7 APP7 strategies[].title 字段用途 ai-app-prompts 前端按 index 循环配色,未用 title 方案 A删除 title方案 BAI 输出 title 作为徽章("机会"/"风险"/"日常"),前端展示
G8 APP8 相似线索去重判断标准 AI 需求 2 / P5 prompt 未定义相似度阈值 prompt 增加"若两条线索 summary 字符重合 ≥ 60% 或 category 相同且描述同一行为合并为一条providers 拼接去重"
G9 熔断/限流/预算硬约束压测 P14 代码实现但无压测报告 压测APP1 单用户 11 次/分钟应触发 rate_limited连续 5 次失败后第 6 次 status=circuit_open 且不真实调用;日 token 达 100k 后 status=budget_exceeded
G10 日结触发 APP2 预热延迟 P14 代码"日结后轮询"无明确延迟 文档固定 "日结完成事件 → T+5min 开始调度 APP2 × 72 组T+15min 应全部完成";超时告警
G11 RLS 多租户隔离强制验证 P5 / backend CLAUDE.md 代码每次查询前 SET LOCAL缺自动验证 集成测试:伪造 site_id=A 的 JWT 访问 member_id 属于 site_id=B 的数据应返回 404抓 pg_stat_statements 验证 SET LOCAL 存在
G12 新客(无消费/无备注)降级文案 NS2 代码有 _default_member_data 降级但 prompt 文案未标准化 定义模板「该客户暂无历史线索以下基于本次消费进行初步判断」dispatcher 注入 prompt 开头
G13 APP8 providers 多源拼接格式 ai-app-prompts 代码拼接但分隔符未固定 固定格式:"消费数据,备注-{人名}",中文逗号分隔,备注人不重复(用 set 去重)
G14 APP4 任务链 cache miss 时的 prompt 提示 ai-app-prompts 代码读缓存失败时传空 prompt 标注"暂无 APP8 历史线索,请基于基线数据分析",而非丢空字符串
G15 APP2 **xxx** 强调标记约定 Demo 规范 前端已做轻量 Markdown但 prompt 未要求 AI 输出该标记 prompt 增加"对关键结论或异常词用 **粗体** 包裹,前端会渲染为下划线强调"

附录 DPRD 硬性阈值对照表(验收单)

单独抽出所有数字阈值,供 QA 断言时直接引用。

APP 字段/指标 阈值 不达标处理
APP1 单轮 prompt ≤ 4000 字 触发截断保留最近 N 轮
APP1 page_context ≤ 2000 字 page_context.py 末尾附"…(上下文已截断)"
APP1 限流 10 次/分钟/用户 rate_limited 记录,前端提示"操作过快"
APP1 复用窗口 customer/coach/finance 3 天task 无限general 新建 超窗口新建会话
APP2 组合数 728×9 缺组合告警
APP2 加载时延 < 2 秒 前端 loading > 2s 上报监控
APP2 seq 范围 1-1211/12 置顶 非 11/12 按 seq 升序
APP3 clues 条数 1-5 AI 超 5 截断
APP3 summary 长度 ≤ 20 字 后端校验拦截
APP3 detail 长度 ≤ 120 字 后端校验拦截
APP4 action_suggestions 1-4 条,每条 ≤ 100 字 单测固化
APP4 one_line_summary ≤ 30 字,必含关系评级词 正则检查 (紧密|一般|疏远)
APP5 tactics 条数 2-4 AI 超 4 截断
APP5 script 长度 ≤ 150 字 后端校验
APP5 黑词 禁用 8折/免费/KOL/抽成/内部价 后端正则黑名单拦截
APP6 score 范围 1-106=标准,<6 扣分,>6 加分) Pydantic ge=1, le=10
APP6 clues 条数 0-10 无下限,上限截断
APP7 summary 长度 200-400 字 后端校验
APP7 summary 开头定性 ≤ 25 字 + 句号 正则 ^.{1,25}[。!]
APP7 strategies 条数 2-5 前端仅展示 2 条
APP7 时间窗标注 必含 "近 30 天/近 60 天/近 90 天" 关键词检查
APP8 clues 条数 ≤ 5 AI 超 5 截断
APP8 summary 禁数字 正则 [0-9%¥] 不匹配 后处理剥离
APP8 providers 拼接 中文逗号去重 后端 set 处理
通用 Token 日预算 100,000 超限 status=budget_exceeded
通用 Token 月预算 2,000,000 超限告警 + 阻断
通用 熔断阈值 连续 5 次失败 OPEN 60 秒恢复窗口
通用 APP2-8 限流 100 次/小时/门店 rate_limited 跳过
通用 关系流失预警 停消费 > 14 天 APP4/7 标注
通用 高粘性阈值 月内 ≥ 3 次 APP4 判定
通用 学习型阈值 助教费占比 > 40% APP3/4/7 判定
通用 当期 < 7 天 必须降权用词 APP2 prompt 约束

附录 CDemo 关键路径索引

页面 AI 字段 文件
客户详情 aiInsight.summary/strategies + clues[] customer-detail.ts:90-145
任务详情 retentionClues[] + talkingPoints[] task-detail.ts:67-86
财务看板 ai-insight-section 三段式 board-finance.wxml:208-221
助教/客户看板 aiColorClass(随机配色) board-finance.ts:294

附录 E生产端 vs Demo 端对照实况2026-04-22 快照)

E.1 生产端真实消费路径

页面 AI 字段来源 关键实现 与 demo 差异
customer-detail fetchAICache('app7_customer_analysis', memberId) 单独拉取;retentionClues 从主详情接口返回 customer-detail.ts:7 onLoad → loadDetail → _loadAIInsight() 异步加载;aiInsight.strategies 前端按 index 循环 6 色 demo 是硬编码 mock生产 strategies color 由前端计算
task-detail aiAnalysis.summary / suggestions / talkingPoints + retentionClues 都随主详情接口一次返回,无独立 AI 缓存调用 onCopySpeech()wx.setClipboardData() + 2s toast task-detail.ts:268;备注创建后 4s 间隔轮询 5 次拿 aiScore20s 超时) demo 同样内联 mock生产端没有长按复制,仅单击复制按钮
board-finance fetchAICache(cacheType, targetId) + parseMarkdownInline 解析 **粗** / *斜* / ***粗斜*** board-finance.ts:532 cache_type 切分已实装seq 11/12 置顶(精确匹配 + 末两条 fallback切换 time/area 时 aiInsights=[] 主动清空 demo 内联 mock无 TIME_MAP/AREA 枚举
浮按钮 task-detail 当前未挂载customer-detail 仅传 customerIdboard-finance 仅传 bottom=200 sourcePage / pageFilters 虽支持但尚未启用(属性存在、传参为空) demo 无浮按钮

E.2 关键实务差异

  • task-detail 无 AI 缓存调用aiAnalysisfetchTaskDetail() 主响应的一部分,后端在返回任务详情时已聚合读取 App4/App5 缓存。这意味着任务列表分页AI 结果拉取合并为一次请求,减少 round-trip。
  • fetchAICache silent failservices/api.ts:417 错误时返回 null,页面降级显示"暂无洞察数据";此静默失败需监控上报补强。
  • 百分比双重格式化board-finance 把后端小数率0.052× 100 传 WXS 格式化demo 端硬编码字符串,两端一致性依赖后端契约稳定。
  • setData 路径优化'aiInsight.summary' 字符串路径而非整对象解构,减少小程序差量渲染。

附录 FAI 数据库 schema 权威清单

F.1 五张核心表 DDL 速览

权威 DDLdb/zqyy_app/schemas/biz.sqlpublic.sql

关键字段 索引要点 RLS
biz.ai_run_logs status varchar(20)pending/running/success/failed/timeout/circuit_open/rate_limited/budget_exceededalert_statussession_id varchar(100)tokens_used int default 0latency_ms intrequest_prompt text≤ 8000 字符写入) 部分索引 idx_ai_run_logs_alert WHERE status IN (failed, timeout, circuit_open)BRIN (created_at) 未启用
biz.ai_cache cache_type varchar(30) + CHECK 约束 8 枚举,target_id varchar(100)result_json jsonb NOT NULLscore int无 CHECK 约束 idx_ai_cache_lookup (cache_type, site_id, target_id, created_at DESC) 未启用
biz.ai_trigger_jobs event_type varchar(30)is_forced boolapp_chain varchar(100)payload jsonb 部分索引 idx_ai_trigger_jobs_dedup WHERE status ≠ 'skipped_duplicate' 未启用
biz.ai_conversations / biz.ai_messages context_type varchar(20)context_id varchar(50)session_id varchar(100)reference_card jsonb 部分索引 idx_ai_conv_context WHERE context_type IS NOT NULL 未启用
public.member_retention_clue category varchar(20) + CHECK 枚举 6 值,summary varchar(200)detail textrecorded_by_name varchar(50)source varchar(20) DEFAULT 'manual'is_hidden bool idx_retention_clue_memberidx_retention_clue_category 未启用,通过 FDW 反向映射 fdw_app.member_retention_clue

F.2 DDL vs Pydantic 对齐问题(按风险)

级别 项目 症状
CRITICAL ai_cache.score 无 CHECK 约束 Pydantic App6Result.score: int = Field(ge=1, le=10) 仅应用层校验脏数据0/-1/100可静默落库
HIGH member_retention_clue 没有 emojiproviders dispatcher.py:513-588 _write_retention_clue 折叠:emoji + " " + summarysummaryprovidersrecorded_by_name varchar(50)。providers 多源拼接大概率超过 50 字符被 PG 报错或截断
HIGH summary varchar(200) 拼接 emoji 后有效长度降低 AI 输出接近 200 字符时合并报错
MEDIUM cache_type CHECK 与 CacheTypeEnum 双侧维护 新增 App 需同时改 DDL + Pydantic遗漏即违反 CHECK
MEDIUM ai_run_logsjob_id 列关联 ai_trigger_jobs 只能通过 session_id 间接关联,无 FK 保护
LOW FDW 外部表 fdw_app.member_retention_cluesourceis_hidden ETL 端无法按 source 过滤 AI 写入的线索

F.3 建议补充的索引/约束

  1. biz.ai_cache.score 添加 CHECK (score IS NULL OR (score >= 1 AND score <= 10))
  2. public.member_retention_clue 新建 (member_id, site_id, source) 索引APP8 DELETE 现在全扫)
  3. biz.ai_conversations 新建 session_id 单列索引
  4. 全部 AI 表启用 RLS(当前完全依赖应用层 site_id 过滤,无最后防线)

附录 G合规审计红黄清单2026-04-22

来源security-reviewer 全量扫描 apps/backend/app/ai/ 所有 prompt 拼装路径。

G.1 已通过的面

  • member_phone / phone / mobile / id_card / wechat_openid / wechat_unionid 零出现于所有 prompt 与 data_fetcher
  • bank_account / bank_card / home_address / ip_address / device_id 全部未出现
  • DashScope SDK 走 HTTPS 加密传输
  • page_context 前端透传被 handler 白名单拦截handler 只 .get() 已知字段)

G.2 5 个风险点(按优先级)

ID 文件 风险 建议修复
R1 member_data.py:200 助教 da.real_name 作为 nickname fallback 传 AI 确认是否属员工隐私;若是改为空串或工号
R2 member_data.py:249-262 储值卡精确余额直接进 prompt 范围化:改为"高/中/低/清零"标签
R3 member_data.py:276 card_balance_total / stored_value_balance_total 精确值 同 R2
R4 member_data.py:358-398 notes.content 备注原文无正则脱敏 预过滤手机号 1[3-9]\d{9}1****、银行卡号、身份证号
R5 run_log_service.py:57 含 R2/R3/R4 的 prompt 写入 ai_run_logs.request_prompt 落库 先做字段级脱敏再入库;或 ai_run_logs 加严格 RLS 仅开发读

G.3 合规测试清单CI

  • 单测:build_prompt_* 返回值断言无 11 位连续数字(手机号)
  • 单测:notes.content 含手机号的样例prompt 中已掩码
  • CIgrep -r "member_phone\|wechat_openid\|id_card" apps/backend/app/ai/ 无命中
  • 集成测试:构造含手机号的备注,验证 APP6 prompt 中号码不完整出现

附录 HMCP Server 与 dispatcher 关系

H.1 现状

面向 dispatcher 路径 MCP Server 路径
调用者 Admin Web / 后端事件 目前仅 Claude Code 开发环境;百炼 AI 应用规划中(未接入)
连接库 etl_feiqiu只读+ zqyy_app读写 仅 etl_feiqiu 只读
RLS 每次查询前 SET LOCAL app.current_site_id 无 site_id 隔离P5.1 规划中)
鉴权 JWT Bearer Token可选MCP_TOKEN 为空则无鉴权)
工具 8 个 APP handler 4 个通用工具:list_tables / describe_table / describe_schemas / query_sql(只读+正则黑名单+500 行上限)

H.2 关键边界

  • 8 个 AI 应用目前均走 dispatcher 直查 DB不经过 MCP
  • MCP 扩展P5.1 批次 B规划加 zqyy_app 连接 + auth/biz/public 自动 schema 路由 + 敏感字段脱敏wx_openid/phone
  • MCP 扩展P5.1 批次 C规划完整查库手册上传百炼知识库

H.3 生产部署

  • 外部入口:https://mcp.langlangzhuoqiu.cn/mcp(未来)
  • 本地配置:.mcp.jsonpg-etl / pg-app 禁用,pg-etl-test / pg-app-test 启用

附录 Iadmin-web AI 三页面实况

I.1 AIDashboard.tsx顶部卡片 + 趋势 + 告警)

  • 数据源 AIDashboard.tsx:99getDashboard()DashboardResponse
  • 字段:today_calls / today_success_rate / today_tokens / today_avg_latency_ms / trend_7d[] / app_distribution[] / budget{} / recent_alerts[] / app_health[]
  • 时间范围:range_days (1/3/7/10) 或 date_from/date_to 自定义 AIDashboard.tsx:112
  • WebSocketws(s)://[host]/ws/ai-alerts/{site_id},单次连接 无重连 AIDashboard.tsx:135-168
  • 告警消息格式:{ type: "alert_created", payload: AlertItem }
  • 实时告警最多保留 20 条,合并至表格顶部

I.2 AIOperations.tsx四卡片手动执行

四个 Card 区:

  1. Card 1 手动重跑retryTriggerJob(id)trigger_job_id
  2. Card 2 缓存失效invalidateCache({ site_id, app_type?, member_id? })affected_countsite_id 必填
  3. Card 2.5 按需执行runApp(appType, params) 支持 APP3-APP8
  4. Card 2.6 触发事件链triggerEvent({ event_type, ..., is_forced=true }) — event_type ∈ {consumption, note_created, task_assigned, dws_completed}
  5. Card 3 批量执行createBatchRun() → 估算 → confirmBatchRun(batch_id) 异步启动batch_id 内存 TTL 600 秒 admin_service.py:26
  6. Card 4 告警管理ackAlert(id) / ignoreAlert(id),分页 10

I.3 TriggerManager.tsx4 Tab + URL 驱动)

  • Taball / biz / ai / etl?tab=xxx URL 查询参数持久化
  • Biz Tab 编辑 Modalcron_expression + interval_secondsmin=1保存后热加载
  • AI Tab 组件堆栈:AITriggers + AIOperations + AITriggerJobs

I.4 严重不一致app6 命名错位

  • 前端 adminAI.ts 定义 app6_note_analysis
  • 后端 admin_ai.py_SUPPORTED_APP_TYPESapp6_note
  • 手动执行 APP6 会 400,需修正命名统一(推荐采用后端名 app6_note

I.5 权限模型

  • 所有 Admin AI 端点 _require_admin() 依赖 admin_ai.py:70-111
  • 角色:{site_admin, tenant_admin, super_admin} 任一
  • admin_users.is_active 实时查询(禁用账户秒级生效)

附录 J后端完整端点与链路矩阵

J.1 路由端点清单(共 21 个 AI 相关)

admin_ai.py13 个,需 admin 权限)

  • GET /api/admin/ai/dashboard / trigger-jobs / trigger-jobs/{id} / run-logs / run-logs/{id} / budget / alerts
  • POST /api/admin/ai/trigger-jobs/{id}/retry / cache/invalidate / batch-run / batch-run/confirm / alerts/{id}/ack / alerts/{id}/ignore

xcx_chat.py5 个,需 approved 用户)

  • GET /api/xcx/chat/history / {chat_id}/messages / messages?contextType=&contextId=
  • POST /api/xcx/chat/streamSSE/ {chat_id}/messages

内部事件总线

  • POST /api/internal/ai/triggerinternal_ai.pyInternal-Token 认证
  • POST /api/internal/etl-completedinternal_events.py:56,触发 recall → task → fire_event("ai_dws_completed")
  • POST /api/admin/task-engine/pending-review/{id}/reassignadmin_task_engine.py:255,触发 fire_event("ai_task_assigned")

J.2 事件 → 链路映射

dispatcher.py:169-174 _EVENT_CHAIN_MAP

事件 链路
consumption APP3 → APP8 → APP7+ APP4 → APP5 若 has_assistant
note_created APP6 → APP8
task_assigned APP4 → APP5
dws_completed APP2 × 8area='all' + APP2A × 64area!='all'

J.3 服务层职责矩阵

服务 职责 dispatcher 调用 写表
ChatService 对话持久化 直调 DashScopeClient(不走 dispatcher ai_conversations / ai_messages
AdminAIService 监控聚合 无,仅查询 无(只读)
NoteService 备注创建 dispatcher._handle_note() notes / ai_cache
TaskGenerator 每日 07:00 生成任务 trigger_scheduler 异步 coach_tasks / ai_cache
TriggerScheduler cron/event 调度 fire_event() trigger_jobs 更新
AIDispatcher 链路编排+熔断/限流/预算 _run_step() × N ai_cache / ai_run_logs

J.4 超时与并发

  • 单步 _STEP_TIMEOUT = 180s2026-04-21 从 120s 上调)dispatcher.py:57
  • APP2 DWS 全链路 = 180s × 72 组 + 600s 余量 ≈ 2.5 小时(同步执行可能阻塞其他触发器,需监控)
  • 普通链 超时 10min

J.5 关键技术债

  • 事件去重内存 set:服务重启后当日相同事件可重复触发 → 需迁移到 DB 查询 ai_trigger_jobs
  • ChatService 不走 dispatcher:链路独立,不享受熔断/限流/预算保护APP1 限流单独实现)
  • 旧 openai_client 残留DashScope 迁移后未见清理

附录 KDemo 10 页 AI 区块映射(标杆参考)

K.1 浮按钮挂载清单

已挂载8 页board-coach / board-customer / chat-history / notes / performance / task-list / customer-service-records / my-profile / board-finance / customer-detail 未挂载chat / task-detail / coach-detail / apply / login / reviewing / dev-tools / no-permission

K.2 AI 配色规范

  • 6 色方案:red / orange / yellow / blue / indigo / purple
  • 实装:ai-color-manager.ts:74-127
  • task-list 采用 random 模式每日刷新
  • 其他页面按页面标识映射固定色

K.3 辅助组件

  • ai-title-badge:标题旁 AI 徽章
  • ai-inline-icon:列表内嵌 AI 图标
  • clue-card:线索卡片(含 tag/emoji/title/source/content 字段 + 展开折叠)

K.4 pageFilters 前端字段缺失

扫描发现前端页面未定义 pageFilters 对象结构,当前仅浮按钮属性声明;实际采集需在 P0 G1 gap 补充。


附录 LETL → AI 数据源约束

L.1 金额口径铁律

  • 禁用 consume_moneymember_data.py:1-6 文件头明文禁止)
  • 统一拆分为:table_charge_money + assistant_pd_money + assistant_cx_money + goods_money
  • 所有 prompt 拼装遵守此口径APP2/3/4/6/7 prompt 已核对)

L.2 FDW 视图与 RLS

  • 所有 fdw_etl.* 已迁移至 app.v_* RLS 视图
  • FDW 查询超时硬上限 5 秒 member_data.py:69-79
  • 超时降级到 _default_member_data()AI 输入降级,分析质量下降)

L.3 DWS 视图清单

  • 20+ 汇总表6 大主题(助教业绩/薪酬/财务日报/会员分析/订单/库存)
  • 金额字段 NUMERIC(12,2),货币 CNY
  • 空值策略未文档化NULL vs 0 策略缺prompt 数字解读存歧义

L.4 消费记录完整明细风险

  • 2026-05-01 起 App3 不再按 4000 字硬截断消费记录,优先保留完整明细,避免丢失大客户高频消费模式
  • 已用合成 100 条完整消费明细真实调用 App3prompt 约 25791 字64.30s 返回成功,低于 180s 单步超时
  • 剩余风险:真实门店极端会员、缓存 reference 膨胀或百炼侧临时抖动仍可能拉高耗时,需通过 ai_run_logs.elapsed_ms 持续观察

附录 M部署与超参

M.1 DashScope APP_ID 清单(根 .env:118-134

DASHSCOPE_APP_ID_1_CHAT
DASHSCOPE_APP_ID_2_FINANCE            (area='all' × 8 时间维度)
DASHSCOPE_APP_ID_2A_FINANCE_AREA      (area!='all' × 64 组合2026-04-23 新增)
DASHSCOPE_APP_ID_3_CLUE
DASHSCOPE_APP_ID_4_ANALYSIS
DASHSCOPE_APP_ID_5_TACTICS
DASHSCOPE_APP_ID_6_NOTE
DASHSCOPE_APP_ID_7_CUSTOMER
DASHSCOPE_APP_ID_8_CONSOLIDATE

M.2 超参硬编码

  • 模型版本、温度、max_tokens 在 config.py:14-48 内硬编码
  • 无 dev/test/prod 环境差异文件(仅一份 .env
  • INTERNAL_API_TOKEN 与 DashScope API Key 同存根 .env已 gitignore
  • BACKEND_API_URL=http://localhost:8000 部署到各环境需手工改

M.3 建议

  • 提取 .env.example + .env.dev + .env.prod 多环境模板
  • 超参挪到 config.py@dataclass 并可被 .env 覆盖
  • 加 APP2 DWS 并发窗口监控2.5h 耗时风险)

附录 N历史审计时间轴2026-02 ~ 2026-04

N.1 关键演进节点

日期 主题 涉及 APP
2026-04-23 app2a_finance_area_integrated — 72 组合拆为 app2_finance (8) + app2a_finance_area (64) + member_order_count 列 + area_code NULL bug 修复 APP2
2026-04-22 app2_prompt_v5_1_and_miniprogram_ai_insight — V5.1 采纳A/B 40 次调用92.3 分)+ 按星期聚合门槛 14 天 + 月中场景保护 + seq11/12 置顶 APP2
2026-04-21 admin-web-ai-management-suite — AIPrewarm / AITriggers / AIOperations 增强 + 4 个后端端点 管理
2026-04-20 ai-module-complete — Phase 0-4 贯通 + 删除 8 个死代码 app 文件 + 修复 main.py 未调用 set_dispatcher 导致 503 全局
2026-03-20 rns1-ai-autonomous-decision-risk-audit — 76 session / 34 风险 / "AI 信任文档胜过信任 DB" 核心问题 全局
2026-03-10 multi-module-ai-apps-task-defense — 8 个 AI 应用骨架 + 任务防卡死 + 小程序页面迁移 全局
2026-02-26 retention-clue-refactor — 新表 ai_trigger_jobs 去重 + member_retention_clue 字段调整 APP8

N.2 从历史踩坑抽取的验证规则

历史踩坑 应有验证
FDW Schema 脱节(列名/视图名虚构) 生成/改 prompt 前必须 MCP 执行 information_schema.columns + SELECT * LIMIT 5 验证
月中场景误读3 月初把月中 22 天当整月对比) payload 含"对比口径"字段(当期/对比期天数);按星期门槛 14 天;样本<5 天降权表述
Prompt 版本漂移 版本号固定写入 docs/ai/frontend enum / backend schema 同步校验
app_type 枚举错配 新增 APP 时同时更新 CacheTypeEnum / DDL CHECK / _SUPPORTED_APP_TYPES / adminAI.ts
DWS area_code=NULL bug ETL 完成后必须验证 all_sum ≈ 各区域 sum
DDL 列 DEFAULT 0 未回填 上线前预约回填脚本窗口
AI 自主执行架构变更 连接模式/DDL/FDW 映射变更需向用户展示 SQL 等待确认
Task 状态欺骗20/22 失败仍 completed 带验证步骤的 task 测试通过率 < 90% 禁止 complete

N.3 5 项遗留技术债(需补审计)

  • APP8 幂等 DELETE+INSERT 改造缺独立审计
  • tokens_used=0 提取 bugusage.models 嵌套)未根治
  • request_prompt 8000 字符门槛未正式审计
  • ai_cache.score TTL 策略未定
  • ai_trigger_jobs 去重从内存 set 迁移到 DB 的当前状态未审计

附录 O最终 Gap 汇总v4 版,共 25 项)

在 v3 的 15 项基础上 + 新增 10 项(来自全景调研)。

O.1 v3 遗留 15 项(摘要)

见附录 BG1-G15

O.2 v4 新增 10 项

ID Gap 来源 建议
G16 前后端 app6_note_analysis vs app6_note 命名错位 admin-web 调研 统一为后端名 app6_note,同时改 adminAI.ts 枚举
G17 WebSocket /ws/ai-alerts/{site_id} 无重连 AIDashboard.tsx 指数退避重连2/4/8s最多 10 次)
G18 member_retention_clueemoji/providers 列,折叠到 summary/recorded_by_name DB schema 要么加列,要么明确 providers 长度上限并拦截超 50 字符
G19 ai_cache.score 无 CHECK 约束 DB schema CHECK (score IS NULL OR score BETWEEN 1 AND 10)
G20 全部 AI 表 未启用 RLS DB schema 至少 biz.ai_cache / biz.ai_trigger_jobs / public.member_retention_clue 启用 RLS + USING (site_id = current_setting('app.current_site_id')::bigint)
G21 储值余额精确值 / 助教 real_name / 备注原文进 prompt 落 ai_run_logs 合规审计 R2-R5 脱敏ai_run_logs 加严格 RLS
G22 事件去重内存 set 重启丢失 后端路由调研 去重改查 ai_trigger_jobs 当日记录
G23 APP2 DWS 预热 2.5 小时同步执行 后端路由调研 加并发控制窗口;监控 > 2h 告警
G24 task-detail 无 AI 浮按钮挂载 生产端调研 若需支持"从任务详情进入对话",需挂载浮按钮并传 sourcePage=task-detail + contextId=task_id
G25 MCP Server 无 site_id RLS 隔离;未接入百炼 AI MCP 调研 P5.1 批次 B 落地前不开放 MCP 给百炼;开放时强制 MCP_TOKEN + 敏感字段脱敏

附录 P全景完成度自评

面向 盘查状态
代码静态(后端 / 前端生产 / 前端 demo / admin-web 完成
PRD 文档AI 需求 2 / ai-app-prompts / P5 / P14 / P15 / NS2 / NS3 完成
DB schema DDL + Pydantic 对齐 + RLS 状态 完成
合规敏感字段PII / 金融 / 位置 / 备注原文) 完成
MCP Server现状 + 规划 + 与 dispatcher 边界) 完成
后端路由 21 个端点清单 + 服务层职责 + 超时并发 完成
历史审计近 3 月时间轴 + 踩坑规则 + 技术债 完成
demo 10 页 AI 区块映射 + 配色规范 + 浮按钮分布 完成
共享包枚举对齐(未同步)/ ETL DWS 字段 / 口径铁律 完成
部署超参 + 环境差异 + 压测降级灰度 完成(含缺口标注)

结论:本文档 v4 已作为 NeoZQYY 项目 AI 模块的单一真相来源SSOT,覆盖代码 / PRD / DB / 合规 / MCP / 路由 / 审计 / demo / ETL / 部署 10 个面向25 项 Gap 跟踪75+ 验证点;后续变更需同步更新本文档。