Преглед изворни кода

"feat: 添加 HealthRecordAgent 毕业设计项目"

Shawnxyxy пре 3 месеци
родитељ
комит
e66dec2786
23 измењених фајлова са 1577 додато и 0 уклоњено
  1. 105 0
      Co-creation-projects/Shawnxyxy-HealthRecordAgent/README.md
  2. 19 0
      Co-creation-projects/Shawnxyxy-HealthRecordAgent/backend/agents/__init__.py
  3. 83 0
      Co-creation-projects/Shawnxyxy-HealthRecordAgent/backend/agents/advice.py
  4. 302 0
      Co-creation-projects/Shawnxyxy-HealthRecordAgent/backend/agents/base.py
  5. 67 0
      Co-creation-projects/Shawnxyxy-HealthRecordAgent/backend/agents/health_indicator.py
  6. 120 0
      Co-creation-projects/Shawnxyxy-HealthRecordAgent/backend/agents/planner.py
  7. 93 0
      Co-creation-projects/Shawnxyxy-HealthRecordAgent/backend/agents/report.py
  8. 63 0
      Co-creation-projects/Shawnxyxy-HealthRecordAgent/backend/agents/risk_assess.py
  9. 18 0
      Co-creation-projects/Shawnxyxy-HealthRecordAgent/backend/api/main.py
  10. 59 0
      Co-creation-projects/Shawnxyxy-HealthRecordAgent/backend/api/routes/health.py
  11. 13 0
      Co-creation-projects/Shawnxyxy-HealthRecordAgent/backend/core/__init__.py
  12. 58 0
      Co-creation-projects/Shawnxyxy-HealthRecordAgent/backend/core/config.py
  13. 29 0
      Co-creation-projects/Shawnxyxy-HealthRecordAgent/backend/core/exceptions.py
  14. 83 0
      Co-creation-projects/Shawnxyxy-HealthRecordAgent/backend/core/llm_adapter.py
  15. 5 0
      Co-creation-projects/Shawnxyxy-HealthRecordAgent/backend/main.py
  16. 0 0
      Co-creation-projects/Shawnxyxy-HealthRecordAgent/backend/service/__init__.py
  17. 87 0
      Co-creation-projects/Shawnxyxy-HealthRecordAgent/backend/service/health_analysis.py
  18. 53 0
      Co-creation-projects/Shawnxyxy-HealthRecordAgent/data/sample_reports/report.txt
  19. 131 0
      Co-creation-projects/Shawnxyxy-HealthRecordAgent/frontend/app.js
  20. 62 0
      Co-creation-projects/Shawnxyxy-HealthRecordAgent/frontend/index.html
  21. BIN
      Co-creation-projects/Shawnxyxy-HealthRecordAgent/frontend/screenshots/example.png
  22. 88 0
      Co-creation-projects/Shawnxyxy-HealthRecordAgent/frontend/style.css
  23. 39 0
      Co-creation-projects/Shawnxyxy-HealthRecordAgent/requirements.txt

+ 105 - 0
Co-creation-projects/Shawnxyxy-HealthRecordAgent/README.md

@@ -0,0 +1,105 @@
+# 健康档案助手-HealthRecordAgent
+
+> 基于 HelloAgents 框架的多智能体健康档案助手
+> 支持体检报告、健康档案(文本、PDF)结构化解析、指标解读和健康建议生成
+
+## 📝 项目简介
+
+在现实场景中,体检报告通常以 PDF 或表格形式给出,包含大量医学指标,但:
+- 指标含义不清晰
+- 正常 / 异常范围难以判断
+- 缺乏整体健康解读和可执行建议
+
+本项目通过 **多智能体协作(Multi-Agent)** 的方式,对体检报告进行:
+- 结构化解析
+- 指标语义解释
+- 风险初步评估
+- 个性化健康建议生成
+
+适用于:
+- 个人健康管理
+- 健康数据理解与科普
+- 多智能体应用 / Agent 系统毕业设计示例
+
+## ✨ 核心功能
+
+- [x] **体检报告解析**
+  - 支持 PDF / 文本形式的体检报告输入
+  - 自动抽取关键健康指标(如血常规、生化指标等)
+- [x] **健康指标解读**
+  - 给出指标含义、参考范围与异常提示
+  - 使用自然语言进行“非医学术语”的解释
+- [x] **多智能体协作分析**
+  - 不同 Agent 分工完成解析、判断与建议生成
+  - 提高分析的结构性与可解释性
+- [ ] **健康档案长期管理(规划中)**
+  - 多次体检记录对比
+  - 趋势分析与健康变化追踪
+
+## 🛠️ 技术栈
+
+- HelloAgents框架
+- 使用的智能体范式(如ReAct、Plan-and-Solve等)
+    Plan-and-Solve + ReAct(混合)
+    由一个 Planner 规划健康分析流程,多个 Specialist Agent 按步骤协作完成健康档案解读   
+- **后端框架**: FastAPI + Uvicorn
+- **异步处理**: asyncio
+- **PDF 解析**: pdfplumber
+
+智能体协作方式:
+- PlannerAgent 负责整体分析流程规划  
+- HealthIndicatorAgent 解析指标并解读  
+- RiskAssessmentAgent 进行风险评估  
+- AdviceAgent 生成个性化健康建议  
+- ReportAgent 汇总输出最终报告 
+
+## 🚀 快速开始
+
+### 环境要求
+
+- Python 3.10+
+- 其他要求
+
+### 安装依赖
+
+pip install -r requirements.txt
+
+### 配置API密钥
+
+# 创建.env文件
+cp .env.example .env
+
+# 编辑.env文件,填入你的API密钥
+
+
+### 运行项目
+
+uvicorn backend.api.routes.main:app --reload
+
+服务启动后,可通过浏览器或前端调用 API 接口:
+	•	文本报告分析: POST /api/health/analysis
+	•	PDF 报告分析: POST /api/health/analysis/pdf
+	•	任务状态查询: GET /api/health/task_status/{task_id}
+
+## 🎯 项目亮点
+	•	多智能体分工协作,结构化解析体检报告
+	•	支持文本与 PDF 输入,自动抽取健康指标
+	•	可扩展健康档案管理与趋势分析
+	•	异步任务处理,前端实时显示 Agent 执行状态
+
+## 📖 使用示例
+
+![报告分析](frontend/screenshots/example.png)
+
+## 🤝 贡献指南
+
+欢迎提出Issue和Pull Request!
+
+## 👤 作者
+
+- GitHub: [@Shawnxyxy](https://github.com/Shawnxyxy)
+- Email: 852679909@qq.com
+
+## 🙏 致谢
+
+感谢Datawhale社区和Hello-Agents项目!

+ 19 - 0
Co-creation-projects/Shawnxyxy-HealthRecordAgent/backend/agents/__init__.py

@@ -0,0 +1,19 @@
+"""
+Health AI Agents
+"""
+
+from .base import BaseAgent
+from .planner import PlannerAgent
+from .health_indicator import HealthIndicatorAgent
+from .risk_assess import RiskAssessmentAgent
+from .advice import AdviceAgent
+from .report import ReportAgent
+
+__all__ = [
+    "BaseAgent",
+    "PlannerAgent",
+    "HealthIndicatorAgent",
+    "RiskAssessmentAgent",
+    "AdviceAgent",
+    "ReportAgent"
+]

+ 83 - 0
Co-creation-projects/Shawnxyxy-HealthRecordAgent/backend/agents/advice.py

@@ -0,0 +1,83 @@
+"""
+健康建议 Agent
+"""
+
+import json
+from typing import Dict, Any, List
+from agents.base import BaseAgent
+from core.exceptions import AgentException
+
+class AdviceAgent(BaseAgent):
+    def __init__(self, task_id=None, llm=None):
+        super().__init__(name="AdviceAgent",  task_id=task_id, llm=llm)
+
+    async def run(self, input_data: Dict[str, Any]) -> Dict[str, Any]:
+        self.set_state("running")
+
+        overall_risk_level = input_data.get("overall_risk_level")
+        risk_factors = input_data.get("risk_factors", [])
+        potential_conditions = input_data.get("potential_conditions", [])
+        confidence = input_data.get("confidence", 0.0)
+
+        prompt = self._build_prompt(
+            overall_risk_level,
+            risk_factors,
+            potential_conditions,
+            confidence
+        )
+
+        response = await self.think(prompt)
+
+        try:
+            result = json.loads(response)
+        except json.JSONDecodeError:
+            result = {
+                "summary": "解析失败,返回原始结果",
+                "raw_response": response
+            }
+
+        self.set_state("completed")
+
+    def _build_prompt(
+        self,
+        overall_risk_level: str,
+        risk_factors: List[str],
+        potential_conditions: List[str],
+        confidence: float
+    ) -> str:
+        return f"""
+你是一名专业的健康管理助手。
+请基于以下健康风险评估结果,为用户生成合理、可执行的健康建议。
+
+健康风险评估结果:
+- 总体风险等级:{overall_risk_level}
+- 风险因素:{risk_factors}
+- 潜在健康问题:{potential_conditions}
+- 评估置信度:{confidence}
+
+请遵循以下原则:
+- 不进行医学诊断
+- 建议应偏向生活方式、预防、监测和就医提示
+- 建议应具体、可执行
+- 根据风险等级调整建议的优先级
+
+请以 JSON 格式返回,例如:
+{{
+  "advice": [
+    {{
+      "target": "<对应的风险因素>",
+      "category": "生活方式 | 饮食 | 运动 | 就医建议 | 监测",
+      "suggestion": "<具体可执行建议>",
+      "priority": "high | medium | low"
+    }}
+  ],
+  "overall_tone": "<整体建议风格,如:保守 / 积极干预>"
+}}
+"""
+
+    def get_required_fields(self) -> list[str]:
+        return [
+            "risk_level",
+            "risk_factors"
+        ]
+        

+ 302 - 0
Co-creation-projects/Shawnxyxy-HealthRecordAgent/backend/agents/base.py

@@ -0,0 +1,302 @@
+"""
+HealthRecordAgent 基础智能体类
+"""
+
+import asyncio
+import json
+import logging
+from abc import ABC, abstractmethod
+from typing import Any, Dict, List, Callable, Optional, ClassVar
+from datetime import datetime
+
+from core.config import get_config
+from core.llm_adapter import get_llm_adapter
+from core.exceptions import AgentException, TimeoutException
+
+from enum import Enum
+
+# 全局任务状态管理
+TASKS = {}
+
+def create_task(task_id: str):
+    TASKS[task_id] = {
+        "task_id": task_id,
+        "state": "running",
+        "agents": {
+            "PlannerAgent": "pending",
+            "HealthIndicatorAgent": "pending",
+            "RiskAssessmentAgent": "pending",
+            "AdviceAgent": "pending",
+            "ReportAgent": "pending"},
+        "report": None,  # 最终报告
+    }
+
+def update_agent_state(task_id: str, agent_name: str, state: str, partial_report=None):
+    task = TASKS.get(task_id)
+    if not task:
+        return
+    task["agents"][agent_name] = state
+    if partial_report:
+        task["report"] = partial_report
+    
+def complete_task(task_id: str, report: dict):
+    task = TASKS.get(task_id)
+    if not task:
+        return
+    task["state"] = "completed"
+    task["report"] = report
+    for agent in task["agents"]:
+        task["agents"][agent] = "completed"
+
+def get_task_status(task_id: str):
+    return TASKS.get(task_id)
+
+class TraceLevel(str, Enum):
+    INFO = "INFO"
+    DEBUG = "DEBUG"
+    TRACE = "TRACE"
+    ERROR = "ERROR"
+
+logger = logging.getLogger(__name__)
+
+class BaseAgent(ABC):
+    """
+    基础智能体抽象类
+    """
+
+    def __init__(
+        self, name: str, llm = None, 
+                 max_steps: int = None, timeout: int = None, debug: bool = True, task_id = None):
+        self.name = name
+        self.config = get_config()
+        self.llm = llm or get_llm_adapter()
+        
+        self.max_steps = max_steps or self.config.agent.max_steps
+        self.timeout = timeout or self.config.agent.timeout
+
+        self.history = []
+        self.tools = {}
+        self.state = "idle"
+        self.created_at = datetime.now()
+        self.debug = debug
+        self.traces: List[Dict[str, Any]] = []
+        self.task_id = task_id
+    # ========== 核心接口 ==========
+    @abstractmethod
+    async def run(self, **kwargs) -> Any:
+        """Agent 执行入口"""
+        pass
+
+    # ========== LLM 思考 ==========
+    async def think(self, prompt: str, context: Dict = None) -> str:
+        """调用LLM进行思考"""
+        try:
+            # 构建完整的提示词
+            full_prompt = prompt
+            
+            # 添加上下文信息
+            if context:
+                context_str = json.dumps(context, ensure_ascii=False, indent=2)
+                full_prompt = f"上下文信息:\n{context_str}\n\n任务:\n{prompt}"
+            
+            # 添加历史记录
+            if self.history:
+                history_str = "\n".join(self.history[-10:])  # 只保留最近10条
+                full_prompt += f"\n\n历史记录:\n{history_str}"
+            
+            self.trace("LLM CALL",
+                {
+                    "prompt_length": len(full_prompt),
+                    "history_length": len(self.history)
+                },
+                TraceLevel.INFO
+            )
+
+            start = datetime.now()
+            
+            # 调用 HelloAgent LLM
+            response = await asyncio.wait_for(
+                self.llm.ainvoke(full_prompt),
+                timeout=self.timeout
+            )
+
+            duration = (datetime.now() - start).total_seconds()
+
+            self.trace("LLM TTHINKING TIME",
+                {
+                    "duration_sec": duration,
+                    "prompt_tokens": len(full_prompt),
+                }
+            )
+            
+            response_text = response.content if hasattr(response, 'content') else str(response)
+            
+            self.trace("LLM RESPONSE", response_text)
+
+            self._add_to_history(f"LLM prompt: {prompt}")
+            self._add_to_history(f"LLM response: {response_text}")
+            
+            return response_text
+            
+        except asyncio.TimeoutError:
+            raise TimeoutException(f"LLM思考超时")
+        except Exception as e:
+            raise AgentException(f"LLM思考失败: {str(e)}")
+    # ========== Tool 机制 ==========
+    def add_tool(self, tool_name: str, tool_func: Callable, description: str = ""):
+        """添加工具"""
+        self.tools[tool_name] = {
+            "function": tool_func,
+            "description": description
+        }
+    
+    def get_tools_description(self) -> str:
+        """获取工具描述"""
+        if not self.tools:
+            return "暂无可用工具"
+        
+        descriptions = []
+        for name, tool_info in self.tools.items():
+            descriptions.append(f"- {name}: {tool_info['description']}")
+        
+        return "\n".join(descriptions)
+    
+    async def call_tool(self, tool_name: str, tool_input: Any) -> Any:
+        """调用工具"""
+        if tool_name not in self.tools:
+            raise AgentException(f"工具 '{tool_name}' 不存在")
+        
+        try:
+            tool_func = self.tools[tool_name]["function"]
+            if asyncio.iscoroutinefunction(tool_func):
+                result = await asyncio.wait_for(
+                    tool_func(tool_input), 
+                    timeout=self.timeout
+                )
+            else:
+                result = await asyncio.wait_for(
+                    asyncio.to_thread(tool_func, tool_input),
+                    timeout=self.timeout
+                )
+            
+            self._add_to_history(f"Tool {tool_name} called with input: {tool_input}")
+            self._add_to_history(f"Tool {tool_name} result: {result}")
+            
+            return result
+            
+        except asyncio.TimeoutError:
+            raise TimeoutException(f"工具 '{tool_name}' 执行超时")
+        except Exception as e:
+            raise AgentException(f"工具 '{tool_name}' 执行失败: {str(e)}")
+    # ========== 状态 & 历史 ==========
+    def _add_to_history(self, message: str):
+        """添加到历史记录"""
+        timestamp = datetime.now().isoformat()
+        self.history.append(f"[{timestamp}] {message}")
+        
+        # 限制历史记录长度
+        if len(self.history) > 100:
+            self.history = self.history[-50:]
+    
+    def get_history(self, limit: int = 10) -> List[str]:
+        """获取历史记录"""
+        return self.history[-limit:]
+    
+    def clear_history(self):
+        """清空历史记录"""
+        self.history = []
+    
+    def set_state(self, state: str):
+        """设置智能体状态"""
+        self.state = state
+
+        # 更新全局任务状态
+        if self.task_id:
+            update_agent_state(self.task_id, self.name, state)
+
+        self.trace("STATE CHANGE",
+            {
+                "state": state
+            }
+        )
+        logger.info(f"Agent {self.name} state changed to: {state}")
+    
+    def get_status(self) -> Dict[str, Any]:
+        """获取智能体状态"""
+        return {
+            "name": self.name,
+            "state": self.state,
+            "created_at": self.created_at.isoformat(),
+            "history_count": len(self.history),
+            "tools_count": len(self.tools),
+            "max_steps": self.max_steps,
+            "timeout": self.timeout
+        }
+
+    async def validate_input(self, input_data: Dict[str, Any]) -> bool:
+        """验证输入数据"""
+        required_fields = self.get_required_fields()
+        
+        for field in required_fields:
+            if field not in input_data:
+                raise AgentException(f"缺少必需字段: {field}")
+        
+        return True
+    
+    @abstractmethod
+    def get_required_fields(self) -> List[str]:
+        """获取必需的输入字段"""
+        pass
+
+    def trace(self, title: str, data: Any, level: TraceLevel = TraceLevel.DEBUG):
+        """统一Agent调试输出"""
+        event = {
+        "agent": self.name,
+        "title": title,
+        "timestamp": datetime.now().isoformat(),
+        "data": data
+        }
+
+        self.traces.append({
+            **event,
+            "level": level
+        })
+
+        if not self.debug:
+            return
+        
+        if level in [TraceLevel.INFO, TraceLevel.ERROR]:
+            logger.info(f"[{self.name}] {title}")
+            return
+
+        if level == TraceLevel.DEBUG:
+            preview = self._preview(data)
+            logger.debug(f"[{self.name}] {title}: {preview}")
+
+        try:
+            print(json.dumps(data, indent=2, ensure_ascii=False))
+        except Exception:
+            print(data)
+
+    def trace_step(self, step: str, status: str):
+        self.trace(
+            "STEP",
+            {
+                "step": step,
+                "status": status
+            }
+        )
+    def get_traces(self) -> List[Dict[str, Any]]:
+        return self.traces
+    
+    def _preview(self, data, max_len: int = 300):
+        """日志摘要"""
+        if data is None:
+            return ""
+
+        text = str(data)
+
+        if len(text) > max_len:
+            return text[:max_len] + "...(truncated)"
+
+        return text

+ 67 - 0
Co-creation-projects/Shawnxyxy-HealthRecordAgent/backend/agents/health_indicator.py

@@ -0,0 +1,67 @@
+"""
+健康指标分析 Agent
+"""
+
+import json
+from typing import Dict, Any, List
+from agents.base import BaseAgent
+
+class HealthIndicatorAgent(BaseAgent):
+    def __init__(self, task_id=None, llm=None):
+        super().__init__(name="HealthIndicatorAgent",  task_id=task_id, llm=llm)
+    
+    async def run(self, input_data: Dict[str, Any]) -> Dict[str, Any]:
+        await self.validate_input(input_data)
+        self.set_state("running")
+
+        report_text = input_data["report_text"]
+
+        prompt = f"""
+你是一名专业的健康分析助手。
+请从以下体检或健康报告中提取关键健康指标,并判断风险。
+
+报告内容:
+{report_text}
+
+请返回 JSON,严格遵循以下格式:
+{{
+  "indicator_results": {{
+    "<指标名>": {{
+      "value": "<原始数值或描述>",
+      "status": "<normal | borderline | high | low | abnormal>",
+      "risk_level": "<low | medium | high>",
+      "analysis": "<简要分析该指标的健康含义>"
+    }}
+  }}
+}}
+
+要求:
+- 每个指标单独分析,不要给出综合结论
+- 不要给出任何健康建议
+- 如果报告中未提及明确数值,可用描述性判断
+"""
+        response = await self.think(prompt)
+        indicators: List[Dict[str, Any]] = []
+
+        try:
+            result = json.loads(response)
+            indicator_dict = result.get("indicator_results", {})
+            for name, data in indicator_dict.items():
+                indicators.append({
+                    "name": name,
+                    "value": data.get("value"),
+                    "status": data.get("status"),
+                    "risk_level": data.get("risk_level"),
+                    "analysis": data.get("analysis")
+                })
+        except json.JSONDecodeError:
+            # LLM 输出异常保护
+            indicators = []
+
+        self.set_state("completed")
+        return {
+            "indicators": indicators
+        }
+    
+    def get_required_fields(self) -> List[str]:
+        return ["report_text"]

+ 120 - 0
Co-creation-projects/Shawnxyxy-HealthRecordAgent/backend/agents/planner.py

@@ -0,0 +1,120 @@
+"""
+HealthRecord 健康档案规划师 (planner Agent)
+负责对健康档案、体检报告进行分析任务的拆解和规划
+"""
+
+import os
+import json
+from datetime import datetime
+from core.exceptions import AgentException
+from typing import Dict, Any, List
+from agents.base import BaseAgent
+
+class PlannerAgent(BaseAgent):
+    """任务规划智能体"""
+    def __init__(self, task_id=None, llm=None):
+        super().__init__(name="Planner", task_id=task_id, llm=llm)
+
+    async def run(self, input_data: Dict[str, Any]) -> Dict[str, Any]:
+        """
+        Planner 的唯一入口
+        """
+        await self.validate_input(input_data)
+        self.set_state("running")
+
+        try:
+            goal = input_data["goal"]
+            context = input_data.get("context", {})
+
+            prompt = self._build_planner_prompt(goal, context)
+
+            response = await self.think(prompt)
+
+            plan = self._parse_plan(response)
+
+            self.set_state("completed")
+            self._add_to_history(f"生成计划,包含 {len(plan)} 个步骤")
+
+            result = {
+                "status": "success",
+                "goal": goal,
+                "plan": plan,
+                "created_at": datetime.now().isoformat()
+            }
+
+            self.set_state("completed")
+
+            return result
+
+        except Exception as e:
+            self.set_state("error")
+            raise AgentException(f"PlannerAgent 执行失败: {str(e)}")
+    
+    def get_required_fields(self) -> List[str]:
+        """
+        Planner 只关心 goal
+        """
+        return ["goal"]
+
+    # ======================
+    # 内部方法
+    # ======================
+    def _build_planner_prompt(self, goal: str, context: Dict[str, Any]) -> str:
+        """
+        构造 Planner Prompt (Plan-And-Solve)
+        """
+        return f"""
+你是一个 Planner Agent,擅长将复杂目标拆解为可执行的子任务。
+
+【总目标】
+{goal}
+
+【上下文信息】
+{json.dumps(context, ensure_ascii=False, indent=2)}
+
+请遵循以下原则:
+1.将目标拆解为 3 到 6 个清晰、可执行的步骤
+2.每个步骤只做一件事
+3.明确该步骤最适合由哪类智能体完成
+4.步骤之间应具有逻辑顺序
+5.不要执行任务,只做规划
+
+【可用智能体类型示例】
+- HealthAnalyzer:健康数据解析
+- RiskEvaluator:风险评估
+- KnowledgeRetriever:医学知识查询
+- ReportWriter:总结与建议生成
+
+【输出格式】
+请严格以 JSON 格式输出,不要包含多余解释:
+
+{{
+  "plan": [
+    {{
+      "step": 1,
+      "agent": "AgentName",
+      "task": "任务描述",
+      "input": "该步骤需要的输入"
+    }}
+  ]
+}}
+"""
+    def _parse_plan(self, response: str) -> List[Dict[str, Any]]:
+        """
+        解析 LLM 输出的 Plan
+        """
+        try:
+            data = json.loads(response)
+            plan = data.get("plan", [])
+            if not plan:
+                raise ValueError("Plan 为空")
+            return plan
+        except Exception:
+            return [
+                {
+                    "step": 1,
+                    "agent": "FallbackAgent",
+                    "task": "解析失败,需人工或二次规划",
+                    "input": response
+                }
+            ]

+ 93 - 0
Co-creation-projects/Shawnxyxy-HealthRecordAgent/backend/agents/report.py

@@ -0,0 +1,93 @@
+"""
+健康报告生成 Agent
+"""
+
+import json
+from typing import Dict, Any, List
+from agents.base import BaseAgent
+from core.exceptions import AgentException
+
+class ReportAgent(BaseAgent):
+    def __init__(self, task_id=None, llm=None):
+        super().__init__(name="ReportAgent",  task_id=task_id, llm=llm)
+
+    async def run(self, input_data: Dict[str, Any]) -> Dict[str, Any]:
+        self.set_state("running")
+
+        indicators = input_data.get("indicators", [])
+        risk_assessment = input_data.get("risk_assessment", {})
+        advice = input_data.get("advice") or {}
+        confidence = risk_assessment.get("confidence", 0.5)
+
+        advice_list = advice.get("advice", [])
+
+        prompt = self._build_prompt(indicators, risk_assessment, advice_list, confidence)
+        response = await self.think(prompt)
+
+        try:
+            result = json.loads(response)
+            summary = result.get("summary", "根据当前分析生成的健康报告摘要。")
+        except json.JSONDecodeError:
+            result = {
+                "summary": "解析失败,返回原始结果",
+                "raw_response": response
+            }
+
+        # 构建最终报告
+        report = {
+            "title": "个人健康评估报告",
+            "summary": summary,
+            "indicator_section": indicators,
+            "risk_section": risk_assessment,
+            "advice_section": advice_list,
+            "confidence": confidence,
+            "disclaimer": "本报告仅供健康管理参考,不构成医疗诊断。"
+        }
+
+        self.set_state("completed")
+
+        return {
+            "report": {
+                **report,
+                "report_text": summary
+            }
+        }
+    
+    def _build_prompt(
+    self,
+    indicators: List[Dict[str, Any]],
+    risk_assessment: Dict[str, Any],
+    advice_list: List[Dict[str, Any]],
+    confidence: float
+) -> str:
+
+        return f"""
+你是一名健康报告整理助手。
+请根据以下结构化分析结果,生成一份清晰、专业、易读的健康评估报告。
+
+健康指标分析结果:
+{json.dumps(indicators, ensure_ascii=False, indent=2)}
+
+健康风险评估结果:
+{json.dumps(risk_assessment, ensure_ascii=False, indent=2)}
+
+健康建议:
+{json.dumps(advice_list, ensure_ascii=False, indent=2)}
+
+整体置信度:
+{confidence}
+
+要求:
+- 不新增分析结论
+- 不修改已有判断
+- 语言清晰、结构清楚
+- 面向普通用户
+
+请返回 JSON 格式:
+{{
+  "summary": "..."
+}}
+"""
+
+    def get_required_fields(self) -> list[str]:
+        return []

+ 63 - 0
Co-creation-projects/Shawnxyxy-HealthRecordAgent/backend/agents/risk_assess.py

@@ -0,0 +1,63 @@
+"""
+健康风险评估 Agent
+"""
+import json
+from typing import Dict, Any, List
+from agents.base import BaseAgent
+from core.exceptions import AgentException
+
+class RiskAssessmentAgent(BaseAgent):
+    def __init__(self, task_id=None, llm=None):
+        super().__init__(name="RiskAssessment", task_id=task_id, llm=llm)
+
+    async def run(self, input_data: Dict[str, Any]) -> Dict[str, Any]:
+        try:
+            indicator_results = input_data["indicator_results"]
+            if not indicator_results:
+                raise AgentException("缺少健康指标分析结果")
+            self.set_state("running")
+
+            result = await self._assess_risk(indicator_results)
+
+            self.set_state("completed")
+            return result
+        except Exception as e:
+            self.set_state("error")
+            raise AgentException(f"RiskAssessmentAgent 执行失败: {str(e)}")
+
+    async def _assess_risk(self, indicator_results: Dict[str, Any]) -> Dict[str, Any]:
+        risk_prompt = f"""
+你是一名专业的健康风险评估专家。
+
+以下是某用户的健康指标分析结果(已由其他智能体完成分析):
+{indicator_results}
+
+请你完成以下任务:
+1. 综合判断用户的整体健康风险等级(low / medium / high)
+2. 列出主要风险因素(不超过 5 条)
+3. 推测可能存在的潜在健康风险或疾病方向
+4. 给出你评估的置信度(0~1 之间的小数)
+
+请以 JSON 格式返回,例如:
+{{
+  "overall_risk_level": "medium",
+  "risk_factors": ["高胆固醇", "睡眠不足"],
+  "potential_conditions": ["心血管疾病风险"],
+  "confidence": 0.78
+}}
+"""
+        response = await self.think(risk_prompt)
+
+        try:
+            result = json.loads(response)
+        except json.JSONDecodeError:
+            result = {
+                "summary": "解析失败,返回原始结果",
+                "raw_response": response
+            }
+
+        self.set_state("completed")
+        return result
+    
+    def get_required_fields(self) -> list[str]:
+        return ["age", "weight", "height", "blood_pressure"]

+ 18 - 0
Co-creation-projects/Shawnxyxy-HealthRecordAgent/backend/api/main.py

@@ -0,0 +1,18 @@
+from fastapi import FastAPI
+from api.routes.health import router as health_router
+from fastapi.middleware.cors import CORSMiddleware
+
+app = FastAPI(
+    title="HealthRecordAgent API",
+    version="1.0.0"
+)
+
+app.include_router(health_router, prefix="/api")
+
+app.add_middleware(
+    CORSMiddleware,
+    allow_origins=["*"],  # 开发阶段允许全部
+    allow_credentials=True,
+    allow_methods=["*"],
+    allow_headers=["*"],
+)

+ 59 - 0
Co-creation-projects/Shawnxyxy-HealthRecordAgent/backend/api/routes/health.py

@@ -0,0 +1,59 @@
+from fastapi import APIRouter, UploadFile, File
+from pydantic import BaseModel
+from io import BytesIO
+import pdfplumber
+
+from uuid import uuid4
+import asyncio
+
+from service.health_analysis import HealthAnalysisService
+
+router = APIRouter()
+
+class HealthRequest(BaseModel):
+    report_text: str
+
+@router.post("/health/analysis")
+async def analysis_health(request: HealthRequest):
+    task_id = str(uuid4())
+
+    service = HealthAnalysisService(task_id=task_id)
+    # 异步启动分析,不阻塞接口返回
+    asyncio.create_task(service.run(request.report_text))
+
+    return {"task_id": task_id}
+
+@router.post("/health/analysis/pdf")
+async def analysis_health_pdf(file: UploadFile = File(...)):
+
+    # 读取 PDF 二进制
+    contents = await file.read()
+
+    text = ""
+
+    # 使用 pdfplumber 提取文本
+    with pdfplumber.open(BytesIO(contents)) as pdf:
+        for page in pdf.pages:
+            page_text = page.extract_text()
+            if page_text:
+                text += page_text + "\n"
+
+    if not text.strip():
+        return {"error": "无法从PDF中提取文本"}
+
+    task_id = str(uuid4())
+    service = HealthAnalysisService(task_id=task_id)
+
+    asyncio.create_task(service.run(text))
+
+    return {"task_id": task_id}
+
+@router.get("/health/task_status/{task_id}")
+async def task_status(task_id: str):
+    from agents.base import get_task_status
+
+    status = get_task_status(task_id)
+    if not status:
+        return {"error": "task not found"}
+
+    return status

+ 13 - 0
Co-creation-projects/Shawnxyxy-HealthRecordAgent/backend/core/__init__.py

@@ -0,0 +1,13 @@
+"""
+Core module
+"""
+
+from .config import get_config
+from .llm_adapter import get_llm_adapter
+from .exceptions import AgentException
+
+_all_ = {
+    "get_config",
+    "get_llm_adapter",
+    "AgentException",
+}

+ 58 - 0
Co-creation-projects/Shawnxyxy-HealthRecordAgent/backend/core/config.py

@@ -0,0 +1,58 @@
+"""
+HealthAgent 核心配置模块
+"""
+
+from dataclasses import dataclass, field
+from typing import Optional
+import os
+from dotenv import load_dotenv
+
+load_dotenv()
+
+# ========== LLM ==========
+@dataclass
+class LLMConfig:
+    model_name: str = field(
+        default_factory=lambda: os.getenv("OPENAI_MODEL_ID", "qwen-turbo")
+    )
+    api_key: Optional[str] = field(
+        default_factory=lambda: os.getenv("OPENAI_API_KEY")
+    )
+    base_url: Optional[str] = field(
+        default_factory=lambda: os.getenv("OPENAI_BASE_URL")
+    )
+    temperature: float = 0.7
+    max_tokens: int = 2048
+    timeout: int = 60
+# ========== Agent ==========
+@dataclass
+class AgentConfig:
+    max_steps: int = 5
+    timeout: int = 300
+    history_limit: int = 50
+# ========== RAG ==========
+@dataclass
+class RAGConfig:
+    enabled: bool = False
+    top_k: int = 5
+# ========== App ==========
+@dataclass
+class AppConfig:
+    app_name: str = "HealthRecordAgent"
+    debug: bool = False
+    log_level: str = "INFO"
+
+# ========== Main ==========
+@dataclass
+class HealthAgentConfig:
+    app: AppConfig = field(default_factory=AppConfig)
+    llm: LLMConfig = field(default_factory=LLMConfig)
+    agent: AgentConfig = field(default_factory=AgentConfig)
+    rag: RAGConfig = field(default_factory=RAGConfig)
+
+# 全局配置
+_config = HealthAgentConfig()
+
+
+def get_config() -> HealthAgentConfig:
+    return _config

+ 29 - 0
Co-creation-projects/Shawnxyxy-HealthRecordAgent/backend/core/exceptions.py

@@ -0,0 +1,29 @@
+"""
+HealthRecordAgent 项目异常体系
+"""
+
+class HealthAgentException(Exception):
+    """ 基础异常类"""
+    def __init__(self, message: str):
+        self.message = message
+        super().__init__(message)
+
+
+class AgentException(HealthAgentException):
+    """Agent 执行异常"""
+    pass
+
+
+class ValidationException(HealthAgentException):
+    """输入 / 输出校验异常"""
+    pass
+
+
+class LLMException(HealthAgentException):
+    """LLM 调用异常"""
+    pass
+
+
+class TimeoutException(HealthAgentException):
+    """超时异常"""
+    pass

+ 83 - 0
Co-creation-projects/Shawnxyxy-HealthRecordAgent/backend/core/llm_adapter.py

@@ -0,0 +1,83 @@
+"""
+LLM 适配器 - 基于 HelloAgent 框架
+"""
+
+import asyncio
+import logging
+from typing import Any, List, Union
+
+from core.config import get_config
+from core.exceptions import LLMException
+
+logger = logging.getLogger(__name__)
+
+class LLMAdapter:
+    def __init__(self):
+        self.config = get_config()
+        self.llm = None
+        self._init_llm()
+
+    def _init_llm(self):
+        """初始化 HelloAgent LLM"""
+        try:
+            from hello_agents import HelloAgentsLLM
+
+            self.llm = HelloAgentsLLM(
+                model=self.config.llm.model_name,
+                api_key=self.config.llm.api_key,
+                base_url=self.config.llm.base_url,
+                temperature=self.config.llm.temperature,
+                max_tokens=self.config.llm.max_tokens,
+                timeout=self.config.llm.timeout
+            )
+
+            logger.info(f"HelloAgent LLM 初始化成功: {self.config.llm.model_name}")
+        except ImportError as e:
+            logger.error(f"hello-agents 未安装: {str(e)}")
+            raise ImportError("请安装 hello-agents: pip install 'hello-agents[all]>=0.2.7'")
+        except Exception as e:
+            logger.error(f"HelloAgent LLM 初始化失败: {str(e)}")
+            raise
+    
+    def _format_messages(self, prompt: Union[str, List[dict]]) -> List[dict]:
+        if isinstance(prompt, str):
+            return [{"role": "user", "content": prompt}]
+        if isinstance(prompt, list):
+            return prompt
+        return [{"role": "user", "content": str(prompt)}]
+
+    async def ainvoke(self, prompt: Union[str, List[dict]], **kwargs) -> str:
+        try:
+            messages = self._format_messages(prompt)
+            response = await asyncio.to_thread(self.llm.invoke, messages, **kwargs)
+            return self._extract_text(response)
+        except Exception as e:
+            raise LLMException(f"LLM 调用失败: {e}")
+
+    def invoke(self, prompt: Union[str, List[dict]], **kwargs) -> str:
+        try:
+            messages = self._format_messages(prompt)
+            response = self.llm.invoke(messages, **kwargs)
+            return self._extract_text(response)
+        except Exception as e:
+            raise LLMException(f"LLM 调用失败: {e}")
+
+    def _extract_text(self, response: Any) -> str:
+        if isinstance(response, str):
+            return response
+        if hasattr(response, "content"):
+            return response.content
+        if hasattr(response, "text"):
+            return response.text
+        return str(response)
+
+
+# 全局实例
+_llm_adapter: LLMAdapter | None = None
+
+
+def get_llm_adapter() -> LLMAdapter:
+    global _llm_adapter
+    if _llm_adapter is None:
+        _llm_adapter = LLMAdapter()
+    return _llm_adapter

+ 5 - 0
Co-creation-projects/Shawnxyxy-HealthRecordAgent/backend/main.py

@@ -0,0 +1,5 @@
+import uvicorn
+
+if __name__ == "__main__":
+    # 指定 app 实例的位置:api.routes.main:app
+    uvicorn.run("api.routes.main:app", host="0.0.0.0", port=8000, reload=True)

+ 0 - 0
Co-creation-projects/Shawnxyxy-HealthRecordAgent/backend/service/__init__.py


+ 87 - 0
Co-creation-projects/Shawnxyxy-HealthRecordAgent/backend/service/health_analysis.py

@@ -0,0 +1,87 @@
+"""
+健康分析工作流服务
+负责串联多个 Agent,完成一次完整的健康报告分析
+"""
+
+import asyncio
+from typing import Dict, Any
+from uuid import uuid4
+
+from agents.planner import PlannerAgent
+from agents.health_indicator import HealthIndicatorAgent
+from agents.risk_assess import RiskAssessmentAgent
+from agents.advice import AdviceAgent
+from agents.report import ReportAgent
+from agents.base import create_task, update_agent_state, complete_task
+
+class HealthAnalysisService:
+    def __init__(self, task_id: str = None):
+        self.task_id = task_id or str(uuid4())
+        # 任务初始化
+        create_task(self.task_id)
+
+        self.planner = PlannerAgent(task_id=self.task_id)
+        self.indicator_agent = HealthIndicatorAgent(task_id=self.task_id)
+        self.risk_agent = RiskAssessmentAgent(task_id=self.task_id)
+        self.advice_agent = AdviceAgent(task_id=self.task_id)
+        self.report_agent = ReportAgent(task_id=self.task_id)
+
+    async def run(self, report_text: str) -> Dict[str, Any]:
+        """
+        执行完整的健康分析流程
+        """
+
+        # 1.任务规划
+        update_agent_state(self.task_id, "PlannerAgent", "running")
+        plan_result = await self.planner.run({"goal": f"分析以下体检报告并制定执行计划:\n{report_text}"})
+        update_agent_state(self.task_id, "PlannerAgent", "completed")
+
+        # 2.健康指标分析
+        update_agent_state(self.task_id, "HealthIndicatorAgent", "running")
+        indicator_result = await self.indicator_agent.run({
+            "report_text": report_text,
+            "plan": plan_result
+        })
+        update_agent_state(self.task_id, "HealthIndicatorAgent", "completed", partial_report={"indicator_results": indicator_result})
+
+        # 3. 风险评估
+        update_agent_state(self.task_id, "RiskAssessmentAgent", "running")
+        risk_result = await self.risk_agent.run({
+            "indicator_results": indicator_result
+        })
+        update_agent_state(self.task_id, "RiskAssessmentAgent", "completed", partial_report={"risk_assessment": risk_result})
+
+        # 4. 健康建议生成
+        update_agent_state(self.task_id, "AdviceAgent", "running")
+        advice_result = await self.advice_agent.run({
+            "risk_assessment": risk_result
+        })
+        update_agent_state(self.task_id, "AdviceAgent", "completed", partial_report={"advice": advice_result})
+
+
+        # 5. 报告汇总
+        update_agent_state(self.task_id, "ReportAgent", "running")
+        final_report = await self.report_agent.run({
+            "indicators": indicator_result,
+            "risk_assessment": risk_result,
+            "advice": advice_result
+        })
+        update_agent_state(self.task_id, "ReportAgent", "completed")
+        complete_task(self.task_id, final_report)
+
+        return self.task_id
+
+# ---------- 临时本地验证入口 ----------
+
+async def _demo():
+    demo_text = """
+        男性,28岁,BMI 27.3,血压 145/95 mmHg,
+        总胆固醇 6.2 mmol/L,空腹血糖 6.1 mmol/L。
+        """
+
+    workflow = HealthAnalysisService()
+    result = await workflow.run(demo_text)
+    print(result)
+
+if __name__ == "__main__":
+    asyncio.run(_demo())

+ 53 - 0
Co-creation-projects/Shawnxyxy-HealthRecordAgent/data/sample_reports/report.txt

@@ -0,0 +1,53 @@
+姓名:张三
+性别:男
+年龄:30岁
+身高:175 cm
+体重:70 kg
+BMI:22.9
+
+血压:120/80 mmHg
+心率:72 次/分钟
+总胆固醇:4.5 mmol/L
+甘油三酯:1.2 mmol/L
+高密度脂蛋白(HDL):1.4 mmol/L
+低密度脂蛋白(LDL):2.5 mmol/L
+空腹血糖:5.2 mmol/L
+肝功能:ALT 25 U/L, AST 22 U/L, 肌酐 70 μmol/L
+心电图:正常
+生活习惯:每周运动3次,饮食均衡,无吸烟饮酒
+
+姓名:李四
+性别:女
+年龄:45岁
+身高:160 cm
+体重:68 kg
+BMI:26.6
+
+血压:138/88 mmHg
+心率:85 次/分钟
+总胆固醇:6.0 mmol/L
+甘油三酯:2.0 mmol/L
+高密度脂蛋白(HDL):1.0 mmol/L
+低密度脂蛋白(LDL):4.0 mmol/L
+空腹血糖:6.1 mmol/L
+肝功能:ALT 35 U/L, AST 30 U/L, 肌酐 80 μmol/L
+心电图:偶发早搏
+生活习惯:久坐,饮食油腻,偶尔饮酒
+
+姓名:王五
+性别:男
+年龄:50岁
+身高:170 cm
+体重:85 kg
+BMI:29.4
+
+血压:150/95 mmHg
+心率:90 次/分钟
+总胆固醇:6.8 mmol/L
+甘油三酯:2.5 mmol/L
+高密度脂蛋白(HDL):0.9 mmol/L
+低密度脂蛋白(LDL):4.5 mmol/L
+空腹血糖:6.8 mmol/L
+肝功能:ALT 50 U/L, AST 45 U/L, 肌酐 100 μmol/L
+心电图:轻度心律不齐
+生活习惯:不运动,吸烟,饮食高脂

+ 131 - 0
Co-creation-projects/Shawnxyxy-HealthRecordAgent/frontend/app.js

@@ -0,0 +1,131 @@
+const API_BASE = "http://127.0.0.1:8000";
+// 显示多Agent执行状态
+function showAgentProgress(agentContainer, agents, statusFunc) {
+    agentContainer.innerHTML = "";
+    agents.forEach(agent => {
+        const li = document.createElement("li");
+        const status = typeof statusFunc === "function" ? statusFunc(agent.key) : statusFunc;
+        li.textContent = `${agent.label}: ${status}`;
+        agentContainer.appendChild(li);
+    });
+}
+
+// 公共函数:提交任务并轮询状态
+async function submitAndPollTask(url, body, agents, resultCard, reportDiv, analysisDiv, progressList, loadingText, doneText, errorText) {
+    reportDiv.innerHTML = "";
+    analysisDiv.innerText = loadingText;
+    progressList.classList.remove("hidden");
+    showAgentProgress(progressList, agents, () => "⏳ 执行中...");
+    resultCard.classList.add("hidden");
+
+    try {
+        const response = await fetch(url, body);
+        if (!response.ok) throw new Error(`服务器返回错误状态:${response.status}`);
+
+        const data = await response.json();
+        const taskId = data.task_id;
+
+        let taskStatus = await fetch(`${API_BASE}/api/health/task_status/${taskId}`).then(r => r.json());
+        while (taskStatus.state !== "completed") {
+            showAgentProgress(progressList, agents, agentKey => taskStatus.agents?.[agentKey] ?? "⏳ 执行中...");
+            await new Promise(res => setTimeout(res, 1000));
+            taskStatus = await fetch(`${API_BASE}/api/health/task_status/${taskId}`).then(r => r.json());
+        }
+        // 任务完成后刷新一次 agent 状态,保证 ReportAgent 也显示 completed
+        showAgentProgress(progressList, agents, agentKey => taskStatus.agents?.[agentKey] ?? "⏳ 执行中...");
+        // 显示最终报告
+        const summary = taskStatus.report?.report?.summary || "<p>❌ 未返回报告内容</p>";
+        reportDiv.innerHTML = typeof summary === "string" ? summary : JSON.stringify(summary, null, 2);
+        analysisDiv.innerText = doneText;
+        resultCard.classList.remove("hidden");
+
+    } catch (error) {
+        const errorMessage = error?.message || JSON.stringify(error);
+        console.error("任务提交或轮询出错:", errorMessage);
+        reportDiv.innerHTML = `<p>❌ ${errorText}: ${errorMessage}</p>`;
+        analysisDiv.innerText = `❌ ${errorText}`;
+        progressList.innerHTML = "";
+    }
+}
+
+// 文本报告分析
+async function analyze() {
+    const reportText = document.getElementById("reportText").value;
+    if (!reportText) {
+        alert("请输入体检报告内容");
+        return;
+    }
+
+    const resultCard = document.getElementById("resultCard");
+    const reportDiv = document.getElementById("report");
+    const analysisDiv = document.getElementById("analysis");
+    const progressList = document.getElementById("progressList");
+
+    const agents = [
+        { key: "PlannerAgent", label: "PlannerAgent 规划分析" },
+        { key: "HealthIndicatorAgent", label: "HealthIndicatorAgent 指标分析" },
+        { key: "RiskAssessmentAgent", label: "RiskAssessmentAgent 风险评估" },
+        { key: "AdviceAgent", label: "AdviceAgent 建议生成" },
+        { key: "ReportAgent", label: "ReportAgent 报告生成" }
+    ];
+
+    await submitAndPollTask(
+        `${API_BASE}/api/health/analysis`,
+        {
+            method: "POST",
+            headers: { "Content-Type": "application/json" },
+            body: JSON.stringify({ report_text: reportText })
+        },
+        agents,
+        resultCard,
+        reportDiv,
+        analysisDiv,
+        progressList,
+        "⏳ 正在分析文本报告,请稍候...",
+        "✅ 文本分析完成",
+        "报告生成失败"
+    );
+}
+
+// PDF报告分析
+async function uploadPDF() {
+    const fileInput = document.getElementById("pdfFile");
+    const file = fileInput.files[0];
+    if (!file) {
+        alert("请选择PDF文件");
+        return;
+    }
+
+    const formData = new FormData();
+    formData.append("file", file);
+
+    const resultCard = document.getElementById("resultCard");
+    const reportDiv = document.getElementById("report");
+    const analysisDiv = document.getElementById("analysis");
+    const progressList = document.getElementById("progressList");
+
+    const agents = [
+        { key: "PlannerAgent", label: "PlannerAgent 规划分析" },
+        { key: "HealthIndicatorAgent", label: "HealthIndicatorAgent 指标分析" },
+        { key: "RiskAssessmentAgent", label: "RiskAssessmentAgent 风险评估" },
+        { key: "AdviceAgent", label: "AdviceAgent 建议生成" },
+        { key: "ReportAgent", label: "ReportAgent 报告生成" }
+    ];
+
+    await submitAndPollTask(
+        `${API_BASE}/api/health/analysis/pdf`,
+        { method: "POST", body: formData },
+        agents,
+        resultCard,
+        reportDiv,
+        analysisDiv,
+        progressList,
+        "⏳ 正在分析 PDF 报告,请稍候...",
+        "✅ PDF分析完成",
+        "上传失败"
+    );
+}
+
+// 绑定按钮事件
+document.getElementById("analyzeBtn")?.addEventListener("click", analyze);
+document.getElementById("uploadBtn")?.addEventListener("click", uploadPDF);

+ 62 - 0
Co-creation-projects/Shawnxyxy-HealthRecordAgent/frontend/index.html

@@ -0,0 +1,62 @@
+<!DOCTYPE html>
+<html lang="zh">
+<head>
+    <meta charset="UTF-8">
+    <title>HealthRecordAgent</title>
+    <link rel="stylesheet" href="style.css">
+    <style>
+        .container { max-width: 800px; margin: auto; padding: 20px; font-family: sans-serif; }
+        h1 { text-align: center; margin-bottom: 30px; }
+        .card { background: #f7f7f7; padding: 20px; border-radius: 8px; margin-bottom: 20px; box-shadow: 0 2px 6px rgba(0,0,0,0.1); }
+        textarea { width: 100%; height: 120px; padding: 10px; border-radius: 4px; border: 1px solid #ccc; resize: vertical; }
+        input[type="file"] { width: 100%; padding: 6px; }
+        button { padding: 8px 16px; border: none; border-radius: 4px; background-color: #007bff; color: white; cursor: pointer; }
+        button:hover { background-color: #0056b3; }
+        .flex-row { display: flex; gap: 20px; flex-wrap: wrap; }
+        .flex-column { display: flex; flex-direction: column; gap: 10px; flex: 1; }
+        #analysis { margin-top: 10px; font-weight: bold; }
+        #report { margin-top: 15px; white-space: pre-wrap; }
+        #progressList { margin-top: 10px; padding-left: 20px; list-style: none; }
+        #progressList li { margin-bottom: 5px; }
+        .hidden { display: none; }
+    </style>
+</head>
+<body>
+
+<div class="container">
+    <h1>HealthRecordAgent 多Agent健康分析系统</h1>
+
+    <div class="card">
+        <h2>📄 输入体检报告或上传 PDF</h2>
+
+        <div class="flex-row">
+            <!-- 左边:文字输入 -->
+            <div class="flex-column">
+                <label>📝 文本报告输入</label>
+                <textarea id="reportText" placeholder="请输入体检报告内容..."></textarea>
+                <button onclick="analyze()">💡 分析文本报告</button>
+            </div>
+
+            <!-- 右边:PDF 上传 -->
+            <div class="flex-column">
+                <label>📄 PDF 报告上传</label>
+                <input type="file" id="pdfFile" accept=".pdf" />
+                <button onclick="uploadPDF()">💡 分析 PDF 报告</button>
+            </div>
+        </div>
+
+        <!-- 分析状态显示 -->
+        <div id="analysis"> </div>
+        <ul id="progressList" class="hidden"></ul>
+    </div>
+
+    <!-- 分析结果 -->
+    <div id="resultCard" class="card hidden">
+        <h2>📊 分析结果</h2>
+        <div id="report"></div>
+    </div>
+</div>
+
+<script src="app.js"></script>
+</body>
+</html>

BIN
Co-creation-projects/Shawnxyxy-HealthRecordAgent/frontend/screenshots/example.png


+ 88 - 0
Co-creation-projects/Shawnxyxy-HealthRecordAgent/frontend/style.css

@@ -0,0 +1,88 @@
+body {
+    font-family: Arial, sans-serif;
+    background: #f4f6f9;
+    margin: 0;
+    padding: 30px;
+}
+
+.container {
+    max-width: 900px;
+    margin: auto;
+}
+
+h1 {
+    text-align: center;
+    margin-bottom: 30px;
+}
+
+.card {
+    background: white;
+    padding: 20px;
+    border-radius: 10px;
+    margin-bottom: 20px;
+    box-shadow: 0 2px 8px rgba(0,0,0,0.05);
+}
+
+textarea {
+    width: 100%;
+    height: 150px;
+    padding: 10px;
+    margin-top: 10px;
+    margin-bottom: 10px;
+}
+
+button {
+    padding: 10px 20px;
+    font-size: 15px;
+    cursor: pointer;
+}
+
+.section {
+    margin-top: 20px;
+}
+
+.hidden {
+    display: none;
+}
+
+ul {
+    padding-left: 20px;
+}
+
+li {
+    margin-bottom: 5px;
+}
+
+.score-container {
+    margin-top: 20px;
+    padding: 10px;
+    background: #f8f9fa;
+    border-radius: 8px;
+}
+
+.score-label {
+    font-weight: bold;
+    margin-bottom: 8px;
+}
+
+.score-bar-bg {
+    width: 100%;
+    height: 24px;
+    background-color: #ddd;
+    border-radius: 12px;
+    overflow: hidden;
+}
+
+.score-bar {
+    height: 100%;
+    width: 0%;
+    background-color: green;
+    transition: width 0.8s ease;
+    border-radius: 12px;
+}
+
+.score-text {
+    margin-top: 8px;
+    font-size: 15px;
+    font-weight: bold;
+}

+ 39 - 0
Co-creation-projects/Shawnxyxy-HealthRecordAgent/requirements.txt

@@ -0,0 +1,39 @@
+# 后端 Web 框架
+fastapi==0.128.8
+uvicorn==0.40.0
+starlette==0.52.1
+sse-starlette==3.2.0
+aiofiles==23.2.1
+httpx==0.28.1
+httpx-sse==0.4.3
+anyio==4.12.1
+sniffio==1.3.1
+aiohttp==3.13.3
+aiosignal==1.4.0
+
+# Agent 框架 / LLM
+hello-agents==0.2.8
+openai==1.109.1
+transformers==4.57.6
+tiktoken==0.12.0
+accelerate==1.12.0
+bitsandbytes==0.49.1
+torch==2.10.0
+peft==0.18.1
+
+# PDF 解析
+pdfplumber==0.11.9
+pdfminer.six==20251230
+pypdf==6.7.0
+
+# 数据处理
+numpy==2.4.2
+pandas==2.3.3
+
+# JSON / Pydantic
+pydantic==2.12.5
+pydantic-settings==2.12.0
+
+# 可选工具库
+python-dotenv==1.2.1
+rich==14.3.2