Przeglądaj źródła

docs: 五维自检 AI 集成 P0+P1 设计稿(纯AI建议+计划生成+趋势+Chat注入)

iwt 2 tygodni temu
rodzic
commit
9e98094fc1

+ 385 - 0
docs/superpowers/specs/2026-08-31-self-check-ai-integration-design.md

@@ -0,0 +1,385 @@
+# 五维家庭自检 AI 结合(P0 + P1)设计
+
+> 日期:2026-08-31
+> 状态:已获用户方案确认(P0+P1 第一批,P2 后续)
+> 关联:`FiveDimensionSelfCheckService` / `AiGateway` / `cfc-langgraph`
+
+---
+
+## 1. 背景与目标
+
+现有五维自检使用**静态五行寻源表**(`WuxingSourcingService` 硬编码 5 条建议)生成结果,所有用户看到相同文案,无个性化、无趋势感知。
+
+本批次实现 P0+P1 共 4 个子系统,按优先级排序:
+
+| 优先级 | 子系统 | 目标 |
+|:------:|--------|------|
+| **P0-1** | 静态建议 → AI 个性化建议 | 每次自检生成有温度、有针对性的解读 |
+| **P0-2** | 用户点击生成健康计划 | 自检低分可一键生成家庭健康计划草稿 |
+| **P1-1** | 历史趋势 + AI 解读 | 3 次自检趋势可视化 + AI 趋势分析 |
+| **P1-2** | 自检结果注入 AI Chat | 聊天页自动携带自检上下文 |
+
+P2(家庭成员差异化建议)延后单独实现。
+
+已确认决策:
+- P0-1:**纯 AI 替换**,不保留静态五行寻源建议(`WuxingSourcingService` 仅保留 `levelOf/levelName/dimensionName`)
+- P0-2:**用户点击才生成**,不自动创建 draft plan
+- P0-1 字段设计:**直接改造 `advices` 字段**,新结构 `[{dimension, interpretation, microActions, aiInsight}]`
+
+---
+
+## 2. 现状盘点
+
+### 后端(自检)
+
+- `FiveDimensionSelfCheckController`:`/questions` `/submit` `/latest` `/history`
+- `FiveDimensionSelfCheckService`:
+  - `QUESTION_BANK`(15题)、`SUB_DIMENSION_QUESTION_BANK`(19题)
+  - `submitSelfCheck`:计分 → `wuxingSourcingService.getAdvicesForLowScores()` → 落库 `adviceJson`
+  - `getQuestions()` 已改为支持 `retake` 参数(上个计划完成)
+- `WuxingSourcingService`:静态 `SOURCING_TABLE`(5行×5列)+ `levelOf/levelName/dimensionName` 纯工具方法
+- 表 `five_dimension_self_checks`:`adviceJson TEXT`
+
+### 后端(AI 基础设施)
+
+- `AiGateway.java`:统一网关(熔断+降级+PII脱敏)
+  - `chat(query, userId, conversationId, inputs)` → `POST /api/v1/chat`
+  - `generateInnateReading(portrait)` → `POST /api/v1/innate/reading`
+  - `generateHealthPlan(inputs)` → `POST /api/v1/analysis/run`
+- LangGraph Python(`cfc-langgraph/app/`,端口 9000):
+  - `app/graphs/chat_graph.py`(78行,Dify 兼容)
+  - `app/graphs/health_plan_graph.py`(297行,Java 客户端 JavaClient 读取家庭数据)
+  - `app/graphs/innate_portrait_graph.py`(65行,参考实现)
+  - `app/api/adapter.py`(Dify 兼容路由)
+  - `app/api/innate_portrait.py`(`/api/v1/innate/reading`)
+  - `app/main.py` 注册各 router
+- 参考链路:`InnatePortraitController` → `InnatePortraitService.generateAiReading()` → `AiGateway.generateInnateReading()` → LangGraph `/api/v1/innate/reading` → `innate_portrait_graph.py` → 成功返回 reading,失败 null(调用方降级模板)
+
+### 前端
+
+- `pages/family/self-check-result.vue`:结果页,advices 展示在第 71-123 行(五行相生寻源建议区块)
+- `pages/family/self-check-entry.vue`:中间页(刚实现)
+- `pages/ai/chat.vue`:AI 聊天页(需确认路径)
+- `utils/api.js`:自检相关方法(`getSelfCheckStatus/ignoreSelfCheck/getSelfCheckQuestions/submitSelfCheck/getSelfCheckLatest/getSelfCheckHistory`)
+
+---
+
+## 3. 数据层设计
+
+### 3.1 `adviceJson` 结构变更
+
+**旧结构**(静态五行寻源):
+```json
+[
+  {
+    "dimension": "mind",
+    "dimensionName": "心",
+    "element": "火",
+    "color": "#FF6B9D",
+    "score": 2,
+    "level": "tense",
+    "levelName": "紧绷",
+    "upstreamDimension": "action",
+    "upstreamName": "行",
+    "upstreamElement": "木",
+    "upstreamColor": "#10B981",
+    "upstreamReason": "关系顺畅了,内心才安定",
+    "restrainerDimension": "wealth",
+    "restrainerName": "富",
+    "restrainerElement": "水",
+    "restrainerColor": "#F59E0B",
+    "restrainerReason": "钱多了,情薄了",
+    "action": "关系周记;家庭夜谈"
+  }
+]
+```
+
+**新结构**(AI 生成):
+```json
+[
+  {
+    "dimension": "mind",
+    "dimensionName": "心",
+    "element": "火",
+    "color": "#FF6B9D",
+    "score": 2,
+    "level": "tense",
+    "levelName": "紧绷",
+    "interpretation": "你的心能量偏低,可能最近情绪压力较大...",
+    "microActions": ["今晚睡前做10分钟深呼吸", "和伴侣约定每周一次夜谈"],
+    "aiInsight": "建议从'行'维度入手...",
+    "fallbackUsed": false
+  }
+]
+```
+
+**注意**:`dimension`/`dimensionName`/`element`/`color`/`score`/`level`/`levelName` 字段保留,与现有 VO 兼容。`upstreamDimension`/`upstreamReason`/`restrainerDimension` 等静态字段移除,新增 `interpretation`/`microActions`/`aiInsight`/`fallbackUsed`。
+
+### 3.2 新增 LangGraph Graph 输入/输出
+
+**self_check_analysis_graph**(P0-1):
+- 输入 `dict`:`scores`(Map<dimension, score>)、`questionIds`(List[int])、`userId`(int)、`recentHistory`(List[dict] 最近3次自检快照)
+- 输出 `dict`:`advice_json`(新结构 JSON string)、`fallback_used`(bool)
+
+**self_check_trend_graph**(P1-1):
+- 输入 `dict`:`history`(List[dict] 最近3次含 totalScore/dimensions/createdAt)、`userId`(int)
+- 输出 `dict`:`aiInsight`(str)、`trendSummary`(str)
+
+---
+
+## 4. 后端接口设计
+
+### 4.1 修改 `POST /api/family/self-check/submit`(P0-1)
+
+AI 建议在 `submitSelfCheck` 时**同步生成**并落库 `adviceJson`(不单独暴露 analysis 接口,避免 YAGNI 冗余)。
+
+```
+POST /api/family/self-check/submit
+  ↓
+submitSelfCheck():计分 → selfCheckAnalysisService.generateAdvice(...) → 落库
+```
+
+### 4.2 新增 `POST /api/family/self-check/generate-plan`(P0-2)
+
+请求:`{ "checkId": 12 }`(可空,默认取最近一次)。
+
+响应 `Result<Map>`:
+```json
+{ "planId": 45, "status": "draft" }
+```
+
+### 4.3 新增 `POST /api/family/self-check/trend-analysis`(P1-1)
+
+请求:无 body(JWT 取 `userId`)。
+
+响应 `Result<TrendAnalysisVO>`:
+```json
+{
+  "history": [{ "createdAt": "...", "totalScore": 32, "dimensions": [...] }],
+  "aiInsight": "近三次身维度持续下降,可能...",
+  "trendSummary": "身-2 智+1 富0 行-1 心+2"
+}
+```
+
+### 4.4 修改 `POST /api/family/self-check/submit`(P0-1)
+
+删除 `WuxingSourcingService.getAdvicesForLowScores()` 调用,改为:
+```java
+SelfCheckAdviceResult adviceResult = selfCheckAnalysisService.generateAdvice(userId, scoreMap, questionIds);
+// adviceResult 含 adviceJson + fallbackUsed
+// 直接落库
+```
+
+### 4.5 `SelfCheckResultVO` 字段
+
+`SelfCheckResultVO.advices` 类型保持 `List<WuxingSourcingAdviceVO>` 不变,字段结构按 5.4 改造后的 VO 承载 AI 建议内容。
+
+---
+
+## 5. Java 服务层设计
+
+### 5.1 新增 `SelfCheckAnalysisService`
+
+```java
+@Service
+public class SelfCheckAnalysisService {
+    @Resource private AiGateway aiGateway;
+    @Resource private FiveDimensionSelfCheckMapper selfCheckMapper;
+    
+    /** 生成自检建议(AI 优先,LangGraph 失败返回 null) */
+    public SelfCheckAdviceResult generateAdvice(Long userId, Map<String, Integer> scoreMap, List<Integer> questionIds) {
+        // 1. 组装 inputs
+        // 2. 调 aiGateway.generateSelfCheckAdvice(inputs)
+        // 3. 返回 SelfCheckAdviceResult(含 adviceJson + fallbackUsed)
+    }
+    
+    /** 生成趋势分析 */
+    public TrendAnalysisVO getTrendAnalysis(Long userId) { ... }
+}
+```
+
+### 5.2 AiGateway 新增方法
+
+```java
+public Map<String, Object> generateSelfCheckAdvice(Map<String, Object> inputs) {
+    // POST /api/v1/self-check/analysis
+    // 失败返回 null(调用方设置 fallbackUsed=true)
+}
+
+public Map<String, Object> generateSelfCheckTrend(Map<String, Object> inputs) {
+    // POST /api/v1/self-check/trend
+}
+```
+
+### 5.3 WuxingSourcingService 变更
+
+- **保留**:`DIMENSION_META`、`levelOf()`、`levelName()`、`dimensionName()`(被 `buildDimensionScoreVO` 使用)
+- **删除**:`SOURCING_TABLE`、`getAdvice()`、`getAdvicesForLowScores()`
+
+### 5.4 `WuxingSourcingAdviceVO` 改造(P0-1)
+
+不新建 VO,直接改造现有 `dto/WuxingSourcingAdviceVO.java`(保持 `SelfCheckResultVO.advices` 类型不变,最小改动):
+
+- **保留字段**:`dimension` / `dimensionName` / `element` / `color` / `score` / `level` / `levelName`(与现有 VO 兼容)
+- **移除字段**:`upstreamDimension` / `upstreamName` / `upstreamElement` / `upstreamColor` / `upstreamReason` / `restrainerDimension` / `restrainerName` / `restrainerElement` / `restrainerColor` / `restrainerReason` / `action`
+- **新增字段**:
+  - `interpretation`(String,AI 对低分维度的解读)
+  - `microActions`(List&lt;String&gt;,2-3 个本周微行动)
+  - `aiInsight`(String,AI 补充洞察,可空)
+  - `fallbackUsed`(Boolean,AI 不可用降级标记)
+
+---
+
+## 6. LangGraph Python 设计
+
+### 6.1 新增 `self_check_analysis_graph.py`
+
+```python
+# cfc-langgraph/app/graphs/self_check_analysis_graph.py
+SYSTEM_PROMPT = """你是一位家庭健康顾问,基于五维自检结果(身·智·富·行·心,每维0-9分,满分45)给出个性化建议。
+
+要求:
+1. 用第二人称"你"称呼
+2. 对每个低分维度(≤6分)给出1-2句解读和2-3个具体可执行的微行动
+3. 如有历史数据,简要对比趋势
+4. 语气温暖口语化,不超过300字/维度
+5. 最后给出1句家庭整体洞察
+
+返回 JSON:
+{
+  "advice": [
+    {"dimension": "mind", "dimensionName": "心", "interpretation": "...", "microActions": ["...", "..."]},
+    ...
+  ],
+  "familyInsight": "..."
+}
+"""
+
+class SelfCheckAnalysisAgent:
+    async def run(self, scores: dict, questionIds: list, recentHistory: list, userId: int) -> dict:
+        # 格式化输入 → LLM → JSON 解析 → 返回
+```
+
+### 6.2 新增 `self_check_trend_graph.py`
+
+```python
+SYSTEM_PROMPT = """分析用户近3次五维自检趋势,输出:
+- aiInsight: 趋势解读(2-3句,指出最大变化维度和可能原因)
+- trendSummary: 各维度 delta(如"身-2 智+1 富0 行-1 心+2")
+返回 JSON:{"aiInsight": "...", "trendSummary": "..."}
+"""
+```
+
+### 6.3 注册路由
+
+`cfc-langgraph/app/api/self_check.py`:
+- `POST /api/v1/self-check/analysis` → `self_check_analysis_graph`
+- `POST /api/v1/self-check/trend` → `self_check_trend_graph`
+
+`cfc-langgraph/app/main.py`:追加 `app.include_router(self_check.router)`
+
+---
+
+## 7. 前端设计
+
+### 7.1 P0-1:`self-check-result.vue` 建议区块重写
+
+删除现有 `scr-card` 寻源建议区块(第 71-123 行),替换为:
+
+```html
+<view class="ai-advice-card" v-if="result.adviceJson">
+  <view class="ai-advice-header">
+    <text class="ai-advice-title">AI 健康解读</text>
+    <text class="ai-advice-sub">基于你的五维自检结果</text>
+  </view>
+  <view v-for="item in result.adviceJson" :key="item.dimension" class="ai-advice-item">
+    <view class="ai-advice-dim-badge" :style="{background: item.color}">
+      <text>{{ item.dimensionName }}</text>
+      <text>{{ item.score }}分</text>
+    </view>
+    <text class="ai-advice-interpretation">{{ item.interpretation }}</text>
+    <view class="ai-advice-actions">
+      <text class="ai-advice-action-label">本周行动:</text>
+      <text v-for="(action, i) in item.microActions" :key="'a'+i" class="ai-advice-action">{{ i+1 }}. {{ action }}</text>
+    </view>
+  </view>
+  <view class="ai-advice-family" v-if="result.adviceJson[0] && result.adviceJson[0].familyInsight">
+    <text class="ai-advice-family-label">💡 家庭整体洞察</text>
+    <text class="ai-advice-family-text">{{ result.adviceJson[0].familyInsight }}</text>
+  </view>
+</view>
+<view class="ai-advice-fallback" v-else-if="result.fallbackUsed">
+  <text>AI 分析暂时不可用,请稍后再试</text>
+</view>
+```
+
+### 7.2 P0-2:结果页加「生成健康计划」按钮
+
+在结果页底部按钮区追加(有低分维度时显示):
+```html
+<button v-if="hasLowScore && !planGenerating" class="plan-btn" @click="generatePlan">
+  生成健康计划
+</button>
+```
+
+### 7.3 P1-1:趋势展示
+
+新增 `POST /api/family/self-check/trend-analysis`,结果页中间页可展示(或结果页底部加「查看趋势」链接)。
+
+### 7.4 P1-2:Chat 注入 context
+
+在 `self-check-result.vue` 加「问问 AI」按钮:
+```html
+<button @click="askAI">问问 AI</button>
+```
+跳转:`uni.navigateTo({ url: '/pages/ai/chat?selfCheckId=' + result.id })`
+
+`AIChatController` 读取 `selfCheckId`,调 `getSelfCheckLatest`,将结果放入 context 传给 AiGateway。
+
+---
+
+## 8. 边界行为与错误处理
+
+1. **LangGraph 不可用**:`AiGateway` 熔断/open 时,`generateAdvice` 返回 `fallbackUsed=true`,`adviceJson=null`,前端显示"AI 分析暂时不可用"
+2. **LLM 返回非 JSON**:Python 侧 `validate` 节点捕获,返回 error,Java 侧 fallbackUsed=true
+3. **P0-2 计划生成失败**:toast "计划生成失败,请稍后重试",不阻断结果页
+4. **P1-1 历史不足 3 次**:用现有次数展示(1-2 次),LLM 做有限对比
+5. **P1-2 chat 上下文**:selfCheckId 无效时静默跳过,不影响 chat 正常流程
+
+---
+
+## 9. 测试与验证
+
+- 后端:`mvn clean compile` BUILD SUCCESS
+- 前端:`node --check` api.js 语法校验
+- 手动验证路径:
+  1. 提交自检 → 查看 adviceJson 含 AI 生成内容(非静态表)
+  2. 点「生成健康计划」→ 跳转 plan 详情(draft 状态)
+  3. 点「查看趋势」→ 展示趋势 + AI 解读
+  4. 点「问问 AI」→ chat 页,LLM 回答知晓自检背景
+
+---
+
+## 10. 文件变更清单
+
+**LangGraph Python(新增 2 graph + 1 api + 改 main)**:
+- `cfc-langgraph/app/graphs/self_check_analysis_graph.py`(P0-1)
+- `cfc-langgraph/app/graphs/self_check_trend_graph.py`(P1-1)
+- `cfc-langgraph/app/api/self_check.py`(路由注册)
+- `cfc-langgraph/app/main.py`(追加 router include)
+
+**Java 后端**:
+- `dto/WuxingSourcingAdviceVO.java`(改造:移除 upstream/restrainer/action,新增 interpretation/microActions/aiInsight/fallbackUsed)
+- `service/SelfCheckAnalysisService.java`(新建)
+- `service/WuxingSourcingService.java`(删除 SOURCING_TABLE/getAdvice/getAdvicesForLowScores)
+- `service/FiveDimensionSelfCheckService.java`(修改 submitSelfCheck 调新 Service,新增 trend 方法)
+- `service/AiGateway.java`(新增 2 方法)
+- `controller/family/FiveDimensionSelfCheckController.java`(新增 3 接口,修改 submit)
+- `service/HealthPlanService.java`(新增 generateFromSelfCheck)
+- `service/impl/HealthPlanServiceImpl.java`(实现 generateFromSelfCheck)
+
+**前端**:
+- `utils/api.js`(新增 3 方法)
+- `pages/family/self-check-result.vue`(P0-1/P0-2/P1-2 改造)
+- `pages/family/self-check-entry.vue`(P1-1 趋势展示)
+- `pages/ai/chat.vue`(P1-2 context 注入,需确认是否存在)