PregoPal / README.md
J.B-Lin
merge: 解决合并冲突
6cce707
|
Raw
History Blame Contribute Delete
27.8 kB

A newer version of the Gradio SDK is available: 6.19.0

Upgrade
metadata
title: PregoPal
emoji: 📈
colorFrom: pink
colorTo: green
sdk: gradio
sdk_version: 6.16.0
python_version: '3.13'
app_file: app.py
pinned: false
license: mit
short_description: Voice-based Pregnant Meal & Nutrition Tracker

PregoPal — 孕期陪护 AI 助手

🔴 【Cline 并行协作核心文档】

本 README 是所有并行 Cline 同步接口规范、数据结构、协作约定的唯一全局文档。 任何 Cline 不得随意简化、删除或修改本 README 的结构性内容(包括但不限于:模块接口签名、数据 Schema、协作流程、冲突策略)。

修改原则:

  1. 改接口签名 → 必须同步更新对应章节
  2. 新增模块 → 必须在架构图/模块表中添加条目
  3. 新增配置 → 必须在 config 章节追加说明
  4. 数据格式变更 → 更新 Schema 表

每次 commit 前请检查:本 README 是否仍反映项目最新状态。


1. 项目概述

PregoPal 是一款面向孕期家庭的 AI 陪护工具,支持:

功能 状态 负责模块
🎤 声纹识别与家庭成员区分 基线可用 modules/voiceprint.py
🍽️ 今日菜品推荐(营养+家庭能力) 基线可用 modules/meal_recommender.py
📝 饮食记录储存(JSON + Markdown) 已实现 modules/diet_logger.py + modules/diet_extractor.py
📊 营养分析与可视化报告 基线可用 modules/nutrition_analyzer.py + modules/nutrition_standards.py
🤖 多模态大模型后端(MiniCPM-o 4.5) ✅ 已部署 Modal modal_deploy/deploy.py + modal_deploy/client.py

技术栈: Python 3.13 · Gradio 6.16 · MiniCPM-o 4.5 · Modal · Matplotlib · Pandas

大模型部署状态:✅ 已完成,详见 第11节


2. 架构总览

app.py                          ← Gradio 薄入口
├── ui/app_builder.py           ← 前端 Tab 布局(3 Tab: 首页/家庭/营养报告)
├── loop.py                     ← 每日自动分析循环(状态机)
│   ├── plugins/base.py         ← 插件基类(LoopPlugin, PluginRegistry, LoopContext)
│   ├── plugins/family_quiz.py  ← 家庭问卷插件(菜谱/体重检查)
│   ├── plugins/diet_summary.py ← 昨日饮食总结
│   ├── plugins/weight_check.py ← 体重检查插件
│   ├── plugins/family_memory.py← 家庭记忆处理
│   ├── plugins/dri_analysis.py ← DRIs 营养对比分析
│   ├── plugins/briefing_generator.py ← 今日简报生成
│   ├── plugins/three_day_summary.py  ← 三天深度缺失分析(数据由营养报告Tab调用)
│   └── plugins/preset_writer.py      ← 预设/缓存写入
├── modules/                    ← 核心业务逻辑(纯函数/无状态)
│   ├── voiceprint.py           ← 声纹识别
│   ├── meal_recommender.py     ← 菜品推荐
│   ├── diet_extractor.py       ← AI 回复数据提取
│   ├── diet_logger.py          ← 饮食记录存储
│   ├── family_manager.py       ← 家庭信息管理
│   ├── nutrition_standards.py  ← 中国官方营养标准
│   ├── nutrition_analyzer.py   ← 营养分析与可视化
│   └── core/                   ← 核心能力层
│       ├── model_loader.py     ← MiniCPM-o 模型加载
│       ├── voice_processor.py  ← 语音处理
│       ├── vision_processor.py ← 视觉处理
│       └── conversation_manager.py ← 对话管理
├── config.py                   ← 全局配置(路径/常量/数据模板)
├── utils.py                    ← 工具函数(CSS/翻译/HTML渲染/营养报告+三天缺失分析)
├── modal_deploy/               ← Modal 部署层
│   ├── deploy.py               ← Modal ASGI 部署入口(llama-server → FastAPI)
│   └── client.py               ← Python API 客户端(chat / chat_with_image / embed)
└── data/                       ← 持久化存储
    ├── diet_logs.json          ← 结构化饮食记录 JSON
    ├── nutrition_db.json       ← 营养数据库 JSON
    ├── family.json             ← 家庭成员声纹 JSON
    ├── family/                 ← 家庭信息 Markdown(recipes/preferences/memory)
    ├── logs/                   ← 每日饮食日志 Markdown
    ├── presets/                ← 预设/缓存 + .daily_status.json
    ├── reports/                ← 营养报告 Markdown
    ├── voices/                 ← 声纹音频文件
    └── nutrition/              ← 营养标准原始文档

3. 核心模块接口说明

约定:模块间通过函数调用传递数据。所有返回 dict 的接口,其 key 约定在下方列出。 修改清单:改任何模块的输入/输出签名时,必须同步更新本 README 对应条目。

3.1 声纹识别 — modules/voiceprint.py

类:VoiceprintManager

方法 输入 输出 说明
register_member(name, relation, audio_path) name: str, relation: str, audio_path: str (Path) (member_info: dict|None, msg: str) 注册新成员声纹,返回 {"id","name","relation","registered_at","audio_path","features"}
identify_speaker(audio_path) audio_path: str (Path) (member_info: dict|None, msg: str) 识别说话人,需声纹库非空
get_members_list() str (格式化文本) 获取已注册成员列表
delete_member(member_id) member_id: str str (结果消息) 删除指定成员

依赖:

  • VOICE_DIRdata/voices/)从 config.py 导入
  • FAMILY_FILEdata/family.json)— JSON 格式 {"members": [...], "voiceprints": {...}}
  • config.VOICEPRINT_SIMILARITY_THRESHOLD(默认 0.7)

内部数据结构data/family.json):

{
  "members": [
    {
      "id": "abc12345",
      "name": "小红",
      "relation": "孕妇",
      "registered_at": "2026-06-09T07:00:00",
      "audio_path": "data/voices/abc12345.wav",
      "features": {"mean": 0.01, "std": 0.05, "max": 0.5, "min": -0.5, "zero_crossing_rate": 0.3, "energy": 0.001, "duration": 3.5}
    }
  ],
  "voiceprints": {
    "abc12345": {"mean": 0.01, ...}
  }
}

⚠️ 风险等级:中 — 当前使用频谱统计特征(baseline),后续升级 Whisper encoder 需保持 register_member/identify_speaker 签名不变。


3.2 菜品推荐 — modules/meal_recommender.py

类:MealRecommender

方法 输入 输出 说明
get_recommendation(preference, trimester, restrictions) preference: str="", trimester: str="孕中期", restrictions: str="" dict 返回今日推荐食谱
format_meal_plan(recommendation) recommendation: dict(上一个方法的返回值) str 格式化为可读文本

输出 dict schemaget_recommendation 返回):

{
  "date": "2026-06-09",
  "trimester": "孕中期",
  "preference": "想吃清淡的",
  "focus": "补充蛋白质、钙、铁",
  "meals": {
    "早餐": "小米粥+包子+煮鸡蛋",
    "午餐": "番茄牛腩+杂粮饭+凉拌黄瓜",
    "晚餐": "蒸蛋羹+小米粥+炒青菜",
    "加餐": "酸奶+坚果"
  },
  "tips": ["🌿 孕中期建议:...", "💡 建议每天饮水..."]
}

依赖:

  • config.MEAL_TEMPLATES(4 餐次 × 4 选项的食谱模板 dict)
  • config.TRIMESTER_ADJUSTMENTS(各孕期阶段的 focus/avoid 建议)
  • config.TRIMESTER_TIPS(各孕期阶段的饮食提示字符串)

⚠️ 风险等级:中 — 当前为随机模板推荐。后续对接 AI 对话推荐时需保持 get_recommendation 签名,内部逻辑可任意替换。


3.3 AI 对话数据提取 — modules/diet_extractor.py

类:DietExtractor(全静态方法)

方法 输入 输出 说明
extract_all(text) text: str (AI 回复全文) dict 正则提取所有结构化数据
fallback_extract_diet(text) text: str dict|None 关键词 fallback 提取饮食
fallback_extract_thinking(text) text: str dict|None 关键词 fallback 提取思考
robust_extract(text) text: str dict 正则优先 → fallback 兜底

extract_all 返回 dict schema:

{
  "diets": [
    {
      "meals": {"早餐": "全麦面包+鸡蛋+牛奶", "午餐": "清蒸鱼", "晚餐": "", "加餐": ""},
      "日期": "2026-06-09",
      "记录人": "孕妇",
      "备注": "孕妇吃了"
    }
  ],
  "recipes": [{"菜名": "清蒸鱼", "制作人": "丈夫", "难度": "简单", "食材": "鱼、姜", "备注": ""}],
  "preferences": [{"人员": "孕妇", "类型": "偏好", "内容": "爱吃酸"}],
  "weights": [{"日期": "2026-06-09", "体重": "65", "记录人": "孕妇自己"}],
  "memories": [{"类型": "事件", "内容": "婆婆今天来家里"}],
  "thinking": {"当前步骤": "记录饮食", "下一步": "分析营养"}  // or None
}

Markdown 提取标记格式(AI 回复中需含):

[EXTRACT_DIET]...[/EXTRACT_DIET]
[EXTRACT_RECIPE]...[/EXTRACT_RECIPE]
[EXTRACT_PREFERENCE]...[/EXTRACT_PREFERENCE]
[EXTRACT_WEIGHT]...[/EXTRACT_WEIGHT]
[EXTRACT_MEMORY]...[/EXTRACT_MEMORY]
[THINKING]...[/THINKING]

辅助函数:

  • get_extract_prompt(date_str=None) -> str — 返回含日期占位符的 System Prompt 模板

风险等级:低 — 接口稳定,已存在完整测试(tests/test_diet_extractor.py)。


3.4 饮食记录存储 — modules/diet_logger.py

类:DietLogger

方法 输入 输出 说明
add_record(member_name, member_relation, date, meals, mood, notes) member_name: str, member_relation: str, date: str (ISO), meals: dict, mood: str="", notes: str="" (record: dict, md_path: Path) 添加记录 → JSON + MD 双写
get_recent_records(days=7) days: int list[dict] 获取近 N 天记录
get_all_markdown_files() list[Path] 所有 MD 日志文件
parse_diet_record(text) — static text: str dict|None [DIET_RECORD] 标记解析(后续 AI 版本用)

add_record 返回的 record dict:

{
  "id": "a1b2c3d4",
  "member_name": "小红",
  "member_relation": "孕妇",
  "date": "2026-06-09",
  "meals": {"早餐": "燕麦粥+坚果", "午餐": "清蒸鱼+米饭"},
  "mood": "挺好",
  "notes": "今天胃口不错",
  "extensions": {},
  "created_at": "2026-06-09T12:00:00"
}

依赖:

  • config.DIET_LOG_FILEdata/diet_logs.json)— JSON 格式
  • config.LOGS_DIRdata/logs/)— Markdown 日志目录
  • config.DIET_LOG_SCHEMA_VERSION(当前 "1.0")

Markdown 日志文件格式data/logs/饮食日志_YYYY-MM-DD.md):

# 🥗 孕期饮食日志

## 📋 基本信息
- **日期**: 2026-06-09
- **记录人**: 孕妇 - 小红
- **记录时间**: 2026-06-09T12:00:00

## 🍽️ 今日饮食记录

### 早餐
- 燕麦粥+坚果
... (output truncated) ...

3.5 家庭信息管理 — modules/family_manager.py

类:RecipeManager, PreferenceManager, MemoryManager

方法 输入 输出 说明
RecipeManager.add_recipe(name, maker, difficulty, ingredients, notes) 字段见函数签名 str (结果消息) 添加菜谱
RecipeManager.load_all() list[dict] 加载所有菜谱
PreferenceManager.add_preference(member, ptype, content) member/type/content: str str 添加偏好/忌口
PreferenceManager.load_all() list[dict] 加载所有偏好
MemoryManager.add_memory(mtype, content) mtype/content: str str 添加家庭记事
MemoryManager.load_all() list[dict] 加载所有记忆

依赖:

  • config.FAMILY_DIRdata/family/)— 目录包含 recipes.md / preferences.md / memory.md

3.6 营养标准 — modules/nutrition_standards.py

类:DRIsParser(全静态方法)

方法 输入 输出 说明
get_dri_for_trimester(trimester) trimester: str(孕早期/中期/晚期) dict 获取对应孕期 DRIs 推荐值
calculate_bmi(weight, height) weight: float (kg), height: float (cm) float BMI 计算
get_weight_gain_goal(pre_bmi, trimester) pre_bmi: float, trimester: str dict 体重增长建议
get_dietary_guidelines(trimester) trimester: str dict 膳食指南建议
assess_nutrient_intake(actual, dri) actual: dict (mg), dri: dict (mg) dict 实际 vs 推荐对比

输出示例:

DRIsParser.get_dri_for_trimester("孕中期")
# 返回:
{
  "能量": {"推荐摄入量_kcal": 2100, ...},
  "蛋白质": {"推荐摄入量_g": 70, ...},
  "钙": {"推荐摄入量_mg": 1000, ...},
  "铁": {"推荐摄入量_mg": 24, ...},
  "叶酸": {"推荐摄入量_ug": 600, ...},
  "维生素D": {"推荐摄入量_ug": 10, ...},
  "DHA": {"适宜摄入量_mg": 200, ...}
}

风险等级:低 — 接口清晰。如需调整推荐值数值,修改 DRIsParser 内部常量即可。


3.7 营养分析与可视化 — modules/nutrition_analyzer.py

类:NutritionAnalyzer

方法 输入 输出 说明
analyze_diet(records) records: list[dict] (diet_logger 格式) dict 营养覆盖分析
generate_report_chart(analysis) analysis: dict(上一个方法返回) matplotlib.figure.Figure 4 面板可视化图表
generate_report_text(analysis) analysis: dict str 文本格式报告
export_report_markdown(analysis, filename) analysis: dict, filename: str|None Path 导出 Markdown 报告

analyze_diet 返回 dict schema(最新版,含 score 综合评分):

{
  "score": 72,
  "total_days": 7,
  "total_records": 21,
  "meal_counts": {"早餐": 5, "午餐": 6, "晚餐": 6, "加餐": 4},
  "food_items": ["全麦面包", "鸡蛋", "牛奶", "清蒸鱼", ...],
  "nutrition_coverage": {
    "叶酸": {
      "matched_foods": ["菠菜"],
      "covered": true,
      "recommended_foods": ["菠菜", "西兰花", "芦笋"],
      "benefit": "预防胎儿神经管畸形",
      "daily_recommend": "0.4",
      "unit": "mg"
    },
    ...
  },
  "diversity_score": 75,
  "diversity_details": ["[OK] 早餐: 6/7天 (86%)", ...],
  "suggestions": ["⚠️ 以下营养素摄入不足: 铁, 钙", ...]
}

图表输出generate_report_chart — 保留 matplotlib 版本):

  1. 左上:营养覆盖雷达图(最多 8 种营养素)
  2. 右上:各餐次频率柱状图
  3. 左下:饮食多样性评分环形图
  4. 右下:营养建议文本框

依赖:

  • config.NUTRITION_DB_FILEdata/nutrition_db.json)— 可自定义
  • config.DEFAULT_NUTRITION_DB(10 种营养素的内置推荐)
  • config.REPORTS_DIRdata/reports/
  • utils.setup_chinese_font() — matplotlib 中文字体

🔴 风险等级:高(待改造) — 当前使用通用 DEFAULT_NUTRITION_DB 做营养覆盖分析,尚未对接 modules/nutrition_standards.py 的 DRIs 数据。改造时需:

  1. _calculate_nutrition_coverage 中的 self.nutrition_db 替换为 DRIsParser.get_dri_for_trimester()
  2. 保持 analyze_diet(records) -> dict 签名不变
  3. 保持 nutrition_coverage 的 dict key 不变(nutrient name 为 key)

3.8 插件基类 — plugins/base.py

核心类型(供所有插件和 loop 使用):

# 阶段枚举
class LoopStage(Enum):
    FAMILY_QUIZ = "family_quiz"
    SUMMARIZE = "summarize"
    ANALYZE = "analyze"
    BRIEF = "brief"
    THREE_DAY = "three_day"
    CONSOLIDATE = "consolidate"

# 上下文容器(插件间共享)
@dataclass
class LoopContext:
    briefing: dict = {}           # 累积的简报数据
    weight_data: dict = {}
    diet_records: list = []
    family_recipes: list = []     # family_manager.RecipeManager.load_all() 格式
    family_memory: dict = {}      # family_manager.MemoryManager.load_all() 格式
    analysis_results: dict = {}
    errors: list = []

# 插件结果
@dataclass
class PluginResult:
    success: bool = True
    data: dict = {}
    message: str = ""

# 插件基类
class LoopPlugin(ABC):
    @abstractmethod
    def stage(self) -> LoopStage: ...
    @abstractmethod
    def name(self) -> str: ...
    @abstractmethod
    async def run(self, ctx: LoopContext) -> PluginResult: ...

风险等级:低 — 核心架构稳定。新增插件:实现 LoopPlugin,在 loop.py_register_default_plugins 中注册即可。


4. 插件管线一览

插件名 阶段 文件 职责
FamilyRecipeQuizPlugin FAMILY_QUIZ plugins/family_quiz.py 检查是否需要询问家庭菜谱
WeightQuizPlugin FAMILY_QUIZ plugins/family_quiz.py 检查是否需要询问体重
DietSummaryPlugin SUMMARIZE plugins/diet_summary.py 读取昨日饮食日志
WeightCheckPlugin SUMMARIZE plugins/weight_check.py 体重变化分析
FamilyMemoryPlugin SUMMARIZE plugins/family_memory.py 提取家庭记忆
DRIAnalysisPlugin ANALYZE plugins/dri_analysis.py DRIs 营养对比
BriefingGeneratorPlugin BRIEF plugins/briefing_generator.py 汇总生成今日简报
ThreeDaySummaryPlugin THREE_DAY plugins/three_day_summary.py 三天深度缺失分析(被营养报告Tab调用)
PresetWriterPlugin CONSOLIDATE plugins/preset_writer.py 写入预设/缓存

插件向 ctx.briefing 写入的 keyBriefingGeneratorPlugin 最终消费):

ctx.briefing["trimester"]              # str
ctx.briefing["need_ask_weight"]        # bool
ctx.briefing["weight_quiz_message"]    # str
ctx.briefing["weight_evaluation"]      # dict
ctx.briefing["need_ask_recipe"]        # bool
ctx.briefing["recipe_quiz_message"]    # str
ctx.briefing["yesterday_diet"]         # dict {"status","summary","meal_count"}
ctx.briefing["dri_analysis"]           # dict {"focus_nutrients","summary"}
ctx.briefing["recommended_foods"]      # list[str]
ctx.briefing["family_memory"]          # dict
ctx.briefing["thinking_keywords"]      # str
ctx.briefing["three_day_summary"]      # dict(由报告Tab选择性地读取)

5. 配置模块 — config.py

每个 Cline 如需新增全局常量,追加到同类型区域末尾,并在 commit message 中注明。

配置区 主要内容 修改风险
目录路径 DATA_DIR, VOICE_DIR, LOGS_DIR, REPORTS_DIR, FAMILY_FILE, DIET_LOG_FILE, NUTRITION_DB_FILE ⚠️ 中
常量枚举 FAMILY_ROLES, TRIMESTERS ✅ 低
数据模板 DEFAULT_NUTRITION_DB (10种营养素), MEAL_TEMPLATES (4×4), TRIMESTER_ADJUSTMENTS, TRIMESTER_TIPS ⚠️ 中
Schema 版本 DIET_LOG_SCHEMA_VERSION = "1.0" ⚠️(改版本号前需评估向后兼容)
声纹阈值 VOICEPRINT_SIMILARITY_THRESHOLD = 0.7 ✅ 低

6. 数据文件格式规范

文件 格式 Schema 读模块 写模块
data/diet_logs.json JSON {"schema_version": "1.0", "records": [...]} diet_logger, nutrition_analyzer diet_logger
data/nutrition_db.json JSON {"营养素名": {"category", "daily_recommend_mg/g/mcg", "foods": [...], "benefit"}} nutrition_analyzer nutrition_analyzer (首次初始化)
data/family.json JSON {"members": [...], "voiceprints": {...}} voiceprint voiceprint
data/family/recipes.md Markdown ### 菜名\n- **制作人**: ... family_manager.RecipeManager family_manager.RecipeManager
data/family/preferences.md Markdown ### 成员:姓名\n- **偏好**: ... family_manager.PreferenceManager family_manager.PreferenceManager
data/family/memory.md Markdown ## 重要事件\n- **日期**: ... family_manager.MemoryManager family_manager.MemoryManager
data/logs/饮食日志_*.md Markdown 见 3.4 节 plugins/diet_summary diet_logger
data/reports/营养报告_*.md Markdown 见 3.7 节 用户 nutrition_analyzer
data/presets/.daily_status.json JSON {"YYYY-MM-DD": {"summary_done": bool, "day_ended": bool}} loop.DailyStatus loop.DailyStatus

7. 并行 Cline 协作规范

7.1 分工建议

Cline A: Modules 强化(营养分析对接 DRIs、菜谱推荐 AI 化)
Cline B: UI 界面优化(Gradio 前端增强、报告模板美化)           ← 当前 Cline
Cline C: 声纹升级(Whisper encoder 替换频谱特征)
Cline D: 数据处理与插件增强(diet_extractor fallback、新插件)
Cline E: 大模型集成(对接 Modal API,实现全双工语音对话)        ← 新增(部署已完成)

注:MiniCPM-o 4.5 已在 Modal 完成部署,API 客户端 modal_deploy/client.py 已就绪。 接下来可并行推进:A) 前端接入大模型 B) 营养分析对接 DRIs C) 声纹升级。

7.2 关键约定

  1. 改接口前先 grep:用 search_files 搜索方法名找到所有调用方
  2. config.py 修改需沟通:新增常量追加到同类型区域,不删改已有常量名
  3. 数据文件向后兼容:新增字段优先用 extensions: {} 或新增可选 key,不删除已有 key
  4. 测试保持通过python tests/run_tests.py 零失败
  5. README 同步更新:接口签名变更 → 更新本 README 对应章节

7.3 Git 工作流(强制执行)

# 工作前
git pull --rebase origin main

# 开发中:每完成一个独立功能点就提交
git add -A
git commit -m "feat: 描述做了什么"

# 推送前再次 pull
git pull --rebase origin main

# 测试通过后推送
python tests/run_tests.py
git push origin main

7.4 Commit Message 规范

feat: 新功能/新模块/新接口
fix: 修复 bug
test: 添加或修改测试
refactor: 重构(不改功能)
docs: 文档更新(含本 README)
chore: 配置/依赖/路径调整

7.5 冲突处理策略

冲突类型 处理方式
config.py 常量冲突 取并集,双方新增常量都保留
同函数不同实现 保留逻辑更完整的一方,另一方改动如果无冲突则合并到合适位置
数据文件(JSON/MD)冲突 保留两个版本共有的条目 + 各自独有条目(去重)
测试文件冲突 取并集:保留所有测试用例
本 README 冲突 取行数更长的一方,手动整合
.gitignore 冲突 取并集

8. UI 设计说明(当前 Cline B 负责)

8.1 页面结构

3 Tab 布局,使用 唯一一个 @gr.render 包裹全部 Tab(避免 Gradio 内部字典迭代崩溃):

Tab ID 功能
🏠 首页 tab_home 语音按钮 + AI 思考 + 信息卡片 + 最近记录
👨‍👩‍👧‍👦 家庭 tab_family 家庭菜谱/偏好/记事 HTML 卡片展示
📈 营养报告 tab_report 纯 HTML 营养仪表盘 + 三天缺失深度分析(已合并)

架构变更说明(v2.0):

  • 原「三天总结」Tab 已合并到营养报告 Tab 中,作为一个统一下沉展示区块
  • 营养报告使用 Slider 防抖自动生成(0.8s),包含基础营养评分 + 三天缺失深度分析
  • 三天缺失分析页面使用与营养报告一致的设计语言(渐变 Header + 紧凑标签 + 结构化 Summary)

8.2 设计风格

  • Glassmorphism 毛玻璃backdrop-filter: blur(20px) 半透明卡片
  • 粉色暖调主题:主色 #E91E63、渐变 #FF6B9D → #FFD6E0
  • 纯 HTML 渲染:家庭信息卡、营养报告、三天缺失分析均使用 HTML + CSS,无需生成图片
  • 中英文切换lang_state 驱动唯一 @gr.render
  • 三天缺失分析
    • 持续不足营养素采用紧凑标签式显示(pill 标签,一行多个)
    • 食材补充建议区域采用 1:2 扩大布局(右侧占 2/3 宽度,每行 6 个食物标签)
    • 总结部分采用结构化 UI 卡片(红色/橙色/绿色 左侧边框,按严重程度分类显示)

8.3 关键文件

文件 职责
ui/app_builder.py Gradio 界面构建(3 Tab + 语言切换 + 报告自动生成)
utils.py CSS 定义 + 翻译字典 + HTML 渲染函数(家庭卡片 + 营养仪表盘 + 三天缺失分析)
plugins/three_day_summary.py 三天缺失分析插件(数据层,被报告 Tab 异步调用)

9. 测试

# 运行全部测试
python tests/run_tests.py

当前测试覆盖:

  • test_diet_extractor.py — ✅ 通过(正则解析 + fallback 提取)
  • test_family_manager.py — ✅ 通过(菜谱/偏好/记忆 CRUD)
  • test_nutrition_standards.py — ✅ 通过(BMI/膳食指南/DRIs 数据)
  • test_loop.py — 循环执行测试
  • test_plugins.py — 插件集成测试

10. Model: MiniCPM-o 4.5

Model to be Used: MiniCPM-o 4.5

核心能力层(core/ 目录)提供模型加载、语音处理、视觉处理、对话管理的底层能力封装。详见各文件 docstring。

部署状态: ✅ 已完成 Modal 部署(见第11节)


11. 多模态大模型部署 (Modal)

11.1 部署状态

项目 状态
API URL https://andrew-jiabin--prego-pal-minicpm-serve.modal.run
Modal App prego-pal-minicpm
GPU T4 (16GB)
模型 MiniCPM-o 4.5 Q4_K_M (~12GB) + vision/audio/TTS 投影层
冷启动 ~2-8 分钟(首次请求触发容器启动 + 模型加载)
闲置缩容 300s 无请求自动缩容
并发 max_inputs=10

11.2 API 端点

端点 方法 功能
/v1/chat/completions POST OpenAI 兼容聊天(文本)
/v1/embeddings POST 文本嵌入
/v1/models GET 模型列表
/v1/multimodal/chat POST 多模态(图片+文本)
/health GET 健康检查
/ GET 服务信息

11.3 Python 客户端

from modal_deploy.client import MiniCPMClient

client = MiniCPMClient(base_url="https://andrew-jiabin--prego-pal-minicpm-serve.modal.run")

# 文本对话
response = client.ask("今天孕妇可以吃什么?")

# 多模态(图片理解)
response = client.describe_image(image_base64_str)

# 嵌入向量
embeddings = client.embed(["孕期饮食建议", "番茄牛腩"])

详见 modal_deploy/client.py 完整文档。

11.4 部署文件

文件 说明
modal_deploy/deploy.py Modal ASGI 部署入口(llama-server → FastAPI 包装)
modal_deploy/client.py Python API 客户端(chat / chat_with_image / embed)
docs/部署经验_Modal_MiniCPM-o.md 详细部署经验文档(踩坑记录、成本估算、后续优化)

最后更新: 2026-06-10 | 由 PregoPal Cline 团队维护

⚠️ 重要提醒:本 README 是所有并行 Cline 的协作基础,任何 Cline 在修改代码后,若涉及接口、数据格式、配置的变更,必须同步更新本文件。不得随意简化、删除结构性内容。