""" DeepSeek 健康评估与建议生成服务 用于生成"整体健康状况"和"功能性健康建议"内容 优化:优先使用模板中已有的项目解释,只有模板中没有的项目才调用 DeepSeek 生成 """ import os import json import requests from pathlib import Path from typing import List, Dict, Any class DeepSeekHealthService: """DeepSeek 健康内容生成服务""" def __init__(self, api_key: str = None): self.api_key = api_key or os.getenv("DEEPSEEK_API_KEY", "") self.api_url = "https://api.deepseek.com/v1/chat/completions" # 加载模板中的解释 self.template_explanations = self._load_template_explanations() def _load_template_explanations(self) -> Dict[str, Dict[str, str]]: """加载模板中已有的项目解释""" explanations_file = Path(__file__).parent.parent / "template_explanations.json" if explanations_file.exists(): try: with open(explanations_file, 'r', encoding='utf-8') as f: explanations = json.load(f) print(f" ✓ 已加载 {len(explanations)} 个模板解释") return explanations except Exception as e: print(f" ⚠️ 加载模板解释失败: {e}") return {} def get_template_explanation(self, abb: str) -> Dict[str, str]: """ 获取模板中的项目解释 Args: abb: 项目缩写 Returns: {"clinical_en": "...", "clinical_cn": "..."} 或空字典 """ # 尝试多种匹配方式 abb_upper = abb.upper().strip() # 直接匹配 if abb_upper in self.template_explanations: return self.template_explanations[abb_upper] # 去除特殊字符后匹配 abb_clean = ''.join(c for c in abb_upper if c.isalnum()) for key, value in self.template_explanations.items(): key_clean = ''.join(c for c in key if c.isalnum()) if abb_clean == key_clean: return value return {} def is_available(self) -> bool: """检查服务是否可用""" return bool(self.api_key) def call_deepseek(self, prompt: str) -> str: """调用 DeepSeek API""" if not self.api_key: raise ValueError("未配置 DEEPSEEK_API_KEY") headers = { "Authorization": f"Bearer {self.api_key}", "Content-Type": "application/json" } data = { "model": "deepseek-chat", "messages": [{"role": "user", "content": prompt}], "temperature": 0.1, "max_tokens": 8000 } response = requests.post( self.api_url, headers=headers, json=data, timeout=120 ) response.raise_for_status() return response.json()["choices"][0]["message"]["content"] def collect_abnormal_items(self, analysis: Dict[str, Any]) -> List[Dict[str, str]]: """ 从分析结果中收集异常项 Args: analysis: LLM 分析结果,包含 abnormal_items 字段 Returns: 异常项列表 """ abnormal_items = [] # 从 abnormal_items 字段提取 raw_items = analysis.get("abnormal_items", []) for item in raw_items: if isinstance(item, dict): abnormal_items.append({ "name": item.get("name", ""), "abb": item.get("abb", item.get("name", "")), "result": str(item.get("result", item.get("value", ""))), "reference": item.get("reference", ""), "unit": item.get("unit", ""), "level": item.get("level", ""), "point": "↑" if item.get("level") == "high" else ("↓" if item.get("level") == "low" else "") }) elif isinstance(item, str): # 如果是字符串格式,尝试解析 abnormal_items.append({ "name": item, "abb": "", "result": "", "reference": "", "unit": "", "level": "", "point": "" }) return abnormal_items def get_item_explanations(self, abnormal_items: List[Dict[str, str]]) -> Dict[str, Dict[str, str]]: """ 为异常项获取解释(优先使用模板中的解释,缺失的才调用 DeepSeek 生成) Args: abnormal_items: 异常项列表 Returns: { "ABB": {"clinical_en": "...", "clinical_cn": "..."}, ... } """ explanations = {} items_need_generation = [] print("\n 📋 检查模板中的项目解释...") for item in abnormal_items: abb = item.get("abb", "").upper().strip() name = item.get("name", "") if not abb: continue # 尝试从模板获取解释 template_exp = self.get_template_explanation(abb) if template_exp and template_exp.get("clinical_en") and template_exp.get("clinical_cn"): explanations[abb] = template_exp print(f" ✓ {abb}: 使用模板解释") else: items_need_generation.append(item) print(f" ○ {abb}: 需要生成解释") # 如果有需要生成的项目,调用 DeepSeek if items_need_generation and self.api_key: print(f"\n 🤖 调用 DeepSeek 为 {len(items_need_generation)} 个项目生成解释...") generated = self._generate_missing_explanations(items_need_generation) explanations.update(generated) return explanations def _generate_missing_explanations(self, items: List[Dict[str, str]]) -> Dict[str, Dict[str, str]]: """ 调用 DeepSeek 为缺失解释的项目生成临床意义 Args: items: 需要生成解释的项目列表 Returns: 生成的解释字典 """ if not items: return {} # 构建项目描述 items_desc = [] for item in items: desc = f"- {item['abb']}: {item['name']}" if item.get('result'): desc += f", 结果: {item['result']}" if item.get('unit'): desc += f" {item['unit']}" if item.get('reference'): desc += f", 参考范围: {item['reference']}" items_desc.append(desc) prompt = f"""你是一位医学检验专家,请为以下医疗检测项目生成临床意义解释。 ## 需要解释的项目: {chr(10).join(items_desc)} ## 要求: 1. 为每个项目提供英文和中文的临床意义解释 2. 解释应包含:该指标的作用、正常范围的意义、异常时可能的原因 3. 语言专业但易于理解 4. 每个解释约50-100字 ## 输出格式(JSON): ```json {{ "ABB1": {{ "clinical_en": "English clinical significance...", "clinical_cn": "中文临床意义..." }}, "ABB2": {{ "clinical_en": "...", "clinical_cn": "..." }} }} ``` 只返回JSON,不要其他说明。""" try: response = self.call_deepseek(prompt) # 解析 JSON if "```json" in response: response = response.split("```json")[1].split("```")[0] elif "```" in response: response = response.split("```")[1].split("```")[0] result = json.loads(response.strip()) print(f" ✓ 成功生成 {len(result)} 个项目的解释") return result except Exception as e: print(f" ✗ 生成解释失败: {e}") return {} def generate_health_assessment(self, abnormal_items: List[Dict[str, str]]) -> Dict[str, Any]: """ 生成"整体健康状况"评估内容 Args: abnormal_items: 异常项列表 Returns: 包含多个小节的健康评估内容 """ if not self.api_key or not abnormal_items: return {"sections": []} # 构建异常项描述 abnormal_desc = [] for item in abnormal_items: direction = "偏高" if item.get("point") in ["↑", "H", "高"] or item.get("level") == "high" else "偏低" desc = f"- {item['name']}" if item.get('abb'): desc += f" ({item['abb']})" desc += f": {item['result']}" if item.get('unit'): desc += f" {item['unit']}" desc += f" ({direction}" if item.get('reference'): desc += f", 参考范围: {item['reference']}" desc += ")" abnormal_desc.append(desc) prompt = f"""你是一位功能医学专家,请根据以下所有异常检测指标,撰写"整体健康状况评估"的内容。 ## 异常指标: {chr(10).join(abnormal_desc)} ## 要求: 1. 根据异常指标的类型,自动分成合适的小节(如血液学、内分泌、免疫、代谢等,根据实际异常项决定) 2. 每个小节包含英文和中文两个版本 3. 从功能医学和整体健康角度分析 4. 解释可能的原因和健康影响 5. 语言专业但易于理解 6. 每个小节的每个语言版本约150-250字 ## 输出格式(JSON): ```json {{ "sections": [ {{ "title_en": "(I) Section Title in English", "title_cn": "(一)中文小节标题", "content_en": "English analysis content...", "content_cn": "中文分析内容..." }} ] }} ``` 只返回JSON,不要其他说明。根据实际异常项情况决定分几个小节,不要硬套固定模板。""" try: response = self.call_deepseek(prompt) # 解析 JSON if "```json" in response: response = response.split("```json")[1].split("```")[0] elif "```" in response: response = response.split("```")[1].split("```")[0] result = json.loads(response.strip()) print(f" ✓ 生成健康评估内容,共 {len(result.get('sections', []))} 个小节") return result except Exception as e: print(f" ✗ 生成健康评估内容失败: {e}") return {"sections": []} def generate_health_advice(self, abnormal_items: List[Dict[str, str]]) -> Dict[str, Any]: """ 生成"功能性健康建议"内容 Args: abnormal_items: 异常项列表 Returns: 包含5个固定小节的健康建议内容 """ if not self.api_key or not abnormal_items: return {"sections": []} # 异常项描述 abnormal_desc = [] for item in abnormal_items: direction = "偏高" if item.get("point") in ["↑", "H", "高"] or item.get("level") == "high" else "偏低" desc = f"- {item['name']}" if item.get('abb'): desc += f" ({item['abb']})" desc += f": {item['result']}" if item.get('unit'): desc += f" {item['unit']}" desc += f" ({direction})" abnormal_desc.append(desc) prompt = f"""你是一位功能医学专家,请根据以下异常检测指标,撰写"功能医学健康建议"的内容。 ## 异常指标: {chr(10).join(abnormal_desc)} ## 要求: 1. 必须包含以下5个固定小节(按顺序): - Nutrition Intervention 营养干预 - Exercise Intervention 运动干预 - Sleep & Stress Management 睡眠与压力管理 - Lifestyle Adjustment 生活方式调整 - Long-term Follow-up Plan 长期随访计划 2. 每个小节针对这些异常指标提供具体、可执行的建议 3. 从功能医学角度出发,强调预防和整体调理 4. 每个小节包含3-5条具体建议措施 5. 语言专业但易于理解 6. 分别提供英文和中文版本 7. 每个小节的每个语言版本约200-300字 ## 输出格式(JSON): ```json {{ "sections": [ {{ "title_en": "Nutrition Intervention", "title_cn": "营养干预", "content_en": "English nutrition advice...", "content_cn": "中文营养建议..." }}, {{ "title_en": "Exercise Intervention", "title_cn": "运动干预", "content_en": "...", "content_cn": "..." }}, {{ "title_en": "Sleep & Stress Management", "title_cn": "睡眠与压力管理", "content_en": "...", "content_cn": "..." }}, {{ "title_en": "Lifestyle Adjustment", "title_cn": "生活方式调整", "content_en": "...", "content_cn": "..." }}, {{ "title_en": "Long-term Follow-up Plan", "title_cn": "长期随访计划", "content_en": "...", "content_cn": "..." }} ] }} ``` 只返回JSON,不要其他说明。""" try: response = self.call_deepseek(prompt) # 解析 JSON if "```json" in response: response = response.split("```json")[1].split("```")[0] elif "```" in response: response = response.split("```")[1].split("```")[0] result = json.loads(response.strip()) print(f" ✓ 生成健康建议内容,共 {len(result.get('sections', []))} 个小节") return result except Exception as e: print(f" ✗ 生成健康建议内容失败: {e}") return {"sections": []} def generate_health_content(self, analysis: Dict[str, Any]) -> Dict[str, Any]: """ 生成完整的健康评估和建议内容 优化:优先使用模板中已有的项目解释,只有模板中没有的项目才调用 DeepSeek 生成 Args: analysis: LLM 分析结果 Returns: 包含 health_assessment, health_advice, item_explanations 的字典 """ if not self.is_available(): print(" ⚠️ DeepSeek API Key 未配置,跳过健康内容生成") return {} print("\n============================================================") print("DeepSeek 健康内容生成") print("============================================================") # 收集异常项 print("\n 📝 正在收集异常项...") abnormal_items = self.collect_abnormal_items(analysis) if not abnormal_items: print(" ℹ️ 没有检测到异常项目,跳过内容生成") return {} print(f" 发现 {len(abnormal_items)} 个异常项目:") for item in abnormal_items[:10]: direction = "↑" if item.get("level") == "high" or item.get("point") == "↑" else "↓" print(f" - {item['name']}: {item['result']} {direction}") if len(abnormal_items) > 10: print(f" ... 等共 {len(abnormal_items)} 项") # 获取项目解释(优先使用模板,缺失的才生成) item_explanations = self.get_item_explanations(abnormal_items) # 统计使用情况 template_count = sum(1 for abb in item_explanations if self.get_template_explanation(abb)) generated_count = len(item_explanations) - template_count print(f"\n 📊 解释来源统计: 模板 {template_count} 个, DeepSeek生成 {generated_count} 个") # 生成健康评估 print("\n 🤖 正在调用 DeepSeek 生成整体健康状况...") health_assessment = self.generate_health_assessment(abnormal_items) # 生成健康建议 print("\n 🤖 正在调用 DeepSeek 生成功能性健康建议...") health_advice = self.generate_health_advice(abnormal_items) print("\n ✓ 健康内容生成完成") return { "health_assessment": health_assessment, "health_advice": health_advice, "abnormal_items": abnormal_items, "item_explanations": item_explanations # 包含每个项目的解释 }