J.B-Lin commited on
Commit
bfe4514
·
2 Parent(s): 35f8467e179fe6

Merge branch 'main' of https://huggingface.co/spaces/build-small-hackathon/PregoPal

Browse files
Files changed (1) hide show
  1. 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 在修改对应模块前应先阅读对应批注。*