构建智能医疗诊断助手:基于 LangGraph 和 DeepSeek 的实践指南
在医疗领域,诊断过程是一个复杂、多步骤且需要高度专业知识的流程。随着人工智能技术的发展,我们能够构建智能系统来辅助医生进行诊断,提高效率和准确性。本文将详细介绍如何使用 LangGraph 和 DeepSeek 模型构建一个智能医疗诊断助手,从设计理念到代码实现,为您提供一个完整的实践指南。完整项目见:https://github.com/jiangnanboy/medical-diagnosis-assistant。



1. 核心理念:为什么选择 LangGraph?
传统的 AI 应用通常采用线性流程(如 LangChain 中的链式结构),但医疗诊断过程远非线性。医生需要根据患者信息进行初步评估,决定是否需要进一步检查,根据检查结果调整诊断,制定治疗方案,甚至可能需要回溯和重新评估。这种复杂决策过程更适合用图结构来表示,而不是简单的链。
LangGraph 正是为此设计的框架,它允许我们:
- 定义复杂状态:维护整个诊断过程中的所有信息
- 实现条件路由:根据中间结果决定下一步流程
- 支持循环和回溯:模拟医生可能需要重新评估的情况
- 提供检查点:保存中间状态,便于恢复和调试
我们的智能医疗诊断助手将模拟真实医生的诊断流程,从初步评估到最终报告生成,每个步骤都是一个图中的节点,节点之间的连接则代表了诊断决策路径。
2. 系统架构:智能医疗诊断工作流
2.1 状态定义:系统的记忆
在 LangGraph 中,状态是贯穿整个工作流的核心数据结构。我们使用 TypedDict 定义了一个 MedicalDiagnosisState,它包含了诊断过程中需要的所有信息:
class MedicalDiagnosisState(TypedDict):
"""医疗诊断工作流的状态结构"""
messages: Annotated[List, add_messages] # 对话历史
patient_info: Dict[str, Any] # 患者基本信息
symptoms: List[Dict[str, Any]] # 症状列表
vital_signs: Dict[str, Any] # 生命体征
lab_results: List[Dict[str, Any]] # 检查结果
preliminary_diagnosis: List[str] # 初步诊断
recommended_tests: List[str] # 推荐检查
treatment_plan: Dict[str, Any] # 治疗方案
follow_up_plan: Dict[str, Any] # 随访计划
current_stage: str # 当前阶段
urgency_level: str # 紧急程度
doctor_approval: Dict[str, bool] # 医生审批状态
cycle_count: int # 循环计数
max_cycles: int # 最大循环次数
final_report: str # 最终报告
error: Optional[str] # 错误信息
session_id: str # 会话ID
这个状态结构就像是医生的病历本,记录了从患者入院到诊断完成的所有信息,每个节点都可以读取和更新这些信息。
2.2 节点设计:诊断步骤
我们的诊断流程被分解为多个节点,每个节点代表诊断过程中的一个特定步骤:
- 初步评估节点 (
initial_assessment_node):评估患者紧急程度,识别可能的紧急情况 - 检查策略决策节点 (
decide_testing_strategy_node):根据紧急程度决定检查策略 - 开具检查单节点 (
order_tests_node):生成并执行检查 - 诊断分析节点 (
make_diagnosis_node):基于所有信息进行诊断 - 制定治疗方案节点 (
create_treatment_plan_node):根据诊断结果制定治疗方案 - 医生审批节点 (
doctor_approval_node):模拟医生审批过程 - 随访计划节点 (
follow_up_planning_node):制定患者随访计划 - 生成报告节点 (
generate_final_report_node):生成最终诊断报告
让我们深入分析几个关键节点的实现:
初步评估节点
def initial_assessment_node(state: MedicalDiagnosisState) -> Dict[str, Any]:
"""步骤1:初步评估和分诊"""
try:
patient_info = state["patient_info"]
symptoms = state["symptoms"]
vital_signs = state["vital_signs"]
assessment_prompt = f"""
你是一个经验丰富的急诊科医生。请根据以下信息进行初步评估:
患者信息:
{json.dumps(patient_info, ensure_ascii=False, indent=2)}
症状:
{json.dumps(symptoms, ensure_ascii=False, indent=2)}
生命体征:
{json.dumps(vital_signs, ensure_ascii=False, indent=2)}
请完成以下任务:
1. 评估紧急程度(低/中/高/紧急)
2. 识别可能的紧急情况
3. 推荐立即需要的检查
4. 给出初步印象
请以JSON格式返回:
{{
"urgency_level": "紧急程度",
"emergency_signs": ["紧急征象1", "紧急征象2"],
"immediate_actions": ["立即行动1", "立即行动2"],
"recommended_tests": ["推荐检查1", "推荐检查2"],
"preliminary_impression": "初步印象"
}}
"""
response = llm.invoke([SystemMessage(content=assessment_prompt)])
# 解析JSON响应
try:
response_text = response.content.strip()
if response_text.startswith("```json"):
response_text = response_text[7:-3]
assessment_result = json.loads(response_text)
urgency_level = assessment_result.get("urgency_level", "medium")
recommended_tests = assessment_result.get("recommended_tests", [])
except json.JSONDecodeError:
urgency_level = "medium"
recommended_tests = ["血常规", "体温检查"]
# 翻译紧急程度为中文
urgency_map = {
"low": "低",
"medium": "中",
"high": "高",
"emergency": "紧急"
}
urgency_cn = urgency_map.get(urgency_level, "中")
return {
"urgency_level": urgency_level,
"recommended_tests": recommended_tests,
"current_stage": "assessed",
"messages": [AIMessage(content=f"🏥 初步评估完成,紧急程度:{urgency_cn}")]
}
except Exception as e:
return {
"error": f"初步评估失败: {str(e)}",
"current_stage": "assessment_failed",
"messages": [AIMessage(content=f"❌ 初步评估遇到问题: {str(e)}")]
}
这个节点首先从状态中提取患者信息、症状和生命体征,然后构建一个详细的提示词发送给 DeepSeek 模型。模型会分析这些信息并返回一个 JSON 格式的评估结果,包括紧急程度和推荐的检查。节点解析这个结果,更新状态,并返回一个包含更新信息的字典。
诊断分析节点
def make_diagnosis_node(state: MedicalDiagnosisState) -> Dict[str, Any]:
"""步骤4:诊断分析"""
try:
patient_info = state["patient_info"]
symptoms = state["symptoms"]
vital_signs = state["vital_signs"]
lab_results = state["lab_results"]
diagnosis_prompt = f"""
你是一个专业的诊断医生。请根据以下信息进行诊断分析:
患者信息:
{json.dumps(patient_info, ensure_ascii=False, indent=2)}
症状:
{json.dumps(symptoms, ensure_ascii=False, indent=2)}
生命体征:
{json.dumps(vital_signs, ensure_ascii=False, indent=2)}
检查结果:
{json.dumps(lab_results, ensure_ascii=False, indent=2)}
请提供:
1. 主要诊断(可能多个)
2. 鉴别诊断
3. 诊断依据
4. 严重程度评估(轻度/中度/重度)
5. 是否需要会诊
请以JSON格式返回:
{{
"main_diagnosis": ["诊断1", "诊断2"],
"differential_diagnosis": ["鉴别诊断1", "鉴别诊断2"],
"diagnostic_basis": "诊断依据",
"severity": "mild/moderate/severe",
"needs_consultation": true/false,
"consultation_specialty": "会诊科室"
}}
"""
response = llm.invoke([SystemMessage(content=diagnosis_prompt)])
# 解析JSON响应
try:
response_text = response.content.strip()
if response_text.startswith("```json"):
response_text = response_text[7:-3]
diagnosis_result = json.loads(response_text)
main_diagnosis = diagnosis_result.get("main_diagnosis", [])
needs_consultation = diagnosis_result.get("needs_consultation", False)
except json.JSONDecodeError:
main_diagnosis = ["待查"]
needs_consultation = False
return {
"preliminary_diagnosis": main_diagnosis,
"current_stage": "diagnosed",
"needs_consultation": needs_consultation,
"messages": [AIMessage(content=f"📋 诊断完成:{', '.join(main_diagnosis)}")]
}
except Exception as e:
return {
"error": f"诊断失败: {str(e)}",
"current_stage": "diagnosis_failed",
"messages": [AIMessage(content=f"❌ 诊断过程遇到问题: {str(e)}")]
}
这个节点整合了所有可用信息(患者信息、症状、生命体征和检查结果),并请求 DeepSeek 模型进行全面的诊断分析。返回的结果包括主要诊断、鉴别诊断、诊断依据等,这些信息将用于后续的治疗方案制定。
2.3 边设计:连接决策路径
在 LangGraph 中,边定义了节点之间的连接关系。我们使用两种类型的边:
- 普通边:表示固定的流程顺序
- 条件边:根据状态决定下一步流向哪个节点
条件边是实现智能决策的关键。例如,在初步评估后,系统需要根据紧急程度决定下一步流程:
def route_after_assessment(state: MedicalDiagnosisState) -> Literal[
"decide_testing", "order_tests", "make_diagnosis", "generate_report"]:
"""初步评估后的路由决策"""
if state.get("error") or "failed" in state.get("current_stage", ""):
return "generate_report"
urgency = state.get("urgency_level", "")
if urgency == "emergency":
return "order_tests" # 紧急情况直接检查
return "decide_testing" # 其他情况先决定检查策略
这个路由函数检查状态中的紧急程度,如果是"紧急"情况,则直接进入检查流程;否则,先决定检查策略。这种条件路由使系统能够根据实际情况灵活调整流程,模拟真实医生的决策过程。
3. 工作流整合:构建诊断图
现在,让我们看看如何将这些节点和边组合成一个完整的工作流:
def create_medical_diagnosis_assistant():
"""创建医疗诊断工作流"""
workflow = StateGraph(MedicalDiagnosisState)
# 添加节点
workflow.add_node("initial_assessment", initial_assessment_node)
workflow.add_node("decide_testing", decide_testing_strategy_node)
workflow.add_node("order_tests", order_tests_node)
workflow.add_node("make_diagnosis", make_diagnosis_node)
workflow.add_node("create_treatment", create_treatment_plan_node)
workflow.add_node("doctor_approval", doctor_approval_node)
workflow.add_node("follow_up_planning", follow_up_planning_node)
workflow.add_node("generate_report", generate_final_report_node)
# 设置流程
workflow.add_edge(START, "initial_assessment")
# 添加条件边
workflow.add_conditional_edges(
"initial_assessment",
route_after_assessment,
{
"decide_testing": "decide_testing",
"order_tests": "order_tests",
"make_diagnosis": "make_diagnosis",
"generate_report": "generate_report"
}
)
workflow.add_conditional_edges(
"decide_testing",
route_after_testing_decision,
{
"order_tests": "order_tests",
"make_diagnosis": "make_diagnosis"
}
)
workflow.add_edge("order_tests", "make_diagnosis")
workflow.add_conditional_edges(
"make_diagnosis",
route_after_diagnosis,
{
"create_treatment": "create_treatment",
"doctor_approval": "doctor_approval",
"follow_up_planning": "follow_up_planning"
}
)
workflow.add_edge("create_treatment", "doctor_approval")
workflow.add_conditional_edges(
"doctor_approval",
route_after_approval,
{
"follow_up_planning": "follow_up_planning",
"make_diagnosis": "make_diagnosis",
"create_treatment": "create_treatment"
}
)
workflow.add_edge("follow_up_planning", "generate_report")
workflow.add_edge("generate_report", END)
# 编译图
memory = InMemorySaver()
app = workflow.compile(checkpointer=memory)
return app
这段代码首先创建了一个 StateGraph 实例,然后添加了所有节点。接着,它定义了节点之间的连接关系,包括起点和终点。特别值得注意的是条件边的使用,它们使工作流能够根据中间结果动态调整路径。
最后,我们使用 InMemorySaver 作为检查点,并编译图得到可执行的应用程序。这个检查点允许我们保存和恢复状态,对于长时间运行的诊断过程非常有用。
4. 流程图:智能诊断工作流可视化
为了更直观地理解这个诊断工作流,下面是一个大致的流程图:


这个流程图展示了诊断工作流的主要决策点和路径。从初步评估开始,系统根据紧急程度决定是否需要立即检查。检查完成后,进行诊断分析,然后根据诊断结果制定治疗方案。医生审批环节可能会根据审批结果导致流程回溯,重新进行诊断或调整治疗方案。最后,制定随访计划并生成最终报告。
5. 用户界面:通过 Streamlit 实现交互
为了使我们的智能诊断助手更加用户友好,我们使用 Streamlit 构建了一个 Web 界面。界面分为几个主要部分:
- 患者信息录入:在侧边栏收集患者基本信息、病史、过敏史等
- 症状描述:允许用户动态添加多个症状,包括症状名称、持续时间、严重程度等
- 生命体征:收集血压、心率、体温等关键指标
- 诊断过程展示:使用进度条和展开式面板显示诊断过程的每个步骤
- 结果展示:清晰展示初步诊断、治疗方案、随访计划和最终报告
# 诊断按钮
st.markdown("---")
col_btn1, col_btn2, col_btn3 = st.columns([1, 2, 1])
with col_btn2:
diagnose_button = st.button("🔍 开始智能诊断", type="primary", use_container_width=True)
# 显示诊断结果
if diagnose_button:
# ... 准备输入数据 ...
# 运行诊断流程
with st.spinner("正在进行智能诊断,请稍候..."):
try:
# 使用流式运行以显示进度
stream_results = medical_diagnosis(patient_info, symptoms_list, vital_signs, stream=True)
# 显示每个步骤的结果
step = 0
final_result = {}
with message_container:
for event in stream_results:
step += 1
progress = min(step / 8.0, 1.0)
progress_bar.progress(progress)
for node_name, node_data in event.items():
if node_name != "__end__":
# 更新状态文本
step_name_cn = step_names.get(node_name, node_name)
status_text.markdown(f"**当前步骤:{step_name_cn}**")
# 显示步骤结果
with st.expander(f"步骤 {step}: {step_name_cn}", expanded=True):
for msg in node_data.get("messages", []):
if isinstance(msg, AIMessage):
st.markdown(f'<p class="success-message">{msg.content}</p>',
unsafe_allow_html=True)
# 显示最终报告
# ... 展示诊断结果 ...
except Exception as e:
st.markdown(f'<p class="error-message">诊断过程中出现异常: {str(e)}</p>', unsafe_allow_html=True)
这段代码展示了如何使用 Streamlit 的界面元素收集用户输入,并调用我们的诊断工作流。特别值得注意的是,我们使用了流式运行(stream=True)来实时显示诊断进度,这大大提升了用户体验。
6. 总结与展望
通过本文的介绍,我们了解了如何使用 LangGraph 和 DeepSeek 构建一个智能医疗诊断助手。这个系统展示了 LangGraph 在构建复杂、有状态的工作流方面的强大能力,特别是在需要条件路由和状态管理的场景中。
我们的智能诊断助手具有以下特点:
- 模块化设计:每个诊断步骤都是一个独立的节点,便于维护和扩展
- 智能决策:通过条件边实现基于状态的动态路由
- 状态管理:集中管理诊断过程中的所有信息
- 用户友好:通过 Streamlit 提供直观的界面和实时反馈
未来,我们可以从以下几个方面进一步改进这个系统:
- 集成真实数据:连接实际的医疗数据库和检查设备
- 增加专业节点:添加更多专科诊断节点,如心脏科、神经科等
- 实现人工审核:在关键决策点引入人工审核机制
- 优化提示工程:进一步优化提示词,提高诊断准确性
- 多模态支持:添加对医学影像、音频等多模态数据的支持
LangGraph 为构建复杂的 AI 应用提供了强大的框架,而 DeepSeek 等大语言模型则为这些应用提供了智能核心。结合这两者,我们能够构建出更加智能、实用的 AI 系统,为各行各业带来变革。

浙公网安备 33010602011771号