Spaces:
Runtime error
Runtime error
Merge branch 'main' of https://huggingface.co/spaces/build-small-hackathon/PregoPal
Browse files- docs/项目架构说明书.md +995 -0
docs/项目架构说明书.md
ADDED
|
@@ -0,0 +1,995 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# PregoPal 项目架构说明书
|
| 2 |
+
|
| 3 |
+
> **📌 关于批注区的重要说明**
|
| 4 |
+
>
|
| 5 |
+
> 本文档每个模块/小节均设有「批注区」(用 `> **批注:**` 标记),用于记录开发过程中的讨论、决策、待办事项。
|
| 6 |
+
>
|
| 7 |
+
> **批注区中由你(项目负责人)亲手写入的文字,是重要的原始参考依据,任何人不得修改或删除。**
|
| 8 |
+
>
|
| 9 |
+
> - 如有新的观察或建议,请在 `> **批注:**` 下方以 `>>` 追加新行
|
| 10 |
+
> - 如需修正已有批注内容,请在后方追加 `[修正: ...]` 标记,不得覆盖原始文字
|
| 11 |
+
> - 批注区中的意见将指导后续迭代方向,所有 Cline 协作修改前应先阅读对应批注
|
| 12 |
+
|
| 13 |
+
---
|
| 14 |
+
|
| 15 |
+
## 目录
|
| 16 |
+
|
| 17 |
+
- [一、项目概览](#一项目概览)
|
| 18 |
+
- [二、整体文件结构](#二整体文件结构)
|
| 19 |
+
- [三、app.py — 主入口](#三apppy--主入口)
|
| 20 |
+
- [四、config.py — 全局配置](#四configpy--全局配置)
|
| 21 |
+
- [五、utils.py — 工具函数](#五utilspy--工具函数)
|
| 22 |
+
- [六、loop.py — 核心循环引擎](#六looppy--核心循环引擎)
|
| 23 |
+
- [七、plugins/ — 插件系统](#七plugins--插件系统)
|
| 24 |
+
- [八、modules/ — 业务逻辑层](#八modules--业务逻辑层)
|
| 25 |
+
- [九、core/ — AI 核心层](#九core--ai-核心层)
|
| 26 |
+
- [十、ui/ — 表现层](#十ui--表现层)
|
| 27 |
+
- [十一、data/ — 数据存储](#十一data--数据存储)
|
| 28 |
+
- [十二、tests/ — 测试套件](#十二tests--测试套件)
|
| 29 |
+
- [十三、配置文件](#十三配置文件)
|
| 30 |
+
- [十四、协作规范与 Git 工作流](#十四协作规范与-git-工作流)
|
| 31 |
+
- [十五、当前运行状态](#十五当前运行状态)
|
| 32 |
+
- [十六、待办与已知问题](#十六待办与已知问题)
|
| 33 |
+
|
| 34 |
+
---
|
| 35 |
+
|
| 36 |
+
## 一、项目概览
|
| 37 |
+
|
| 38 |
+
### 一句话定位
|
| 39 |
+
|
| 40 |
+
**一个基于每日生命周期 Loop 驱动的孕期陪护 AI 助手**:每天首次启动时自动总结昨日饮食、对比国家营养标准(DRIs/膳食指南/体重标准),生成今日简报注入对话上下文;白天与家庭成员(通过声纹区分)自然交互,实时记录饮食、推荐食谱;次日循环往复。
|
| 41 |
+
|
| 42 |
+
### 技术栈
|
| 43 |
+
|
| 44 |
+
| 层级 | 技术 | 用途 |
|
| 45 |
+
|------|------|------|
|
| 46 |
+
| 核心模型 | **MiniCPM-o 4.5**(统一多模态模型) | 全双工推理:视觉+语音+文本+语音输出 |
|
| 47 |
+
| 前端框架 | **Gradio 6.x** | Web 界面,4 个子页面 |
|
| 48 |
+
| 声纹识别 | 频谱统计特征(baseline) | 说话人身份识别 |
|
| 49 |
+
| 频谱方案 | NumPy + SoundFile(备选 fallback) | 声纹识别兜底 |
|
| 50 |
+
| 数据存储 | JSON + Markdown | 饮食记录 + 报告存档 + 每日简报 + 家庭信息 |
|
| 51 |
+
| 营养标准 | 三个 MD 文件(已下载)+ 自写解析器 | 体重标准/膳食指南/DRIs |
|
| 52 |
+
| 循环引擎 | 自研状态机 + 插件注册 | 每日生命周期管理 |
|
| 53 |
+
| 数据提取 | 正则表达式(system prompt 标记格式) | 从对话中提取结构化数据 |
|
| 54 |
+
| 部署平台 | HuggingFace Spaces(目前)+ Modal(后续) | 云端部署 |
|
| 55 |
+
|
| 56 |
+
> **批注:** ________
|
| 57 |
+
|
| 58 |
+
---
|
| 59 |
+
|
| 60 |
+
## 二、整体文件结构
|
| 61 |
+
|
| 62 |
+
```
|
| 63 |
+
PregoPal/
|
| 64 |
+
│
|
| 65 |
+
├── app.py # 主入口:启动 Gradio + Loop
|
| 66 |
+
├── config.py # 全局配置(路径/常量/数据模板)
|
| 67 |
+
├── utils.py # 工具函数(中文字体设置/国际化/首页卡片数据)
|
| 68 |
+
├── loop.py # 核心循环引擎(状态机驱动)
|
| 69 |
+
├── requirements.txt # 依赖清单
|
| 70 |
+
├── .gitignore # Git 忽略规则
|
| 71 |
+
├── README.md # 并行 Cline 协作接口文档
|
| 72 |
+
│
|
| 73 |
+
├── plugins/ # Loop 插件
|
| 74 |
+
│ ├── __init__.py
|
| 75 |
+
│ ├── base.py # LoopPlugin 基类 + PluginRegistry + LoopContext
|
| 76 |
+
│ ├── family_quiz.py # 家庭菜谱/体重询问检查
|
| 77 |
+
│ ├── diet_summary.py # 昨日饮食总结
|
| 78 |
+
│ ├── weight_check.py # 体重检查(WS/T 801)
|
| 79 |
+
│ ├── family_memory.py # 家庭记忆加载
|
| 80 |
+
│ ├── dri_analysis.py # DRIs 营养素对比分析
|
| 81 |
+
│ ├── briefing_generator.py # 今日简报生成器
|
| 82 |
+
│ ├── three_day_summary.py # 每三天总结
|
| 83 |
+
│ └── preset_writer.py # 预设文件写入
|
| 84 |
+
│
|
| 85 |
+
├── modules/ # 业务逻辑层(纯函数/无状态)
|
| 86 |
+
│ ├── __init__.py
|
| 87 |
+
│ ├── voiceprint.py # 声纹识别
|
| 88 |
+
│ ├── meal_recommender.py # 菜品推荐
|
| 89 |
+
│ ├── diet_extractor.py # AI 回复数据提取
|
| 90 |
+
│ ├── diet_logger.py # 饮食记录存储
|
| 91 |
+
│ ├── family_manager.py # 家庭信息管理
|
| 92 |
+
│ ├── nutrition_standards.py # 中国官方营养标准
|
| 93 |
+
│ └── nutrition_analyzer.py # 营养分析与可视化
|
| 94 |
+
│
|
| 95 |
+
├── core/ # AI 核心层(等待 MiniCPM-o)
|
| 96 |
+
│ ├── __init__.py
|
| 97 |
+
│ ├── model_loader.py # 模型加载器(空接口)
|
| 98 |
+
│ ├── voice_processor.py # 语音处理器(空接口)
|
| 99 |
+
│ ├── vision_processor.py # 视觉处理器(空接口)
|
| 100 |
+
│ └── conversation_manager.py # 对话管理器(部分实现)
|
| 101 |
+
│
|
| 102 |
+
├── ui/ # 表现层
|
| 103 |
+
│ ├── __init__.py
|
| 104 |
+
│ └── app_builder.py # Gradio 界面(4 个 Tab)
|
| 105 |
+
│
|
| 106 |
+
├── data/ # 持久化存储
|
| 107 |
+
│ ├── nutrition/
|
| 108 |
+
│ │ └── raw/ # 三个官方 MD 文件
|
| 109 |
+
│ ├── family/ # 家庭信息
|
| 110 |
+
│ │ ├── recipes.md
|
| 111 |
+
│ │ ├── preferences.md
|
| 112 |
+
│ │ └── memory.md
|
| 113 |
+
│ ├── presets/ # 每日简报 + 状态位
|
| 114 |
+
│ ├── voices/ # 声纹音频文件
|
| 115 |
+
│ ├── logs/ # 每日饮食日志 Markdown
|
| 116 |
+
│ ├── reports/ # 营养报告 Markdown
|
| 117 |
+
│ ├── diet_logs.json # 结构化饮食记录 JSON
|
| 118 |
+
│ ├── nutrition_db.json # 营养数据库 JSON
|
| 119 |
+
│ └── family.json # 家庭成员声纹 JSON
|
| 120 |
+
│
|
| 121 |
+
├── tests/ # 测试套件
|
| 122 |
+
│ ├── __init__.py
|
| 123 |
+
│ ├── run_tests.py # 测试运行器
|
| 124 |
+
│ ├── test_diet_extractor.py
|
| 125 |
+
│ ├── test_family_manager.py
|
| 126 |
+
│ ├── test_nutrition_standards.py
|
| 127 |
+
│ ├── test_loop.py
|
| 128 |
+
│ └── test_plugins.py
|
| 129 |
+
│
|
| 130 |
+
└── docs/ # 文档
|
| 131 |
+
├── 项目理解_技术架构.md
|
| 132 |
+
├── 开发日志.md
|
| 133 |
+
└── 项目架构说明书.md # ← 本文件
|
| 134 |
+
```
|
| 135 |
+
|
| 136 |
+
> **批注:** 文档中的目录结构以实际代码为准,后续新增模块/文件需同步更新此处。三代营养文档(体重标准/膳食指南/DRIs)的原始 md 文件在 `data/nutrition/raw/` 中,解析器在 `modules/nutrition_standards.py` 中硬编码了结构化数据。
|
| 137 |
+
|
| 138 |
+
---
|
| 139 |
+
|
| 140 |
+
## 三、app.py — 主入口
|
| 141 |
+
|
| 142 |
+
### 文件路径
|
| 143 |
+
|
| 144 |
+
`app.py`
|
| 145 |
+
|
| 146 |
+
### 职责
|
| 147 |
+
|
| 148 |
+
Gradio 应用的薄入口,不包含任何业务逻辑。启动时:
|
| 149 |
+
1. 设置中文字体(调用 `utils.setup_chinese_font()`)
|
| 150 |
+
2. 检查并运行每日 Loop(调用 `loop.check_and_run_loop()`)
|
| 151 |
+
3. 创建 Gradio 应用(调用 `ui.app_builder.create_app()`)
|
| 152 |
+
4. 启动 Gradio 服务(绑定 `0.0.0.0:7860`)
|
| 153 |
+
|
| 154 |
+
### 关键代码
|
| 155 |
+
|
| 156 |
+
```python
|
| 157 |
+
# 启动时设置中文字体
|
| 158 |
+
_CHINESE_FONT = setup_chinese_font()
|
| 159 |
+
|
| 160 |
+
if __name__ == "__main__":
|
| 161 |
+
loop = check_and_run_loop()
|
| 162 |
+
demo = create_app(loop)
|
| 163 |
+
demo.launch(server_name="0.0.0.0", server_port=7860, ...)
|
| 164 |
+
```
|
| 165 |
+
|
| 166 |
+
### 外部依赖
|
| 167 |
+
|
| 168 |
+
- `ui.app_builder.create_app` — 构建 Gradio 界面
|
| 169 |
+
- `loop.check_and_run_loop` — 检查并运行每日循环
|
| 170 |
+
- `utils.setup_chinese_font` — 中文字体设置
|
| 171 |
+
|
| 172 |
+
> **批注:** ________
|
| 173 |
+
|
| 174 |
+
---
|
| 175 |
+
|
| 176 |
+
## 四、config.py — 全局配置
|
| 177 |
+
|
| 178 |
+
### 文件路径
|
| 179 |
+
|
| 180 |
+
`config.py`
|
| 181 |
+
|
| 182 |
+
### 职责
|
| 183 |
+
|
| 184 |
+
所有全局常量的集中管理。包含六大配置区:
|
| 185 |
+
|
| 186 |
+
#### 4.1 目录路径
|
| 187 |
+
|
| 188 |
+
| 常量 | 值 | 用途 |
|
| 189 |
+
|------|-----|------|
|
| 190 |
+
| `DATA_DIR` | `data/` | 数据根目录 |
|
| 191 |
+
| `VOICE_DIR` | `data/voices/` | 声纹音频存储 |
|
| 192 |
+
| `LOGS_DIR` | `data/logs/` | 饮食日志 Markdown |
|
| 193 |
+
| `REPORTS_DIR` | `data/reports/` | 营养报告导出 |
|
| 194 |
+
| `FAMILY_FILE` | `data/family.json` | 家庭成员声纹数据 |
|
| 195 |
+
| `DIET_LOG_FILE` | `data/diet_logs.json` | 饮食记录 JSON |
|
| 196 |
+
| `NUTRITION_DB_FILE` | `data/nutrition_db.json` | 营养数据库 |
|
| 197 |
+
|
| 198 |
+
启动时自动创建所有目录。
|
| 199 |
+
|
| 200 |
+
#### 4.2 常量枚举
|
| 201 |
+
|
| 202 |
+
- `FAMILY_ROLES = ["孕妇", "丈夫", "婆婆", "妈妈", "爸爸", "其他家人"]`
|
| 203 |
+
- `TRIMESTERS = ["孕早期", "孕中期", "孕晚期"]`
|
| 204 |
+
|
| 205 |
+
#### 4.3 数据模板
|
| 206 |
+
|
| 207 |
+
- `DEFAULT_NUTRITION_DB` — 10 种营养素的内置推荐数据
|
| 208 |
+
- `MEAL_TEMPLATES` — 4 餐次 × 4 选项的食谱模板
|
| 209 |
+
- `TRIMESTER_ADJUSTMENTS` — 各孕期阶段 focus/avoid 建议
|
| 210 |
+
- `TRIMESTER_TIPS` — 各孕期阶段饮食提示字符串
|
| 211 |
+
|
| 212 |
+
#### 4.4 Schema 版本
|
| 213 |
+
|
| 214 |
+
- `DIET_LOG_SCHEMA_VERSION = "1.0"`
|
| 215 |
+
|
| 216 |
+
#### 4.5 声纹阈值
|
| 217 |
+
|
| 218 |
+
- `VOICEPRINT_SIMILARITY_THRESHOLD = 0.7`
|
| 219 |
+
|
| 220 |
+
### 修改规范
|
| 221 |
+
|
| 222 |
+
- 新增常量追加到同类型区域末尾,不删改已有常量名
|
| 223 |
+
- 改版本号前需评估向后兼容
|
| 224 |
+
- commit message 需注明新增常量的用途
|
| 225 |
+
|
| 226 |
+
> **批注:** ________
|
| 227 |
+
|
| 228 |
+
---
|
| 229 |
+
|
| 230 |
+
## 五、utils.py — 工具函数
|
| 231 |
+
|
| 232 |
+
### 文件路径
|
| 233 |
+
|
| 234 |
+
`utils.py`
|
| 235 |
+
|
| 236 |
+
### 职责
|
| 237 |
+
|
| 238 |
+
提供通用工具函数,包含三个主要功能:
|
| 239 |
+
|
| 240 |
+
#### 5.1 中文字体设置
|
| 241 |
+
|
| 242 |
+
`setup_chinese_font()` — 遍历候选字体列表(SimHei → Microsoft YaHei → SimSun → ...),找到可用字体后设置 `matplotlib.rcParams`。支持 Windows 系统字体文件 fallback。
|
| 243 |
+
|
| 244 |
+
#### 5.2 国际化(i18n)
|
| 245 |
+
|
| 246 |
+
提供中英文字典(`ZH` / `EN`)+ `t(key, lang)` 翻译函数。
|
| 247 |
+
|
| 248 |
+
字典覆盖:应用标题、4 个 Tab 名称、语音交互页标签、家庭饮食习惯页标签、三天总结页标��、营养报告页标签。
|
| 249 |
+
|
| 250 |
+
#### 5.3 首页卡片数据
|
| 251 |
+
|
| 252 |
+
`get_home_cards(loop)` — 从 Loop 上下文提取首页展示数据:
|
| 253 |
+
- `trimester` — 孕期阶段
|
| 254 |
+
- `focus_nutrients` — 营养关注点
|
| 255 |
+
- `recommended_foods` — 推荐食材
|
| 256 |
+
- `recipe_count` / `recipe_names` — 家庭菜谱统计
|
| 257 |
+
- `yesterday_summary` / `meal_count` — 昨日饮食摘要
|
| 258 |
+
- `weight_status` / `weight_trend` — 体重评估
|
| 259 |
+
- `thinking_keywords` — AI 思考关键词
|
| 260 |
+
|
| 261 |
+
#### 5.4 自定义 CSS
|
| 262 |
+
|
| 263 |
+
`CUSTOM_CSS` — Gradio 界面样式表,包含:
|
| 264 |
+
- 圆角卡片(`border-radius: 16px`)
|
| 265 |
+
- 大圆形语音按钮(`160px × 160px`)
|
| 266 |
+
- 状态指示灯(`green/gray dot`)
|
| 267 |
+
- 聊天气泡样式
|
| 268 |
+
- Tab 按钮美化
|
| 269 |
+
- 响应式适配
|
| 270 |
+
|
| 271 |
+
> **批注:** ________
|
| 272 |
+
|
| 273 |
+
---
|
| 274 |
+
|
| 275 |
+
## 六、loop.py — 核心循环引擎
|
| 276 |
+
|
| 277 |
+
### 文件路径
|
| 278 |
+
|
| 279 |
+
`loop.py`
|
| 280 |
+
|
| 281 |
+
### 职责
|
| 282 |
+
|
| 283 |
+
状态机驱动的每日生命周期管理。是 PregoPal 与「被动响应式应用」的核心区别。
|
| 284 |
+
|
| 285 |
+
### 6.1 状态枚举
|
| 286 |
+
|
| 287 |
+
```python
|
| 288 |
+
class LoopState(Enum):
|
| 289 |
+
LAUNCH # 启动检查:检查今日状态位
|
| 290 |
+
FAMILY_QUIZ # 家庭问卷:检查是否需要询问菜谱/体重
|
| 291 |
+
SUMMARIZE # 昨日总结:分析昨日饮食/体重/家庭记忆
|
| 292 |
+
ANALYZE # 营养分析:对比 DRIs
|
| 293 |
+
BRIEF # 生成今日简报
|
| 294 |
+
INTERACT # 白天交互模式(等待用户操作)
|
| 295 |
+
THREE_DAY # 每三天自动总结
|
| 296 |
+
CONSOLIDATE # 晚间整理
|
| 297 |
+
DONE # 标记今日完成
|
| 298 |
+
```
|
| 299 |
+
|
| 300 |
+
### 6.2 状态转移表
|
| 301 |
+
|
| 302 |
+
```
|
| 303 |
+
LAUNCH -- need_summary → FAMILY_QUIZ
|
| 304 |
+
LAUNCH -- already_done → INTERACT
|
| 305 |
+
FAMILY_QUIZ -- ok → SUMMARIZE
|
| 306 |
+
SUMMARIZE -- ok → ANALYZE
|
| 307 |
+
ANALYZE -- ok → BRIEF
|
| 308 |
+
BRIEF -- ok → INTERACT
|
| 309 |
+
INTERACT -- day_ended → CONSOLIDATE
|
| 310 |
+
CONSOLIDATE -- need_3day → THREE_DAY
|
| 311 |
+
CONSOLIDATE -- ok → DONE
|
| 312 |
+
THREE_DAY -- ok → DONE
|
| 313 |
+
```
|
| 314 |
+
|
| 315 |
+
### 6.3 主类:`PregoPalLoop`
|
| 316 |
+
|
| 317 |
+
#### 核心属性
|
| 318 |
+
- `plugins` — `PluginRegistry` 实例,管理所有插件
|
| 319 |
+
- `state` — 当前状态(`LoopState`)
|
| 320 |
+
- `context` — `LoopContext` 实例,插件间共享数据
|
| 321 |
+
|
| 322 |
+
#### 主循环
|
| 323 |
+
```python
|
| 324 |
+
async def run(self):
|
| 325 |
+
while self.state is not LoopState.DONE:
|
| 326 |
+
handler = getattr(self, f"_state_{self.state.value}")
|
| 327 |
+
event = await handler()
|
| 328 |
+
next_state = _TRANSITIONS[(self.state, event)]
|
| 329 |
+
self.state = next_state
|
| 330 |
+
```
|
| 331 |
+
|
| 332 |
+
#### 状态处理器(9 个)
|
| 333 |
+
每个状态对应一个 `_state_xxx` 异步方法,负责执行该阶段逻辑或调用对应插件。
|
| 334 |
+
|
| 335 |
+
#### 外部接口
|
| 336 |
+
- `get_briefing()` → `dict`(获取今日简报)
|
| 337 |
+
- `get_thinking_keywords()` → `str`(获取 AI 思考关键词)
|
| 338 |
+
- `get_errors()` → `list[str]`(获取错误列表)
|
| 339 |
+
- `run_sync()` → 同步运行入口
|
| 340 |
+
|
| 341 |
+
### 6.4 状态位管理:`DailyStatus`
|
| 342 |
+
|
| 343 |
+
管理 `data/presets/.daily_status.json`,记录每日完成状态。
|
| 344 |
+
|
| 345 |
+
关键方法:
|
| 346 |
+
- `is_today_done()` — 检查今日是否已完成
|
| 347 |
+
- `mark_summary_done()` — 标记今日总结完成
|
| 348 |
+
- `mark_day_ended()` — 标记今日结束
|
| 349 |
+
- `days_since_last_summary()` — 距离上次总结天数
|
| 350 |
+
- `should_three_day_summary()` — 是否需要进行三天总结
|
| 351 |
+
|
| 352 |
+
### 6.5 便捷函数
|
| 353 |
+
|
| 354 |
+
- `run_daily_loop()` — 创建并运行每日循环
|
| 355 |
+
- `check_and_run_loop()` — 检查状态位后决定是否运行循环(供 Gradio 启动时调用)
|
| 356 |
+
|
| 357 |
+
> **批注:** 状态机设计根据目的迭代。INTERACT 状态目前是简单的 `return "day_ended"`,实际使用时需通过 Gradio 事件队列触发状态转换。三天的触发条件在 `DailyStatus.should_three_day_summary()` 中定义为 `days_since_last_summary() >= 3`。
|
| 358 |
+
|
| 359 |
+
---
|
| 360 |
+
|
| 361 |
+
## 七、plugins/ — 插件系统
|
| 362 |
+
|
| 363 |
+
### 7.1 基类与注册中心
|
| 364 |
+
|
| 365 |
+
**文件:** `plugins/base.py`
|
| 366 |
+
|
| 367 |
+
核心类型:
|
| 368 |
+
|
| 369 |
+
| 类型 | 说明 |
|
| 370 |
+
|------|------|
|
| 371 |
+
| `LoopStage` | 阶段枚举:`FAMILY_QUIZ`, `SUMMARIZE`, `ANALYZE`, `BRIEF`, `THREE_DAY`, `CONSOLIDATE` |
|
| 372 |
+
| `LoopContext` | 插件间共享数据容器(`briefing`, `weight_data`, `diet_records`, `family_recipes`, `family_memory`, `analysis_results`, `errors`) |
|
| 373 |
+
| `PluginResult` | 插件执行结果(`success`, `data`, `message`) |
|
| 374 |
+
| `LoopPlugin` | 插件抽象基类(`stage()`, `name()`, `run(ctx)`) |
|
| 375 |
+
| `PluginRegistry` | 注册中心(`register`, `get_plugins`, `get_all`, `unregister`) |
|
| 376 |
+
|
| 377 |
+
### 7.2 插件清单
|
| 378 |
+
|
| 379 |
+
| 插件名 | 类名 | 注册阶段 | 文件 | 功能 | 数据源 |
|
| 380 |
+
|--------|------|---------|------|------|--------|
|
| 381 |
+
| 家庭菜谱检查 | `FamilyRecipeQuizPlugin` | `FAMILY_QUIZ` | `family_quiz.py` | 检查是否需要询问新菜谱 | `data/family/recipes.md` |
|
| 382 |
+
| 体重询问检查 | `WeightQuizPlugin` | `FAMILY_QUIZ` | `family_quiz.py` | 检查是否需要询问体重 | `data/logs/` |
|
| 383 |
+
| 昨日饮食总结 | `DietSummaryPlugin` | `SUMMARIZE` | `diet_summary.py` | 读取昨日饮食日志,汇总三餐 | `data/logs/饮食日志_*.md` |
|
| 384 |
+
| 体重检查 | `WeightCheckPlugin` | `SUMMARIZE` | `weight_check.py` | 检查体重记录,对比 WS/T 801 | `WeightStandardParser` |
|
| 385 |
+
| 家庭记忆加载 | `FamilyMemoryPlugin` | `SUMMARIZE` | `family_memory.py` | 加载家庭关系/事件记忆 | `data/family/memory.md` |
|
| 386 |
+
| DRIs 分析 | `DRIAnalysisPlugin` | `ANALYZE` | `dri_analysis.py` | 对比营养素参考摄入量 | `DRIsParser` + 昨日饮食 |
|
| 387 |
+
| 简报生成 | `BriefingGeneratorPlugin` | `BRIEF` | `briefing_generator.py` | 汇总分析为结构化 JSON | `LoopContext` |
|
| 388 |
+
| 三天总结 | `ThreeDaySummaryPlugin` | `THREE_DAY` | `three_day_summary.py` | 分析营养缺失趋势,改进家庭菜单 | 近三天饮食记录 |
|
| 389 |
+
| 预设写入 | `PresetWriterPlugin` | `CONSOLIDATE` | `preset_writer.py` | 生成明日预设文件 | `data/presets/` |
|
| 390 |
+
|
| 391 |
+
### 7.3 插件向 `ctx.briefing` 写入的 Key
|
| 392 |
+
|
| 393 |
+
```
|
| 394 |
+
trimester # str
|
| 395 |
+
need_ask_weight # bool
|
| 396 |
+
weight_quiz_message # str
|
| 397 |
+
weight_evaluation # dict
|
| 398 |
+
need_ask_recipe # bool
|
| 399 |
+
recipe_quiz_message # str
|
| 400 |
+
yesterday_diet # dict {"status","summary","meal_count"}
|
| 401 |
+
dri_analysis # dict {"focus_nutrients","summary","comparison_table"}
|
| 402 |
+
recommended_foods # list[str]
|
| 403 |
+
family_memory # dict
|
| 404 |
+
thinking_keywords # str
|
| 405 |
+
three_day_summary # dict(仅 THREE_DAY 阶段)
|
| 406 |
+
```
|
| 407 |
+
|
| 408 |
+
### 7.4 扩展方式
|
| 409 |
+
|
| 410 |
+
```python
|
| 411 |
+
# 新增功能只需两步:
|
| 412 |
+
class NewPlugin(LoopPlugin):
|
| 413 |
+
def stage(self) -> LoopStage: return LoopStage.SUMMARIZE
|
| 414 |
+
def name(self) -> str: return "new_feature"
|
| 415 |
+
async def run(self, ctx: LoopContext) -> PluginResult: ...
|
| 416 |
+
|
| 417 |
+
loop.plugins.register(NewPlugin())
|
| 418 |
+
# 不需要修改 loop.py 的状态机代码
|
| 419 |
+
```
|
| 420 |
+
|
| 421 |
+
> **批注:** ________
|
| 422 |
+
|
| 423 |
+
---
|
| 424 |
+
|
| 425 |
+
## 八、modules/ — 业务逻辑层
|
| 426 |
+
|
| 427 |
+
### 8.1 声纹识别 — `modules/voiceprint.py`
|
| 428 |
+
|
| 429 |
+
**类:** `VoiceprintManager`
|
| 430 |
+
|
| 431 |
+
| 方法 | 输入 | 输出 | 说明 |
|
| 432 |
+
|------|------|------|------|
|
| 433 |
+
| `register_member(name, relation, audio_path)` | str, str, str | (dict\|None, str) | 注册新成员声纹 |
|
| 434 |
+
| `identify_speaker(audio_path)` | str | (dict\|None, str) | 识别说话人 |
|
| 435 |
+
| `get_members_list()` | — | str | 格式化成员列表 |
|
| 436 |
+
| `delete_member(member_id)` | str | str | 删除指定成员 |
|
| 437 |
+
|
| 438 |
+
**技术方案:**
|
| 439 |
+
- 当前:频谱统计特征(mean/std/max/min/zero_crossing_rate/energy/duration)
|
| 440 |
+
- 后续:Whisper-medium encoder embedding
|
| 441 |
+
|
| 442 |
+
**数据存储:** `data/family.json`(JSON 格式)
|
| 443 |
+
```json
|
| 444 |
+
{
|
| 445 |
+
"members": [{"id", "name", "relation", "registered_at", "audio_path", "features"}],
|
| 446 |
+
"voiceprints": {"member_id": {"mean": ..., "std": ..., ...}}
|
| 447 |
+
}
|
| 448 |
+
```
|
| 449 |
+
|
| 450 |
+
依赖:`config.VOICE_DIR`、`config.FAMILY_FILE`、`config.VOICEPRINT_SIMILARITY_THRESHOLD`(默认 0.7)
|
| 451 |
+
|
| 452 |
+
> **批注:** 当前为频谱统计特征 baseline,后续升级 Whisper encoder 时需保持 `register_member`/`identify_speaker` 签名不变。
|
| 453 |
+
|
| 454 |
+
---
|
| 455 |
+
|
| 456 |
+
### 8.2 菜品推荐 — `modules/meal_recommender.py`
|
| 457 |
+
|
| 458 |
+
**类:** `MealRecommender`
|
| 459 |
+
|
| 460 |
+
| 方法 | 输入 | 输出 | 说明 |
|
| 461 |
+
|------|------|------|------|
|
| 462 |
+
| `get_recommendation(preference, trimester, restrictions)` | str, str, str | dict | 返回今日推荐食谱 |
|
| 463 |
+
| `format_meal_plan(recommendation)` | dict | str | 格式化为可读文本 |
|
| 464 |
+
|
| 465 |
+
**输出 schema:**
|
| 466 |
+
```json
|
| 467 |
+
{
|
| 468 |
+
"date": "2026-06-09",
|
| 469 |
+
"trimester": "孕中期",
|
| 470 |
+
"preference": "想吃清淡的",
|
| 471 |
+
"focus": "补充蛋白质、钙、铁",
|
| 472 |
+
"meals": {"早餐": "...", "午餐": "...", "晚餐": "...", "加餐": "..."},
|
| 473 |
+
"tips": ["🌿 ...", "💡 ..."]
|
| 474 |
+
}
|
| 475 |
+
```
|
| 476 |
+
|
| 477 |
+
**当前方案:** 随机模板推荐(从 `config.MEAL_TEMPLATES` 中随机选取)
|
| 478 |
+
**后续方案:** MiniCPM-o 4.5 AI 对话推荐
|
| 479 |
+
|
| 480 |
+
> **批注:** 当前为随机模板推荐。后续对接 AI 对话推荐时需保持 `get_recommendation` 签名,内部逻辑可任意替换。
|
| 481 |
+
|
| 482 |
+
---
|
| 483 |
+
|
| 484 |
+
### 8.3 AI 对话数据提取 — `modules/diet_extractor.py`
|
| 485 |
+
|
| 486 |
+
**类:** `DietExtractor`(全静态方法)
|
| 487 |
+
|
| 488 |
+
| 方法 | 输入 | 输出 | 说明 |
|
| 489 |
+
|------|------|------|------|
|
| 490 |
+
| `extract_all(text)` | str | dict | 正则提取所有结构化数据 |
|
| 491 |
+
| `fallback_extract_diet(text)` | str | dict\|None | 关键词 fallback 提取饮食 |
|
| 492 |
+
| `fallback_extract_thinking(text)` | str | dict\|None | 关键词 fallback 提取思考 |
|
| 493 |
+
| `robust_extract(text)` | str | dict | 正则优先 → fallback 兜底 |
|
| 494 |
+
|
| 495 |
+
**extract_all 返回 schema:**
|
| 496 |
+
```json
|
| 497 |
+
{
|
| 498 |
+
"diets": [{"meals": {"早餐": "...", "午餐": "..."}, "日期": "...", "记录人": "...", "备注": "..."}],
|
| 499 |
+
"recipes": [{"菜名": "...", "制作人": "...", "难度": "...", "食材": "...", "备注": "..."}],
|
| 500 |
+
"preferences": [{"人员": "...", "类型": "偏好/忌口/过敏", "内容": "..."}],
|
| 501 |
+
"weights": [{"日期": "...", "体重": "...", "记录人": "..."}],
|
| 502 |
+
"memories": [{"类型": "关系/事件/日常", "内容": "..."}],
|
| 503 |
+
"thinking": {"当前步骤": "...", "下一步": "..."}
|
| 504 |
+
}
|
| 505 |
+
```
|
| 506 |
+
|
| 507 |
+
**Markdown 提取标记格式**(需在 system prompt 中告知模型使用):
|
| 508 |
+
```
|
| 509 |
+
[EXTRACT_DIET]...[/EXTRACT_DIET]
|
| 510 |
+
[EXTRACT_RECIPE]...[/EXTRACT_RECIPE]
|
| 511 |
+
[EXTRACT_PREFERENCE]...[/EXTRACT_PREFERENCE]
|
| 512 |
+
[EXTRACT_WEIGHT]...[/EXTRACT_WEIGHT]
|
| 513 |
+
[EXTRACT_MEMORY]...[/EXTRACT_MEMORY]
|
| 514 |
+
[THINKING]...[/THINKING]
|
| 515 |
+
```
|
| 516 |
+
|
| 517 |
+
**辅助函数:** `get_extract_prompt(date_str=None) → str` — 返回含日期占位符的 System Prompt 模板
|
| 518 |
+
|
| 519 |
+
> **批注:** ________
|
| 520 |
+
|
| 521 |
+
---
|
| 522 |
+
|
| 523 |
+
### 8.4 饮食记录存储 — `modules/diet_logger.py`
|
| 524 |
+
|
| 525 |
+
**类:** `DietLogger`
|
| 526 |
+
|
| 527 |
+
| 方法 | 输入 | 输出 | 说明 |
|
| 528 |
+
|------|------|------|------|
|
| 529 |
+
| `add_record(member_name, member_relation, date, meals, mood, notes)` | str, str, str, dict, str, str | (record, md_path) | JSON + MD 双写 |
|
| 530 |
+
| `get_recent_records(days=7)` | int | list[dict] | 获取近 N 天记录 |
|
| 531 |
+
| `get_all_markdown_files()` | — | list[Path] | 所有 MD 日志文件 |
|
| 532 |
+
| `parse_diet_record(text) — static` | str | dict\|None | [DIET_RECORD] 标记解析 |
|
| 533 |
+
|
| 534 |
+
**add_record 返回的 record schema:**
|
| 535 |
+
```json
|
| 536 |
+
{
|
| 537 |
+
"id": "a1b2c3d4",
|
| 538 |
+
"member_name": "小红",
|
| 539 |
+
"member_relation": "孕妇",
|
| 540 |
+
"date": "2026-06-09",
|
| 541 |
+
"meals": {"早餐": "燕麦粥+坚果", "午餐": "清蒸鱼+米饭"},
|
| 542 |
+
"mood": "挺好",
|
| 543 |
+
"notes": "今天胃口不错",
|
| 544 |
+
"extensions": {},
|
| 545 |
+
"created_at": "2026-06-09T12:00:00"
|
| 546 |
+
}
|
| 547 |
+
```
|
| 548 |
+
|
| 549 |
+
**双写机制:**
|
| 550 |
+
1. `data/diet_logs.json` — 结构化 JSON(所有记录累积)
|
| 551 |
+
2. `data/logs/饮食日志_YYYY-MM-DD.md` — 每日可读 Markdown
|
| 552 |
+
|
| 553 |
+
> **批注:** schema 已加版本号,`extensions: {}` 字段可向后兼容扩展。
|
| 554 |
+
|
| 555 |
+
---
|
| 556 |
+
|
| 557 |
+
### 8.5 家庭信息管理 — `modules/family_manager.py`
|
| 558 |
+
|
| 559 |
+
三个管理器类,全部使用 `@classmethod`:
|
| 560 |
+
|
| 561 |
+
#### RecipeManager — 家庭菜谱
|
| 562 |
+
| 方法 | 说明 |
|
| 563 |
+
|------|------|
|
| 564 |
+
| `load_all()` → `list[dict]` | 读取所有菜谱 |
|
| 565 |
+
| `add_recipe(name, cook, difficulty, ingredients, notes)` → `str` | 添加菜谱 |
|
| 566 |
+
| `get_names()` → `list[str]` | 所有菜名 |
|
| 567 |
+
| `format_for_prompt()` → `str` | System Prompt 格式 |
|
| 568 |
+
|
| 569 |
+
菜谱 dict:`{"name", "cook", "difficulty", "ingredients", "notes"}`
|
| 570 |
+
|
| 571 |
+
#### PreferenceManager — 饮食偏好
|
| 572 |
+
| 方法 | 说明 |
|
| 573 |
+
|------|------|
|
| 574 |
+
| `load_all()` → `list[dict]` | 所有成员偏好 |
|
| 575 |
+
| `add_member(name, role, preferences, avoid, allergies, notes)` → `str` | 添加/更新(覆盖同名) |
|
| 576 |
+
| `format_for_prompt()` → `str` | System Prompt 格式 |
|
| 577 |
+
|
| 578 |
+
偏好 dict:`{"name", "role", "preferences", "avoid", "allergies", "notes"}`
|
| 579 |
+
|
| 580 |
+
#### MemoryManager — 家庭记忆
|
| 581 |
+
| 方法 | 说明 |
|
| 582 |
+
|------|------|
|
| 583 |
+
| `load_all()` → `dict` | 所有记忆(relationships/events/daily) |
|
| 584 |
+
| `add_event(description)` → `str` | 添加重要事件 |
|
| 585 |
+
| `add_daily(content)` → `str` | 添加日常记录 |
|
| 586 |
+
| `format_for_prompt()` → `str` | System Prompt 格式 |
|
| 587 |
+
|
| 588 |
+
#### 统一接口
|
| 589 |
+
- `load_all_family_info() → dict` — 一次性加载所有家庭信息
|
| 590 |
+
- `format_all_for_prompt() → str` — 全量 System Prompt 文本
|
| 591 |
+
|
| 592 |
+
#### 数据文件(`data/family/`)
|
| 593 |
+
| 文件 | 示例内容 |
|
| 594 |
+
|------|---------|
|
| 595 |
+
| `recipes.md` | 5 道菜谱(番茄牛腩/清蒸鲈鱼/鲫鱼豆腐汤/番茄炒蛋/红枣枸杞鸡汤) |
|
| 596 |
+
| `preferences.md` | 3 位成员(小红-孕妇/小明-丈夫/张阿姨-婆婆) |
|
| 597 |
+
| `memory.md` | 家庭关系 + 重要事件 + 日常记录 |
|
| 598 |
+
|
| 599 |
+
> **批注:** 更新 `add_member` 覆盖逻辑时注意保留已有数据。
|
| 600 |
+
|
| 601 |
+
---
|
| 602 |
+
|
| 603 |
+
### 8.6 营养标准 — `modules/nutrition_standards.py`
|
| 604 |
+
|
| 605 |
+
三个只读数据类,全部使用 `@classmethod`:
|
| 606 |
+
|
| 607 |
+
#### BMIStandards — 体重增长标准
|
| 608 |
+
| 方法 | 说明 |
|
| 609 |
+
|------|------|
|
| 610 |
+
| `get_standard(bmi_before_pregnancy)` → `dict` | 根据孕前 BMI 获取标准 |
|
| 611 |
+
| `evaluate(bmi_before_pregnancy, current_week, total_gain)` → `dict` | 评估体重增长是否在推荐范围内 |
|
| 612 |
+
| `load()` → `dict` | 兼容接口 |
|
| 613 |
+
|
| 614 |
+
4 个 BMI 分类(低体重/正常/超重/肥胖)的硬编码标准(WS/T 801-2022 表1)。
|
| 615 |
+
|
| 616 |
+
#### DietaryGuideStandards — 膳食指南
|
| 617 |
+
| 方法 | 说明 |
|
| 618 |
+
|------|------|
|
| 619 |
+
| `get_recommendations(trimester)` → `dict` | 返回指定孕期食物推荐量 |
|
| 620 |
+
| `get_core_principles()` → `list[str]` | 六条核心建议 |
|
| 621 |
+
| `format_for_prompt(trimester)` → `str` | System Prompt 格式 |
|
| 622 |
+
|
| 623 |
+
4 个阶段(备孕/孕中期/孕晚期/哺乳期)的平衡膳食宝塔数据。
|
| 624 |
+
|
| 625 |
+
#### DRIsParser — 膳食营养素参考摄入量
|
| 626 |
+
| 方法 | 说明 |
|
| 627 |
+
|------|------|
|
| 628 |
+
| `get_rni(stage)` → `dict` | 返回指定阶段 RNI 数据 |
|
| 629 |
+
| `compare_with_intake(stage, intake)` → `dict` | 对比实际摄入与 RNI |
|
| 630 |
+
| `format_comparison_table(comparisons)` → `str` | 格式化为文本对比表 |
|
| 631 |
+
|
| 632 |
+
12 种营养素的 RNI 数据(4 阶段),含分类和推荐食物源。
|
| 633 |
+
|
| 634 |
+
#### 统一接口
|
| 635 |
+
- `load_all_standards() → dict` — 加载所有标准
|
| 636 |
+
- `compile_standards_to_presets()` — 编译为 JSON 缓存
|
| 637 |
+
|
| 638 |
+
> **批注:** ________
|
| 639 |
+
|
| 640 |
+
---
|
| 641 |
+
|
| 642 |
+
### 8.7 营养分析与可视化 — `modules/nutrition_analyzer.py`
|
| 643 |
+
|
| 644 |
+
**类:** `NutritionAnalyzer`
|
| 645 |
+
|
| 646 |
+
| 方法 | 输入 | 输出 | 说明 |
|
| 647 |
+
|------|------|------|------|
|
| 648 |
+
| `analyze_diet(records)` | list[dict] | dict | 营养覆盖分析 |
|
| 649 |
+
| `generate_report_chart(analysis)` | dict | matplotlib Figure | 4 面板可视化图表 |
|
| 650 |
+
| `generate_report_text(analysis)` | dict | str | 文本格式报告 |
|
| 651 |
+
| `export_report_markdown(analysis, filename)` | dict, str | Path | 导出 Markdown 报告 |
|
| 652 |
+
|
| 653 |
+
**analyze_diet 返回 schema:**
|
| 654 |
+
```json
|
| 655 |
+
{
|
| 656 |
+
"total_days": 7,
|
| 657 |
+
"total_records": 21,
|
| 658 |
+
"meal_counts": {"早餐": 5, "午餐": 6, "晚餐": 6, "加餐": 4},
|
| 659 |
+
"food_items": ["全麦面包", "鸡蛋", ...],
|
| 660 |
+
"nutrition_coverage": {
|
| 661 |
+
"叶酸": {"matched_foods": ["菠菜"], "covered": true, ...},
|
| 662 |
+
...
|
| 663 |
+
},
|
| 664 |
+
"diversity_score": {"score": 75, "details": [...]},
|
| 665 |
+
"suggestions": ["⚠️ 以下营养素摄入不足: 铁, 钙", ...]
|
| 666 |
+
}
|
| 667 |
+
```
|
| 668 |
+
|
| 669 |
+
**图表布局(4 面板):**
|
| 670 |
+
1. 左上:营养覆盖雷达图
|
| 671 |
+
2. 右上:各餐次频率柱状图
|
| 672 |
+
3. 左下:饮食多样性评分环形图
|
| 673 |
+
4. 右下:营养建议文本框
|
| 674 |
+
|
| 675 |
+
> **批注:** **高优先级待改造** — 当前使用通用 `DEFAULT_NUTRITION_DB` 做营养覆盖分析,尚未对接 `modules/nutrition_standards.py` 的 DRIs 数据。改造时需保持 `analyze_diet(records) -> dict` 签名不变。
|
| 676 |
+
|
| 677 |
+
---
|
| 678 |
+
|
| 679 |
+
## 九、core/ — AI 核心层
|
| 680 |
+
|
| 681 |
+
### 文件路径
|
| 682 |
+
|
| 683 |
+
`core/` 目录下所有文件当前均为「空接口」状态,等待 MiniCPM-o 4.5 部署后填充。
|
| 684 |
+
|
| 685 |
+
### 9.1 `__init__.py`
|
| 686 |
+
|
| 687 |
+
空模块,标注等待 MiniCPM-o 部署。
|
| 688 |
+
|
| 689 |
+
### 9.2 `model_loader.py`
|
| 690 |
+
|
| 691 |
+
**类:** `ModelLoader`
|
| 692 |
+
|
| 693 |
+
| 方法 | 说明 |
|
| 694 |
+
|------|------|
|
| 695 |
+
| `load()` | 加载模型(`raise NotImplementedError`) |
|
| 696 |
+
| `unload()` | 卸载模型释放显存 |
|
| 697 |
+
|
| 698 |
+
### 9.3 `voice_processor.py`
|
| 699 |
+
|
| 700 |
+
**类:** `VoiceProcessor`
|
| 701 |
+
|
| 702 |
+
| 方法 | 说明 |
|
| 703 |
+
|------|------|
|
| 704 |
+
| `transcribe(audio_path)` | 语音转文字(`raise NotImplementedError`) |
|
| 705 |
+
| `extract_speaker_embedding(audio_path)` | 提取说话人声纹 embedding(`raise NotImplementedError`) |
|
| 706 |
+
|
| 707 |
+
### 9.4 `vision_processor.py`
|
| 708 |
+
|
| 709 |
+
**类:** `VisionProcessor`
|
| 710 |
+
|
| 711 |
+
| 方法 | 说明 |
|
| 712 |
+
|------|------|
|
| 713 |
+
| `analyze_frame(image_path)` | 分析单帧图像(`raise NotImplementedError`) |
|
| 714 |
+
|
| 715 |
+
### 9.5 `conversation_manager.py`
|
| 716 |
+
|
| 717 |
+
**类:** `ConversationManager`
|
| 718 |
+
|
| 719 |
+
当前已有部分骨架实现:
|
| 720 |
+
- `current_mode` / `current_speaker` / `conversation_history`
|
| 721 |
+
- `switch_mode(mode)` / `set_speaker(speaker_info)`
|
| 722 |
+
- `parse_response(response)` — 解析 `[DIET_RECORD]` 标记
|
| 723 |
+
|
| 724 |
+
待实现:
|
| 725 |
+
- `build_system_prompt()` — 构建完整系统提示词
|
| 726 |
+
|
| 727 |
+
> **批注:** ________
|
| 728 |
+
|
| 729 |
+
---
|
| 730 |
+
|
| 731 |
+
## 十、ui/ — 表现层
|
| 732 |
+
|
| 733 |
+
### 文件路径
|
| 734 |
+
|
| 735 |
+
`ui/app_builder.py`
|
| 736 |
+
|
| 737 |
+
### 职责
|
| 738 |
+
|
| 739 |
+
基于 Gradio 6.x 构建 4 个子页面的 Web 界面。
|
| 740 |
+
|
| 741 |
+
### 10.1 主入口
|
| 742 |
+
|
| 743 |
+
`create_app(loop=None)` — 创建 Gradio Blocks 应用。
|
| 744 |
+
|
| 745 |
+
设计要点:
|
| 746 |
+
- 只使用 **一个** `@gr.render` 包裹所有 Tab,避免多个 `@gr.render` 并发触发导致 `RuntimeError`
|
| 747 |
+
- 语言切换通过 `gr.State(value="zh")` + `lang_selector` Radio 实现
|
| 748 |
+
- 每个 Tab 的标题通过 `t()` 函数实现中英文切换
|
| 749 |
+
|
| 750 |
+
### 10.2 页面结构
|
| 751 |
+
|
| 752 |
+
| Tab | ID | 功能说明 |
|
| 753 |
+
|-----|-----|---------|
|
| 754 |
+
| 🏠 首页 | `tab_home` | 语音启动按钮 + AI 思考状态 + 5 个信息卡片 + 最近饮食记录表格 |
|
| 755 |
+
| 👨👩👧👦 家庭饮食习惯 | `tab_family` | 3 个子 Tab:偏好设置 / 菜谱管理 / 家庭记忆 |
|
| 756 |
+
| 📊 三天总结 | `tab_summary` | 孕期阶段选择 + 分析结果 + 菜单改进建议 |
|
| 757 |
+
| 📈 营养报告 | `tab_report` | 天数滑块 + 文本报告 + 可视化图表 + 导出 Markdown |
|
| 758 |
+
|
| 759 |
+
### 10.3 全局实例
|
| 760 |
+
|
| 761 |
+
```python
|
| 762 |
+
voiceprint_mgr = VoiceprintManager()
|
| 763 |
+
meal_recommender = MealRecommender()
|
| 764 |
+
diet_logger = DietLogger()
|
| 765 |
+
nutrition_analyzer = NutritionAnalyzer()
|
| 766 |
+
```
|
| 767 |
+
|
| 768 |
+
> **批注:** ________
|
| 769 |
+
|
| 770 |
+
---
|
| 771 |
+
|
| 772 |
+
## 十一、data/ — 数据存储
|
| 773 |
+
|
| 774 |
+
### 11.1 数据文件一览
|
| 775 |
+
|
| 776 |
+
| 文件/目录 | 格式 | 内容 | 读模块 | 写模块 |
|
| 777 |
+
|-----------|------|------|--------|--------|
|
| 778 |
+
| `data/diet_logs.json` | JSON | 所有饮食记录(累积) | `diet_logger`, `nutrition_analyzer` | `diet_logger` |
|
| 779 |
+
| `data/nutrition_db.json` | JSON | 10 种营养素数据 | `nutrition_analyzer` | `nutrition_analyzer` |
|
| 780 |
+
| `data/family.json` | JSON | 成员声纹数据 | `voiceprint` | `voiceprint` |
|
| 781 |
+
| `data/family/recipes.md` | Markdown | 5 道家庭菜谱 | `family_manager.RecipeManager` | `family_manager.RecipeManager` |
|
| 782 |
+
| `data/family/preferences.md` | Markdown | 3 位成员偏好 | `family_manager.PreferenceManager` | `family_manager.PreferenceManager` |
|
| 783 |
+
| `data/family/memory.md` | Markdown | 家庭关系/事件/日常 | `family_manager.MemoryManager` | `family_manager.MemoryManager` |
|
| 784 |
+
| `data/logs/饮食日志_*.md` | Markdown | 每日饮食记录 | `plugins/diet_summary` | `diet_logger` |
|
| 785 |
+
| `data/reports/营养报告_*.md` | Markdown | 营养分析报告 | 用户 | `nutrition_analyzer` |
|
| 786 |
+
| `data/presets/.daily_status.json` | JSON | 每日完成状态 | `loop.DailyStatus` | `loop.DailyStatus` |
|
| 787 |
+
| `data/presets/今日简报_*.json` | JSON | 每日简报缓存 | `loop` | `plugins/preset_writer` |
|
| 788 |
+
| `data/voices/*.wav` | WAV | 声纹注册音频 | `voiceprint` | `voiceprint` |
|
| 789 |
+
| `data/nutrition/raw/*.md` | Markdown | 三个官方营养文档 | 解析器 | 手动下载 |
|
| 790 |
+
|
| 791 |
+
### 11.2 家庭信息示例数据
|
| 792 |
+
|
| 793 |
+
**`recipes.md`**:5 道菜谱(番茄牛腩/清蒸鲈鱼/鲫鱼豆腐汤/番茄炒蛋/红枣枸杞鸡汤)
|
| 794 |
+
|
| 795 |
+
**`preferences.md`**:
|
| 796 |
+
- 小红(孕妇):爱吃水果/鱼/清淡,不吃辣/油腻,芒果过敏
|
| 797 |
+
- 小明(丈夫):爱吃肉/面食,不吃香菜
|
| 798 |
+
- 张阿姨(婆婆):会做各种汤
|
| 799 |
+
|
| 800 |
+
**`memory.md`**:
|
| 801 |
+
- 3 条家庭关系
|
| 802 |
+
- 3 条重要事件(2026-06-07 ~ 06-09)
|
| 803 |
+
- 3 条日常记录
|
| 804 |
+
|
| 805 |
+
### 11.3 状态位文件
|
| 806 |
+
|
| 807 |
+
```json
|
| 808 |
+
{
|
| 809 |
+
"2026-06-09": {
|
| 810 |
+
"summary_done": true,
|
| 811 |
+
"completed_at": "2026-06-09T00:40:53.606526"
|
| 812 |
+
}
|
| 813 |
+
}
|
| 814 |
+
```
|
| 815 |
+
|
| 816 |
+
### 11.4 营养标准原始文档
|
| 817 |
+
|
| 818 |
+
`data/nutrition/raw/` 中的三个 MD 文件:
|
| 819 |
+
1. `妊娠期妇女体重增长推荐值标准.md`(WS/T 801-2022)
|
| 820 |
+
2. `中国孕期妇女膳食指南2022图片转md版.md`
|
| 821 |
+
3. `备孕&孕早中晚三期和哺乳期的所有膳食营养素参考摄入量.md`
|
| 822 |
+
|
| 823 |
+
> **批注:** 所有 md 文件都需要我们自己写函数读取,而不是让大模型自己读。数据类的需要写全面的读取比较函数接口,文本类的需要设计好自动提取输出的函数。
|
| 824 |
+
|
| 825 |
+
---
|
| 826 |
+
|
| 827 |
+
## 十二、tests/ — 测试套件
|
| 828 |
+
|
| 829 |
+
### 12.1 测试运行器
|
| 830 |
+
|
| 831 |
+
**文件:** `tests/run_tests.py`
|
| 832 |
+
|
| 833 |
+
无需 pytest,内置 `TestRunner` 类:
|
| 834 |
+
- 自动发现 `test_*.py` 中的 `Test*` 类和方法
|
| 835 |
+
- 每个测试方法独立实例(setup/teardown 隔离)
|
| 836 |
+
- 生成 HTML 和 JSON 两种报告格式
|
| 837 |
+
|
| 838 |
+
运行方式:
|
| 839 |
+
```bash
|
| 840 |
+
python tests/run_tests.py
|
| 841 |
+
```
|
| 842 |
+
|
| 843 |
+
### 12.2 测试覆盖
|
| 844 |
+
|
| 845 |
+
| 测试文件 | 覆盖内容 | 状态 |
|
| 846 |
+
|---------|---------|------|
|
| 847 |
+
| `test_diet_extractor.py` | 正则解析 + fallback 提取(18 个测试用例) | ✅ |
|
| 848 |
+
| `test_family_manager.py` | 菜谱/偏好/记忆 CRUD | ✅ |
|
| 849 |
+
| `test_nutrition_standards.py` | BMI/膳食指南/DRIs 数据 | ✅ |
|
| 850 |
+
| `test_loop.py` | 循环执行测试 | 待确认 |
|
| 851 |
+
| `test_plugins.py` | 插件集成测试 | 待确认 |
|
| 852 |
+
|
| 853 |
+
> **批注:** ________
|
| 854 |
+
|
| 855 |
+
---
|
| 856 |
+
|
| 857 |
+
## 十三、配置文件
|
| 858 |
+
|
| 859 |
+
### 13.1 `.gitignore`
|
| 860 |
+
|
| 861 |
+
```gitignore
|
| 862 |
+
.vscode/ # IDE 配置
|
| 863 |
+
__pycache__/ # Python 缓存
|
| 864 |
+
*.pyc / *.pyo
|
| 865 |
+
*.egg-info/ / dist/ / build/
|
| 866 |
+
*.gguf / models/ # 模型文件
|
| 867 |
+
llamacpp/
|
| 868 |
+
*.wav / *.mp3 # 音频文件
|
| 869 |
+
.env / .venv/ / venv/
|
| 870 |
+
*.tmp / *.log / debug*.txt
|
| 871 |
+
_check_hf.py / _download_models.py
|
| 872 |
+
```
|
| 873 |
+
|
| 874 |
+
### 13.2 `requirements.txt`
|
| 875 |
+
|
| 876 |
+
```
|
| 877 |
+
gradio>=6.0.0
|
| 878 |
+
modal>=0.60.0
|
| 879 |
+
numpy>=1.24.0
|
| 880 |
+
matplotlib>=3.7.0
|
| 881 |
+
pandas>=2.0.0
|
| 882 |
+
soundfile>=0.12.0
|
| 883 |
+
pillow>=10.0.0
|
| 884 |
+
```
|
| 885 |
+
|
| 886 |
+
> **批注:** 依赖相对精简,核心依赖为 Gradio 6.x + NumPy + Matplotlib。后续集成 MiniCPM-o 后可能需要追加 PyTorch / Transformers 等。
|
| 887 |
+
|
| 888 |
+
---
|
| 889 |
+
|
| 890 |
+
## 十四、协作规范与 Git 工作流
|
| 891 |
+
|
| 892 |
+
### 14.1 并行 Cline 分工建议
|
| 893 |
+
|
| 894 |
+
```
|
| 895 |
+
Cline A: Modules 强化(营养分析对接 DRIs、菜谱推荐 AI 化)
|
| 896 |
+
Cline B: UI 界面优化(Gradio 前端增强、报告模板美化)
|
| 897 |
+
Cline C: 声纹升级(Whisper encoder 替换频谱特征)
|
| 898 |
+
Cline D: 数据处理与插件增强(diet_extractor fallback、新插件)
|
| 899 |
+
```
|
| 900 |
+
|
| 901 |
+
### 14.2 关键约定
|
| 902 |
+
|
| 903 |
+
1. **改接口前先 grep**:用 `search_files` 搜索方法名找到所有调用方
|
| 904 |
+
2. **config.py 修改需沟通**:新增常量追加到同类型区域,不删改已有常量名
|
| 905 |
+
3. **数据文件向后兼容**:新增字段优先用 `extensions: {}` 或新增可选 key,不删除已有 key
|
| 906 |
+
4. **测试保持通过**:`python tests/run_tests.py` 零失败
|
| 907 |
+
5. **README 同步更新**:接口签名变更 → 更新 README 对应章节
|
| 908 |
+
|
| 909 |
+
### 14.3 Git 工作流
|
| 910 |
+
|
| 911 |
+
```bash
|
| 912 |
+
# 工作前
|
| 913 |
+
git pull --rebase origin main
|
| 914 |
+
|
| 915 |
+
# 开发中:每完成一个独立功能点就提交
|
| 916 |
+
git add -A
|
| 917 |
+
git commit -m "feat/fix/test/refactor/docs/chore: 描述"
|
| 918 |
+
|
| 919 |
+
# 推送前再次 pull
|
| 920 |
+
git pull --rebase origin main
|
| 921 |
+
|
| 922 |
+
# 测试通过后推送
|
| 923 |
+
python tests/run_tests.py
|
| 924 |
+
git push origin main
|
| 925 |
+
```
|
| 926 |
+
|
| 927 |
+
### 14.4 Commit Message 规范
|
| 928 |
+
|
| 929 |
+
```
|
| 930 |
+
feat: 新功能/新模块/新接口
|
| 931 |
+
fix: 修复 bug
|
| 932 |
+
test: 添加或修改测试
|
| 933 |
+
refactor: 重构(不改功能)
|
| 934 |
+
docs: 文档更新
|
| 935 |
+
chore: 配置/依赖/路径调整
|
| 936 |
+
```
|
| 937 |
+
|
| 938 |
+
### 14.5 冲突处理策略
|
| 939 |
+
|
| 940 |
+
| 冲突类型 | 处理方式 |
|
| 941 |
+
|---------|---------|
|
| 942 |
+
| `config.py` 常量冲突 | 取并集,双方新增常量都保留 |
|
| 943 |
+
| 同函数不同实现 | 保留逻辑更完整的一方,另一方的无冲突改动合并到合适位置 |
|
| 944 |
+
| 数据文件(JSON/MD)冲突 | 保留两个版本共有的条目 + 各自独有条目(去重) |
|
| 945 |
+
| 测试文件冲突 | **取并集**:保留所有测试用例 |
|
| 946 |
+
| 本架构说明书冲突 | **取行数更长的一方**,手动整合 |
|
| 947 |
+
| `.gitignore` 冲突 | 取并集 |
|
| 948 |
+
|
| 949 |
+
> **批注:** ________
|
| 950 |
+
|
| 951 |
+
---
|
| 952 |
+
|
| 953 |
+
## 十五、当前运行状态
|
| 954 |
+
|
| 955 |
+
| 项目 | 状态 |
|
| 956 |
+
|------|------|
|
| 957 |
+
| Gradio 6.16.0 运行端口 | `localhost:7860` |
|
| 958 |
+
| 4 个 Tab | 全部正常加载 |
|
| 959 |
+
| 声纹识别 | 可用(频谱统计 baseline) |
|
| 960 |
+
| 菜品推荐 | 可用(随机模板) |
|
| 961 |
+
| 饮食记录 | 可用(JSON + MD 双写) |
|
| 962 |
+
| 营养分析 | 可用(内置数据库) |
|
| 963 |
+
| 每日 Loop | 可用(状��机驱动) |
|
| 964 |
+
| core/ 层 | 空接口(等待 MiniCPM-o) |
|
| 965 |
+
| Modal 部署 | 代码就绪,CUDA 编译中 |
|
| 966 |
+
| Git 远程 | HuggingFace Spaces `build-small-hackathon/PregoPal` |
|
| 967 |
+
|
| 968 |
+
---
|
| 969 |
+
|
| 970 |
+
## 十六、待办与已知问题
|
| 971 |
+
|
| 972 |
+
### 16.1 高优先级
|
| 973 |
+
|
| 974 |
+
- [ ] **营养分析对接 DRIs**:`NutritionAnalyzer` 当前使用内置 `DEFAULT_NUTRITION_DB`,需替换为 `DRIsParser` 的中国官方标准
|
| 975 |
+
- [ ] **菜谱推荐 AI 化**:当前为随机模板,后续需对接 MiniCPM-o 对话推荐
|
| 976 |
+
|
| 977 |
+
### 16.2 中优先级
|
| 978 |
+
|
| 979 |
+
- [ ] **core/ 层实现**:MiniCPM-o 4.5 模型加载、语音/视觉处理
|
| 980 |
+
- [ ] **声纹升级**:从频谱统计特征升级到 Whisper encoder embedding
|
| 981 |
+
- [ ] **INTERACT 状态激活**:当前 `_state_interact()` 直接返回 `day_ended`,需对接 Gradio 事件队列
|
| 982 |
+
|
| 983 |
+
### 16.3 低优先级
|
| 984 |
+
|
| 985 |
+
- [ ] **测试补全**:`test_loop.py` 和 `test_plugins.py` 需确认状态
|
| 986 |
+
- [ ] **Modal 部署修复**:`@modal.concurrent` 兼容性、`build_server_args` 参数命名、多模态端点验证
|
| 987 |
+
- [ ] **data/nutrition/raw/ 中 MD 文件的解析**:当前营养标准数据为硬编码,后续可从原始 MD 文件中自动解析
|
| 988 |
+
|
| 989 |
+
> **批注:** ________
|
| 990 |
+
|
| 991 |
+
---
|
| 992 |
+
|
| 993 |
+
*文档生成时间:2026-06-09 | 由 PregoPal 项目架构说明书 v1 生成*
|
| 994 |
+
|
| 995 |
+
*本文档各小节批注区中的原始文字为项目负责人的重要参考依据,所有 Cline 在修改对应模块前应先阅读对应批注。*
|