从症状分析到治疗建议:用 FastAPI 构建医疗多 Agent 工作流

随着大语言模型能力的提升,AI 应用正在从“单轮问答”逐渐发展为能够执行完整业务流程的智能体系统。医疗辅助问诊就是一个很典型的场景:系统不仅需要理解患者描述,还要完成症状分析、检查判断、诊断推理和治疗建议生成。

本文介绍一个医疗多 Agent 项目——MediAgent。它使用 FastAPI 提供 Web 服务,通过多个医疗 Agent 协作完成问诊流程,并结合 MySQL、RabbitMQ 和 Docker 构建了一套可运行的工程化方案。

本项目仅用于技术演示,生成内容不能替代专业医生的诊断和治疗建议。

一、项目解决什么问题?

传统的医疗问答通常采用一次请求、一次回答的模式,但真实问诊并不是一个单步骤任务。

一次完整问诊大致包含:

  1. 记录患者症状;

  2. 分析关键症状和潜在疾病;

  3. 判断是否需要化验检查;

  4. 根据症状和化验结果进行诊断;

  5. 根据诊断生成治疗建议;

  6. 保存问诊记录并展示报告。

MediAgent 将这些步骤拆分成多个职责明确的智能体:

  • 症状分析 Agent:提取症状特征,判断可能疾病和所需检查;

  • 诊断分析 Agent:结合症状、历史记录和化验结果进行诊断;

  • 治疗建议 Agent:根据诊断结果生成治疗、用药和随访建议。

这种设计比单个超长 Prompt 更容易维护,也方便后续增加新的专业 Agent。

二、系统整体架构

MediAgent 主要由五部分组成:

  • 原生 HTML、CSS、JavaScript 前端;

  • FastAPI 接口服务;

  • 医疗多 Agent 模块;

  • MySQL 数据持久化;

  • RabbitMQ 异步消息通道。

项目中的主要目录如下:

MediAgent-LLM-main/
├── agents/                    # 医疗智能体
│   ├── symptom_analysis.py
│   ├── diagnosis_analysis.py
│   └── treatment_advice.py
├── async_engine/              # RabbitMQ 通信
├── data_models/               # Pydantic 医疗数据模型
├── memory/                    # SQLAlchemy 数据模型和数据库操作
├── static/                    # 前端代码
│   ├── index.html
│   ├── styles.css
│   └── app.js
├── tools/                     # LLM 客户端和授权模块
├── workflow/                  # 医疗工作流
├── app.py                     # FastAPI 入口
└── docker-compose.yml         # 容器编排

系统主流程如下:


不需要

需要

患者或医生填写症状

前端提交问诊

POST /workflow/start

创建问询记录

症状分析 Agent

是否需要化验

诊断分析 Agent

进入 WAITING_LAB 状态

提交化验结果

保存诊断结果

治疗建议 Agent

保存治疗建议

工作流完成

前端展示诊断报告

三、FastAPI 如何连接前后端?

项目入口位于 app.py。

FastAPI 将 static 目录挂载为静态资源目录,并在访问根路径时返回前端首页:

app.mount(
    "/static",
    StaticFiles(directory=BASE_DIR / "static"),
    name="static"
)
​
@app.get("/", include_in_schema=False)
async def frontend():
    return FileResponse(BASE_DIR / "static" / "index.html")

因此,这个项目不需要额外启动 Node.js 或前端开发服务器。

前端代码集中在:

static/index.html
static/styles.css
static/app.js

其中:

  • index.html 负责页面结构;

  • styles.css 负责界面样式;

  • app.js 负责表单提交、状态轮询和结果展示。

前端启动问诊时会调用:

fetch("/workflow/start", {
  method: "POST",
  body: JSON.stringify({
    patient_id: patientId,
    doctor_id: doctorId,
    symptom_description: symptom
  })
})

获得工作流 ID 后,前端继续轮询状态接口:

GET /workflow/{workflow_id}/status

四、多 Agent 如何协同?

1. 症状分析 Agent

症状分析 Agent 接收患者的症状描述,并读取历史问诊数据。

它需要完成:

  • 提取主要症状;

  • 判断可能疾病;

  • 评估风险;

  • 判断是否需要化验;

  • 生成建议检查项目。

分析结果会写入 MySQL,供后续诊断 Agent 使用。

2. 诊断分析 Agent

诊断 Agent 接收:

  • 症状分析结果;

  • 化验结果;

  • 患者历史诊断。

它会生成主要诊断、鉴别诊断、诊断依据以及风险提示。

如果症状分析阶段判断不需要化验,该 Agent 会被直接调用;如果需要化验,则工作流进入等待状态,收到检查结果后再继续。

3. 治疗建议 Agent

治疗建议 Agent 根据诊断结果生成:

  • 治疗方案;

  • 用药建议;

  • 注意事项;

  • 随访建议。

项目中还预留了过敏禁忌检查逻辑。不过在当前工作流里,患者过敏史暂时使用空列表,后续可以连接真实患者档案。

五、工作流的一个重要实现细节

项目中已经使用 LangGraph 定义了完整工作流图,包括:

create_inquiry
    ↓
symptom_analysis
    ↓
check_lab_needed
    ↓
diagnosis_analysis
    ↓
treatment_advice
    ↓
complete_workflow

不过,当前真正运行的主流程没有直接调用编译后的 LangGraph,而是在 start_workflow() 中显式执行各个步骤。

简化后的代码逻辑类似:

state = self._create_inquiry_record(initial_state)
state = self._perform_symptom_analysis(state)
state = self._check_lab_tests_needed(state)
​
if state.current_status == InquiryStatus.WAITING_LAB:
    return workflow_id
​
state = self._perform_diagnosis_analysis(state)
state = self._perform_treatment_advice(state)
state = self._complete_workflow(state)

这样做可以规避旧版本 LangGraph 对普通 Python 状态对象合并和持久化不稳定的问题,但也意味着 LangGraph 当前更多承担“工作流定义”的角色。

后续可以将状态重构为 TypedDict 或符合 LangGraph 要求的数据结构,让实际执行重新回到图引擎。

六、化验结果为什么有两条通道?

项目中存在两种化验结果接入方式。

第一种是 HTTP 接口:

POST /lab/results

该接口会调用 handle_lab_results(),然后继续执行:

诊断分析 → 治疗建议 → 完成工作流

第二种是 RabbitMQ 消息队列。

应用启动时会创建一个后台消费者,监听:

exchange: lab_results_exchange
queue: lab_results
routing key: lab_result

不过需要特别注意:当前 RabbitMQ 消费者只负责解析消息、记录日志并确认消息,还没有调用 handle_lab_results() 恢复工作流。

因此,目前真正可用的化验续跑通道仍然是 /lab/results 接口。

可以在后续将消费者改造成:

def handle_lab_result(self, message):
    inquiry_record_id = message["inquiry_record_id"]
    lab_results = message["lab_results"]
​
    medical_workflow.handle_lab_results(
        inquiry_record_id=inquiry_record_id,
        lab_results=lab_results
    )
​
    return True

这样 RabbitMQ 才会真正参与业务流程。

七、如何启动项目?

项目已经提供 Docker Compose 配置,可以一次启动 FastAPI、MySQL、Redis 和 RabbitMQ。

进入项目目录:

cd E:\WorkSpace\MediAgent-LLM-main\MediAgent-LLM-main

构建并启动:

docker compose up -d --build

查看容器状态:

docker compose ps

启动完成后可以访问:

  • 问诊工作台:http://127.0.0.1:8000/

  • API 文档:http://127.0.0.1:8000/docs

  • 健康检查:http://127.0.0.1:8000/health

  • RabbitMQ 管理页:http://127.0.0.1:15672/

停止服务:

docker compose down

如果希望保留数据库数据,不要删除 Compose volumes。

八、目前还可以改进什么?

MediAgent 已经具备完整的工程骨架,但距离生产环境还有一些工作。

工作流状态持久化

当前工作流状态主要保存在进程内存的 _states 字典中。服务重启后,等待化验的工作流无法继续。

可以将状态保存到 Redis 或数据库中,并在服务启动时恢复。

RabbitMQ 与工作流真正联动

消费者需要调用工作流恢复方法,而不是只记录消息。

同时还应增加:

  • 消息幂等控制;

  • 重试次数限制;

  • 死信队列;

  • 重复化验结果检测。

患者档案系统

当前过敏史等信息还没有接入真实档案。可以增加:

  • 既往病史;

  • 过敏史;

  • 当前用药;

  • 慢性疾病;

  • 检查和诊断历史。

医疗安全控制

医疗类 AI 系统不能只依赖模型自由生成结果。生产方案还需要:

  • 高风险症状规则;

  • 紧急就医提示;

  • 药物禁忌校验;

  • 医生人工审核;

  • 操作审计;

  • 敏感信息脱敏。

九、总结

MediAgent 展示了一条比较完整的医疗多 Agent 工程化路线:

前端采集信息
    → FastAPI 接收请求
    → 症状分析 Agent
    → 化验条件分支
    → 诊断分析 Agent
    → 治疗建议 Agent
    → MySQL 持久化
    → 前端展示报告

它的价值不只在于“使用大模型生成医疗文本”,更重要的是把模型调用放进了一个可追踪、可持久化、可扩展的业务流程中。

如果继续完善状态持久化、消息队列联动、人工审核和医疗安全规则,这套架构还可以扩展到检验报告解读、慢病管理、远程随访和医院内部辅助决策等场景。

Logo

更多推荐