上周,一个做医疗信息化的朋友找到我,说他们团队想做一个能初步分诊的智能问诊助手。需求听起来很直接:用户描述症状,系统能给出可能的科室建议和初步健康指导。他们团队有医生资源,也有结构化知识库,但问题卡在了“怎么让AI用起来像人”。

他们试过直接调用大模型API,把知识库内容一股脑塞进提示词。结果呢?模型要么答非所问,要么在复杂的多轮对话中“失忆”,忘了之前用户说过什么。更麻烦的是,当需要查询外部数据库、调用计算工具或者根据对话状态跳转流程时,简单的“一问一答”模式彻底失灵了。朋友的原话是:“感觉模型很聪明,但让它按我们的流程‘走’起来,特别费劲。”

这恰恰是当前很多开发者从“调用大模型”迈向“构建AI应用”时遇到的核心瓶颈。我们不再满足于单次完美的回答,而是需要构建一个能感知状态、拥有记忆、可以规划并执行一系列动作的智能体(Agent)。而 LangChain、LangGraph与LangSmith ,正是为了解决“如何让AI应用‘走’起来并‘走’得稳”这一系列工程问题而生的工具链。很多人把它们看作三个独立的库,但实际上,它们共同构成了一个从组件组装、到流程编排、再到生产监控的完整闭环。

这篇文章,我们就以构建一个 医疗问诊Agent 为线索,彻底讲清楚这三者的核心定位、区别与协作关系。你会发现,它们解决的远不止是代码怎么写,而是如何将AI能力系统地工程化。

1. LangChain:你的“乐高积木箱”,解决“有什么”和“怎么连”的问题

当你开始构建AI应用时,第一个问题通常是:我需要哪些部件?LangChain就是这个问题的答案。它不是一个框架,而是一个庞大的、标准化的“乐高积木箱”。

1.1 核心价值:标准化接口与丰富生态

在LangChain出现之前,连接不同的模型、向量数据库、工具链,需要写大量的胶水代码。每个模型的API格式不同,每个数据库的查询语法各异。LangChain通过定义一套标准的抽象接口(如 LLM ChatModel Embeddings Retriever Tool ),把这一切标准化了。

对于我们的医疗问诊Agent,LangChain提供了以下关键“积木”:

  • 模型交互 ( LLM/ChatModel ) :无论是使用OpenAI的GPT、阿里的Qwen,还是本地部署的模型,都可以通过统一的 invoke stream 方法调用。
  • 记忆管理 ( Memory ) :这是实现多轮对话的基石。 ConversationBufferMemory ConversationSummaryMemory 等组件,帮助Agent记住之前的对话历史。比如,用户先说“我头疼”,过了一会儿又说“还流鼻涕”,记忆模块能确保模型知道这两个症状属于同一次问诊。
  • 检索增强 ( Retriever ) :这是实现知识库问答(RAG)的核心。 VectorStoreRetriever 可以从向量数据库中,根据用户问题语义检索出相关的医学指南、药品说明书片段,作为上下文提供给模型,让回答有据可依。
  • 工具调用 ( Tool ) :让模型学会“使用外部能力”。我们可以把“查询挂号科室表”、“计算BMI指数”、“获取药品相互作用信息”等封装成 Tool 。模型在推理后,可以决定调用哪个工具,并生成正确的调用参数。
# 一个简化的LangChain组件组装示例(概念代码)
from langchain_openai import ChatOpenAI
from langchain.memory import ConversationBufferMemory
from langchain.agents import create_react_agent, AgentExecutor
from langchain.tools import Tool

# 1. 定义模型(积木A)
llm = ChatOpenAI(model="gpt-4", temperature=0)

# 2. 定义记忆(积木B)
memory = ConversationBufferMemory(memory_key="chat_history", return_messages=True)

# 3. 定义工具(积木C)
def query_medical_guideline(symptom: str) -> str:
    # 模拟检索知识库
    return f"根据指南,{symptom}可能关联的科室有:神经内科、耳鼻喉科。"
medical_tool = Tool(name="QueryGuideline", func=query_medical_guideline, description="查询医学诊疗指南")

# 4. 使用LangChain的“蓝图”将它们组装成一个基础Agent
agent = create_react_agent(llm, tools=[medical_tool])
agent_executor = AgentExecutor(agent=agent, tools=[medical_tool], memory=memory, verbose=True)

# 运行
response = agent_executor.invoke({"input": "我最近经常头痛,应该挂哪个科?"})

关键理解 :LangChain此时的价值在于**“组装”**。它提供了丰富的预制件和一套标准的连接器(接口),让你能快速搭出一个能跑起来的原型。它的 AgentExecutor 是一种简单的、线性的“思考-行动”循环(ReAct模式)。但这也引出了下一个问题:当业务流程复杂、非线性时,这种简单的循环还够用吗?

2. LangGraph:你的“流程图设计器”,解决“怎么走”和“何时走”的问题

医疗问诊不是一个简单的Q&A。它可能是一个有状态、有分支、可循环的复杂流程。例如:

  1. 用户输入症状。
  2. Agent需要判断信息是否足够(如持续时间、疼痛程度)。如果不够, 返回 到提问状态。
  3. 信息足够后,检索知识库,给出初步判断。
  4. 用户可能追问“这个病严重吗?”,此时需要 结合历史 再次检索。
  5. 最后,根据严重程度,流程可能 分支 :轻症建议家庭护理,重症建议立即就医并推荐科室。

这种带有条件判断、循环、并行分支的流程,用LangChain的基础Agent来描述就非常吃力。而这,正是 LangGraph 要解决的核心问题。

2.1 核心范式:将应用建模为有向图

LangGraph建立在LangChain之上,它引入了“图”的概念。你可以把应用的每一个步骤定义为一个 节点(Node) ,步骤之间的流转定义为 边(Edge) 。节点之间不仅可以线性连接,还可以根据条件(Conditional Edge)或状态(State)动态决定下一步走向。

对于医疗问诊Agent,我们可以用LangGraph构建一个更健壮的流程:

from typing import TypedDict, Annotated, Literal
from langgraph.graph import StateGraph, END
from langchain_core.messages import HumanMessage

# 1. 定义全局状态(State)
class AgentState(TypedDict):
    messages: Annotated[list, "对话历史"]
    symptoms: Annotated[list, "已收集的症状列表"]
    sufficient_info: Annotated[bool, "信息是否已收集充分"]
    preliminary_advice: Annotated[str, "初步建议"]

# 2. 定义各个节点(Node)
def collect_symptoms(state: AgentState):
    """节点A:收集症状"""
    latest_message = state["messages"][-1]
    # 解析症状,更新到state[“symptoms”]中
    new_symptoms = parse_symptoms(latest_message.content)
    state["symptoms"].extend(new_symptoms)
    return {"symptoms": state["symptoms"]}

def assess_information_sufficiency(state: AgentState):
    """节点B:评估信息充分性"""
    # 根据业务逻辑判断,例如是否收集了关键症状、持续时间等
    is_sufficient = len(state["symptoms"]) >= 2 # 简化逻辑
    return {"sufficient_info": is_sufficient}

def route_based_on_sufficiency(state: AgentState) -> Literal["collect_more", "give_advice"]:
    """条件边:根据评估结果路由"""
    if state["sufficient_info"]:
        return "give_advice"
    else:
        return "collect_more"

def give_preliminary_advice(state: AgentState):
    """节点C:给出初步建议"""
    # 基于已收集的症状,调用LLM和工具生成建议
    advice = generate_advice(state["symptoms"])
    return {"preliminary_advice": advice, "messages": [HumanMessage(content=advice)]}

# 3. 构建图
workflow = StateGraph(AgentState)
workflow.add_node("collect_symptoms", collect_symptoms)
workflow.add_node("assess_info", assess_information_sufficiency)
workflow.add_node("give_advice", give_preliminary_advice)

# 设置边
workflow.set_entry_point("collect_symptoms")
workflow.add_edge("collect_symptoms", "assess_info")
workflow.add_conditional_edges(
    "assess_info",
    route_based_on_sufficiency,
    {"collect_more": "collect_symptoms", "give_advice": "give_advice"}
)
workflow.add_edge("give_advice", END)

# 编译图
app = workflow.compile()

关键理解 :LangGraph将Agent的“工作流”显式地定义了出来。它带来的最大改变是 可控性和可观测性

  • 可控 :你可以清晰地看到业务逻辑的完整路径,哪里循环,哪里分支。调试时,你可以精确知道当前执行到了哪个节点。
  • 可观测 :整个应用的 State 是集中管理的,任何节点都可以读取和修改状态,避免了信息在函数间隐式传递的混乱。

在医疗问诊场景中,这意味着你可以轻松实现“反复追问直到信息充分”的循环,或者根据“是否紧急”进行不同的分支处理。 LangGraph让你从“组装智能体”升级到了“编排智能流程”

3. LangSmith:你的“全链路调试与监控平台”,解决“看得清”和“管得住”的问题

当你用LangChain和LangGraph搭建好一个酷炫的医疗问诊Agent后,新的挑战接踵而至:

  • 用户反馈某个回答不准确,你怎么复现和定位问题?是检索器没找到资料,还是模型理解错了?
  • 生产环境调用延迟变高,是哪个环节慢了?是模型调用,还是工具查询?
  • 想优化提示词(Prompt),如何科学地对比不同版本的效果?
  • 如何跟踪每天处理了多少问诊,成功率如何?

在本地开发时,你可以打印日志。但在复杂的、多步骤的Agent应用中,打印日志就像用望远镜看细胞结构——模糊且低效。 LangSmith 就是为此而生的AI应用开发平台。

3.1 核心能力:可观测性、测试与评估

LangSmith与LangChain/LangGraph无缝集成。只需设置一个环境变量,你所有通过LangChain/LangGraph发起的调用,都会被自动追踪并发送到LangSmith平台。

它主要提供三大价值:

1. 全链路追踪(Tracing) 每一次Agent运行,在LangSmith上都会生成一个清晰的 溯源图 。这个图会展示整个执行过程:何时调用了LLM,输入输出的提示词是什么;何时调用了检索工具,返回了哪些文档;何时进入了LangGraph的哪个节点,状态如何变化。

对于医疗问诊Agent的调试,这意味著:

  • 如果模型给出了错误建议,你可以立刻回溯到是检索环节没有提供关键文档,还是模型在推理时忽略了某条信息。
  • 如果流程卡住了,你可以看到它停在了哪个Graph节点,当时的状态数据是什么。

2. 数据集管理与提示词工程 你可以将出错的问诊对话保存为 数据集 。然后,在不修改代码的情况下,在LangSmith界面上直接编辑和优化提示词,并针对这个数据集运行批量测试,直观地比较不同提示词版本的效果(如准确性、成本、延迟)。

3. 自动化评估与监控 你可以定义评估函数(例如,用另一个LLM判断回答是否包含安全警告、是否引用了知识库),让LangSmith自动对大量运行结果进行评分。你还可以设置监控看板,跟踪生产环境Agent的延迟、成本、错误率等关键指标。

关键理解 :LangSmith解决的是AI应用生命周期中**“开发后”**的痛点。它让Agent从一个黑盒变成了白盒,让迭代优化从“凭感觉”变成了“看数据”。它是确保你的医疗问诊Agent能稳定、可靠、持续改进的“运维大脑”。

4. 实战框架:从零构建医疗问诊Agent的层级化策略

理解了这三者的分工,我们可以形成一个清晰的构建策略。不要试图一步到位,而应遵循“由简入繁,逐层加固”的路径。

4.1 第一层:用LangChain快速验证核心逻辑

目标 :在几小时内验证想法是否可行。 做法

  1. ChatOpenAI / ChatQwen 连接LLM。
  2. ConversationBufferMemory 实现简单记忆。
  3. 封装1-2个核心工具(如 检索医学知识库工具 )。
  4. 使用 create_react_agent 组装一个基础Agent。 检查点 :能否完成一次简单的、包含历史上下文和工具调用的问答?例如,用户说“我头痛”,接着问“该吃什么药?”,Agent能否结合记忆,调用工具检索头痛的常用非处方药并回答?

4.2 第二层:用LangGraph设计健壮业务流程

目标 :将验证过的逻辑,扩展为可处理复杂场景的稳定流程。 做法

  1. 定义清晰的 State 结构,包含对话、已收集信息、阶段标志等。
  2. 将第一层的功能拆解为Graph节点: 信息收集节点 信息充分性判断节点 知识检索节点 推理建议节点 科室推荐节点
  3. 设计节点间的边和条件分支。例如, 判断节点 后,根据信息是否充分,决定是跳回 收集节点 还是进入 推理节点
  4. 推理节点 中,复用第一层中验证过的LLM调用和工具调用逻辑。 检查点 :流程能否处理“信息不足->追问->再判断”的循环?能否根据“症状是否紧急”走不同的分支(如常规建议 vs. 紧急就医)?

4.3 第三层:用LangSmith实现可观测与可迭代

目标 :让应用变得透明、可调试、可优化。 做法

  1. 集成LangSmith SDK,将所有运行记录上报。
  2. 将线上遇到的错误案例和典型用例,保存为LangSmith数据集。
  3. 针对数据集中回答不佳的案例,在LangSmith上创建“提示词变体”实验,进行A/B测试。
  4. 为关键节点(如最终建议)添加自动化评估,监控回答质量。
  5. 在仪表盘中关注平均响应延迟、工具调用失败率等运营指标。 检查点 :能否在界面上清晰追溯一次失败问诊的全链路?能否通过数据证明,新提示词将回答准确率从70%提升到了85%?

4.4 避坑指南:从原型到生产的关键考量

  • 状态设计的纯净性 :LangGraph的 State 是共享的,设计时要像管理数据库一样小心。确保每个节点只修改自己负责的部分,避免副作用。对于医疗场景,患者隐私数据需特别处理,不应长期保留在原始状态中。
  • 工具的稳定性 :Agent的强大依赖于工具的可靠。确保你的知识库检索工具能处理网络超时、结果为空等情况,并返回结构化的错误信息供LLM理解。
  • LangGraph的持久化 :对于长时间的对话(如一次问诊可能间隔数小时),你需要将 State 持久化到数据库(如Redis),并在下次请求时恢复。LangGraph支持 Checkpointer 机制来实现这一点。
  • 成本与延迟监控 :通过LangSmith密切关注LLM调用的token消耗和耗时。对于非关键路径,考虑使用更小、更快的模型。设置报警,防止异常流量导致成本激增。

5. 总结:不是三选一,而是一个递进的工具箱

回到我朋友的那个医疗问诊Agent项目。最初的困惑,源于试图用一个工具解决所有问题。而LangChain、LangGraph、LangSmith的本质,是提供了三个不同维度、相互衔接的工具:

  • LangChain是“砖瓦厂” :它提供了构建AI应用所需的各种标准化原材料(模型、记忆、检索器、工具)和粘合剂(链、代理)。它的核心价值是 降低集成门槛
  • LangGraph是“建筑设计图” :它提供了描述复杂、有状态、非线性业务流程的高级范式。它的核心价值是 提升复杂逻辑的控制力与清晰度
  • LangSmith是“施工监理与质检中心” :它提供了从开发调试到生产监控的全链路可观测性。它的核心价值是 保障应用质量、驱动持续优化

对于绝大多数AI应用开发者,学习路径应该是清晰的: 从LangChain开始,理解组件化思维;当业务逻辑变得复杂时,引入LangGraph进行流程编排;当应用需要部署和持续改进时,使用LangSmith进行监控和优化。

最终,一个成熟的、可用于生产的医疗问诊Agent,必然是三者协同工作的结果:用LangChain的标准接口调用模型和工具,用LangGraph的图结构编排严谨的问诊流程,再用LangSmith的追踪和评估能力确保每一次“数字医生”的出诊都稳定、可靠、可改进。这不再是简单的提示词工程,而是正儿八经的软件工程。

更多推荐