""" 报告解析 Agent:算法解析 + LLM 兜底 """ import json import logging from typing import Optional from app.config import settings from app.parsers.pdf_parser import parse_report_pdf_with_fallback logger = logging.getLogger(__name__) class ReportParseAgent: """报告解析 Agent""" def __init__(self): self.llm_api_key = getattr(settings, 'llm_api_key', '') async def parse(self, file_path: str) -> dict: """解析 PDF 报告,算法解析 + LLM 兜底""" # 1. 算法解析 result = parse_report_pdf_with_fallback(file_path) logger.info("算法解析完成: format=%s, overview_keys=%d", result.get('format'), len(result.get('overview', {}))) # 2. 如果解析不完整,LLM 兜底 if result.get('_parse_incomplete') or not result.get('disease_risks'): logger.info("算法解析不完整,尝试 LLM 兜底") llm_result = await self._parse_with_llm(file_path) if llm_result: # 合并 LLM 结果到算法结果上(LLM 覆盖缺失字段) for key in ['disease_risks', 'nutrition', 'amino_acids', 'vitamins', 'trace_elements', 'indicators']: if key in llm_result and not result.get(key): result[key] = llm_result[key] if llm_result.get('overview'): for k, v in llm_result['overview'].items(): if k not in result.get('overview', {}): result.setdefault('overview', {})[k] = v # 清理内部标记 result.pop('_parse_incomplete', None) return result async def _parse_with_llm(self, file_path: str) -> Optional[dict]: """LLM 兜底解析""" try: from PyPDF2 import PdfReader reader = PdfReader(file_path) text = '\n'.join(page.extract_text() or '' for page in reader.pages) # 构造 prompt prompt = f"""你是一个肠道菌群检测报告解析专家。请从以下PDF文本中提取结构化数据,返回JSON格式。 文本内容: {text[:8000]} 请按以下JSON Schema返回: {{ "overview": {{ "person_name": "", "report_number": "", "age": 0, "gender": "male/female", "overallScore": 0, "gutHealthScore": 0, "chronicDiseaseScore": 0, "nutritionScore": 0, "gutAge": "", "gutType": "" }}, "disease_risks": [{{"name": "", "value": "", "status": ""}}], "nutrition": [{{"name": "", "value": "", "status": ""}}], "amino_acids": [{{"name": "", "value": "", "status": ""}}], "vitamins": [{{"name": "", "value": "", "status": ""}}], "trace_elements": [{{"name": "", "value": "", "status": ""}}] }} 只返回JSON,不要其他文字。""" if self.llm_api_key: # 调 OpenAI 兼容 API import httpx async with httpx.AsyncClient(timeout=60) as client: resp = await client.post( f"{settings.llm_base_url}/chat/completions", json={ "model": settings.llm_model or "gpt-4o", "messages": [{"role": "user", "content": prompt}], "temperature": 0.1, }, headers={"Authorization": f"Bearer {self.llm_api_key}"}, ) resp.raise_for_status() data = resp.json() content = data['choices'][0]['message']['content'] content = content.replace('```json', '').replace('```', '').strip() return json.loads(content) else: logger.warning("LLM 未配置,跳过 LLM 兜底") return None except Exception as e: logger.warning("LLM 解析失败: %s", e) return None