30款热门AI模型一站整合DeepSeek/GLM/Qwen 随心用限时 5 折。 点击领海量免费额度最近在尝试将大语言模型LLM应用到具体的垂直业务场景时比如医疗问诊我发现一个核心痛点如何让一个AI助手不仅能理解用户问题还能可靠地执行一系列复杂的、有状态的流程例如从症状收集、到初步分析、再到建议追问、最后生成报告。单纯调用一次LLM API是远远不够的我们需要一个能编排“思考”和“行动”的框架。这正是LangChain生态的价值所在。本文将从一个具体的“医疗问诊智能体Agent”项目出发手把手带你构建一个具备多轮对话、状态管理和工具调用能力的AI助手。通过这个实战案例你将清晰地理解LangChain、LangGraph和LangSmith这三个核心组件各自扮演的角色、如何协同工作以及它们如何解决AI应用开发中的可靠性、可观测性和工程化难题。无论你是刚接触Agent概念的新手还是希望将原型落地的开发者这篇系统化的教程都能提供从零到一的完整路径。1. 背景与核心概念为什么需要Agent框架在深入代码之前我们有必要厘清几个关键概念以及它们所要解决的核心问题。1.1 什么是AI Agent智能体简单来说一个AI Agent是一个能够感知环境、进行决策并执行行动以达到目标的系统。在LLM的语境下Agent通常指一个能够使用大语言模型进行“思考”并调用外部工具如搜索、计算、数据库查询来“行动”的程序。它不再是简单的问答机器人而是一个具备自主规划和执行复杂任务能力的智能体。在我们的“医疗问诊”场景中一个理想的Agent应该能够理解用户描述的症状。规划问诊流程可能需要先询问病史再询问症状细节。执行行动调用医学知识库工具进行查询或调用诊断逻辑进行计算。维持状态记住之前对话中已获取的信息如患者年龄、既往病史。迭代根据工具返回的结果和用户的新输入决定下一步是继续提问、给出建议还是结束问诊。1.2 LangChain、LangGraph与LangSmith的角色与关系这是本文的核心。很多人对这三个工具感到困惑其实它们构成了一个从快速原型到生产部署的完整技术栈。LangChain 应用构建的“脚手架”和“工具箱”。它是什么一个用于开发由LLM驱动的应用程序的框架。它提供了大量的标准化组件如提示词模板、文档加载器、向量存储接口、链式调用等让你能像搭积木一样快速组合出功能。核心价值降低开发门槛。它封装了与不同LLM提供商OpenAI, Anthropic等的交互、文本处理、记忆管理等通用功能让你专注于业务逻辑。在问诊Agent中我们用LangChain来定义与LLM的对话、创建提示词模板、管理对话历史Memory、以及封装我们要调用的工具如症状诊断工具。LangGraph 智能体工作流的“编排引擎”和“状态机”。它是什么一个基于图Graph的框架专门用于构建有状态的、多步骤的、可能循环的Agent工作流。它将工作流定义为节点Node和边Edge组成的有向图。核心价值实现复杂、可靠的流程控制。它解决了传统链式调用难以处理循环、条件分支和持久化状态的问题。LangGraph提供了对Agent推理和行动过程的细粒度控制。在问诊Agent中我们用LangGraph来定义问诊的完整流程开始 - 分析症状 - [是否需要更多信息] - 是提问否调用诊断工具 - 生成报告 - 结束。这个流程可以循环状态已收集的信息在整个图中传递。LangSmith 智能体开发的“调试台”和“监控中心”。它是什么一个AI应用开发的平台提供调试、测试、评估和监控功能。核心价值提升开发效率与系统可观测性。它让你能可视化地追踪每一次Agent调用的完整生命周期输入的提示词、LLM的思考过程、调用的工具、返回的结果方便定位问题、评估效果并进行迭代优化。在问诊Agent中我们用LangSmith来记录每一次问诊会话查看Agent在哪个步骤做出了什么决策调用工具的参数和结果是什么从而优化提示词或工作流逻辑。三者的关系可以类比为LangChain提供了建造房屋的砖瓦和水泥基础组件。LangGraph提供了施工蓝图和脚手架告诉你如何把这些材料组装成坚固的、结构复杂的房屋工作流编排。LangSmith则是工程监理和质检仪确保建造过程可控房屋质量达标并能持续改进开发运维。接下来我们将通过构建医疗问诊Agent将这三者串联起来。2. 环境准备与项目初始化我们使用Python作为开发语言。请确保你的Python版本在3.8以上。2.1 创建虚拟环境与安装依赖首先创建一个新的项目目录并设置虚拟环境这是一个好习惯可以隔离项目依赖。# 创建项目目录 mkdir medical-chat-agent cd medical-chat-agent # 创建虚拟环境 (以venv为例) python -m venv venv # 激活虚拟环境 # Windows: venv\Scripts\activate # Linux/Mac: source venv/bin/activate安装核心依赖库。我们将安装langchain,langgraph,langsmith的客户端以及OpenAI的SDK因为我们使用GPT模型作为LLM。同时安装python-dotenv来管理环境变量。pip install langchain langgraph langsmith openai python-dotenv2.2 配置API密钥与环境变量你需要准备以下API密钥OpenAI API Key用于访问GPT模型。LangSmith API Key可选但强烈推荐用于将追踪日志发送到LangSmith平台。你可以在 LangSmith官网 注册并获取。在项目根目录下创建一个.env文件来存储这些敏感信息# .env OPENAI_API_KEYsk-your-openai-api-key-here LANGCHAIN_API_KEYls-your-langsmith-api-key-here # LangSmith默认会将数据发送到其云服务如需自定义可设置以下变量 # LANGCHAIN_TRACING_V2true # LANGCHAIN_ENDPOINThttps://api.smith.langchain.com # LANGCHAIN_PROJECTmedical-agent-tutorial # 你的项目名重要请将.env文件添加到.gitignore中避免将密钥提交到版本控制系统。# .gitignore .env venv/ __pycache__/ *.pyc2.3 项目结构规划我们的项目结构将如下所示这有助于代码组织medical-chat-agent/ ├── .env # 环境变量不提交 ├── .gitignore ├── requirements.txt # 依赖列表 ├── config.py # 配置加载 ├── tools/ # 自定义工具目录 │ └── medical_tools.py ├── graph/ # LangGraph定义目录 │ └── medical_agent.py ├── run_agent.py # 主运行脚本 └── utils.py # 工具函数现在让我们开始编写代码。首先创建config.py来加载环境变量# config.py import os from dotenv import load_dotenv load_dotenv() # 从 .env 文件加载环境变量 OPENAI_API_KEY os.getenv(OPENAI_API_KEY) LANGCHAIN_API_KEY os.getenv(LANGCHAIN_API_KEY) # 简单验证 if not OPENAI_API_KEY: raise ValueError(请在 .env 文件中设置 OPENAI_API_KEY) # LANGCHAIN_API_KEY 可选没有则仅本地运行无LangSmith追踪3. 核心组件拆解工具、模型与记忆在构建图之前我们需要用LangChain创建几个基础组件。3.1 定义医疗问诊工具Tools工具是Agent与外界交互的“手”。这里我们创建两个简单的模拟工具一个用于根据症状查询可能的疾病另一个用于计算BMI身体质量指数。在实际应用中这些工具可以连接真实的医学数据库或API。# tools/medical_tools.py from langchain.tools import tool from typing import Dict, List import json tool def search_medical_knowledge(symptoms: str) - str: 根据输入的症状描述查询医学知识库返回可能的疾病列表及相关信息。 参数: symptoms: 患者描述的症状如“头痛、发烧、咳嗽”。 返回: 一个格式化的字符串包含可能的疾病和建议。 # 这里是模拟逻辑。真实场景应接入医学知识图谱或数据库。 symptom_keywords symptoms.lower() knowledge_base { headache,fever,cough: [ {disease: Common Cold, confidence: Medium, advice: Rest, hydrate, over-the-counter cold medicine.}, {disease: Influenza (Flu), confidence: Medium, advice: Rest, antiviral medication if early, see doctor if severe.}, ], chest pain,shortness of breath: [ {disease: Angina, confidence: High, advice: **This is a medical emergency. Seek immediate medical attention.**}, ], fatigue,weight loss,increased thirst: [ {disease: Diabetes, confidence: Medium, advice: Consult a doctor for blood sugar tests.}, ] } # 简单匹配逻辑 for key, value in knowledge_base.items(): if all(kw in symptom_keywords for kw in key.split(,)): return json.dumps(value, indent2, ensure_asciiFalse) return json.dumps([{disease: Symptom pattern not specifically recognized., confidence: Low, advice: Please consult a healthcare professional for accurate diagnosis.}]) tool def calculate_bmi(weight_kg: float, height_m: float) - Dict: 计算身体质量指数 (BMI)。 参数: weight_kg: 体重单位千克。 height_m: 身高单位米。 返回: 包含BMI值和分类的字典。 bmi weight_kg / (height_m ** 2) category if bmi 18.5: category Underweight elif bmi 25: category Normal weight elif bmi 30: category Overweight else: category Obesity return { bmi: round(bmi, 2), category: category, interpretation: fA BMI of {round(bmi,2)} is considered {category}. } # 将工具放入列表方便后续使用 MEDICAL_TOOLS [search_medical_knowledge, calculate_bmi]3.2 初始化LLM与提示词我们使用LangChain的ChatOpenAI来封装与GPT的交互并创建一个系统提示词来设定Agent的角色和行为准则。# graph/medical_agent.py (部分代码) from langchain_openai import ChatOpenAI from langchain.prompts import ChatPromptTemplate, MessagesPlaceholder from langchain.schema import SystemMessage from config import OPENAI_API_KEY # 1. 初始化LLM。我们使用gpt-3.5-turbo成本较低适合演示。 llm ChatOpenAI( modelgpt-3.5-turbo, temperature0.1, # 温度调低使输出更稳定、更专业 openai_api_keyOPENAI_API_KEY, streamingTrue, # 启用流式输出提升用户体验 ) # 2. 定义系统提示词赋予Agent“医生助手”的角色 system_prompt SystemMessage(content你是一个专业的医疗问诊助手。你的目标是帮助用户初步分析症状收集必要信息并提供基于医学知识的可能性参考和建议。 请遵循以下准则 1. 保持专业、共情和谨慎的态度。你并非替代医生不能提供最终诊断。 2. 主动、有条理地收集信息。例如当用户说“我头疼”你应该询问疼痛部位、性质、持续时间、伴随症状等。 3. 在信息足够时可以调用工具来查询医学知识或进行计算。 4. 所有涉及严重疾病如胸痛、呼吸困难的建议必须强调立即寻求专业医疗帮助。 5. 每次回复应清晰、结构化并引导对话进行下去。 ) # 3. 创建提示词模板预留对话历史的位置 prompt_template ChatPromptTemplate.from_messages([ system_prompt, MessagesPlaceholder(variable_namemessages), # 历史消息 MessagesPlaceholder(variable_nameagent_scratchpad), # Agent的思考过程 ])3.3 构建基础的Agent执行器非LangGraph版本为了理解LangGraph带来的价值我们先看看只用LangChain如何构建一个简单的、具有工具调用能力的Agent。这通常使用create_react_agent或类似高阶函数。# graph/medical_agent.py (继续添加) from langchain.agents import create_react_agent, AgentExecutor from langchain.memory import ConversationBufferMemory from tools.medical_tools import MEDICAL_TOOLS # 1. 创建记忆保存对话历史 memory ConversationBufferMemory(memory_keychat_history, return_messagesTrue) # 2. 使用LangChain的ReAct范式创建Agent # 注意create_react_agent 在较新版本中可能有所变化这里展示核心逻辑。 # 实际中我们更推荐使用LangGraph但此例用于对比。 from langchain.agents import AgentExecutor, create_openai_tools_agent # 创建Agent agent create_openai_tools_agent(llm, MEDICAL_TOOLS, prompt_template) # 创建执行器 agent_executor_simple AgentExecutor( agentagent, toolsMEDICAL_TOOLS, memorymemory, verboseTrue, # 打印详细执行过程 handle_parsing_errorsTrue, # 处理解析错误 ) # 这个执行器可以运行但控制流是黑盒难以自定义复杂循环和状态。这个agent_executor_simple可以处理工具调用但其内部的工作流何时思考、何时调用工具、何时结束是固定的、不透明的。当我们需要实现“先问三个问题再调用工具然后根据结果决定是否继续问”这样的定制流程时它就力不从心了。这就是我们需要LangGraph的原因。4. 使用LangGraph构建可控的问诊工作流现在我们来用LangGraph重新定义问诊Agent。我们将工作流建模为一个有向图。4.1 定义状态State首先我们需要定义在整个工作流中传递的“状态”。状态是一个字典包含了对话所需的所有信息。# graph/medical_agent.py (引入新部分) from typing import TypedDict, Annotated, List, Union from langgraph.graph.message import AnyMessage, add_messages import operator # 使用TypedDict定义状态的结构这有助于类型检查和代码清晰度。 class AgentState(TypedDict): # 消息列表包含用户输入、AI回复、工具结果等所有消息 messages: Annotated[List[AnyMessage], add_messages] # 从对话中提取的关键信息结构化数据 extracted_info: dict # 一个标志位指示Agent下一步应该做什么。例如ask_question, call_tool, finalize next_action: str # 记录已询问的问题避免重复 questions_asked: List[str]Annotated[List[AnyMessage], add_messages]这是一个LangGraph的“缩减器”reducer。add_messages是一个函数它确保新的消息被正确地追加到messages列表中而不是覆盖。这是管理对话历史的最佳实践。extracted_info我们将把从对话中提取的年龄、症状描述等信息结构化地存储在这里。next_action这是控制工作流走向的关键。questions_asked用于实现简单的记忆避免重复提问。4.2 创建图节点Nodes节点是图中的函数它们接收状态执行操作并返回更新后的状态。节点1路由节点Router这个节点是图的大脑它根据当前状态决定下一步该去哪个节点。# graph/medical_agent.py from langchain.schema import HumanMessage, AIMessage def router_node(state: AgentState) - str: 根据当前状态决定下一步动作。 返回一个字符串代表下一个节点的名称。 messages state[messages] last_message messages[-1] # 如果是用户的第一次输入或者我们刚问完问题需要分析 if isinstance(last_message, HumanMessage): # 这里可以加入更复杂的逻辑比如分析信息是否足够 # 简单起见我们设定如果extracted_info里还没有symptoms就去提问 if not state.get(extracted_info, {}).get(symptoms): return ask_questions else: # 如果有症状信息了就去调用工具分析 return call_tool # 如果是AI的回复可能是提问或最终答案并且我们设定了下一步动作 elif isinstance(last_message, AIMessage) and state.get(next_action): return state[next_action] # 默认情况结束对话 return __end__节点2提问节点Ask Questions这个节点负责生成追问以收集更多信息。def question_node(state: AgentState) - AgentState: 生成一个追问以收集更多症状信息。 from langchain_core.prompts import ChatPromptTemplate # 构建一个专注于信息收集的提示词 prompt ChatPromptTemplate.from_messages([ SystemMessage(content你是一个细心的医生助手。根据当前对话历史提出一个最相关的问题来收集更多关于患者症状的信息。只问一个问题。), MessagesPlaceholder(variable_namemessages), ]) chain prompt | llm # 调用LLM生成问题 question chain.invoke({messages: state[messages]}).content # 更新状态将AI生成的问题添加到消息历史并记录已问过的问题 new_messages state[messages] [AIMessage(contentquestion)] new_questions state.get(questions_asked, []) [question] return { messages: new_messages, extracted_info: state[extracted_info], next_action: wait_for_user, # 设置下一步等待用户回答 questions_asked: new_questions, }节点3调用工具节点Call Tool当信息收集得差不多时这个节点会调用我们之前定义的医学工具。def tool_node(state: AgentState) - AgentState: 调用医学工具进行分析。 # 从状态中提取症状信息 symptoms state[extracted_info].get(symptoms, ) if not symptoms: # 如果没有症状回到提问节点 return {**state, next_action: ask_questions} # 1. 准备调用工具的提示词让LLM决定使用哪个工具及参数 from langchain.agents.format_scratchpad.openai_tools import format_to_openai_tool_messages from langchain.agents.output_parsers.openai_tools import OpenAIToolsAgentOutputParser # 将工具绑定到LLM llm_with_tools llm.bind_tools(MEDICAL_TOOLS) # 构建Agent提示词 agent_prompt ChatPromptTemplate.from_messages([ system_prompt, MessagesPlaceholder(variable_namemessages), (user, 根据上述对话历史特别是患者的症状描述{symptoms}请调用合适的工具进行分析。如果信息不足请说明需要补充什么。), MessagesPlaceholder(variable_nameagent_scratchpad), ]) # 创建Agent链 agent agent_prompt | llm_with_tools | OpenAIToolsAgentOutputParser() # 2. 执行Agent这里简化了实际LangGraph有更优雅的方式集成 # 为了清晰我们直接模拟调用 search_medical_knowledge 工具 tool_input symptoms tool_result search_medical_knowledge.invoke(tool_input) # 3. 将工具结果添加到消息历史 tool_message AIMessage(contentf我查询了医学知识库结果如下\n{tool_result}) new_messages state[messages] [tool_message] # 4. 更新状态准备生成最终建议 return { messages: new_messages, extracted_info: state[extracted_info], next_action: generate_final_advice, questions_asked: state[questions_asked], }节点4生成最终建议节点 等待用户节点我们还需要节点来处理最终输出和等待用户输入。def final_advice_node(state: AgentState) - AgentState: 根据工具结果和对话历史生成最终的建议和总结。 prompt ChatPromptTemplate.from_messages([ SystemMessage(content你是一名医生助手。请根据工具查询的结果和整个对话历史为用户生成一份清晰、谨慎的初步评估和建议总结。务必提醒用户这不能替代专业医疗诊断。), MessagesPlaceholder(variable_namemessages), ]) chain prompt | llm final_advice chain.invoke({messages: state[messages]}).content new_messages state[messages] [AIMessage(contentfinal_advice)] # 对话结束next_action 设为结束 return { messages: new_messages, extracted_info: state[extracted_info], next_action: __end__, questions_asked: state[questions_asked], } def wait_for_user_node(state: AgentState) - AgentState: 这是一个特殊节点它不执行任何操作只是将控制权交还给用户输入。 在实际的LangGraph流中我们通常通过外部调用来注入用户消息。 这个节点可以简单地将next_action设置为router让路由节点处理新消息。 return {**state, next_action: router}4.3 组装图Graph并编译现在我们将所有节点和边组合起来形成一个完整的工作流。# graph/medical_agent.py from langgraph.graph import StateGraph, END # 1. 创建图构建器 workflow StateGraph(AgentState) # 2. 添加节点 workflow.add_node(router, router_node) workflow.add_node(ask_questions, question_node) workflow.add_node(call_tool, tool_node) workflow.add_node(generate_final_advice, final_advice_node) workflow.add_node(wait_for_user, wait_for_user_node) # 用于等待外部输入 # 3. 设置入口点 workflow.set_entry_point(router) # 4. 添加边定义节点间的流转逻辑 workflow.add_conditional_edges( router, router_node, # 路由函数本身也决定下一个节点 { ask_questions: ask_questions, call_tool: call_tool, __end__: END, } ) workflow.add_edge(ask_questions, wait_for_user) workflow.add_edge(wait_for_user, router) # 用户输入后返回路由节点 workflow.add_edge(call_tool, generate_final_advice) workflow.add_edge(generate_final_advice, END) # 5. 编译图得到可执行的对象 medical_agent_graph workflow.compile()这个图定义了一个清晰的流程router- (ask_questions-wait_for_user-router) 循环 -call_tool-generate_final_advice- 结束。4.4 创建信息提取节点关键步骤我们漏掉了一个重要环节如何将用户非结构化的描述提取成结构化的extracted_info我们需要一个专门的节点来做这件事。def extract_info_node(state: AgentState) - AgentState: 从最新的用户消息中提取结构化信息如症状。 messages state[messages] last_message messages[-1] if not isinstance(last_message, HumanMessage): return state # 如果不是用户消息直接返回 user_input last_message.content # 使用一个简单的提示词让LLM提取信息 extraction_prompt ChatPromptTemplate.from_messages([ SystemMessage(content你是一个信息提取助手。从用户的输入中提取与医疗问诊相关的结构化信息。只返回一个JSON对象包含以下可能的字段symptoms症状描述字符串、duration持续时间、severity严重程度。如果某个字段无法提取就设为空字符串。), HumanMessage(contentuser_input), ]) extraction_chain extraction_prompt | llm # 注意这里需要LLM支持JSON模式或者使用Pydantic输出解析器更可靠。 # 为简化我们假设LLM返回了JSON字符串。 try: import json # 在实际中应使用更健壮的解析方式例如 # from langchain.output_parsers import PydanticOutputParser # from pydantic import BaseModel extracted_json json.loads(extraction_chain.invoke({}).content) except: extracted_json {symptoms: user_input, duration: , severity: } # 降级方案 # 更新状态中的 extracted_info current_info state.get(extracted_info, {}) updated_info {**current_info, **extracted_json} return { messages: state[messages], extracted_info: updated_info, next_action: state[next_action], questions_asked: state[questions_asked], }然后我们需要修改图在router节点之后、决定下一步之前加入这个信息提取节点。更优雅的方式是修改router_node的逻辑或者在wait_for_user到router之间插入extract_info_node。为了流程清晰我们调整图结构# 修改后的图组装逻辑替代之前的add_node和add_edge部分 workflow.add_node(router, router_node) workflow.add_node(extract_info, extract_info_node) # 新增节点 workflow.add_node(ask_questions, question_node) workflow.add_node(call_tool, tool_node) workflow.add_node(generate_final_advice, final_advice_node) workflow.add_node(wait_for_user, wait_for_user_node) workflow.set_entry_point(router) # 新的边逻辑 workflow.add_conditional_edges( router, router_node, { ask_questions: extract_info, # 先去提取信息 call_tool: extract_info, # 调用工具前也确保信息最新 __end__: END, } ) workflow.add_edge(extract_info, ask_questions) # 提取信息后提问 # 注意这里需要根据router的结果动态决定extract_info后的走向上述简化了。 # 更正确的做法是让router返回后经过extract_info再根据一个条件跳转到ask_questions或call_tool。 # 为了教程清晰我们采用一个简化但可工作的版本 def router_after_extract(state: AgentState) - str: 在提取信息后再次路由。 # 简单逻辑如果已有症状就去调用工具否则提问。 if state[extracted_info].get(symptoms): return call_tool else: return ask_questions workflow.add_conditional_edges( extract_info, router_after_extract, { ask_questions: ask_questions, call_tool: call_tool, } ) workflow.add_edge(ask_questions, wait_for_user) workflow.add_edge(wait_for_user, router) # 用户输入后回到主路由 workflow.add_edge(call_tool, generate_final_advice) workflow.add_edge(generate_final_advice, END) medical_agent_graph workflow.compile()现在我们的图变得更加智能router-extract_info- (ask_questions或call_tool) - ...。5. 运行Agent并与LangSmith集成5.1 编写主运行脚本创建一个脚本来与编译好的图进行交互。# run_agent.py import asyncio from langchain.schema import HumanMessage from graph.medical_agent import medical_agent_graph from config import LANGCHAIN_API_KEY import os # 如果设置了LangSmith API Key则启用追踪 if LANGCHAIN_API_KEY: os.environ[LANGCHAIN_TRACING_V2] true os.environ[LANGCHAIN_PROJECT] Medical-Agent-Tutorial print(LangSmith tracing enabled.) else: print(LangSmith tracing disabled. Set LANGCHAIN_API_KEY in .env to enable.) async def chat_with_agent(): 与医疗问诊Agent进行异步对话。 print( 医疗问诊智能助手 ) print(描述你的症状或健康问题。输入 quit 或 退出 结束对话。\n) # 初始化图的状态 initial_state { messages: [], extracted_info: {}, next_action: router, questions_asked: [], } current_state initial_state while True: # 获取用户输入 user_input input(\n你: ).strip() if user_input.lower() in [quit, 退出, exit]: print(助手: 感谢使用请保重身体) break # 将用户输入添加到消息历史 human_message HumanMessage(contentuser_input) # 注意我们需要将新消息“注入”到当前状态中。 # LangGraph的stream或ainvoke方法可以接受一个包含新消息的配置。 # 这里我们模拟一个更新状态并调用图的过程。 new_messages current_state[messages] [human_message] config {configurable: {thread_id: user_session_1}} # 用于会话标识 # 使用 astream_events 进行流式输出推荐 print(\n助手: , end, flushTrue) full_response async for event in medical_agent_graph.astream_events( {messages: new_messages, **{k:v for k,v in current_state.items() if k ! messages}}, configconfig, versionv1 ): # 这里可以处理不同类型的事件例如节点开始、结束流式token等。 # 为了简化我们只打印AI生成的内容。 if event[event] on_chat_model_stream and event[name] ChatOpenAI: content event[data][chunk].content if content: print(content, end, flushTrue) full_response content print() # 换行 # 更新当前状态在实际中需要从图的最终输出获取完整状态 # 更简单的方式是直接调用ainvoke获取最终状态 final_state await medical_agent_graph.ainvoke( {messages: new_messages, **{k:v for k,v in current_state.items() if k ! messages}}, configconfig ) current_state final_state if __name__ __main__: asyncio.run(chat_with_agent())5.2 在LangSmith中查看追踪运行脚本前确保你的.env文件中的LANGCHAIN_API_KEY已设置。运行后打开 LangSmith网站 你会在指定的项目Medical-Agent-Tutorial下看到这次会话的完整追踪记录。在LangSmith的追踪界面中你可以可视化工作流看到图Graph的每个节点是如何被执行的。检查输入输出点击每个节点查看输入的状态、LLM的提示词、生成的响应、调用的工具及其结果。调试问题如果Agent行为异常你可以精确地定位是哪个节点的逻辑或LLM的回复出了问题。评估与迭代你可以对多次运行的结果进行比较评估Agent的表现并据此优化提示词或图的结构。6. 常见问题与排查思路在开发过程中你可能会遇到以下问题问题现象可能原因解决思路导入错误ModuleNotFoundError依赖未正确安装或虚拟环境未激活。1. 确认已激活虚拟环境 (venv\Scripts\activate或source venv/bin/activate)。2. 运行pip install -r requirements.txt或重新安装langchain langgraph langsmith openai。API密钥错误.env文件未创建或密钥格式错误。1. 检查项目根目录下是否存在.env文件。2. 确认.env文件中OPENAI_API_KEY和LANGCHAIN_API_KEY的赋值正确没有多余空格或引号。3. 在代码中打印os.getenv(“OPENAI_API_KEY”)的前几位确认已加载。LangGraph图编译错误状态State定义与节点返回值不匹配。1. 检查AgentState的TypedDict定义确保每个字段的类型正确。2. 确保每个节点函数返回的字典包含AgentState中定义的所有键。3. 使用print或日志输出节点返回的状态进行调试。Agent陷入循环不停提问路由逻辑 (router_node) 有缺陷或信息提取失败导致extracted_info永远为空。1. 在router_node和extract_info_node中添加打印语句查看extracted_info的内容。2. 优化信息提取提示词或使用PydanticOutputParser确保LLM返回结构化的JSON。3. 在路由逻辑中设置一个最大提问次数限制。工具调用失败或参数错误工具函数的参数定义与LLM生成的调用参数不匹配。1. 在LangSmith中检查工具调用的具体输入。2. 确保工具函数的参数有清晰的文档字符串docstring这有助于LLM理解。3. 考虑使用tool装饰器的args_schema参数来明确定义参数类型。LangSmith看不到追踪数据1. API Key 无效或未设置。2. 网络问题。3. 项目名未设置或没有写入权限。1. 在LangSmith网站检查API Key是否正确并确认有对应项目的写入权限。2. 检查代码中LANGCHAIN_TRACING_V2和LANGCHAIN_PROJECT环境变量是否已设置。3. 尝试在代码开头添加import langchain; langchain.verbose True查看是否有连接错误。7. 最佳实践与工程建议将原型Agent转化为可生产部署的系统需要考虑更多因素状态管理的持久化目前的AgentState存在于内存中会话结束即消失。在生产环境中你需要将状态尤其是messages和extracted_info持久化到数据库如Redis、PostgreSQL中并通过configurable的thread_id来检索和恢复会话状态。更健壮的路由与错误处理在图中添加错误处理节点Error Handling Node用于捕获工具调用失败、LLM响应解析失败等异常并引导工作流到恢复路径或优雅失败。为路由逻辑设置超时和最大步数限制防止Agent陷入无限循环。工具的可扩展性与安全性将工具注册到中央仓库方便管理和复用。对于执行写操作或访问敏感数据的工具如预约挂号必须实现严格的权限检查和用户认证。考虑为工具调用添加“人工审核”节点Human-in-the-loop在高风险决策前介入。提示词工程与管理不要将提示词硬编码在代码中。使用LangChain的PromptTemplate并从外部文件如YAML、JSON或配置中心加载。为不同的节点提问、信息提取、总结设计专用的、优化的提示词。利用LangSmith的“Playground”功能迭代和测试你的提示词。性能与成本优化对于复杂的图考虑异步执行节点以提高吞吐量。使用更便宜的模型如gpt-3.5-turbo进行简单的路由和分类任务仅在需要深度分析和生成时使用更强大的模型如gpt-4。利用LangSmith的追踪数据来分析耗时和成本优化工作流。测试与评估使用LangSmith的**数据集Datasets和评估Evaluation**功能构建一组测试用例例如不同的症状描述并定义评估标准如“是否询问了关键信息”、“最终建议是否包含安全警告”。定期运行评估监控Agent性能的回归情况。通过这个从零构建医疗问诊Agent的旅程你应该对LangChain、LangGraph和LangSmith的定位与协作有了深刻的理解。LangChain提供了构建AI应用的基础模块LangGraph赋予了我们对复杂、有状态工作流进行精细编排的能力而LangSmith则为我们提供了洞察整个系统运行状况的眼睛是迭代和优化过程中不可或缺的利器。这套组合拳正是开发现代、可靠、可维护的AI Agent应用的最佳实践。 30款热门AI模型一站整合DeepSeek/GLM/Qwen 随心用限时 5 折。 点击领海量免费额度