LangChain与LangGraph实战:构建可调试的医疗问诊智能体 30款热门AI模型一站整合DeepSeek/GLM/Qwen 随心用限时 5 折。 点击领海量免费额度这类主题最值得先看的不是框架列表而是它到底解决了什么实际问题。一个医疗问诊 Agent核心是把大模型、知识库和业务流程串联起来让 AI 能像医生一样通过多轮对话完成信息收集、初步判断和分诊建议。这背后LangChain 负责基础编排LangGraph 处理复杂的状态流转LangSmith 则用来监控和调试整个流程。如果你正在评估或开发类似的 AI 应用这篇文章会带你走一遍从零到一的完整路径重点不是框架 API 调用而是如何把零散的工具组合成一个能稳定运行、可调试、能迭代的智能体。我建议先从结果看起一个能用的医疗问诊 Agent最终应该能处理“我头疼三天了还有点发烧”这样的输入然后通过几轮交互问出关键信息比如体温、有无其他症状、用药史并给出“建议优先线上问诊或线下就诊”的初步结论。整个过程需要状态管理、工具调用和决策逻辑。下面我会按实际落地的顺序拆解如何用 LangChain、LangGraph 和 LangSmith 把这件事做出来。1. 先拆解需求医疗问诊 Agent 到底要做什么在动手写代码之前必须把需求边界画清楚。一个“医疗问诊 Agent”听起来很泛直接上手容易陷入细节。我一般会先把它拆成几个可验证的模块。1.1 核心流程与状态定义医疗问诊不是单次问答而是一个有状态的流程。典型的流程可以抽象为以下几个状态节点症状初筛接收用户主诉如“头疼”提取关键症状实体。信息补全根据已知症状主动询问缺失的关键信息如“发烧吗体温多少”“疼痛是持续性的还是阵发性的”。初步评估基于收集到的信息调用医学知识库或规则引擎进行初步的严重程度分级和分诊建议。建议生成输出建议可能包括自我护理指导、线上问诊推荐、紧急就医提醒等。会话结束/转人工完成本次问诊或判断需要人工介入。这个流程的关键在于状态流转。用户的下一次输入需要根据当前处于哪个状态来决定下一步做什么。这就是 LangGraph 的核心价值所在。1.2 技术栈选型与分工基于上述流程我们来分配三个框架的角色LangChain“粘合剂”和“工具箱”。它提供与大模型LLM对话的基础能力ChatPromptTemplate,LLMChain、连接外部工具如知识库检索、数据库查询的标准化接口Tool以及管理对话历史ConversationBufferMemory的组件。在 Agent 中它负责“单次动作”的标准化执行比如“根据当前对话历史生成下一个问题”。LangGraph“流程控制器”和“状态机”。它用“图”Graph的概念来定义上述多状态流程。每个节点Node是一个处理单元可以调用 LangChain 的 Chain 或 Tool边Edge定义了节点之间的流转条件。它持久化整个对话的“状态”State确保 Agent 有记忆、能根据上下文决策。没有它你只能用一堆if-else来硬编码流程难以维护和扩展。LangSmith“监控调试台”。当你把 LangChain 和 LangGraph 的流程跑起来后你会发现黑盒问题为什么这一步 LLM 会这样回答检索结果对吗工具调用失败在哪LangSmith 可以记录每一次 LLM 调用、工具执行、状态变化的输入输出、耗时和 Token 消耗并以可视化链路Trace的形式呈现。这对于迭代优化 Prompt、调整流程逻辑、定位生产环境问题至关重要。简单说LangChain 做零件LangGraph 组装成有逻辑的机器LangSmith 给这台机器装上仪表盘和黑匣子。2. 环境准备与最小可行性验证不要一上来就构建完整流程。先确保每个基础组件都能在你的环境下跑通。环境不稳定后面所有复杂设计都是空中楼阁。2.1 基础环境搭建假设我们使用 Python 环境。核心依赖如下pip install langchain langgraph langsmith此外你需要一个 LLM 的 API 密钥。为了通用性我们可以使用 OpenAI 兼容的接口如 OpenAI 官方、DeepSeek、智谱等。这里以配置 OpenAI 为例export OPENAI_API_KEYyour-api-key export OPENAI_BASE_URLhttps://api.openai.com/v1 # 如果使用其他兼容服务修改此处关键点国内环境访问可能受限请确保你的网络环境能稳定调用你选择的 LLM 服务。同时注意 API 成本初始测试时可以使用小模型如 gpt-3.5-turbo来验证流程。2.2 用 LangChain 完成一次简单的症状提取我们先抛开 Graph用最基础的 LangChain 验证 LLM 和基础 Prompt 是否工作。from langchain_openai import ChatOpenAI from langchain_core.prompts import ChatPromptTemplate from langchain_core.output_parsers import StrOutputParser # 1. 初始化模型 llm ChatOpenAI(modelgpt-3.5-turbo, temperature0) # 2. 构建一个提取症状的Prompt symptom_extraction_prompt ChatPromptTemplate.from_messages([ (system, 你是一个医疗助手。请从用户的描述中提取关键的症状名称。只输出症状用逗号分隔。), (human, {user_input}) ]) # 3. 创建链 extraction_chain symptom_extraction_prompt | llm | StrOutputParser() # 4. 测试 user_input 我从昨天开始喉咙痛有点咳嗽头也昏昏沉沉的。 result extraction_chain.invoke({user_input: user_input}) print(f提取的症状: {result}) # 预期输出类似喉咙痛咳嗽头晕这个简单的Chain验证了1) 模型能连通2) Prompt 能起作用3) 我们能拿到结构化的输出。这是所有复杂能力的起点。2.3 连接 LangSmith 查看执行轨迹在深入之前先把 LangSmith 配置好它能让你“看见”每一次调用。import os os.environ[LANGCHAIN_TRACING_V2] true os.environ[LANGCHAIN_ENDPOINT] https://api.smith.langchain.com # LangSmith API端点 os.environ[LANGCHAIN_API_KEY] your-langsmith-api-key # 从LangSmith网站获取 os.environ[LANGCHAIN_PROJECT] medical-agent-demo # 你的项目名配置好后再次运行上面的extraction_chain.invoke()。然后打开 LangSmith 网站你就能在对应的项目下看到这次执行的完整 Trace。里面包含了发送给 LLM 的精确 Prompt、LLM 的返回、耗时和 Token 数。这是调试 Agent 的黄金工具一定要先习惯使用它。3. 用 LangGraph 构建问诊流程的状态机基础验证通过后进入核心环节用 LangGraph 把离散的步骤串联成智能的、有状态的流程。3.1 定义共享状态StateState 是一个字典包含了流程中所有节点都需要访问和更新的数据。我们为医疗问诊设计一个简单的 Statefrom typing import TypedDict, Annotated, List import operator class MedicalAgentState(TypedDict): # 用户输入 user_input: str # 整个对话历史 messages: Annotated[List, operator.add] # 关键这个注解使得节点可以向列表追加消息 # 已收集的症状列表 collected_symptoms: List[str] # 当前需要询问的关键问题由Agent决定 next_question: str # 流程是否结束 is_finished: bool # 最终建议 final_advice: strAnnotated[List, operator.add]是 LangGraph 的一个魔法它声明messages这个字段每个节点都可以向它添加内容而 Graph 会自动帮我们合并。这极大地简化了对话历史的管理。3.2 构建图节点Nodes节点是图中的处理单元。我们至少需要三个节点triage分诊/信息收集、generate_question生成问题、give_advice给出建议。节点1triage分诊与信息收集这个节点负责分析当前对话历史提取或更新症状并判断信息是否足够。from langchain_core.prompts import ChatPromptTemplate from langchain_core.output_parsers import JsonOutputParser from pydantic import BaseModel, Field # 定义我们希望LLM返回的结构化数据 class TriageResult(BaseModel): updated_symptoms: List[str] Field(description更新后的症状列表) is_info_sufficient: bool Field(description当前信息是否足够做出初步判断) missing_info: str Field(description如果信息不足还缺什么关键信息) def triage_node(state: MedicalAgentState): # 1. 准备给LLM的Prompt包含对话历史 prompt ChatPromptTemplate.from_messages([ (system, 你是一个分诊护士。请基于以下对话历史完成以下任务 1. 更新已收集的症状列表。 2. 判断当前信息是否足够给出初步就医建议。 3. 如果信息不足指出最需要追问的一个关键信息。 对话历史{history} 当前已收集症状{current_symptoms} ), (human, 用户最新输入{latest_input}) ]) # 2. 创建链并指定输出为TriageResult格式 triage_chain prompt | llm | JsonOutputParser(pydantic_objectTriageResult) # 3. 调用链 result triage_chain.invoke({ history: state[messages], current_symptoms: state[collected_symptoms], latest_input: state[user_input] }) # 4. 更新状态 new_state { collected_symptoms: result[updated_symptoms], is_finished: result[is_info_sufficient], next_question: if result[is_info_sufficient] else result[missing_info] } # 将用户输入也添加到历史消息中方便后续节点使用 new_state[messages] [{role: user, content: state[user_input]}] return new_state节点2generate_question生成追问当triage_node判断信息不足时这个节点负责将缺失的信息转化为一个自然语言问题。def generate_question_node(state: MedicalAgentState): if not state[next_question]: # 如果没有需要问的直接返回原状态 return {} prompt ChatPromptTemplate.from_messages([ (system, 请将以下需要收集的医学信息转化为一个对患者友好的、简洁的提问。), (human, 需要收集的信息{missing_info}) ]) question_chain prompt | llm | StrOutputParser() question question_chain.invoke({missing_info: state[next_question]}) # 将生成的问题也存入消息历史保持上下文连贯 return { next_question: question, # 更新为自然语言问题 messages: [{role: assistant, content: question}] }节点3give_advice生成建议当信息足够时这个节点综合所有症状生成初步建议。def give_advice_node(state: MedicalAgentState): prompt ChatPromptTemplate.from_messages([ (system, 你是一个医疗助手。请根据患者描述的症状提供初步的、谨慎的就医建议。 症状{symptoms} 请按以下结构输出 1. **可能情况**列举1-3种常见的可能性非诊断。 2. **建议措施**根据严重程度建议自我观察、线上问诊、门诊就医或急诊。 3. **注意事项**提醒在就医前可做的缓解措施或需要警惕的危重信号。 ), ]) advice_chain prompt | llm | StrOutputParser() advice advice_chain.invoke({symptoms: , .join(state[collected_symptoms])}) return { final_advice: advice, is_finished: True, messages: [{role: assistant, content: advice}] }3.3 定义边Edges与条件路由节点定义好了现在需要定义它们之间的流转逻辑。这是 LangGraph 最核心的部分。from langgraph.graph import StateGraph, END # 1. 创建图 workflow StateGraph(MedicalAgentState) # 2. 添加节点 workflow.add_node(triage, triage_node) workflow.add_node(generate_question, generate_question_node) workflow.add_node(give_advice, give_advice_node) # 3. 设置入口点 workflow.set_entry_point(triage) # 4. 定义条件边triage节点之后去哪 def decide_after_triage(state: MedicalAgentState): # 根据triage_node更新的is_finished字段决定 if state.get(is_finished): # 信息足够去给出建议 return give_advice else: # 信息不足去生成问题 return generate_question workflow.add_conditional_edges( triage, decide_after_triage, { give_advice: give_advice, generate_question: generate_question, } ) # 5. 定义普通边 # generate_question节点执行完后流程应该回到triage节点等待用户回答新问题 workflow.add_edge(generate_question, triage) # give_advice节点执行完后流程结束 workflow.add_edge(give_advice, END) # 6. 编译图 app workflow.compile()这个图定义了一个循环triage- (信息不足) -generate_question- (等待用户输入) -triage- (信息足够) -give_advice- END。3.4 运行与测试现在我们可以像运行一个函数一样运行这个图但需要以“流式”的方式与它交互模拟多轮对话。# 初始化状态 initial_state { user_input: 医生我肚子疼。, messages: [], collected_symptoms: [], next_question: , is_finished: False, final_advice: } # 创建图应用实例 graph_app app # 第一轮交互 print(用户肚子疼) result graph_app.invoke(initial_state) print(fAgent: {result[next_question]}) # 输出生成的问题例如“哪个位置疼是绞痛还是隐痛” # 模拟用户回答进行第二轮交互 next_state { **result, user_input: 右上腹一阵一阵的绞痛。, next_question: # 清空上一个问题 } result2 graph_app.invoke(next_state) print(fAgent: {result2[next_question]}) # 可能继续追问例如“有没有发烧或者皮肤发黄” # 继续交互直到 is_finished 为 True # ...通过这种“调用-更新状态-再调用”的方式我们就实现了一个有状态的多轮对话 Agent。所有中间状态对话历史、症状列表都自动维护在state对象中。4. 集成工具与知识库RAG基础的问答流程跑通了但一个实用的医疗 Agent 不能只靠 LLM 的通用知识。它需要查询药品信息、医学指南、医院科室等结构化知识。这就需要为 Agent 装配“工具”Tools。4.1 创建医疗知识查询工具假设我们有一个简单的“症状-科室”匹配知识库实际项目中可能是向量数据库。from langchain.tools import tool # 一个模拟的医学知识库 MEDICAL_KB { 头痛伴有发烧: {possible_causes: [感冒, 流感, 脑膜炎], recommended_dept: 神经内科/发热门诊}, 右上腹绞痛: {possible_causes: [胆囊炎, 胆结石], recommended_dept: 肝胆外科}, 胸闷气短: {possible_causes: [冠心病, 哮喘, 焦虑症], recommended_dept: 心内科/呼吸科}, } tool def query_medical_kb(symptom_description: str) - str: 根据症状描述查询可能的病因和推荐就诊科室。 # 这里做简单的关键词匹配实际应用应使用向量检索 for key, info in MEDICAL_KB.items(): if key in symptom_description: return f可能病因{info[possible_causes]}。建议就诊科室{info[recommended_dept]} return 未在知识库中找到明确匹配建议根据其他症状综合判断或咨询全科医生。4.2 让 Agent 学会调用工具我们需要修改triage_node或新建一个节点让 LLM 在需要时决定调用工具。这需要用到 LangChain 的Tool绑定和AgentExecutor。但在 LangGraph 中我们可以更灵活地集成。一种方法是在give_advice_node中集成工具调用def give_advice_node_with_tools(state: MedicalAgentState): symptoms_text , .join(state[collected_symptoms]) # 1. 先查询知识库 kb_result query_medical_kb.invoke(symptoms_text) # 2. 将知识库结果作为上下文让LLM生成更专业的建议 prompt ChatPromptTemplate.from_messages([ (system, 你是一个医疗助手。请根据患者症状和以下医学知识库信息提供初步就医建议。 【知识库信息】{kb_info} --- 患者症状{symptoms} 请按以下结构输出 1. **关联分析**结合知识库分析症状可能指向的方向。 2. **建议措施**给出具体的就医建议科室、紧急程度。 3. **重要提醒**强调必须由专业医生诊断本建议仅供参考。 ), ]) advice_chain prompt | llm | StrOutputParser() advice advice_chain.invoke({kb_info: kb_result, symptoms: symptoms_text}) return { final_advice: advice, is_finished: True, messages: [{role: assistant, content: advice}] }然后在构建图时用give_advice_node_with_tools替换原来的give_advice_node。这样Agent 在给出最终建议时就能结合领域知识了。更高级的用法是创建一个工具调用节点让 LLM 自主决定在流程中何时查询知识库。这需要利用 LangChain 的bind_tools和create_react_agent等功能并将其封装为一个 LangGraph 节点。这涉及到更复杂的 Agent 决策逻辑是进阶话题但核心模式不变定义工具 - 让 LLM 获得调用能力 - 在图中管理工具调用的流程。5. 利用 LangSmith 进行调试、评估与迭代代码写完了图也能跑了但这离“可用”还差得远。你需要验证它的表现找出问题。这就是 LangSmith 的舞台。5.1 追踪与可视化当你运行app.invoke()时如果已经配置了 LangSmith整个执行过程会被自动记录。打开 LangSmith 控制台你可以看到完整的 Trace 视图以时间线或树状图展示所有节点的执行顺序、耗时和父子关系。每个节点的输入输出点击任何一个节点如triage_node你能看到当时传入的state具体内容以及 LLM 收到的完整 Prompt 和返回的 Response。这是调试 Prompt 的终极武器。Token 与成本统计了解每次调用消耗的 Token 和估算成本为优化和预算提供依据。5.2 构建测试集进行评估单次测试靠运气系统评估靠数据。你需要在 LangSmith 中创建一个数据集Dataset包含一系列典型的用户问诊开场白如“我发烧了”、“孩子咳嗽厉害”。然后你可以用 LangSmith 的 SDK 批量运行你的 Graph并自动记录结果。from langsmith import Client client Client() dataset_name medical-agent-test-v1 # 创建或获取数据集 dataset client.create_dataset(dataset_name) # 添加测试用例 client.create_examples( inputs[{user_input: 头痛感觉有点发烧}, {user_input: 肚子疼拉肚子}], outputsNone, # 这里可以先不设预期输出 dataset_iddataset.id ) # 配置一个评估函数例如检查最终建议是否包含关键安全提示 def eval_function(run, example): advice run.outputs.get(final_advice, ) score 1 if 仅供参考 in advice or 医生诊断 in advice else 0 return {key: safety_warning_present, score: score} # 在LangSmith UI中你可以对数据集运行你的Graph并应用这个评估函数自动得到评分报告。通过评估报告你可以量化地看到 Agent 在哪些案例上表现好哪些案例上会“胡说八道”或遗漏安全警告从而有针对性地迭代 Prompt 或流程逻辑。5.3 基于反馈的持续优化LangSmith 允许你手动对运行结果打分或添加评论。你可以邀请领域专家如医生查看一些问诊 Trace让他们对 Agent 的追问是否合理、建议是否谨慎进行打分。这些人工反馈会成为优化模型和流程的宝贵数据。迭代循环运行 Agent - 在 LangSmith 查看 Trace - 发现不合理之处如追问多余、建议不准确- 修改对应节点的 Prompt 或工具逻辑 - 重新运行测试集评估 - 确认改进。这个循环是 Agent 开发的核心。6. 生产化部署与关键注意事项一个在笔记本里能跑的 Demo 和能服务真实用户的系统是两回事。最后这部分聊聊从 Demo 到生产需要跨过的坎。6.1 状态持久化与并发我们的 Graph 在内存中维护状态。在生产中你需要将MedicalAgentState持久化到数据库如 Redis、PostgreSQL。LangGraph 支持自定义Checkpointer来实现这一点。同时要考虑并发请求下的状态隔离确保每个用户会话有独立的状态存储。6.2 超时、重试与错误处理LLM 调用超时网络或服务方不稳定可能导致 LLM 调用超时。必须在调用链上设置超时和重试机制。LangChain 的Runnable组件可以配置retry和timeout参数。工具调用失败知识库查询、数据库连接可能失败。工具函数内部要有健壮的异常处理并返回结构化的错误信息供 Agent 处理。图执行中断在 LangGraph 中可以在节点周围添加try-except或将错误信息写入状态并设计专门的错误处理节点和边。6.3 安全、合规与伦理医疗领域无小事必须格外谨慎免责声明Agent 的每一次输出都必须包含清晰的免责声明强调其辅助性和非诊断性。内容过滤在 Prompt 的 System Message 中加强约束防止生成具体的用药剂量、绝对化的诊断结论。可以考虑在最终输出前增加一个“安全审核”节点用规则或另一个 LLM 进行内容过滤。数据隐私用户症状、对话历史是敏感数据。确保传输加密HTTPS、存储加密并制定合规的数据保留和删除策略。人工接管必须设计无缝转接人工客服的通道。当 Agent 判断情况可能紧急或用户明确要求时能立即切换。6.4 性能监控与成本控制监控指标除了 LangSmith 的 Trace还需要监控平均对话轮次、任务完成率、用户满意度如果有反馈渠道、各节点平均耗时、Token 消耗分布。成本控制使用gpt-3.5-turbo还是gpt-4不同节点是否可以用不同模型对历史消息进行智能摘要以减少 Token 消耗这些都是生产环境中必须考虑的问题。LangSmith 的成本追踪功能是辅助决策的关键。从一个医疗问诊 Agent 的构建过程可以看出LangChain、LangGraph、LangSmith 构成了一个从组件编排、流程控制到可观测性的完整闭环。新手最容易犯的错误是跳过 LangSmith直接闷头写代码导致调试效率极低或者试图只用 LangChain 硬编码复杂流程导致代码臃肿不堪。我的建议是按照本文的顺序实践先理解需求用 LangChain 搞定单点能力再用 LangGraph 画出清晰的状态流转图最后从一开始就接入 LangSmith让整个系统的运行变得透明、可调试、可优化。这样构建出来的 Agent才具备持续迭代和真正服务用户的能力。 30款热门AI模型一站整合DeepSeek/GLM/Qwen 随心用限时 5 折。 点击领海量免费额度