最近在尝试将大语言模型(LLM)应用到具体的业务场景中,比如医疗问诊、金融客服,发现单纯调用一个模型 API 往往力不从心。模型不知道如何获取最新知识,无法执行多步骤任务,更缺乏对复杂流程的控制。这时, LangChain、LangGraph 和 LangSmith 这三个工具就进入了视野。它们分别解决了 LLM 应用开发中的工具链、流程编排和监控调试三大核心难题。本文将以一个 医疗问诊智能体(Agent) 的构建过程为主线,手把手带你理解这三个框架的核心概念、区别与协作方式,并提供一个可运行的代码示例。无论你是刚接触 AI 应用开发的新手,还是希望系统化工程化的开发者,都能从中获得清晰的路径。

1. 背景与核心概念:为什么需要它们?

在深入代码之前,我们必须先理清这三个工具各自扮演的角色,以及它们如何协同工作。这就像组建一个现代化的医院。

1.1 LangChain:医生的“工具箱”

LangChain 是一个用于开发由语言模型驱动的应用程序的框架。你可以把它想象成一位全能医生随身携带的 工具箱

  • 它是什么 :一套标准化的接口和组件,用于连接 LLM、外部数据源(如数据库、搜索引擎)、记忆存储以及各种工具(如计算器、API)。
  • 解决什么问题 :解决了 LLM 的“孤立”问题。LLM 本身是一个强大的“大脑”,但缺乏“手”(执行工具)、“眼睛”(读取数据)和“记忆”(记住对话)。LangChain 通过 Chains Agents Retrieval 等模块,将这些能力有机地组合起来。
  • 核心组件
    • Models : 封装各种 LLM(OpenAI, Anthropic, 本地模型等)和 Embedding 模型。
    • Prompts : 管理提示词模板,实现动态提示生成。
    • Indexes : 用于文档加载、分割、向量化存储和检索(即 RAG 的核心)。
    • Chains : 将多个组件按顺序链接起来,执行一个固定流程。
    • Agents : 让 LLM 根据当前情况,自主决定调用哪个工具(来自 Tools )来完成任务。这是实现“智能”的关键。
    • Memory : 在对话或交互中持久化状态信息。

简单来说,LangChain 让你能方便地告诉 LLM:“这是计算器(工具),这是病历数据库(检索器),你用它们来帮病人诊断。”

1.2 LangGraph:医院的“就诊流程图”

当问诊流程变得复杂,比如需要分诊、多科室会诊、检查、开药等环节循环时,简单的链(Chain)或基础的智能体(Agent)就难以清晰表达这种带有 循环、分支、并行 的状态逻辑。

LangGraph 是建立在 LangChain 之上的一个库,用于构建具有 状态和循环 的复杂、有状态的应用程序。

  • 它是什么 :一个基于图(Graph)的工作流编排框架。它用节点(Node)和边(Edge)来定义应用程序的执行流程。
  • 解决什么问题 :解决了复杂、多步骤、有状态任务的流程控制问题。它让 Agent 的行为不再是“一次思考-行动”,而是可以像流程图一样,在不同的状态间流转,直到满足终止条件。
  • 核心概念
    • State : 定义整个工作流共享的数据结构(如病人信息、当前症状、已做检查、诊断结果)。
    • Nodes : 图中的节点,代表一个执行步骤(如“分诊”、“问诊”、“开检查单”)。每个节点是一个函数,接收 State,修改 State,并返回结果。
    • Edges : 连接节点的边,决定下一个执行哪个节点。可以是条件边(根据 State 内容决定),也可以是固定边。
    • Checkpointer : 支持持久化状态,实现长时运行工作流的暂停与恢复。

用医院比喻,LangGraph 就是那张清晰的“就诊流程图”,规定了病人从挂号到离院的每一步路径和规则。

1.3 LangSmith:医院的“监控与质控中心”

开发调试一个 LLM 应用非常痛苦:提示词效果不好、链调用出错、Agent 决策诡异、成本不可控。

LangSmith 是一个用于调试、测试、评估和监控 LLM 应用程序的开发平台。

  • 它是什么 :一个云服务平台(也有本地部署方案),为 LangChain/LangGraph 应用提供全生命周期的可观测性。
  • 解决什么问题 :解决了 LLM 应用开发中的“黑盒”问题,提供了追踪、评估、版本管理能力。
  • 核心功能
    • Tracing : 自动记录每次 Chain、Agent、LLM 调用的输入、输出、耗时、token 消耗和内部步骤,形成可视化链路。
    • Testing & Evaluation : 创建数据集,批量测试应用,并利用 LLM 或规则自动评估输出质量。
    • Prompt Management : 版本化管理提示词,方便对比不同提示词的效果。
    • Monitoring : 监控生产环境应用的性能、成本和异常。

它就像医院的监控中心,记录每一次诊疗过程,评估医生(Agent)的表现,并不断优化诊疗规范(Prompt)。

1.4 三者关系总结

  • LangChain 提供砖瓦和水泥 (Models, Tools, Memory等基础组件)。
  • LangGraph 提供建筑设计图和施工流程 (用图来编排这些组件,构建复杂应用)。
  • LangSmith 提供工程监理和质量检测体系 (确保建筑过程可控,结果可靠)。

接下来,我们将用它们构建一个简化的医疗问诊 Agent。

2. 环境准备与版本说明

我们将使用 Python 进行开发。请确保你的环境满足以下要求:

  • 操作系统 : Windows 10/11, macOS 或 Linux (Ubuntu 20.04+)
  • Python : 版本 3.10 或 3.11(推荐 3.11,兼容性最好)
  • 包管理工具 : pip

首先,创建一个新的项目目录并安装必要的依赖。我们主要使用 langchain , langgraph , langsmith 的客户端,以及 OpenAI 的 LLM(你也可以替换为其他兼容 API 的模型,如 Anthropic、通义千问等)。

# 创建项目目录
mkdir medical-agent-tutorial && cd medical-agent-tutorial

# 创建虚拟环境 (可选但推荐)
python -m venv venv
# Windows: venv\Scripts\activate
# macOS/Linux: source venv/bin/activate

# 安装核心依赖
pip install langchain langgraph langsmith openai

# 安装其他可能用到的工具库
pip install python-dotenv  # 用于管理环境变量

版本说明 : 本文示例基于以下主要库版本编写,不同版本间 API 可能有细微差异,请以官方文档为准。

  • langchain==0.1.0 及以上
  • langgraph==0.0.40 及以上
  • langsmith==0.0.85 及以上
  • openai==1.0.0 及以上

重要 :你需要一个 OpenAI API Key 来运行示例。请将其保存在项目根目录的 .env 文件中,不要硬编码在代码里。

# .env 文件内容
OPENAI_API_KEY=sk-your-actual-api-key-here
LANGCHAIN_API_KEY=ls-your-langsmith-api-key-here # 可选,如果使用LangSmith
LANGCHAIN_TRACING_V2=true # 可选,启用LangSmith追踪
LANGCHAIN_PROJECT=Medical-Agent-Tutorial # 可选,指定LangSmith项目名

3. 核心组件拆解:构建问诊 Agent 的基石

在画流程图(LangGraph)之前,我们需要先用 LangChain 准备好“工具箱”里的工具。

3.1 定义工具:医生的“听诊器”和“检查单”

我们的问诊 Agent 需要一些基础工具。这里模拟三个工具:

  1. 症状查询工具 :模拟一个医学知识库,根据症状关键词返回可能的疾病信息。
  2. 检查建议工具 :根据当前怀疑的疾病,建议需要做的检查项目。
  3. 药品查询工具 :根据疾病和患者过敏史,推荐安全药品。
# tools.py
from langchain.tools import tool
from typing import Optional

@tool
def query_symptom_knowledge(symptom: str) -> str:
    """
    根据症状关键词查询医学知识库,返回可能的疾病信息。
    
    Args:
        symptom: 症状描述,如“发烧咳嗽”、“腹痛腹泻”。
    
    Returns:
        可能的疾病列表和简要说明。
    """
    # 这里模拟一个简单的知识库,真实场景应连接向量数据库或专业API
    knowledge_base = {
        "发烧咳嗽": "可能疾病:1. 普通感冒:上呼吸道感染,通常伴有鼻塞、流涕。\n2. 流感:起病急,高热、全身酸痛、乏力明显。\n3. 肺炎:可能伴有胸痛、咳脓痰、呼吸困难。",
        "腹痛腹泻": "可能疾病:1. 急性胃肠炎:多由不洁饮食引起,常伴恶心、呕吐。\n2. 细菌性痢疾:里急后重,便中带脓血。\n3. 肠易激综合征:慢性病程,与情绪、饮食相关。",
        "头痛头晕": "可能疾病:1. 偏头痛:单侧搏动性头痛,可能畏光畏声。\n2. 紧张性头痛:双侧压迫感或紧箍感。\n3. 高血压:需测量血压确认。"
    }
    return knowledge_base.get(symptom, f"知识库中未找到关于'{symptom}'的典型疾病信息。建议提供更详细症状。")

@tool
def suggest_examination(disease_suspicion: str) -> str:
    """
    根据怀疑的疾病,建议需要做的检查项目。
    
    Args:
        disease_suspicion: 怀疑的疾病名称,如“肺炎”、“急性胃肠炎”。
    
    Returns:
        建议的检查项目列表。
    """
    examination_map = {
        "肺炎": "建议检查:1. 血常规 2. C反应蛋白(CRP) 3. 胸部X光或CT 4. 痰培养",
        "急性胃肠炎": "建议检查:1. 血常规 2. 粪便常规+潜血 3. 电解质检查(若脱水严重)",
        "流感": "建议检查:1. 血常规 2. 流感病毒抗原检测(咽拭子)",
        "高血压": "建议检查:1. 血压监测(动态/家庭) 2. 心电图 3. 肾功能、尿常规 4. 眼底检查"
    }
    return examination_map.get(disease_suspicion, f"对于疾病'{disease_suspicion}',建议咨询专科医生确定具体检查。")

@tool
def recommend_medication(disease: str, allergy_history: Optional[str] = None) -> str:
    """
    根据疾病和患者过敏史,推荐安全药品。
    
    Args:
        disease: 确诊或高度怀疑的疾病名称。
        allergy_history: 患者过敏史,如“青霉素过敏”、“磺胺过敏”。
    
    Returns:
        药品推荐及注意事项。
    """
    medication_guide = {
        "普通感冒": "推荐:1. 对症治疗:布洛芬(退烧止痛)。2. 复方感冒药(如酚麻美敏)。\n注意:多休息,多喝水。",
        "细菌性肺炎": "推荐:1. 抗生素:阿莫西林克拉维酸钾(若无青霉素过敏)。若青霉素过敏,可用左氧氟沙星或阿奇霉素。\n2. 止咳化痰药:氨溴索。",
        "高血压": "推荐:1. 一线药物:氨氯地平(钙通道阻滞剂)或厄贝沙坦(ARB类)。\n需在医生指导下长期规律服用,定期监测血压。"
    }
    
    recommendation = medication_guide.get(disease, f"疾病'{disease}'的常规用药建议请咨询药师或医生。")
    
    if allergy_history:
        if "青霉素" in allergy_history and "阿莫西林" in recommendation:
            recommendation += f"\n【重要警告】患者有{allergy_history},禁止使用青霉素类抗生素(如阿莫西林),请更换其他类别抗生素。"
        if "磺胺" in allergy_history and "复方" in recommendation:
            recommendation += f"\n【注意】部分复方感冒药含磺胺成分,有过敏史者慎用。"
    
    return recommendation

3.2 创建智能体(Agent):赋予 LLM 使用工具的能力

我们将使用 LangChain 的 create_react_agent 来创建一个 ReAct 模式的智能体。这种模式的 Agent 会进行“思考(Reason)”和“行动(Act)”的循环。

# agent_basic.py
from langchain_openai import ChatOpenAI
from langchain.agents import create_react_agent, AgentExecutor
from langchain.prompts import PromptTemplate
from tools import query_symptom_knowledge, suggest_examination, recommend_medication
import os
from dotenv import load_dotenv

load_dotenv()  # 加载 .env 文件中的环境变量

# 1. 初始化大语言模型
llm = ChatOpenAI(model="gpt-3.5-turbo", temperature=0, api_key=os.getenv("OPENAI_API_KEY"))

# 2. 准备工具列表
tools = [query_symptom_knowledge, suggest_examination, recommend_medication]

# 3. 定义ReAct代理的提示词模板
# 这个模板告诉LLM如何思考、如何使用工具、如何格式化输出
react_prompt_template = """
你是一个专业的医疗问诊助手。请根据患者的描述,使用提供的工具来逐步分析病情,并提供最终建议。
你有权使用以下工具:

{tools}

使用工具时,请严格按照以下格式:
Thought: 我需要思考当前应该做什么
Action: 使用的工具名
Action Input: 工具的输入(必须是一个字符串)
Observation: 工具返回的结果

当你有了最终答案时,必须使用以下格式:
Thought: 我现在知道了最终答案
Final Answer: 给患者的最终建议,应清晰、全面、包含诊断思路、检查建议和用药提醒(如有)。

开始!

患者主诉:{input}

{agent_scratchpad}"""  # agent_scratchpad 会自动填充之前的思考和行动历史

prompt = PromptTemplate.from_template(react_prompt_template)

# 4. 创建ReAct Agent 和 执行器
agent = create_react_agent(llm, tools, prompt)
agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True, handle_parsing_errors=True)

# 5. 运行一个简单示例
if __name__ == "__main__":
    patient_input = "我发烧,还咳嗽,浑身没力气。"
    print(f"患者: {patient_input}")
    print("-" * 50)
    result = agent_executor.invoke({"input": patient_input})
    print("\n" + "="*50)
    print("助手最终回答:\n", result["output"])

运行 python agent_basic.py ,你会看到 Agent 的思考过程(因为 verbose=True ):

  1. Thought : 患者有发烧、咳嗽、乏力症状,我需要先查询可能的疾病。
  2. Action : query_symptom_knowledge
  3. Action Input : 发烧咳嗽
  4. Observation : 得到可能疾病列表(感冒、流感、肺炎)。
  5. Thought : 需要区分是感冒、流感还是肺炎,建议做检查来明确。
  6. Action : suggest_examination
  7. Action Input : 肺炎 (它可能根据严重程度选择先排查肺炎)
  8. ... 如此循环,直到它认为可以给出最终建议。

这个基础的 Agent 已经能完成多步任务,但它的流程是隐式的,由 LLM 每次临时决定。对于更复杂、更需要严格控制的流程,我们就需要 LangGraph

4. 完整实战:用 LangGraph 构建结构化问诊工作流

现在,我们来用 LangGraph 设计一个更贴近真实场景的、有状态的问诊流程图。流程包括: 分诊 -> 详细问诊 -> 检查建议 -> 初步诊断与用药建议 -> 结束

4.1 定义状态:患者的“病历本”

首先,我们定义整个工作流共享的状态(State)。这就像一份电子病历,记录问诊过程中的所有信息。

# graph_state.py
from typing import TypedDict, List, Optional, Annotated
import operator

class GraphState(TypedDict):
    """
    定义LangGraph工作流的状态。
    所有节点都读取和修改这个状态字典。
    """
    # 患者输入
    patient_input: str
    # 从症状中提取的关键信息
    extracted_symptoms: List[str]
    # 知识库查询结果
    possible_diseases: Optional[str]
    # 建议的检查项目
    suggested_exams: Optional[str]
    # 患者过敏史(在问诊中获取)
    allergy_history: Optional[str]
    # 初步诊断
    preliminary_diagnosis: Optional[str]
    # 用药建议
    medication_recommendation: Optional[str]
    # 最终输出给患者的信息
    final_advice: Optional[str]
    # 控制流程的标记,决定下一步去哪
    next_step: str  # 例如: "triage", "inquire", "suggest_exam", "diagnose", "end"

4.2 构建图:定义节点和边

我们将创建五个节点函数,并定义它们之间的流转逻辑。

# medical_graph.py
from langgraph.graph import StateGraph, END
from graph_state import GraphState
from tools import query_symptom_knowledge, suggest_examination, recommend_medication
from langchain_openai import ChatOpenAI
import os
from dotenv import load_dotenv

load_dotenv()
llm = ChatOpenAI(model="gpt-3.5-turbo", temperature=0)

# ---------- 节点1: 分诊 (Triage) ----------
def triage_node(state: GraphState) -> GraphState:
    """
    初步分诊节点。
    分析患者主诉,提取关键症状,并决定下一步是直接查询知识库还是进入详细问诊。
    """
    print("[节点] 进入分诊环节...")
    symptoms = state["patient_input"]
    
    # 这里可以用一个简单的LLM调用或规则来提取症状关键词
    # 为了简化,我们假设输入就是症状描述
    state["extracted_symptoms"] = [symptoms]
    
    # 简单规则:如果症状描述简单(如“发烧咳嗽”),直接查知识库;否则进入详细问诊
    if len(symptoms) < 20: # 简单判断
        state["next_step"] = "query_knowledge"
    else:
        state["next_step"] = "inquire"
    return state

# ---------- 节点2: 详细问诊 (Inquire) ----------
def inquire_node(state: GraphState) -> GraphState:
    """
    详细问诊节点。
    模拟医生进一步询问病史、过敏史等。
    """
    print("[节点] 进入详细问诊环节...")
    # 在实际应用中,这里可以是一个复杂的LLM对话,动态生成问题。
    # 本例中我们模拟一个固定询问。
    print("助手:请问您是否有已知的药物过敏史?(例如:青霉素、磺胺等)")
    # 假设我们从状态或外部输入获取答案,这里模拟一个答案
    simulated_allergy = "没有青霉素过敏史"  # 实际应从交互中获取
    state["allergy_history"] = simulated_allergy
    state["next_step"] = "query_knowledge"  # 问诊后去查询知识库
    return state

# ---------- 节点3: 查询知识库 (Query Knowledge) ----------
def query_knowledge_node(state: GraphState) -> GraphState:
    """
    使用工具查询症状对应的可能疾病。
    """
    print("[节点] 查询医学知识库...")
    symptom = state["extracted_symptoms"][0] if state["extracted_symptoms"] else state["patient_input"]
    knowledge = query_symptom_knowledge.invoke(symptom)
    state["possible_diseases"] = knowledge
    print(f"知识库查询结果: {knowledge[:100]}...") # 打印部分结果
    state["next_step"] = "suggest_exam"
    return state

# ---------- 节点4: 建议检查 (Suggest Examination) ----------
def suggest_exam_node(state: GraphState) -> GraphState:
    """
    根据可能的疾病,建议检查项目。
    """
    print("[节点] 生成检查建议...")
    # 从知识库结果中,让LLM提取一个最可能的疾病用于建议检查(简化处理)
    # 实际应用中可能需要更复杂的逻辑
    diseases_text = state["possible_diseases"]
    # 简单提取第一行提到的疾病
    first_line = diseases_text.split('\n')[0] if diseases_text else ""
    target_disease = "肺炎" if "肺炎" in first_line else "急性胃肠炎" if "胃肠炎" in first_line else "感冒"
    
    exams = suggest_examination.invoke(target_disease)
    state["suggested_exams"] = exams
    state["preliminary_diagnosis"] = f"考虑{target_disease}可能性大,需进一步检查明确。"
    state["next_step"] = "diagnose"
    return state

# ---------- 节点5: 诊断与建议 (Diagnose) ----------
def diagnose_node(state: GraphState) -> GraphState:
    """
    综合信息,给出初步诊断和用药建议,形成最终输出。
    """
    print("[节点] 生成最终诊断与建议...")
    diagnosis = state["preliminary_diagnosis"]
    allergy = state["allergy_history"]
    # 同样,简化处理,从初步诊断中提取疾病名
    disease_for_med = "肺炎" if "肺炎" in diagnosis else "普通感冒"
    
    meds = recommend_medication.invoke(disease_for_med, allergy)
    state["medication_recommendation"] = meds
    
    # 组装最终建议
    final_advice = f"""
    【问诊总结】
    患者主诉:{state['patient_input']}
    
    【初步分析】
    {state['possible_diseases']}
    
    【检查建议】
    {state['suggested_exams']}
    
    【初步诊断】
    {diagnosis}
    
    【用药提醒】
    {meds}
    
    【重要提示】
    以上为AI辅助分析,仅供参考,不能替代执业医师诊断。请尽快携带相关资料前往医院就诊,由医生进行最终诊断和治疗。
    """
    state["final_advice"] = final_advice
    state["next_step"] = "end"
    return state

# ---------- 构建图 ----------
def create_medical_graph():
    """创建并返回编译好的问诊工作流图"""
    workflow = StateGraph(GraphState)
    
    # 添加节点
    workflow.add_node("triage", triage_node)
    workflow.add_node("inquire", inquire_node)
    workflow.add_node("query_knowledge", query_knowledge_node)
    workflow.add_node("suggest_exam", suggest_exam_node)
    workflow.add_node("diagnose", diagnose_node)
    
    # 设置入口点
    workflow.set_entry_point("triage")
    
    # 定义边(根据 state[“next_step”] 的值决定流向)
    # 从 triage 出发
    workflow.add_conditional_edges(
        "triage",
        # 这是一个路由函数,根据state决定下一个节点
        lambda state: state["next_step"],
        {
            "query_knowledge": "query_knowledge",
            "inquire": "inquire",
        }
    )
    # inquire 之后固定去 query_knowledge
    workflow.add_edge("inquire", "query_knowledge")
    # 后续流程是固定的
    workflow.add_edge("query_knowledge", "suggest_exam")
    workflow.add_edge("suggest_exam", "diagnose")
    # diagnose 之后结束
    workflow.add_edge("diagnose", END)
    
    # 编译图
    graph = workflow.compile()
    return graph

if __name__ == "__main__":
    # 创建图实例
    medical_graph = create_medical_graph()
    
    # 定义初始状态
    initial_state: GraphState = {
        "patient_input": "我发烧,咳嗽比较厉害,感觉胸口有点闷。",
        "extracted_symptoms": [],
        "possible_diseases": None,
        "suggested_exams": None,
        "allergy_history": None,
        "preliminary_diagnosis": None,
        "medication_recommendation": None,
        "final_advice": None,
        "next_step": "triage",  # 从分诊开始
    }
    
    print("开始执行医疗问诊工作流...")
    print("="*60)
    # 运行图
    final_state = medical_graph.invoke(initial_state)
    
    print("\n" + "="*60)
    print("工作流执行完毕!")
    print("\n【最终给患者的建议】")
    print(final_state["final_advice"])

运行 python medical_graph.py ,你将看到一个清晰的、按步骤执行的问诊流程。每个节点的执行和状态转换一目了然。这就是 LangGraph 带来的价值: 显式、可控、可调试的复杂流程

4.3 集成 LangSmith:监控与调试工作流

要让这个工作流更健壮,我们需要观察其内部运行情况。LangSmith 可以无缝集成。

首先,确保你已设置好 LANGCHAIN_API_KEY 等环境变量(见第2节)。LangChain/LangGraph 的调用会自动被追踪。

我们修改 medical_graph.py 中的工具调用部分,使其使用 @tool 装饰器的工具能被 LangSmith 更好地追踪。实际上,只要你用 langchain tool 装饰器或 Tool 类创建工具,并在有 LangSmith 配置的环境下运行,追踪就会自动发生。

为了更明显,我们可以在主执行代码中增加一个简单的 LangSmith 追踪示例:

# 在 medical_graph.py 的 __main__ 部分稍作修改
if __name__ == "__main__":
    import langsmith
    # 初始化客户端(环境变量已配置则自动进行)
    client = langsmith.Client()
    print(f"LangSmith 追踪已启用,项目: {os.getenv('LANGCHAIN_PROJECT', 'default')}")
    
    medical_graph = create_medical_graph()
    initial_state: GraphState = {...} # 同上
    
    print("开始执行医疗问诊工作流(追踪中)...")
    # invoke 方法会自动将本次运行记录到 LangSmith
    final_state = medical_graph.invoke(initial_state)
    # ... 其余输出不变

运行后,打开 LangSmith 官网 ,在你的项目中就能看到这次 invoke 的完整追踪记录(Trace)。你可以点击查看:

  • 整个 Graph 的调用链路。
  • 每个节点(函数)的输入输出。
  • 每个工具调用的详细输入、输出和耗时。
  • LLM 内部调用的提示词和补全结果。

这对于调试 Agent 的决策过程、优化提示词、分析性能瓶颈至关重要。

5. 常见问题与排查思路

在开发 LangChain/LangGraph 应用时,你可能会遇到以下典型问题:

问题现象 常见原因 解决思路
ModuleNotFoundError: No module named ‘langchain’ 依赖未正确安装或虚拟环境未激活。 1. 确认在项目目录下。
2. 激活虚拟环境: source venv/bin/activate (Linux/Mac) 或 venv\Scripts\activate (Windows)。
3. 重新安装: pip install -r requirements.txt
AuthenticationError / Invalid API Key OpenAI API Key 错误或未设置。 1. 检查 .env 文件中的 OPENAI_API_KEY 是否正确。
2. 确认代码中通过 os.getenv(“OPENAI_API_KEY”) 读取。
3. 确保 Key 有余额且未过期。
Agent 陷入循环,不停调用工具 ReAct 提示词未明确终止条件,或 LLM 无法理解任务已完成。 1. 检查提示词模板中的 Final Answer 格式是否明确。
2. 在 AgentExecutor 中设置 max_iterations 参数(如 max_iterations=5 )强制限制循环次数。
3. 使用 handle_parsing_errors=True 避免解析错误导致循环。
LangGraph 节点状态未更新 节点函数修改了局部变量,但未正确更新并返回 state 字典。 确保每个节点函数都接收 state ,修改它,并返回修改后的 state 。LangGraph 依赖返回值来更新全局状态。
工具调用失败,参数类型错误 @tool 装饰器函数的参数类型提示与 LLM 生成的 Action Input 不匹配。 1. 确保工具函数有清晰的 Args 文档字符串,LLM 依赖这个来生成输入。
2. 工具函数的参数尽量使用简单类型( str , int ),避免复杂对象。
3. 使用 AgentExecutor(..., handle_parsing_errors=True) 捕获错误并让 Agent 重试。
LangSmith 看不到追踪记录 环境变量未正确配置或网络问题。 1. 确认 .env 文件中 LANGCHAIN_TRACING_V2=true LANGCHAIN_API_KEY 已设置。
2. 重启终端或 IDE 使环境变量生效。
3. 访问 LangSmith 网站,确认 API Key 有效,且项目名称正确。
Graph 编译错误 StateGraph 的节点或边定义有误,比如节点名拼写错误、条件边映射的键不存在。 1. 仔细检查 add_node add_edge / add_conditional_edges 中使用的节点名称字符串是否完全一致。
2. 条件边的路由函数返回值必须在提供的映射字典的键中。

6. 最佳实践与工程建议

将原型应用到生产环境,需要考虑更多工程化因素。

6.1 提示词工程

  • 结构化与明确性 :给 Agent 和 Graph 中 LLM 节点的提示词必须清晰定义角色、任务、步骤和输出格式。使用 PromptTemplate 进行管理。
  • 少样本学习 :在提示词中提供 1-2 个高质量的示例(Few-shot),能显著提升复杂任务的表现。
  • 版本控制 :使用 LangSmith 的 Prompt Management 功能管理不同版本的提示词,便于对比和回滚。

6.2 工具设计

  • 单一职责 :每个工具应只做一件事,并且做好。避免创建功能混杂的“巨无霸”工具。
  • 健壮性 :工具函数内部要有充分的错误处理(try-except),返回明确的错误信息,帮助 Agent 理解失败原因。
  • 安全性 :任何执行写操作、访问外部系统或敏感数据的工具,必须加入权限验证和操作确认机制。

6.3 工作流(LangGraph)设计

  • 状态设计精简 State 应只包含流程必需的数据。避免将中间计算的大量临时数据放入状态。
  • 节点粒度适中 :一个节点完成一个逻辑上相对独立的步骤。太粗则不易调试,太细则增加复杂度。
  • 善用条件边 add_conditional_edges 是实现动态流程的关键。确保路由函数逻辑清晰,覆盖所有可能的分支。
  • 持久化检查点 :对于长时运行或需要中断恢复的工作流,使用 Checkpointer 保存状态。

6.4 可观测性与监控(LangSmith)

  • 全面启用追踪 :在开发、测试和生产环境都启用 LangSmith 追踪,这是调试和优化的基石。
  • 创建评估数据集 :针对核心场景,构建一批高质量的输入-输出对,定期运行评估,监控应用效果是否下降。
  • 设置告警 :关注 LangSmith 中的耗时、Token 消耗和错误率指标,设置阈值告警。

6.5 性能与成本

  • 缓存 :对频繁且结果不变的 LLM 调用或工具查询(如知识库检索)实施缓存,减少重复计算和 API 调用。
  • 限制与降级 :在 AgentExecutor 和 Graph 中设置 max_iterations max_execution_time ,防止失控循环产生高额费用。
  • 模型选择 :非核心推理步骤可考虑使用更小、更快的模型(如 gpt-3.5-turbo ),关键总结或诊断步骤再用大模型(如 gpt-4 )。

6.6 安全与合规(尤其对于医疗场景)

  • 免责声明 :任何医疗建议输出必须包含明确的免责声明,指出其辅助性,不能替代专业医疗诊断。
  • 数据脱敏 :处理患者输入时,需注意隐私保护,避免在日志、追踪信息中记录个人身份信息(PII)。
  • 审核机制 :对于高风险建议(如用药),应设计人工审核环节,或将其限制在“建议咨询医生”的范围内。

通过本文的拆解,你应该对 LangChain、LangGraph 和 LangSmith 在构建一个复杂 AI Agent 应用中的角色和协作方式有了直观的理解。从 LangChain 的工具组装,到 LangGraph 的流程编排,再到 LangSmith 的全链路观测,它们共同构成了开发现代化、可维护、可调试的 LLM 应用的坚实基础。下一步,你可以尝试用更复杂的工具(如真实数据库、API)、更精细的 Graph 状态和条件逻辑,来构建属于你自己的智能体应用。

Logo

码道开发者社区,聚焦华为云码道 CodeArts 代码智能体,沉淀 Agent、Skill、鸿蒙开发实战内容,供开发者查阅资料、交流技术、分享工程实践

更多推荐