Initial commit: 个性化饮食推荐助手 - 包含OCR识别、AI分析、现代化界面等功能

This commit is contained in:
赵杰
2025-09-25 14:20:11 +01:00
commit aea5f6bf74
27 changed files with 14015 additions and 0 deletions

620
core/base.py Normal file
View File

@@ -0,0 +1,620 @@
"""
核心基座架构 - Diet Recommendation App
统一的基座设计,支持所有功能模块的扩展
"""
from abc import ABC, abstractmethod
from typing import Dict, List, Optional, Any, Union, Callable
from dataclasses import dataclass, field
import json
import sqlite3
import logging
from datetime import datetime, date
from pathlib import Path
import asyncio
from enum import Enum
import threading
from queue import Queue
import os
try:
from dotenv import load_dotenv
load_dotenv()
except Exception:
# 如果没有.env文件或加载失败使用默认配置
pass
# 配置日志
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
class ModuleType(Enum):
"""模块类型枚举"""
DATA_COLLECTION = "data_collection"
USER_ANALYSIS = "user_analysis"
RECOMMENDATION = "recommendation"
GUI_INTERFACE = "gui_interface"
NOTIFICATION = "notification"
@dataclass
class BaseConfig:
"""基础配置类"""
app_name: str = "个性化饮食推荐助手"
version: str = "1.0.0"
debug: bool = True
database_path: str = "data/app.db"
model_path: str = "models/"
log_level: str = "INFO"
# API配置
qwen_api_key: Optional[str] = None
qwen_base_url: str = "https://dashscope.aliyuncs.com/compatible-mode/v1"
qwen_model: str = "qwen-plus-latest"
# 用户配置
max_recommendations: int = 5
min_training_samples: int = 10
model_update_threshold: int = 50
@dataclass
class UserData:
"""统一用户数据结构"""
user_id: str
profile: Dict[str, Any] = field(default_factory=dict)
meals: List[Dict[str, Any]] = field(default_factory=list)
feedback: List[Dict[str, Any]] = field(default_factory=list)
preferences: Dict[str, Any] = field(default_factory=dict)
created_at: str = field(default_factory=lambda: datetime.now().isoformat())
updated_at: str = field(default_factory=lambda: datetime.now().isoformat())
@dataclass
class AnalysisResult:
"""统一分析结果结构"""
module_type: ModuleType
user_id: str
input_data: Any
result: Dict[str, Any]
confidence: float
timestamp: str = field(default_factory=lambda: datetime.now().isoformat())
metadata: Dict[str, Any] = field(default_factory=dict)
class BaseModule(ABC):
"""基础模块抽象类"""
def __init__(self, config: BaseConfig, module_type: ModuleType):
self.config = config
self.module_type = module_type
self.is_initialized = False
self.logger = logging.getLogger(f"{self.__class__.__name__}")
@abstractmethod
def initialize(self) -> bool:
"""初始化模块"""
pass
@abstractmethod
def process(self, input_data: Any, user_data: UserData) -> AnalysisResult:
"""处理数据"""
pass
@abstractmethod
def cleanup(self) -> bool:
"""清理资源"""
pass
def is_ready(self) -> bool:
"""检查模块是否就绪"""
return self.is_initialized
class DataManager:
"""数据管理基座"""
def __init__(self, config: BaseConfig):
self.config = config
self.db_path = Path(config.database_path)
self.db_path.parent.mkdir(parents=True, exist_ok=True)
self._init_database()
def _init_database(self):
"""初始化数据库"""
conn = sqlite3.connect(self.db_path)
cursor = conn.cursor()
# 用户表
cursor.execute('''
CREATE TABLE IF NOT EXISTS users (
user_id TEXT PRIMARY KEY,
data TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)
''')
# 分析结果表
cursor.execute('''
CREATE TABLE IF NOT EXISTS analysis_results (
id INTEGER PRIMARY KEY AUTOINCREMENT,
user_id TEXT,
module_type TEXT,
input_data TEXT,
result TEXT,
confidence REAL,
timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
metadata TEXT,
FOREIGN KEY (user_id) REFERENCES users (user_id)
)
''')
# 系统配置表
cursor.execute('''
CREATE TABLE IF NOT EXISTS system_config (
key TEXT PRIMARY KEY,
value TEXT,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)
''')
conn.commit()
conn.close()
def save_user_data(self, user_data: UserData) -> bool:
"""保存用户数据"""
try:
conn = sqlite3.connect(self.db_path)
cursor = conn.cursor()
cursor.execute('''
INSERT OR REPLACE INTO users (user_id, data, updated_at)
VALUES (?, ?, CURRENT_TIMESTAMP)
''', (user_data.user_id, json.dumps(user_data.__dict__)))
conn.commit()
conn.close()
return True
except Exception as e:
logger.error(f"保存用户数据失败: {e}")
return False
def get_user_data(self, user_id: str) -> Optional[UserData]:
"""获取用户数据"""
try:
conn = sqlite3.connect(self.db_path)
cursor = conn.cursor()
# 获取用户基本信息
cursor.execute('SELECT data FROM users WHERE user_id = ?', (user_id,))
result = cursor.fetchone()
if not result:
conn.close()
return None
# 解析用户基本信息
data_dict = json.loads(result[0])
# 获取餐食记录
cursor.execute('''
SELECT date, meal_type, foods, quantities, calories, satisfaction_score, food_items
FROM meal_records
WHERE user_id = ?
ORDER BY date DESC
''', (user_id,))
meal_rows = cursor.fetchall()
meals = []
for row in meal_rows:
meal = {
'date': row[0],
'meal_type': row[1],
'foods': json.loads(row[2]) if row[2] else [],
'quantities': json.loads(row[3]) if row[3] else [],
'calories': row[4],
'satisfaction_score': row[5],
'food_items': json.loads(row[6]) if row[6] else []
}
meals.append(meal)
# 获取反馈记录
cursor.execute('''
SELECT date, recommended_foods, user_choice, feedback_type
FROM feedback_records
WHERE user_id = ?
ORDER BY date DESC
''', (user_id,))
feedback_rows = cursor.fetchall()
feedback = []
for row in feedback_rows:
fb = {
'date': row[0],
'recommended_foods': json.loads(row[1]) if row[1] else [],
'user_choice': row[2],
'feedback_type': row[3]
}
feedback.append(fb)
# 获取问卷数据
cursor.execute('''
SELECT questionnaire_type, answers
FROM questionnaire_records
WHERE user_id = ?
''', (user_id,))
questionnaire_rows = cursor.fetchall()
preferences = {}
for row in questionnaire_rows:
preferences[row[0]] = json.loads(row[1]) if row[1] else {}
conn.close()
# 构建完整的用户数据
user_data = UserData(
user_id=data_dict['user_id'],
profile=data_dict.get('profile', {}),
meals=meals,
feedback=feedback,
preferences=preferences,
created_at=data_dict.get('created_at', ''),
updated_at=data_dict.get('updated_at', '')
)
return user_data
except Exception as e:
logger.error(f"获取用户数据失败: {e}")
return None
def save_analysis_result(self, result: AnalysisResult) -> bool:
"""保存分析结果"""
try:
conn = sqlite3.connect(self.db_path)
cursor = conn.cursor()
cursor.execute('''
INSERT INTO analysis_results
(user_id, module_type, input_data, result, confidence, metadata)
VALUES (?, ?, ?, ?, ?, ?)
''', (
result.user_id,
result.module_type.value,
json.dumps(result.input_data),
json.dumps(result.result),
result.confidence,
json.dumps(result.metadata)
))
conn.commit()
conn.close()
return True
except Exception as e:
logger.error(f"保存分析结果失败: {e}")
return False
def get_analysis_history(self, user_id: str, module_type: Optional[ModuleType] = None,
limit: int = 10) -> List[AnalysisResult]:
"""获取分析历史"""
try:
conn = sqlite3.connect(self.db_path)
cursor = conn.cursor()
if module_type:
cursor.execute('''
SELECT module_type, input_data, result, confidence, timestamp, metadata
FROM analysis_results
WHERE user_id = ? AND module_type = ?
ORDER BY timestamp DESC
LIMIT ?
''', (user_id, module_type.value, limit))
else:
cursor.execute('''
SELECT module_type, input_data, result, confidence, timestamp, metadata
FROM analysis_results
WHERE user_id = ?
ORDER BY timestamp DESC
LIMIT ?
''', (user_id, limit))
results = cursor.fetchall()
conn.close()
analysis_results = []
for row in results:
result = AnalysisResult(
module_type=ModuleType(row[0]),
user_id=user_id,
input_data=json.loads(row[1]),
result=json.loads(row[2]),
confidence=row[3],
timestamp=row[4],
metadata=json.loads(row[5])
)
analysis_results.append(result)
return analysis_results
except Exception as e:
logger.error(f"获取分析历史失败: {e}")
return []
class EventBus:
"""事件总线基座"""
def __init__(self):
self.subscribers: Dict[str, List[Callable]] = {}
self.event_queue = Queue()
self.is_running = False
self.worker_thread = None
def subscribe(self, event_type: str, callback: Callable):
"""订阅事件"""
if event_type not in self.subscribers:
self.subscribers[event_type] = []
self.subscribers[event_type].append(callback)
def unsubscribe(self, event_type: str, callback: Callable):
"""取消订阅"""
if event_type in self.subscribers:
self.subscribers[event_type].remove(callback)
def publish(self, event_type: str, data: Any):
"""发布事件"""
self.event_queue.put((event_type, data))
def start(self):
"""启动事件总线"""
self.is_running = True
self.worker_thread = threading.Thread(target=self._process_events)
self.worker_thread.start()
def stop(self):
"""停止事件总线"""
self.is_running = False
if self.worker_thread:
self.worker_thread.join()
def _process_events(self):
"""处理事件"""
while self.is_running:
try:
event_type, data = self.event_queue.get(timeout=1)
if event_type in self.subscribers:
for callback in self.subscribers[event_type]:
try:
callback(data)
except Exception as e:
logger.error(f"事件处理失败: {e}")
self.event_queue.task_done()
except:
continue
class ModuleManager:
"""模块管理器基座"""
def __init__(self, config: BaseConfig):
self.config = config
self.modules: Dict[ModuleType, BaseModule] = {}
self.data_manager = DataManager(config)
self.event_bus = EventBus()
self.is_initialized = False
def register_module(self, module: BaseModule) -> bool:
"""注册模块"""
try:
# 检查模块是否已经注册
if module.module_type in self.modules:
logger.warning(f"模块 {module.module_type.value} 已经注册,跳过重复注册")
return True
if module.initialize():
self.modules[module.module_type] = module
logger.info(f"模块 {module.module_type.value} 注册成功")
return True
else:
logger.error(f"模块 {module.module_type.value} 初始化失败")
return False
except Exception as e:
logger.error(f"注册模块失败: {e}")
return False
def process_request(self, module_type: ModuleType, input_data: Any,
user_id: str) -> Optional[AnalysisResult]:
"""处理请求"""
if module_type not in self.modules:
logger.error(f"模块 {module_type.value} 未注册")
return None
module = self.modules[module_type]
if not module.is_ready():
logger.error(f"模块 {module_type.value} 未就绪")
return None
# 获取用户数据
user_data = self.data_manager.get_user_data(user_id)
if not user_data:
logger.error(f"用户 {user_id} 数据不存在")
return None
try:
# 处理请求
result = module.process(input_data, user_data)
# 保存结果
self.data_manager.save_analysis_result(result)
# 发布事件
self.event_bus.publish(f"{module_type.value}_completed", result)
return result
except Exception as e:
logger.error(f"处理请求失败: {e}")
return None
def get_module_status(self) -> Dict[str, bool]:
"""获取模块状态"""
return {module_type.value: module.is_ready()
for module_type, module in self.modules.items()}
def initialize_all(self) -> bool:
"""初始化所有模块"""
try:
self.event_bus.start()
self.is_initialized = True
logger.info("模块管理器初始化完成")
return True
except Exception as e:
logger.error(f"初始化失败: {e}")
return False
def cleanup_all(self) -> bool:
"""清理所有模块"""
try:
self.event_bus.stop()
for module in self.modules.values():
module.cleanup()
self.is_initialized = False
logger.info("模块管理器清理完成")
return True
except Exception as e:
logger.error(f"清理失败: {e}")
return False
class AppCore:
"""应用核心基座"""
def __init__(self, config: BaseConfig):
self.config = config
self.module_manager = ModuleManager(config)
self.data_manager = DataManager(config)
self.is_running = False
def start(self) -> bool:
"""启动应用"""
try:
if self.module_manager.initialize_all():
self.is_running = True
logger.info("应用启动成功")
return True
else:
logger.error("应用启动失败")
return False
except Exception as e:
logger.error(f"启动应用失败: {e}")
return False
def stop(self) -> bool:
"""停止应用"""
try:
if self.module_manager.cleanup_all():
self.is_running = False
logger.info("应用停止成功")
return True
else:
logger.error("应用停止失败")
return False
except Exception as e:
logger.error(f"停止应用失败: {e}")
return False
def create_user(self, user_id: str, initial_data: Dict[str, Any]) -> bool:
"""创建用户"""
user_data = UserData(
user_id=user_id,
profile=initial_data.get('profile', {}),
preferences=initial_data.get('preferences', {})
)
return self.module_manager.data_manager.save_user_data(user_data)
def process_user_request(self, module_type: ModuleType, input_data: Any,
user_id: str) -> Optional[AnalysisResult]:
"""处理用户请求"""
return self.module_manager.process_request(module_type, input_data, user_id)
def get_user_data(self, user_id: str) -> Optional[UserData]:
"""获取用户数据"""
return self.module_manager.data_manager.get_user_data(user_id)
def get_analysis_history(self, user_id: str, module_type: Optional[ModuleType] = None) -> List[AnalysisResult]:
"""获取分析历史"""
return self.module_manager.data_manager.get_analysis_history(user_id, module_type)
# 全局应用实例
app_core: Optional[AppCore] = None
def get_app_core() -> AppCore:
"""获取应用核心实例"""
global app_core
if app_core is None:
config = BaseConfig()
app_core = AppCore(config)
return app_core
def initialize_app(config: Optional[BaseConfig] = None) -> bool:
"""初始化应用"""
global app_core
if config is None:
config = BaseConfig()
app_core = AppCore(config)
return app_core.start()
def cleanup_app() -> bool:
"""清理应用"""
global app_core
if app_core:
return app_core.stop()
return True
if __name__ == "__main__":
# 测试基座架构
print("测试核心基座架构...")
# 初始化应用
if initialize_app():
print("✅ 应用初始化成功")
# 创建测试用户
test_user_id = "test_user_001"
initial_data = {
"profile": {
"name": "测试用户",
"age": 25,
"gender": ""
},
"preferences": {
"taste": "sweet",
"diet": "balanced"
}
}
app = get_app_core()
if app.create_user(test_user_id, initial_data):
print("✅ 用户创建成功")
# 获取用户数据
user_data = app.get_user_data(test_user_id)
if user_data:
print(f"✅ 用户数据获取成功: {user_data.user_id}")
# 清理应用
cleanup_app()
print("✅ 应用清理完成")
else:
print("❌ 应用初始化失败")
print("基座架构测试完成!")

928
core/base_engine.py Normal file
View File

@@ -0,0 +1,928 @@
"""
核心基座架构 - 个性化饮食推荐系统
提供统一的基础设施和接口,所有功能模块都基于此基座构建
"""
import json
import sqlite3
import asyncio
import logging
from abc import ABC, abstractmethod
from typing import Dict, List, Optional, Any, Union
from dataclasses import dataclass, asdict
from datetime import datetime, date
from pathlib import Path
import os
from dotenv import load_dotenv
load_dotenv()
# 配置日志
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
@dataclass
class UserProfile:
"""用户画像 - 统一数据结构"""
user_id: str
name: str
age: int
gender: str
height: float
weight: float
activity_level: str
# 偏好和限制
taste_preferences: Dict[str, int] = None
dietary_preferences: List[str] = None
allergies: List[str] = None
dislikes: List[str] = None
# 生理信息
is_female: bool = False
menstrual_cycle_length: Optional[int] = None
last_period_date: Optional[str] = None
# 个性化因素
zodiac_sign: Optional[str] = None
personality_traits: List[str] = None
health_goals: List[str] = None
def __post_init__(self):
if self.taste_preferences is None:
self.taste_preferences = {}
if self.dietary_preferences is None:
self.dietary_preferences = []
if self.allergies is None:
self.allergies = []
if self.dislikes is None:
self.dislikes = []
if self.personality_traits is None:
self.personality_traits = []
if self.health_goals is None:
self.health_goals = []
@dataclass
class MealRecord:
"""餐食记录 - 统一数据结构"""
user_id: str
date: str
meal_type: str # breakfast, lunch, dinner
foods: List[str]
quantities: List[str]
calories: Optional[float] = None
satisfaction_score: Optional[int] = None
notes: Optional[str] = None
@dataclass
class RecommendationResult:
"""推荐结果 - 统一数据结构"""
user_id: str
date: str
meal_type: str
recommended_foods: List[str]
reasoning: str
confidence_score: float
special_considerations: List[str] = None
def __post_init__(self):
if self.special_considerations is None:
self.special_considerations = []
class BaseEngine(ABC):
"""基础引擎抽象类 - 所有功能模块的基座"""
def __init__(self, config: Dict[str, Any] = None):
self.config = config or {}
self.logger = logging.getLogger(self.__class__.__name__)
self._initialized = False
@abstractmethod
async def initialize(self) -> bool:
"""初始化引擎"""
pass
@abstractmethod
async def process(self, data: Any) -> Any:
"""处理数据"""
pass
@abstractmethod
async def cleanup(self) -> bool:
"""清理资源"""
pass
def is_initialized(self) -> bool:
"""检查是否已初始化"""
return self._initialized
def get_config(self, key: str, default: Any = None) -> Any:
"""获取配置"""
return self.config.get(key, default)
class DataManager(BaseEngine):
"""数据管理基座 - 统一的数据存储和访问接口"""
def __init__(self, db_path: str = "data/app.db"):
super().__init__()
self.db_path = Path(db_path)
self.db_path.parent.mkdir(parents=True, exist_ok=True)
async def initialize(self) -> bool:
"""初始化数据库"""
try:
await self._create_tables()
self._initialized = True
self.logger.info("数据管理器初始化成功")
return True
except Exception as e:
self.logger.error(f"数据管理器初始化失败: {e}")
return False
async def _create_tables(self):
"""创建数据库表"""
conn = sqlite3.connect(self.db_path)
cursor = conn.cursor()
# 用户表
cursor.execute('''
CREATE TABLE IF NOT EXISTS users (
user_id TEXT PRIMARY KEY,
profile_data TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)
''')
# 餐食记录表
cursor.execute('''
CREATE TABLE IF NOT EXISTS meals (
id INTEGER PRIMARY KEY AUTOINCREMENT,
user_id TEXT,
date TEXT,
meal_type TEXT,
foods TEXT,
quantities TEXT,
calories REAL,
satisfaction_score INTEGER,
notes TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (user_id) REFERENCES users (user_id)
)
''')
# 推荐记录表
cursor.execute('''
CREATE TABLE IF NOT EXISTS recommendations (
id INTEGER PRIMARY KEY AUTOINCREMENT,
user_id TEXT,
date TEXT,
meal_type TEXT,
recommended_foods TEXT,
reasoning TEXT,
confidence_score REAL,
special_considerations TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (user_id) REFERENCES users (user_id)
)
''')
# 用户反馈表
cursor.execute('''
CREATE TABLE IF NOT EXISTS feedback (
id INTEGER PRIMARY KEY AUTOINCREMENT,
user_id TEXT,
date TEXT,
recommendation_id INTEGER,
user_choice TEXT,
feedback_type TEXT,
satisfaction_score INTEGER,
notes TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (user_id) REFERENCES users (user_id),
FOREIGN KEY (recommendation_id) REFERENCES recommendations (id)
)
''')
conn.commit()
conn.close()
async def process(self, operation: str, data: Any) -> Any:
"""处理数据操作"""
if not self._initialized:
await self.initialize()
operations = {
'save_user': self._save_user_profile,
'get_user': self._get_user_profile,
'save_meal': self._save_meal_record,
'get_meals': self._get_meal_records,
'save_recommendation': self._save_recommendation,
'get_recommendations': self._get_recommendations,
'save_feedback': self._save_feedback,
'get_feedback': self._get_feedback
}
if operation in operations:
return await operations[operation](data)
else:
raise ValueError(f"不支持的操作: {operation}")
async def _save_user_profile(self, profile: UserProfile) -> bool:
"""保存用户画像"""
try:
conn = sqlite3.connect(self.db_path)
cursor = conn.cursor()
cursor.execute('''
INSERT OR REPLACE INTO users (user_id, profile_data, updated_at)
VALUES (?, ?, CURRENT_TIMESTAMP)
''', (profile.user_id, json.dumps(asdict(profile))))
conn.commit()
conn.close()
return True
except Exception as e:
self.logger.error(f"保存用户画像失败: {e}")
return False
async def _get_user_profile(self, user_id: str) -> Optional[UserProfile]:
"""获取用户画像"""
try:
conn = sqlite3.connect(self.db_path)
cursor = conn.cursor()
cursor.execute('SELECT profile_data FROM users WHERE user_id = ?', (user_id,))
result = cursor.fetchone()
conn.close()
if result:
profile_dict = json.loads(result[0])
return UserProfile(**profile_dict)
return None
except Exception as e:
self.logger.error(f"获取用户画像失败: {e}")
return None
async def _save_meal_record(self, meal: MealRecord) -> bool:
"""保存餐食记录"""
try:
conn = sqlite3.connect(self.db_path)
cursor = conn.cursor()
cursor.execute('''
INSERT INTO meals (user_id, date, meal_type, foods, quantities,
calories, satisfaction_score, notes)
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
''', (
meal.user_id, meal.date, meal.meal_type,
json.dumps(meal.foods), json.dumps(meal.quantities),
meal.calories, meal.satisfaction_score, meal.notes
))
conn.commit()
conn.close()
return True
except Exception as e:
self.logger.error(f"保存餐食记录失败: {e}")
return False
async def _get_meal_records(self, params: Dict[str, Any]) -> List[MealRecord]:
"""获取餐食记录"""
try:
conn = sqlite3.connect(self.db_path)
cursor = conn.cursor()
user_id = params.get('user_id')
days = params.get('days', 5)
cursor.execute('''
SELECT user_id, date, meal_type, foods, quantities, calories,
satisfaction_score, notes
FROM meals
WHERE user_id = ?
ORDER BY date DESC, meal_type
LIMIT ?
''', (user_id, days * 3))
results = cursor.fetchall()
conn.close()
meals = []
for row in results:
meal = MealRecord(
user_id=row[0],
date=row[1],
meal_type=row[2],
foods=json.loads(row[3]),
quantities=json.loads(row[4]),
calories=row[5],
satisfaction_score=row[6],
notes=row[7]
)
meals.append(meal)
return meals
except Exception as e:
self.logger.error(f"获取餐食记录失败: {e}")
return []
async def _save_recommendation(self, recommendation: RecommendationResult) -> int:
"""保存推荐结果"""
try:
conn = sqlite3.connect(self.db_path)
cursor = conn.cursor()
cursor.execute('''
INSERT INTO recommendations (user_id, date, meal_type, recommended_foods,
reasoning, confidence_score, special_considerations)
VALUES (?, ?, ?, ?, ?, ?, ?)
''', (
recommendation.user_id, recommendation.date, recommendation.meal_type,
json.dumps(recommendation.recommended_foods), recommendation.reasoning,
recommendation.confidence_score, json.dumps(recommendation.special_considerations)
))
recommendation_id = cursor.lastrowid
conn.commit()
conn.close()
return recommendation_id
except Exception as e:
self.logger.error(f"保存推荐结果失败: {e}")
return -1
async def _get_recommendations(self, params: Dict[str, Any]) -> List[RecommendationResult]:
"""获取推荐记录"""
try:
conn = sqlite3.connect(self.db_path)
cursor = conn.cursor()
user_id = params.get('user_id')
days = params.get('days', 7)
cursor.execute('''
SELECT user_id, date, meal_type, recommended_foods, reasoning,
confidence_score, special_considerations
FROM recommendations
WHERE user_id = ?
ORDER BY date DESC
LIMIT ?
''', (user_id, days))
results = cursor.fetchall()
conn.close()
recommendations = []
for row in results:
rec = RecommendationResult(
user_id=row[0],
date=row[1],
meal_type=row[2],
recommended_foods=json.loads(row[3]),
reasoning=row[4],
confidence_score=row[5],
special_considerations=json.loads(row[6]) if row[6] else []
)
recommendations.append(rec)
return recommendations
except Exception as e:
self.logger.error(f"获取推荐记录失败: {e}")
return []
async def _save_feedback(self, feedback_data: Dict[str, Any]) -> bool:
"""保存用户反馈"""
try:
conn = sqlite3.connect(self.db_path)
cursor = conn.cursor()
cursor.execute('''
INSERT INTO feedback (user_id, date, recommendation_id, user_choice,
feedback_type, satisfaction_score, notes)
VALUES (?, ?, ?, ?, ?, ?, ?)
''', (
feedback_data['user_id'], feedback_data['date'],
feedback_data.get('recommendation_id'), feedback_data['user_choice'],
feedback_data['feedback_type'], feedback_data.get('satisfaction_score'),
feedback_data.get('notes')
))
conn.commit()
conn.close()
return True
except Exception as e:
self.logger.error(f"保存用户反馈失败: {e}")
return False
async def _get_feedback(self, params: Dict[str, Any]) -> List[Dict[str, Any]]:
"""获取用户反馈"""
try:
conn = sqlite3.connect(self.db_path)
cursor = conn.cursor()
user_id = params.get('user_id')
days = params.get('days', 7)
cursor.execute('''
SELECT user_id, date, recommendation_id, user_choice, feedback_type,
satisfaction_score, notes
FROM feedback
WHERE user_id = ?
ORDER BY date DESC
LIMIT ?
''', (user_id, days))
results = cursor.fetchall()
conn.close()
feedbacks = []
for row in results:
feedback = {
'user_id': row[0],
'date': row[1],
'recommendation_id': row[2],
'user_choice': row[3],
'feedback_type': row[4],
'satisfaction_score': row[5],
'notes': row[6]
}
feedbacks.append(feedback)
return feedbacks
except Exception as e:
self.logger.error(f"获取用户反馈失败: {e}")
return []
async def cleanup(self) -> bool:
"""清理资源"""
self._initialized = False
return True
class AIAnalyzer(BaseEngine):
"""AI分析基座 - 统一的大模型分析接口"""
def __init__(self, config: Dict[str, Any] = None):
super().__init__(config)
self.openai_client = None
self.anthropic_client = None
async def initialize(self) -> bool:
"""初始化AI客户端"""
try:
import openai
import anthropic
self.openai_client = openai.OpenAI(api_key=os.getenv("OPENAI_API_KEY"))
self.anthropic_client = anthropic.Anthropic(api_key=os.getenv("ANTHROPIC_API_KEY"))
self._initialized = True
self.logger.info("AI分析器初始化成功")
return True
except Exception as e:
self.logger.error(f"AI分析器初始化失败: {e}")
return False
async def process(self, analysis_type: str, data: Any) -> Any:
"""处理AI分析请求"""
if not self._initialized:
await self.initialize()
analysis_types = {
'user_intent': self._analyze_user_intent,
'physiological_state': self._analyze_physiological_state,
'nutrition_analysis': self._analyze_nutrition,
'recommendation_reasoning': self._generate_reasoning
}
if analysis_type in analysis_types:
return await analysis_types[analysis_type](data)
else:
raise ValueError(f"不支持的分析类型: {analysis_type}")
async def _analyze_user_intent(self, data: Dict[str, Any]) -> Dict[str, Any]:
"""分析用户意图"""
user_input = data.get('user_input', '')
context = data.get('context', {})
prompt = f"""
请分析用户的真实意图和需求:
用户输入: "{user_input}"
用户背景: {json.dumps(context, ensure_ascii=False)}
请分析:
1. 真实意图
2. 情绪状态
3. 营养需求
4. 推荐理由
返回JSON格式结果。
"""
try:
response = await self.openai_client.chat.completions.create(
model="gpt-4",
messages=[
{"role": "system", "content": "你是专业的营养师和心理分析师。"},
{"role": "user", "content": prompt}
],
temperature=0.3,
max_tokens=500
)
result_text = response.choices[0].message.content
return self._parse_json_result(result_text)
except Exception as e:
self.logger.error(f"用户意图分析失败: {e}")
return {"intent": "需要饮食建议", "confidence": 0.3}
async def _analyze_physiological_state(self, data: Dict[str, Any]) -> Dict[str, Any]:
"""分析生理状态"""
profile = data.get('profile', {})
current_date = data.get('current_date', '')
if not profile.get('is_female', False):
return {"state": "normal", "needs": []}
# 计算生理周期
cycle_info = self._calculate_cycle_state(profile, current_date)
prompt = f"""
作为女性健康专家,分析用户的生理状态:
用户信息: {json.dumps(profile, ensure_ascii=False)}
当前日期: {current_date}
生理周期: {json.dumps(cycle_info, ensure_ascii=False)}
请分析营养需求和饮食建议。
"""
try:
response = await self.openai_client.chat.completions.create(
model="gpt-4",
messages=[
{"role": "system", "content": "你是专业的女性健康专家。"},
{"role": "user", "content": prompt}
],
temperature=0.2,
max_tokens=400
)
result_text = response.choices[0].message.content
result = self._parse_json_result(result_text)
result['cycle_info'] = cycle_info
return result
except Exception as e:
self.logger.error(f"生理状态分析失败: {e}")
return {"state": "normal", "needs": [], "cycle_info": cycle_info}
async def _analyze_nutrition(self, data: Dict[str, Any]) -> Dict[str, Any]:
"""营养分析"""
foods = data.get('foods', [])
quantities = data.get('quantities', [])
prompt = f"""
请分析以下食物的营养成分:
食物: {', '.join(foods)}
数量: {', '.join(quantities)}
请分析:
1. 总热量
2. 主要营养素
3. 营养均衡性
4. 改进建议
返回JSON格式结果。
"""
try:
response = await self.openai_client.chat.completions.create(
model="gpt-4",
messages=[
{"role": "system", "content": "你是专业的营养师。"},
{"role": "user", "content": prompt}
],
temperature=0.2,
max_tokens=400
)
result_text = response.choices[0].message.content
return self._parse_json_result(result_text)
except Exception as e:
self.logger.error(f"营养分析失败: {e}")
return {"calories": 0, "analysis": "分析失败"}
async def _generate_reasoning(self, data: Dict[str, Any]) -> str:
"""生成推荐理由"""
recommendations = data.get('recommendations', [])
user_profile = data.get('user_profile', {})
prompt = f"""
请为以下推荐生成个性化理由:
推荐食物: {', '.join(recommendations)}
用户画像: {json.dumps(user_profile, ensure_ascii=False)}
请生成简洁明了的推荐理由。
"""
try:
response = await self.openai_client.chat.completions.create(
model="gpt-4",
messages=[
{"role": "system", "content": "你是专业的营养师。"},
{"role": "user", "content": prompt}
],
temperature=0.3,
max_tokens=200
)
return response.choices[0].message.content
except Exception as e:
self.logger.error(f"生成推荐理由失败: {e}")
return "基于您的个人偏好和营养需求推荐"
def _calculate_cycle_state(self, profile: Dict[str, Any], current_date: str) -> Dict[str, Any]:
"""计算生理周期状态"""
try:
last_period = datetime.strptime(profile.get('last_period_date', ''), '%Y-%m-%d')
current = datetime.strptime(current_date, '%Y-%m-%d')
cycle_length = profile.get('menstrual_cycle_length', 28)
days_since_period = (current - last_period).days
days_to_next_period = cycle_length - (days_since_period % cycle_length)
if days_since_period % cycle_length < 5:
phase = "月经期"
elif days_since_period % cycle_length < 14:
phase = "卵泡期"
elif days_since_period % cycle_length < 18:
phase = "排卵期"
else:
phase = "黄体期"
return {
"phase": phase,
"days_since_period": days_since_period % cycle_length,
"days_to_next_period": days_to_next_period,
"is_ovulation": phase == "排卵期"
}
except Exception:
return {
"phase": "未知",
"days_since_period": 0,
"days_to_next_period": 0,
"is_ovulation": False
}
def _parse_json_result(self, text: str) -> Dict[str, Any]:
"""解析JSON结果"""
try:
start_idx = text.find('{')
end_idx = text.rfind('}') + 1
if start_idx != -1 and end_idx != -1:
json_str = text[start_idx:end_idx]
return json.loads(json_str)
except Exception as e:
self.logger.error(f"解析JSON结果失败: {e}")
return {}
async def cleanup(self) -> bool:
"""清理资源"""
self._initialized = False
return True
class RecommendationEngine(BaseEngine):
"""推荐引擎基座 - 统一的推荐算法接口"""
def __init__(self, config: Dict[str, Any] = None):
super().__init__(config)
self.data_manager = None
self.ai_analyzer = None
self.food_database = {}
async def initialize(self) -> bool:
"""初始化推荐引擎"""
try:
# 初始化依赖组件
self.data_manager = DataManager()
await self.data_manager.initialize()
self.ai_analyzer = AIAnalyzer()
await self.ai_analyzer.initialize()
# 加载食物数据库
await self._load_food_database()
self._initialized = True
self.logger.info("推荐引擎初始化成功")
return True
except Exception as e:
self.logger.error(f"推荐引擎初始化失败: {e}")
return False
async def process(self, request_type: str, data: Any) -> Any:
"""处理推荐请求"""
if not self._initialized:
await self.initialize()
request_types = {
'generate_recommendation': self._generate_recommendation,
'update_model': self._update_model,
'get_food_info': self._get_food_info
}
if request_type in request_types:
return await request_types[request_type](data)
else:
raise ValueError(f"不支持的请求类型: {request_type}")
async def _generate_recommendation(self, data: Dict[str, Any]) -> RecommendationResult:
"""生成推荐"""
user_id = data.get('user_id')
user_input = data.get('user_input', '')
current_date = data.get('current_date', datetime.now().strftime('%Y-%m-%d'))
meal_type = data.get('meal_type', 'lunch')
# 获取用户数据
user_profile = await self.data_manager.process('get_user', user_id)
meal_history = await self.data_manager.process('get_meals', {'user_id': user_id, 'days': 5})
if not user_profile:
raise ValueError(f"用户 {user_id} 不存在")
# AI分析用户意图
intent_analysis = await self.ai_analyzer.process('user_intent', {
'user_input': user_input,
'context': asdict(user_profile)
})
# AI分析生理状态
physiological_analysis = await self.ai_analyzer.process('physiological_state', {
'profile': asdict(user_profile),
'current_date': current_date
})
# 生成推荐食物
recommended_foods = await self._select_foods(
user_profile, intent_analysis, physiological_analysis
)
# 生成推荐理由
reasoning = await self.ai_analyzer.process('recommendation_reasoning', {
'recommendations': recommended_foods,
'user_profile': asdict(user_profile)
})
# 创建推荐结果
recommendation = RecommendationResult(
user_id=user_id,
date=current_date,
meal_type=meal_type,
recommended_foods=recommended_foods,
reasoning=reasoning,
confidence_score=intent_analysis.get('confidence', 0.5),
special_considerations=physiological_analysis.get('considerations', [])
)
# 保存推荐结果
await self.data_manager.process('save_recommendation', recommendation)
return recommendation
async def _select_foods(self, user_profile: UserProfile,
intent_analysis: Dict[str, Any],
physiological_analysis: Dict[str, Any]) -> List[str]:
"""选择推荐食物"""
# 基础食物池
base_foods = [
"米饭", "面条", "馒头", "包子", "饺子",
"鸡蛋", "豆腐", "鱼肉", "鸡肉", "瘦肉",
"青菜", "西红柿", "胡萝卜", "土豆", "西兰花",
"苹果", "香蕉", "橙子", "葡萄", "草莓",
"牛奶", "酸奶", "豆浆", "坚果", "红枣"
]
# 根据用户偏好过滤
filtered_foods = []
for food in base_foods:
if not any(dislike in food for dislike in user_profile.dislikes):
if not any(allergy in food for allergy in user_profile.allergies):
filtered_foods.append(food)
# 根据生理需求调整
physiological_needs = physiological_analysis.get('needs', [])
priority_foods = []
for need in physiological_needs:
if need == "铁质":
priority_foods.extend(["菠菜", "瘦肉", "红枣"])
elif need == "蛋白质":
priority_foods.extend(["鸡蛋", "豆腐", "鱼肉"])
elif need == "维生素C":
priority_foods.extend(["橙子", "柠檬", "西红柿"])
# 合并推荐
recommended = list(set(priority_foods + filtered_foods))[:5]
return recommended
async def _update_model(self, data: Dict[str, Any]) -> bool:
"""更新模型"""
# 这里可以实现机器学习模型的更新逻辑
user_id = data.get('user_id')
feedback_data = await self.data_manager.process('get_feedback', {'user_id': user_id, 'days': 30})
# 基于反馈数据更新推荐策略
self.logger.info(f"更新用户 {user_id} 的推荐模型")
return True
async def _get_food_info(self, food_name: str) -> Dict[str, Any]:
"""获取食物信息"""
return self.food_database.get(food_name, {})
async def _load_food_database(self):
"""加载食物数据库"""
# 这里可以从文件或API加载食物营养信息
self.food_database = {
"米饭": {"calories": 130, "protein": 2.7, "carbs": 28},
"鸡蛋": {"calories": 155, "protein": 13, "fat": 11},
"豆腐": {"calories": 76, "protein": 8, "carbs": 2},
# 更多食物数据...
}
async def cleanup(self) -> bool:
"""清理资源"""
if self.data_manager:
await self.data_manager.cleanup()
if self.ai_analyzer:
await self.ai_analyzer.cleanup()
self._initialized = False
return True
if __name__ == "__main__":
# 测试基座架构
async def test_base_engine():
print("测试基座架构...")
# 测试数据管理器
data_manager = DataManager()
await data_manager.initialize()
# 创建测试用户
test_user = UserProfile(
user_id="test_001",
name="测试用户",
age=25,
gender="",
height=165.0,
weight=55.0,
activity_level="moderate",
taste_preferences={"sweet": 4, "salty": 3},
is_female=True,
zodiac_sign="天秤座"
)
# 保存用户
await data_manager.process('save_user', test_user)
print("用户保存成功")
# 获取用户
retrieved_user = await data_manager.process('get_user', "test_001")
if retrieved_user:
print(f"获取用户: {retrieved_user.name}")
# 测试推荐引擎
rec_engine = RecommendationEngine()
await rec_engine.initialize()
# 生成推荐
recommendation = await rec_engine.process('generate_recommendation', {
'user_id': 'test_001',
'user_input': '我今天想吃点清淡的',
'meal_type': 'lunch'
})
print(f"推荐结果: {recommendation.recommended_foods}")
print(f"推荐理由: {recommendation.reasoning}")
# 清理资源
await data_manager.cleanup()
await rec_engine.cleanup()
print("测试完成")
# 运行测试
asyncio.run(test_base_engine())