免费POC, 零成本试错
AI知识库

53AI知识库

学习大模型的前沿技术与行业应用场景


基于LangGraph 构建临床问诊助手实践

发布日期:2025-10-26 08:31:06 浏览次数: 1531
作者:大模型之路

微信搜一搜,关注“大模型之路”

推荐语

用LangGraph构建的临床问诊助手ClinicAssist,解决了传统患者信息录入的痛点,通过智能对话提升效率与准确性。

核心内容:
1. 医疗场景下患者信息录入的痛点与现有方案局限
2. ClinicAssist的三层技术架构与核心组件设计
3. LLM工作流的分阶段实现与症状收集阶段详解

杨芳贤
53AI创始人/腾讯云(TVP)最具价值专家

点击“蓝字” 关注我们

一、背景:医疗场景下的“患者问诊录入”痛点

 在诊所或医院的日常运营中,“患者信息录入”(Patient Intake)是必不可少的环节,但现有方案始终存在局限:

  • 在线表单僵化
    :固定字段无法像人类沟通那样灵活调整——比如患者提到“胸口闷”时,表单无法主动追问“闷痛持续多久”或“是否伴随出汗”;
  • 电话问诊低效
    :依赖医护人员人工沟通,而医疗资源本就紧张,这种方式不仅占用大量时间,还难以规模化覆盖更多患者。

尽管我并非临床专家,也缺乏医院场景的深度经验,但通过调研发现:当前LLM(大语言模型)若经过合理“编排”,完全有能力解决这些问题——核心是让助手能“有逻辑地引导对话”,同时“准确提取临床信息”(避免幻觉)。基于这个想法,我最终决定用LangGraph构建临床问诊助手ClinicAssist。

二、ClinicAssist的技术架构:三层协同设计

整个系统分为三大核心组件,各层职责明确且相互衔接: | 组件 | 技术栈 | 核心作用 | |---------------|--------------|--------------------------------------------------------------------------| | LLM工作流 | LangGraph | 主导对话逻辑与流程编排,控制“什么时候问什么问题”“是否需要追问”“何时进入下一阶段” | | 后端 | FastAPI | 作为前后端桥梁,提供RESTful API(会话管理、消息处理、结构化响应生成),保障数据完整性 | | 前端 | Next.js | 极简用户界面,负责展示对话流程、信息收集进度,以及已提取的临床结构化数据 |

其中,LangGraph驱动的LLM工作流是系统“大脑”——整个问诊流程被拆分为4个递进阶段,每个阶段的逻辑可复用,只需微调细节即可适配不同信息收集目标。

三、核心:LLM工作流的分阶段实现

四个阶段遵循统一逻辑:提问→等待患者输入→提取结构化信息→判断是否足够→循环/进入下一阶段。下面以最关键、最复杂的“症状收集阶段”(Phase 2)为例,拆解具体实现。

1. 先定目标:用Schema明确“要提取什么信息”

在设计任何提示或流程前,首先要明确“最终需要什么结构化数据”——这是避免LLM输出混乱的核心。基于临床文档规范,我用Pydantic定义了“症状信息Schema”:

from pydantic import BaseModel, Field
from typing import Optional, List

class SymptomsPartial(BaseModel):
    main_symptoms: Optional[List[str]] = Field(description="患者主要症状,如‘头痛’‘咳嗽’")
    symptom_onset: Optional[str] = Field(description="症状发作时间,如‘3天前’‘今天早上’")
    associated_symptoms: Optional[List[str]] = Field(description="伴随症状,如‘发烧’‘乏力’")
    additional_symptom_info: Optional[List[str]] = Field(description="其他关键细节:严重程度、诱发因素、缓解方式等")

这个Schema的价值在于:让LLM的输出被“约束”在固定格式中,后续可直接用于医生参考或系统存储,无需额外处理非结构化文本。

2. 症状收集阶段的流程设计:3个节点+1个决策

LangGraph以“图”的方式组织流程,每个“节点”对应一个动作,“边”对应节点间的流转关系:

  • ask_symptoms节点
    :调用LLM生成问题。通过系统提示将“已收集的信息”“对话历史”传给LLM,确保问题有针对性(比如已知道“头痛”,就追问“头痛是刺痛还是胀痛”);
  • human_symptoms_node节点
    :中断节点。暂停LLM流程,等待患者输入回答;
  • extract_symptoms节点
    :提取结构化信息。通过LLM的“结构化输出”功能,强制让模型按照SymptomsPartial格式返回结果:
    # 让LLM仅输出符合SymptomsPartial格式的内容
    symptoms_llm = llm.with_structured_output(SymptomsPartial)

3. 关键难题:如何判断“信息是否足够”?

这是整个阶段最需要思考的部分——既要避免信息缺失影响后续诊疗,又要防止过度追问打扰患者。最终采用“半结构化判断”方案:

  1. 硬条件检查
    :先验证核心字段是否存在(主要症状+发作时间),这两个是医生初步评估的基础,缺失则直接循环追问;
  2. LLM辅助判断
    :若核心字段齐全,再让LLM作为“判断者”,评估是否需要补充细节(如“是否需要知道头痛的严重程度”)。定义判断Schema:
    class SymptomSufficiencyCheck(BaseModel):
        is_sufficient: bool = Field(description="信息是否足够医生初步评估")
        reason: Optional[str] = Field(description="需补充信息的原因,足够则为None")
  3. 路由函数实现:根据判断结果决定流转方向:

    def route_after_symptoms(state: AgentState) -> str:
        # 1. 硬条件检查:核心字段是否存在
        has_main = state.get("main_symptoms") and len(state["main_symptoms"]) > 0
        has_onset = state.get("symptom_onset") is not None

        if not (has_main and has_onset):
            return "ask_symptoms"  # 核心字段缺失,循环追问

        # 2. LLM辅助判断
        check = check_symptom_sufficiency(state)
        return "ask_medhist" if check.is_sufficient else "ask_symptoms"

需要注意:这种“LLM作为判断者”的方案虽灵活,但存在“非确定性”(不同次调用可能出不同结果)。需通过以下方式优化:

  • 早期加入评估:准备标注好“是否需继续追问”的测试用例,量化LLM判断的准确率;
  • 迭代提示词:根据测试结果调整“判断提示”,比如明确“只有当细节影响初步诊断时才需要追问”。

四、整体流程整合:用LangGraph串联四个阶段

四个阶段(患者基本信息→症状→病史→分诊总结)的逻辑相似,只需复用“提问→提取→判断”框架,再通过LangGraph的StateGraph串联:

1. 核心步骤:添加节点与定义流转

from langgraph.graph import StateGraph, START, END
from langgraph.checkpoint.memory import InMemorySaver

def build_clinical_assistant_graph():
    # 1. 初始化状态图(AgentState为自定义状态类,存储对话历史和提取的信息)
    graph_builder = StateGraph(AgentState)

    # 2. 添加各阶段节点(仅展示关键节点)
    # 阶段1:患者基本信息
    graph_builder.add_node("ask_patient_info", ask_patient_info)  # 提问
    graph_builder.add_node("extract_patient_info", extract_patient_info)  # 提取
    graph_builder.add_node("human_patient_node", human_patient_node)  # 等待输入
    # 阶段2-4:症状、病史、分诊总结(节点定义类似,略)

    # 3. 定义流转关系(边)
    # 阶段1流转:提问→等输入→提取→判断是否进入阶段2
    graph_builder.add_edge(START, "ask_patient_info")
    graph_builder.add_edge("ask_patient_info", "human_patient_node")
    graph_builder.add_edge("human_patient_node", "extract_patient_info")
    # 条件边:提取后判断“循环”还是“进入阶段2”
    graph_builder.add_conditional_edges(
        "extract_patient_info",
        route_after_patient_info,  # 路由函数
        {"ask_patient_info": "ask_patient_info", "ask_symptoms": "ask_symptoms"}
    )

    # 阶段2-4流转(逻辑类似,略)
    # 最终:分诊总结→告知患者→结束
    graph_builder.add_edge("triage_summary", "acknowledgement")
    graph_builder.add_edge("acknowledgement", END)

    # 4. 启用检查点(保存会话状态,支持中断后恢复)
    checkpointer = InMemorySaver()
    return graph_builder.compile(checkpointer=checkpointer)

2. LangGraph的核心优势

LangGraph借鉴了NetworkX的图论思想,让流程编排更直观:

  • 节点(Node)
    :对应“动作”(LLM生成问题、提取信息、等待人类输入);
  • 边(Edge)
    :对应“流转规则”(无条件流转、按判断结果条件流转);
  • 检查点(Checkpoint)
    :保存会话状态,避免患者中途退出后需重新开始。

五、总结与展望

ClinicAssist目前仍处于早期阶段,但其核心价值在于:用LangGraph解决了“LLM对话流程可控性”问题——通过分阶段、结构化目标设计,让LLM既能灵活引导对话,又能稳定输出可用的临床信息。

未来优化方向可包括:

  1. 引入临床专家参与:优化Schema设计和信息充分性判断逻辑,确保符合医疗规范;
  2. 增强评估体系:用DeepEval等工具构建更全面的测试集(如“幻觉检测”“信息完整性评分”);
  3. 扩展场景适配:支持不同科室(如儿科、内科)的个性化问诊流程。
git:https://github.com/wjunwei2001/clinicassist

53AI,企业落地大模型首选服务商

产品:场景落地咨询+大模型应用平台+行业解决方案

承诺:免费POC验证,效果达标后再合作。零风险落地应用大模型,已交付160+中大型企业

联系我们

售前咨询
186 6662 7370
预约演示
185 8882 0121

微信扫码

添加专属顾问

回到顶部

加载中...

扫码咨询