# 生成方案标准内容格式设计 **日期:** 2026-08-28 **状态:** 待审查 **优先级:** P1 ## 一、背景与问题 现有健康方案生成链路存在三处脱节: 1. **AI 返回侧**:`cfc-langgraph/app/api/adapter.py` 的 `PLAN_SYSTEM_PROMPT` 已要求 LLM 输出结构化 JSON(`overview` + `sections[].content/items` + `abnormal_indicators`),但 JSON 解析靠 `find("{")`/`rfind("}")` 手动截取,无 Pydantic 校验,失败时静默降级为 raw 文本——前端需同时兼容两种形态。 2. **呈现/编辑侧**:`cfc-frontend/pages/health/health-plan-summary.vue` 已支持分段确认 + 反馈式重生成,但 `submitPlan` 组装 `planJson` 时**漏写 `sections[].tasks`**(当前 schema 根本没有该字段),导致保存时丢失任务意图。规划师端 `HealthPlanReview.vue` 仅提供 `planContent` 纯文本编辑,无结构化任务条目 UI。 3. **任务转化侧**:`HealthPlanServiceImpl.generateDailyTasksFromPlan` 用正则(`TASK_PATTERN` + 动作词分类)从 `plan_content` 纯文本猜测任务行,可靠性建立在"LLM 输出恰好符合正则预期"上。结构化的 `plan_json` 在此链路完全未被消费。前后端各维护一份重复正则(`TASK_PATTERN` 与前端 `parseTasksFromPlan`)。 **目标:** 让 `plan_json.sections[].tasks` 成为任务生成的唯一事实源,AI 有标准格式返回、用户看到标准格式可编辑、结果确定性地转化为任务。 ## 二、方案选型(用户已确认) 采用**方案 A**: - LangGraph prompt 输出 `tasks` 数组 + Pydantic 校验 - 校验失败时用现有正则从 content 兜底提取,仍写入 `tasks` - 任务生成优先读 `planJson.sections[].tasks`,缺失才回落正则 - 存量方案用**懒加载回填**统一(每次生成任务时若发现无 tasks 则即时用正则回填并落库) 不做方案 B(完全移除正则,容错低)或方案 C(仅前端展示,未解决可靠性)。 ## 三、数据契约 在现有 `sections[].items` 基础上,为每个 section 增加 `tasks` 数组(机器可读任务意图),与 `content`(人类可读 Markdown)分离: ```json { "overview": "总体概述", "sections": [ { "key": "nutrition", "title": "营养补充建议", "content": "## 建议 1...(Markdown 展示文本)", "items": [{"name","dosage","timing","reason"}], "tasks": [ { "action_type": "buy", "title": "购买维生素D3补充剂", "dimension": "wealth", "frequency": "once", "notes": "每日一粒,随餐服用" } ] } ], "abnormal_indicators": [] } ``` ### 字段约定 | 字段 | 类型 | 说明 | |---|---|---| | `action_type` | string 枚举 | `buy`/`read`/`exercise`/`checkin`/`diet`/`activity`,决定任务类别与后处理 | | `title` | string | 任务标题(最终写入 Task.title) | | `dimension` | string 枚举 | 五维维度 `body`/`mind`/`wisdom`/`action`/`wealth` | | `frequency` | string | `once`=一次性 / `daily`=每日重复 | | `notes` | string? | 补充说明(可选) | ### action_type 与现有分类映射 | action_type | 现有 TaskDraft 分类 | 维度 | 特殊后处理 | |---|---|---|---| | `buy` | 购买任务 | wealth | 派生"每日使用/服用"子任务(parentTaskId) | | `read` | 阅读任务 | wisdom | — | | `exercise` | 运动任务 | body | — | | `checkin` | 打卡任务 | mind | — | | `diet` | 饮食任务 | body | — | | `activity` | 活动任务 | action | — | ## 四、LangGraph 层改造 **文件:** `cfc-langgraph/app/api/adapter.py`、`cfc-langgraph/app/schemas.py` 1. **`PLAN_SYSTEM_PROMPT` 追加 tasks 输出契约**:在每个 section 内强制输出 `tasks` 数组,明确枚举取值与 frequency 语义。 2. **新增 Pydantic 模型**(`app/schemas.py`): ```python from typing import List, Optional, Literal from pydantic import BaseModel class PlanTask(BaseModel): action_type: Literal["buy","read","exercise","checkin","diet","activity"] title: str dimension: Literal["body","mind","wisdom","action","wealth"] frequency: Literal["once","daily"] = "daily" notes: Optional[str] = None class PlanSection(BaseModel): key: str title: str = "" content: str = "" items: List[dict] = [] tasks: List[PlanTask] = [] class PlanResponse(BaseModel): overview: str = "" sections: List[PlanSection] = [] abnormal_indicators: List[dict] = [] ``` 3. **生成逻辑**:LLM 调用用结构化输出(`response_format={"type":"json_object"}`),随后 `PlanResponse.model_validate_json()` 校验;**校验失败 → 触发一次正则兜底重新提取 + 二次校验**,仍失败则降级(raw 文本 + 前端兼容)。 4. **`regenerate-section`**:反馈重生成改为返回 `{content, tasks}`,前端同步更新,不再只回传纯字符串。 ## 五、Java 层改造 **文件:** `HealthPlanServiceImpl.java`、`DatabaseInitializer.java`、`schema.sql` ### 5.1 任务生成优先读 tasks `generateDailyTasksFromPlan` 新逻辑: 1. **优先**:遍历 `plan_json.sections[].tasks[]`,按 `action_type` 直接映射现有 `TaskDraft` 分类。 2. **兜底**:该 section 无 `tasks` 数组(老数据/LLM 漏输出)→ 回落现有 `TASK_PATTERN` 正则从 `content` 提取。 3. `TaskDraft` 的 dimension/frequency 直接取自 `tasks[].dimension`/`tasks[].frequency`,不再依赖正则猜维度。 ### 5.2 存量方案懒加载回填(用户确认) 在 `generateDailyTasksFromPlan` 开头: - 若 `plan_json` 中该 section 无 `tasks` 数组 → 用现有正则(`TASK_PATTERN` + 动作分类)从 `content` 提取任务,写入 `plan_json.sections[].tasks`,**落库**(`updateById`)。 - 幂等:已回填的 section 跳过;重复调用不产生重复任务。 无需预跑全量脚本,零运维成本;存量方案在首次发布/确认时自动统一。 ## 六、前端改造 **文件:** `cfc-frontend/pages/health/health-plan-summary.vue`、`cfc-web/src/views/teacher/HealthPlanReview.vue` ### 6.1 家长端(只读展示 + 反馈式重生成) - **修复 bug**:`submitPlan` 组装 `planJson` 时补上 `sections[].tasks`(当前只写 overview+sections,漏 tasks)。 - 展示无 tasks 时降级 review 模式(兼容)。 - 反馈式重生成保留;regenerate 返回 tasks 时前端同步更新。 ### 6.2 规划师端(结构化任务条目编辑) - `HealthPlanReview.vue` 当前只有 `planContent` 纯文本编辑,无 tasks UI → **新增任务条目级增删改**(action_type/title/dimension/frequency/notes)。 - 任务编辑直接改 `plan_json.sections[].tasks`,保存走现有 `pending-review/update` 接口(已是透传 planJson)。 ## 七、测试与验证 ### 单元测试 - **LangGraph**:`PlanResponse` 校验(合法/非法 action_type、frequency 越界)、正则兜底回填。 - **Java**:`generateDailyTasksFromPlan` 覆盖"有 tasks(优先走 tasks)"与"无 tasks(回落正则)"两分支;`TaskDraft` 映射正确性(buy→wealth+派生使用子任务等);懒加载回填幂等性。 ### 验证命令 | 模块 | 命令 | |---|---| | 后端 | `cd cfc-backend && mvn clean compile` + 针对性单测 | | LangGraph | `cd cfc-langgraph && pytest tests/ -v` | | cfc-web | `cd cfc-web && npm run build` | ### 手工验收 1. 新方案 AI 生成含 tasks → 发布 → 任务正确生成且维度匹配。 2. 存量无 tasks 老 planJson → 首次发布/确认自动回填 → 任务正常。 3. 规划师 cfc-web 编辑任务条目 → 保存 → 重新发布反映改动。 4. LLM 漏输出 tasks → 正则兜底仍能生成任务。 ## 八、边界与注意事项 - 正则 `TASK_PATTERN` 短期保留作兜底,不退役;等所有活跃方案走新格式后再评估退役。 - 发现 `getPlanList` 中残留 `SortUtil.applySort` 调用(之前 revert 掉的分页+排序残留)——**不在本设计范围内**,仅提示后续清理。 - 前端小程序需在 HBuilderX 重新打包验证(禁止 Agent 自行 `npm run build:mp-weixin`)。