摘要

1) 一句话总结 本文介绍了如何使用 LangGraph 框架,通过定义状态、工具、节点和边,从零构建一个具备自主决策能力的足球问答 AI 智能体。

2) 关键要点

  • 技术演进:AI 智能体通过引入状态(State)、决策(Decision-making)和记忆(Memory),解决了单纯“LLM + RAG”在处理复杂、未知路径问题时的局限性。
  • LangGraph 核心机制:基于图结构构建智能体,其中“节点(Nodes)”代表随时间演变的状态,“边(Edges)”定义节点间的控制流与转换条件。
  • 环境初始化:构建前需加载环境变量(如 OPENAI_API_KEY),示例中初始化了一个基于 GPT-5-nano 模型的智能体。
  • 状态定义(State):通过继承 pydantic.BaseModel 定义状态类(如 PlayerState),作为在图节点间传递和处理的数据实体。
  • 工具定义(Tools):使用 @tool 装饰器定义了 3 个模拟工具(获取俱乐部/国家队、球衣号码、FIFA 评分);必须提供清晰的参数名和文档字符串(Docstrings),以便智能体自主决定调用。
  • 常规节点定义:定义了提取球员姓名和检索出场时间等常规节点,这些节点不由智能体自主调度,而是按固定流程执行。
  • 规划器与条件边:规划器(Planner)根据上下文自主决定调用 0 到 3 个工具;通过 add_conditional_edges() 方法及路由函数(如 route_tools())处理其动态输出逻辑。
  • 图的构建与运行:使用 StateGraph 依次添加节点(add_node)、有向边(add_edge)、起点/终点(START/END),编译后通过 invoke 方法传入包含 question 字段的字典即可运行。

3) 风险与不足

  • 适用性风险:对于简单的任务或问题,使用 LangGraph 框架可能会显得过于繁琐和大材小用(更适合具有大型图结构的复杂项目)。
  • 示例局限性:为简化说明,教程中的工具仅使用了模拟数据,未涉及生产环境中从真实外部数据源检索数据的实际操作。

正文

“AI 智能体(AI Agent)”是当下最热门的概念之一。在大型语言模型(LLM)的热潮之后,人们逐渐意识到,尽管最新的 LLM 能力令人惊叹,但它们只能执行经过明确训练的任务。从这个意义上说,普通的 LLM 缺乏能够让它们处理知识范围之外事务的工具。

背景:从 RAG 到智能体

为了解决 LLM 的局限性,**检索增强生成(RAG)**技术应运而生。它通过从外部数据源检索额外的上下文并将其注入到提示词中,使 LLM 能够掌握更多信息。可以说,RAG 让 LLM 变得更加“博学”。然而,面对更复杂的问题,当解决路径无法提前预知时,单纯的“LLM + RAG”方法依然会失效。

智能体(Agents)则是围绕 LLM 构建的一个卓越概念,它引入了状态(State)决策(Decision-making)记忆(Memory)。智能体可以被视为一组预定义的工具集合,它能够在生成最终答案之前,分析结果并将其存储在记忆中以供后续使用。

LangGraph 核心概念

LangGraph 是一个用于创建智能体的流行框架。顾名思义,它是通过包含“节点(Nodes)”和“边(Edges)”的图(Graphs)来构建智能体的:

  • 节点(Nodes): 代表智能体的状态,该状态会随着时间的推移而演变。
  • 边(Edges): 通过指定节点之间的转换规则和条件来定义控制流。

为了更好地在实践中理解 LangGraph,我们将通过一个详细的示例来进行说明。虽然对于下面这个简单的问题来说,使用 LangGraph 可能显得有些繁琐,但它在处理具有大型图结构的复杂问题时,通常能发挥巨大的作用。

实战:构建足球问答智能体

我们将构建一个能够回答有关足球问题的智能体,其思考过程将基于检索到的球员统计数据。

1. 环境准备与初始化

首先,我们需要安装并导入必要的库和模块。接着,创建一个 .env 文件并在其中添加 OPENAI_API_KEY,通过 load_dotenv() 将环境变量加载到系统中。 此外,我们可以准备一个辅助函数,用于直观地显示构建好的图结构。 最后,使用简单的命令初始化一个基于 GPT-5-nano 的智能体。

2. 定义状态(State)

我们需要定义一个状态,它是一个包含 LLM 所需的所有球员信息的实体。 为了定义状态,我们需要编写一个继承自 pydantic.BaseModel 的类(例如 PlayerState)。当在 LangGraph 的节点之间移动时,每个节点都会接收一个 PlayerState 实例作为输入,并指定如何处理该状态。我们的任务就是定义这些状态的具体处理方式。

3. 定义工具(Tools)

工具可以粗略地理解为智能体可以调用的附加函数,用于检索回答用户问题所需的信息。为了简化示例,我们将使用模拟数据而不是从外部源检索真实数据(后者通常是生产应用程序的做法)。

定义工具需要编写带有 @tool 装饰器的函数。清晰的参数名称和函数文档字符串(Docstrings)至关重要,因为智能体会根据输入上下文参考它们来决定是否调用该工具。

我们将定义以下三个工具:

  • 工具一: 根据姓名返回球员的俱乐部和国家队信息。
    • 注:为了实现清晰的职责分离并方便在多个图节点中复用,我们会将这个工具放在另一个兼容 LangGraph 框架的函数(如 fetch_player_information)中。外层函数接收状态参数并提取姓名,然后调用在参数级别操作的工具函数。
  • 工具二: 检索球员的球衣号码。
  • 工具三: 获取球员的 FIFA 评分。

4. 定义其他图节点

接下来,我们编写几个用于检索外部数据的图节点函数。这些节点不会像前面那样被标记为“工具”,这意味着它们不是由智能体自主决定是否调用的:

  • 一个用于检索过去几个赛季出场时间的节点。
  • 一个从用户问题中提取球员姓名的节点。

5. 规划器与最终生成节点

现在进入最有趣的部分。基于前面定义的三个工具,我们可以创建一个规划器(Planner)。规划器会要求智能体根据情境上下文,自主决定调用哪个特定工具来生成足球球员的摘要。工具下方的文档字符串在这里发挥了重要作用,它们为智能体提供了关于工具的额外上下文。

最后,我们定义最终的图节点。它将接收从前面步骤中检索到的多个字段,并调用 LLM 生成最终的摘要。

图的构建与编译

现在我们拥有了构建图的所有元素:

  1. 初始化图: 使用 StateGraph 构造函数初始化图。
  2. 添加节点: 使用 add_node() 方法逐个添加节点。该方法接收两个参数:分配给节点的名称(字符串),以及与该节点关联的、仅接收图状态作为参数的可调用函数。
  3. 添加边: LangGraph 中的边是有向的,通过 add_edge() 方法添加,需指定起点和终点节点的名称。
  4. 处理条件边(规划器): 规划器的行为与其他节点略有不同,它可以返回包含 0 到 3 个输出节点的 selected_tools 字段。为此,我们需要使用 add_conditional_edges() 方法,它接收三个参数:
    • 规划器节点的名称;
    • 一个可调用函数(接收 LangGraph 节点并返回一个字符串列表,指示应调用的节点名称列表);
    • 一个将第二个参数中的字符串映射到节点名称的字典。
    • 在我们的例子中,我们将定义一个 route_tools() 节点,简单地将 state.selected_tools 字段作为规划器函数的结果返回。
  5. 设置起点与终点: 使用 LangGraph 的常量 STARTEND 来定义图的起点和终点。
  6. 编译与可视化: 最后一步是编译图。我们可以选择使用前面提到的辅助函数将其可视化。

运行示例

现在我们终于可以使用构建好的图了!通过调用 invoke 方法,并传入一个包含自定义用户问题的字典(带有 question 字段)即可运行。

运行后,我们可以观察图的完整状态,并分析智能体为了生成最终答案所选择的工具。最终生成的摘要结果将会非常出色!

总结

AI 智能体为 LLM 开启了新的篇章。配备了先进的工具和决策能力后,我们现在拥有了解决复杂任务的巨大潜力。

本文通过一个示例介绍了 LangGraph——构建智能体最受欢迎的框架之一。它的简单性和优雅性使得构建复杂的决策链成为可能。虽然对于本文的简单示例来说,使用 LangGraph 可能显得有些大材小用,但对于状态和图结构复杂得多的大型项目而言,它将变得极其有用。

关联主题