ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

基于LangChain构建智能体:从核心原理到实战避坑指南

2026/8/12 15:49:48 拓冰建站 浏览量
基于LangChain构建智能体:从核心原理到实战避坑指南

1. 从“玩具”到“生产力”:为什么我们需要Agent?

如果你最近在折腾大语言模型,大概率已经玩过各种聊天界面,也试过用LangChain搭个简单的问答应用。一开始,你会觉得LLM很神奇,能写诗、能编程、能回答各种问题。但玩久了,一个核心痛点就会浮现出来:它好像什么都懂一点,但又什么都做不了。你问它“帮我查一下今天上海的天气”,它能给你编一段像模像样的天气报告;你让它“把这份PDF总结一下发到我的Notion”,它只能告诉你“我理解你的需求,但我无法直接操作你的文件或访问外部应用”。

这就是LLM的“大脑”与“手脚”脱节的问题。它有一个强大的、能理解自然语言、能推理规划的“大脑”,但它没有感知世界、执行具体任务的“手脚”。Agent(智能体)技术,就是为了解决这个问题而生的。它不是一个新概念,但在LLM时代被赋予了全新的生命力。简单说,Agent就是一个由LLM作为“决策大脑”,驱动一系列“工具”(Tools)作为“执行手脚”的智能系统。大脑负责理解你的意图、拆解任务、制定计划、做出决策;手脚则负责调用具体的API、查询数据库、操作软件,把大脑的想法变成现实。

这听起来有点像我们熟悉的“自动化脚本”或“RPA机器人”,但底层逻辑完全不同。传统自动化是“if-else”的确定式流程,而Agent是“思考-行动-观察-再思考”的自主闭环。LLM赋予了它理解模糊指令、处理异常、动态调整计划的能力。比如,你给一个传统脚本下达指令“总结A文档并发邮件”,如果A文档不存在,脚本就卡死了。但一个Agent遇到这种情况,它的LLM大脑可能会判断:“用户要的是A文档的总结,但A找不到。我发现有个名字相似的B文档,是否需要确认后总结B?或者直接告诉用户A缺失?” 这种基于理解的弹性,是Agent的核心价值。

所以,打造第一个Agent,绝不仅仅是调用几个API。它是你从“用LLM聊天”迈向“用LLM解决实际问题”的关键一步。接下来,我会带你从零开始,用LangChain搭建一个能真正“干活”的Agent,并深入每一个环节,告诉你为什么这么设计,以及如何避开我踩过的那些坑。

2. 搭建基石:理解LangChain中的核心组件

在动手写代码之前,我们必须把LangChain里关于Agent的几个核心概念掰扯清楚。很多人一上来就照抄官方示例,结果连自己调用的每个对象是干什么的都不知道,出了问题自然无从下手。

2.1 LLM:不只是聊天模型

在Agent的语境里,LLM是你的“大脑”,它的核心职责是推理和规划。这意味着,你选择的LLM必须有较强的逻辑思维和指令遵循能力。对于入门和实验,OpenAI的GPT-3.5-turbo或GPT-4是稳妥的选择,它们对LangChain的Agent格式有很好的支持。

但这里有个关键点:不要用纯聊天模型。很多国产或开源的模型,虽然在闲聊上表现不错,但在严格遵循特定输出格式(比如要求它必须输出Action:Action Input:)方面可能不稳定。Agent要求LLM严格按照预设的模板进行思考输出,格式错误会导致整个流程解析失败。因此,在初始化LLM时,我强烈建议将temperature参数设置为0或一个较低的值(如0.1),以减少输出的随机性,确保指令遵循的稳定性。

from langchain_openai import ChatOpenAI # 初始化一个适合Agent工作的LLM llm = ChatOpenAI( model="gpt-3.5-turbo", temperature=0, # 降低随机性,让输出更稳定、更可预测 api_key="your-api-key" )

2.2 Tools:给Agent装上“手脚”

Tools是Agent与外部世界交互的接口。一个Tool本质上就是一个函数,它有着明确的名称、描述和输入参数。LLM大脑通过阅读Tool的描述来决定在什么时候调用它。

起名和写描述是门艺术,也是第一个大坑。

  • 名称(name):要简短、具象。比如search_internet就比tool_1好得多。LLM会根据名称来记忆和选择工具。
  • 描述(description):这是最重要的部分!描述必须清晰、无歧义地说明三件事:1)这个工具是干什么的;2)它需要什么输入(参数);3)它会返回什么。描述是LLM决定是否调用以及如何调用该工具的唯一依据。模糊的描述会导致LLM错误调用或不敢调用。

一个反面教材的描述:“这是一个有用的工具”。这等于什么都没说。 一个正面的描述:“在互联网上搜索信息。输入应该是一个搜索查询字符串。返回的是最相关的几条网页摘要。”

LangChain社区提供了大量预置工具(langchain_community.tools),如WikipediaQueryRunDuckDuckGoSearchRun等。但对于我们自己的业务,通常需要自定义工具。

from langchain.tools import tool from typing import Optional @tool def get_weather(city: str, date: Optional[str] = None) -> str: """ 获取指定城市的天气信息。 Args: city: 城市名称,例如“上海”、“北京”。 date: 可选,查询日期,格式为‘YYYY-MM-DD’。默认为今天。 Returns: 返回该城市的天气情况描述字符串。 """ # 这里应该是调用真实天气API的代码 # 例如:response = requests.get(f"https://weather.api?city={city}&date={date}") # 为了演示,我们返回一个模拟结果 if date: return f"{city}在{date}的天气是晴朗,25摄氏度。" else: return f"{city}今天天气多云转晴,22-28摄氏度。"

注意:在实际开发中,工具函数内部一定要做好异常处理(try-except),并返回明确的错误信息给Agent。比如return "调用天气API失败,请检查网络或城市名。"。这样LLM才能根据错误信息决定下一步动作(如重试或向用户报告)。

2.3 AgentExecutor:运行引擎与安全阀

这是把大脑(LLM)和手脚(Tools)组装起来,并让它们安全、可控运行的核心组件。你可以把它想象成Agent的“躯干”和“神经系统”。

  • 组装AgentExecutorAgent(包含LLM和工具定义)和Tools列表绑定在一起。
  • 运行循环:它负责执行“思考-行动-观察”的循环。LLM输出一个Action(要调用哪个工具)和Action Input(调用参数),AgentExecutor就去找对应的工具执行,得到Observation(工具返回的结果),再把Observation连同之前的对话历史一起喂回给LLM,让它进行下一轮思考。
  • 安全控制:这是AgentExecutor最关键的价值。没有它,Agent可能会陷入死循环或执行危险操作。主要参数有:
    • max_iterations: 最大迭代次数。必须设置!我一般设为10-15,防止Agent在一个简单问题上无限思考下去。
    • early_stopping_method: 提前停止方法。“force”表示在达到最大迭代次数后强制停止;“generate”则让LLM自己决定何时生成最终答案。
    • handle_parsing_errors: 处理解析错误。当LLM的输出不符合Action/Action Input格式时,是否进行重试或报错。设置为True可以增加鲁棒性。
from langchain.agents import create_react_agent, AgentExecutor from langchain import hub # 从LangChain Hub拉取一个预设的Agent提示词模板,例如ReAct模板 prompt = hub.pull("hwchase17/react") # 创建Agent agent = create_react_agent(llm, tools=[get_weather], prompt=prompt) # 创建执行引擎,并设置安全限制 agent_executor = AgentExecutor( agent=agent, tools=[get_weather], verbose=True, # 打印详细的执行过程,调试时非常有用 handle_parsing_errors=True, # 优雅处理格式错误 max_iterations=5, # 防止简单问题复杂化 early_stopping_method="force" )

3. 选择你的“大脑”运行模式:AgentType深度解析

LangChain提供了多种预定义的Agent类型(AgentType),它们本质上是不同的“思维框架”或“提示词模板”。选对类型,事半功倍;选错类型,事倍功半。很多人卡在第一步就是因为没搞懂这个。

3.1 入门首选:ZERO_SHOT_REACT_DESCRIPTION

这是最常用、最通用的Agent类型,基于ReAct(Reasoning + Acting)框架。它的工作流非常符合直觉:

  1. Thought(思考):LLM分析当前情况(用户问题、已有信息、可用工具)。
  2. Action(行动):决定调用哪个工具。
  3. Action Input(行动输入):提供调用工具所需的参数。
  4. Observation(观察):获得工具返回的结果。
  5. 回到第1步,直到LLM认为可以给出最终答案(Final Answer)。

它的提示词模板会明确告诉LLM这个流程,并列出所有可用工具及其描述。它的优点是直接、灵活,适合大多数需要多步推理和工具调用的场景。比如“查一下上海今天的天气,如果下雨就推荐一个室内活动,并告诉我怎么去”。

from langchain.agents import initialize_agent, AgentType agent = initialize_agent( tools, llm, agent=AgentType.ZERO_SHOT_REACT_DESCRIPTION, # 指定Agent类型 verbose=True, handle_parsing_errors=True, max_iterations=5 )

3.2 结构化任务专家:STRUCTURED_CHAT_ZERO_SHOT_REACT_DESCRIPTION

这个类型是ZERO_SHOT_REACT_DESCRIPTION的升级版,特别适合工具输入参数比较复杂的场景。普通Agent要求工具输入是一个简单的字符串,但有些工具需要多个参数,或者参数是结构化数据(如列表、字典)。

STRUCTURED_CHAT允许LLM输出一个结构化的Action Input,比如JSON格式,从而更精准地调用复杂工具。如果你的工具函数需要多个参数,或者你希望LLM能更规范地提供输入,就选这个。

# 假设我们有一个需要多个参数的工具 @tool def book_flight(departure_city: str, arrival_city: str, date: str, passengers: int) -> str: """预订航班。需要出发城市、到达城市、日期和乘客人数。""" return f"已为您搜索{date}从{departure_city}到{arrival_city}的航班,共{passengers}位乘客。" # 使用STRUCTURED_CHAT类型,LLM能更好地处理多参数输入 agent = initialize_agent( tools=[book_flight], llm=llm, agent=AgentType.STRUCTURED_CHAT_ZERO_SHOT_REACT_DESCRIPTION, verbose=True )

3.3 开放式对话与工具调用:OPENAI_FUNCTIONS / OPENAI_TOOLS

这是为OpenAI模型量身定制的类型。它利用了OpenAI API原生的“函数调用”(Function Calling)能力。在这种模式下,你在请求LLM时,不仅发送消息,还会附带一个“工具列表”(即函数定义)。LLM会在内部判断是否需要调用函数,并以一个特殊的JSON格式响应来指定要调用的函数和参数。

它的最大优点是稳定性和格式正确率极高,因为函数调用是模型训练的一部分。缺点则是主要绑定OpenAI模型。

# 使用OpenAI专用的Agent类型 agent = initialize_agent( tools, llm, agent=AgentType.OPENAI_FUNCTIONS, # 或 AgentType.OPENAI_TOOLS (更新) verbose=True )

如何选择?

  • 刚入门,工具简单:用ZERO_SHOT_REACT_DESCRIPTION
  • 工具参数复杂,或需要稳定结构化输入:用STRUCTURED_CHAT_ZERO_SHOT_REACT_DESCRIPTION
  • 使用OpenAI模型,追求最稳定的工具调用:用OPENAI_FUNCTIONSOPENAI_TOOLS

4. 实战:构建一个能查天气、搜网页、做计算的“全能小助手”

理论说再多,不如动手跑一遍。我们来构建一个具备三种能力的Agent:查询天气、搜索网络信息、进行数学计算。这个例子麻雀虽小,五脏俱全,涵盖了自定义工具、多工具协作、以及执行流程的完整观察。

4.1 步骤一:准备三个核心工具

首先,我们定义好三个工具函数。注意看它们的描述是如何写的。

from langchain.tools import tool from langchain_community.tools import DuckDuckGoSearchRun from langchain_community.utilities import WikipediaAPIWrapper import math # 工具1:自定义天气工具(模拟) @tool def get_weather(city: str) -> str: """ 获取指定城市的当前天气情况。输入必须是一个明确的城市名称。 Args: city: 城市名,例如“北京”、“New York”。 Returns: 返回该城市的天气描述字符串。 """ # 模拟数据,真实情况应调用API weather_db = { "北京": "北京:晴,15-25摄氏度,西北风2级。", "上海": "上海:多云,18-28摄氏度,东南风1级。", "纽约": "纽约:小雨,10-15摄氏度,东北风3级。" } return weather_db.get(city, f"未找到{city}的天气信息。") # 工具2:网络搜索工具(使用社区工具) search = DuckDuckGoSearchRun() # 工具3:自定义计算器工具 @tool def calculator(expression: str) -> str: """ 执行一个基础数学计算或单位转换。支持加减乘除(+, -, *, /)、乘方(**)、括号。 也可以处理简单的单位转换,如‘100公里 to 英里’。 Args: expression: 数学表达式或转换语句,例如‘(3+5)*2’, ‘100公里 to 英里’。 Returns: 计算或转换结果字符串。 """ try: # 处理单位转换的简单逻辑 if "to" in expression or "TO" in expression: parts = expression.lower().split("to") if len(parts) == 2: value_unit = parts[0].strip().split() if len(value_unit) == 2: value, from_unit = float(value_unit[0]), value_unit[1] to_unit = parts[1].strip() # 简单模拟几种转换 if from_unit == "公里" and to_unit == "英里": result = value * 0.621371 return f"{value} 公里 ≈ {result:.2f} 英里" elif from_unit == "摄氏度" and to_unit == "华氏度": result = value * 9/5 + 32 return f"{value} 摄氏度 = {result:.2f} 华氏度" # 处理数学表达式,使用eval需谨慎,此处仅作演示 # 在生产环境中,应使用更安全的库如`numexpr`或`ast.literal_eval`处理部分运算 result = eval(expression, {"__builtins__": {}}, math.__dict__) return f"{expression} = {result}" except Exception as e: return f"计算‘{expression}’时出错:{str(e)}。请检查表达式格式。" # 将工具放入列表 tools = [get_weather, search, calculator]

4.2 步骤二:初始化Agent与执行器

我们选择ZERO_SHOT_REACT_DESCRIPTION这种通用类型来组装我们的Agent。

from langchain.agents import initialize_agent, AgentType from langchain_openai import ChatOpenAI # 1. 初始化LLM llm = ChatOpenAI(model="gpt-3.5-turbo", temperature=0, api_key="your-api-key-here") # 请替换为你的API Key # 2. 初始化Agent agent = initialize_agent( tools=tools, llm=llm, agent=AgentType.ZERO_SHOT_REACT_DESCRIPTION, # 使用ReAct框架 verbose=True, # 开启详细日志,方便我们看到思考过程 handle_parsing_errors=True, # 处理格式错误 max_iterations=6 # 限制最大步数,防止跑飞 )

4.3 步骤三:运行并观察完整的“思考-行动”闭环

现在,让我们问一个需要多步协作的问题。

query = "上海今天天气怎么样?如果天气好,帮我搜一下上海外滩有什么好玩的活动,然后计算一下如果门票平均100元,3个人要花多少钱。" result = agent.invoke({"input": query}) print(result["output"])

verbose=True时,你会在控制台看到类似下面的详细输出。这是理解Agent工作流的绝佳材料,务必仔细看:

> Entering new AgentExecutor chain... Thought: 用户的问题分为几个部分。首先需要查询上海的天气,然后根据天气决定是否搜索活动,最后计算费用。我应该按顺序执行。 Action: get_weather Action Input: 上海 Observation: 上海:多云,18-28摄氏度,东南风1级。 Thought: 天气是多云,不算坏天气,可以继续搜索活动。现在需要搜索上海外滩的活动。 Action: DuckDuckGoSearchRun Action Input: 上海外滩 近期 活动 推荐 Observation: [这里会是DuckDuckGo返回的实际搜索结果摘要,例如“外滩灯光秀、观光隧道、艺术展览等”] Thought: 我已经找到了活动信息。现在需要计算3个人,每人平均100元的门票总花费。这是一个简单的乘法计算。 Action: calculator Action Input: 3 * 100 Observation: 3 * 100 = 300 Thought: 我现在有了所有信息:天气情况、活动搜索结果和费用计算。可以给用户一个完整的回答了。 Final Answer: 上海今天天气多云,18-28摄氏度,东南风1级,适合出行。根据搜索,上海外滩近期有灯光秀、艺术展览等活动可供选择。如果按平均门票100元计算,3个人的总费用是300元。 > Finished chain.

这个输出完美展示了ReAct框架的闭环:

  1. Thought: Agent分析任务,拆解出第一步是查天气。
  2. Action/Action Input: 它选择了正确的工具get_weather,并输入了正确的参数上海
  3. Observation: 工具返回了天气结果。
  4. 新的Thought: 基于观察(天气尚可),它决定执行下一步:搜索活动。
  5. Action/Action Input: 选择搜索工具,并生成了搜索关键词。
  6. Observation: 获得搜索结果。
  7. Thought: 进行到最后一步,识别出这是一个计算问题。
  8. Action/Action Input: 选择计算器工具,输入3 * 100
  9. Observation: 获得计算结果。
  10. Final Answer: 综合所有观察,组织成一段完整的、人性化的答案回复给用户。

这就是一个完整的、由LLM大脑驱动工具手脚的智能闭环。你可以看到,LLM并非简单地串联工具,而是在中间进行了关键的逻辑判断(“天气是多云,不算坏天气,可以继续”)。

5. 避坑指南:从“能跑”到“好用”的关键细节

让一个Agent跑起来可能只需要半小时,但让它稳定、可靠、高效地工作,却需要填平无数个坑。下面是我从实际项目中总结出的血泪经验。

5.1 工具描述的“魔鬼细节”

工具描述不清是Agent“智障”行为的首要原因。除了前面说的要写清楚功能、输入、输出外,还有几个高级技巧:

  • 用自然语言描述输入格式:如果工具期望一个日期,不要说“输入是日期”,而要说“输入是一个‘YYYY-MM-DD’格式的日期字符串,例如‘2023-10-27’”。
  • 设定边界和约束:在描述中明确说明工具的局限性。例如,“该工具只能查询未来7天内的天气”,或者“该计算器不支持复数运算”。
  • 使用示例:在描述中嵌入示例极其有效。例如:“使用示例:输入‘北京’,返回北京的天气;输入‘巴黎’,返回巴黎的天气。”

一个优化后的天气工具描述可能是:

获取全球主要城市的当前天气状况。输入应为一个明确的英文或中文城市名称(例如‘Beijing’或‘北京’)。该工具返回温度、天气现象、湿度和风力信息。请注意,对于非常小众的城市可能无法获取数据。

5.2 迭代失控与“思考漩涡”

你有没有遇到过Agent在一个简单问题上不停思考,就是不输出最终答案?这就是陷入了“思考漩涡”。除了设置max_iterations这个硬性限制,还有更优雅的解法:

  • 优化提示词(Prompt):在给Agent的系统提示词或用户问题中,明确要求它“在获得足够信息后,请直接给出最终答案,不要重复思考已知信息”。你可以通过自定义prompt模板来实现。
  • 使用更好的early_stopping_method:除了“force”,还可以尝试“generate”。当LLM认为自己可以给出答案时,它会输出一个特殊的停止令牌,AgentExecutor检测到后就会结束循环。但这依赖于LLM本身的判断能力。
  • 设计工具返回“终结信号”:对于一些查询类工具,可以在返回结果后加上一句“【信息已完整】”。LLM在观察中看到这个信号,就更倾向于结束任务。

5.3 错误处理与用户体验

一个成熟的Agent必须能优雅地处理失败。

  • 工具层错误处理:每个工具函数内部必须有完善的try-except,并返回对LLM友好的错误信息。不要抛出未处理的异常,那会导致整个Agent崩溃。
    @tool def get_stock_price(symbol: str) -> str: try: # 调用API price = call_finance_api(symbol) return f"{symbol}的当前股价是{price}美元。" except ConnectionError: return "【网络错误】无法连接到财经数据服务,请稍后再试。" except ValueError: return f"【输入错误】未找到股票代码‘{symbol}’,请检查代码是否正确。" except Exception as e: return f"【系统错误】处理请求时发生未知问题:{str(e)[:50]}..." # 截断长错误信息
  • Agent层面的解析错误:设置handle_parsing_errors=True后,当LLM输出不符合Action:格式时,AgentExecutor会尝试将错误信息反馈给LLM,让它重试。你还可以通过一个自定义函数来更精细地处理:
    def parsing_error_handler(error): return f"你之前的回复格式有误,未能识别出有效的‘Action’。请严格按照‘Thought: ... Action: ... Action Input: ...’的格式重新思考并回答。错误详情:{error}" agent_executor = AgentExecutor( ..., handle_parsing_errors=parsing_error_handler )

5.4 上下文管理与长对话记忆

默认情况下,Agent是“单轮失忆”的。你问完一个问题,它回答完,对话历史就清空了。这对于需要多轮交互的复杂任务(如一步步规划一个旅行行程)是不可行的。

  • 保留对话历史:将agent_executor封装在一个带有记忆功能的链中。最常用的是ConversationBufferMemory
    from langchain.memory import ConversationBufferMemory memory = ConversationBufferMemory(memory_key="chat_history", return_messages=True) agent_executor = AgentExecutor( agent=agent, tools=tools, memory=memory, # 注入记忆 verbose=True ) # 后续调用时,会自动携带历史记录 result1 = agent_executor.invoke({"input": "我叫小明。"}) result2 = agent_executor.invoke({"input": "我的名字是什么?"}) # Agent会记得你叫小明
  • 记忆的代价与优化:记忆意味着每次调用都会将越来越长的历史对话传入LLM,这会增加token消耗、拖慢速度并可能触及上下文长度限制。对于长对话,需要考虑使用ConversationSummaryMemory(总结历史)或ConversationBufferWindowMemory(只保留最近N轮)来优化。

6. 超越基础:从单Agent到多Agent与LangGraph

当你熟练掌握了单个Agent的构建后,自然会遇到更复杂的场景:一个任务需要多个具备不同专长的Agent协作完成,或者任务流程包含复杂的条件判断和循环。这时,就该了解LangChain的进阶生态——LangGraph。

6.1 为什么需要多Agent?

想象一个“旅行规划助手”的任务:

  1. 需要理解用户模糊的需求(“我想去一个温暖的海边度假”)。
  2. 需要搜索和推荐目的地。
  3. 需要查询目的地的天气和航班信息。
  4. 需要根据预算生成一个粗略的行程单。

让一个Agent干所有这些事,工具列表会很长,提示词会非常复杂,而且容易混乱。更好的架构是:

  • 一个“主管Agent”:负责理解用户初始需求,并将任务拆解。
  • 一个“目的地专家Agent”:专门负责搜索和推荐地点,拥有搜索工具和地理知识库。
  • 一个“信息查询Agent”:专门负责调用天气、航班等实时API。
  • 一个“行程规划Agent”:专门负责整合信息,生成结构化行程。

主管Agent根据任务阶段,将工作分配给最专业的子Agent去执行,并汇总结果。这就是多Agent系统(Multi-Agent System)的基本思想。

6.2 LangGraph:为Agent绘制工作流蓝图

LangGraph是LangChain的一个库,它允许你用“图”(Graph)的方式来定义和编排多个Agent或组件的工作流。节点(Node)可以是Agent、工具或任何函数,边(Edge)定义了节点之间的流转条件。

一个简单的“审批”工作流例子:

  1. 节点1(起草Agent):根据用户需求生成一份文档草案。
  2. 节点2(检查工具):调用一个语法检查工具检查草案。
  3. 条件边:如果检查通过,流向节点3(润色Agent);如果不通过,则流回节点1重新起草。
  4. 节点3:对草案进行润色。
  5. 节点4(最终输出):将润色后的文档返回给用户。

在LangGraph中,你可以清晰地定义这个包含循环和条件分支的流程,这是单纯使用AgentExecutor难以实现的。

from langgraph.graph import StateGraph, END from typing import TypedDict, Annotated import operator # 1. 定义状态(State),即工作流中传递的数据结构 class AgentState(TypedDict): task: str draft: str checked: bool final_output: str # 2. 定义各个节点函数(这里用模拟函数代替Agent) def draft_node(state: AgentState): print("起草节点运行...") return {"draft": f"这是关于'{state['task']}'的草案。"} def check_node(state: AgentState): print("检查节点运行...") # 模拟检查逻辑,比如草案长度大于5就算通过 is_ok = len(state["draft"]) > 5 return {"checked": is_ok} def polish_node(state: AgentState): print("润色节点运行...") return {"final_output": f"【润色版】{state['draft']}"} # 3. 构建图 workflow = StateGraph(AgentState) # 添加节点 workflow.add_node("draft", draft_node) workflow.add_node("check", check_node) workflow.add_node("polish", polish_node) # 设置入口 workflow.set_entry_point("draft") # 添加边 workflow.add_edge("draft", "check") # 条件边:根据检查结果决定流向 def decide_next(state): if state["checked"]: return "polish" else: return "draft" # 检查不通过,回去重写 workflow.add_conditional_edges( "check", decide_next, { "polish": "polish", "draft": "draft", } ) workflow.add_edge("polish", END) # 4. 编译并运行图 app = workflow.compile() initial_state = {"task": "写一份周报"} result = app.invoke(initial_state) print(result["final_output"])

虽然这个例子没有用到真实的Agent,但它清晰地展示了LangGraph如何编排一个带有条件判断(检查是否通过)和潜在循环(不通过则重写)的复杂流程。在实际项目中,你可以把draft_nodepolish_node替换成真正的Agent调用。

6.3 何时该用LangGraph?

  • 任务有明确的阶段和依赖关系:比如“先A后B,B失败了再重试A”。
  • 需要多个专业Agent协作:比如前面说的旅行规划。
  • 流程中包含复杂的人工干预或审批节点:比如生成内容后需要人工确认才能发布。
  • 你需要一个可视化、可调试的工作流:LangGraph的图结构非常直观。

对于简单的、线性的“思考-行动”循环,AgentExecutor足够了。但对于真正的企业级、生产级的复杂自动化任务,LangGraph提供的可控性和可扩展性是不可或缺的。

走到这里,你已经不再是仅仅调用API的开发者,而是能够设计并实现一个具备自主推理和行动能力智能系统的工程师。从理解大脑(LLM)与手脚(Tools)的关系开始,到精心设计每一个工具,再到选择合适的思想框架(AgentType),最后通过严密的错误处理和记忆管理让系统变得稳健,甚至迈向多智能体协作的广阔天地。这个过程的核心,始终是理解LLM如何思考,并引导它可靠地使用工具来扩展其能力边界。记住,最好的Agent不是最聪明的,而是最懂得在何时、以何种方式、使用何种工具来完成任务的。