从有限状态机到智能体图:传统 FSM 与 Agent Graph的演进

前言

大家好,这里是程序员阿亮!最近是终于结束了自己第一段实习,又可以开始稳定写博客了!今天来给大家讲解一下现在很主流的Graph框架!
在大模型(LLM)从简单的"单次问答交互(Single-turn QA)"向能够自主执行复杂、长周期任务的"智能体(Agent)"演进的过程中,开发者们面临着一个核心瓶颈:如何以一种结构化、可控且具备高弹性的方式,去编排大模型的不确定性行为?

早期的编排框架(如 LangChain 的早期版本、LlamaIndex 等)多采用线性链式结构(Chains)有向无环图(DAG)。这种单向、无回溯的流水线在处理确定性任务(如数据清洗、简单 RAG)时表现良好,但面对需要反复迭代、自我反思(Reflection)、工具动态调用(ReAct)以及人工介入(Human-in-the-Loop)的复杂 Agent 场景时,便显得力不从心。

为了破解这一工程难题,基于图(Graph)思想的 AI 编排范式 应运而生。以 Python 生态的 LangGraph 和 Java 生态的 Spring AI Alibaba Graph 为代表的框架,通过将"图(Graph)"作为一等公民(First-Class Citizen)引入 AI 编排,彻底重构了智能体应用的开发范式。

本文将从"图"的底层哲学思想出发,系统梳理其与传统编排(DAG、FSM)的本质区别,详细剖析图驱动编排的核心知识体系,并分别基于 Python 与 Java 给出生产级的完整实战代码。

一、 哲学起点:为什么要用"图(Graph)"的思想来理解 AI 编排?

在传统的软件工程中,我们的编程思维是**"算法 = 数据结构 + 控制流"**。控制流通常是确定的:要么是顺序执行,要么是条件分支,要么是有限循环。

然而,大模型的加入引入了"认知循环(Cognitive Loops)"。正如诺贝尔经济学奖得主丹尼尔·卡尼曼在《思考,快与慢》中提出的系统 1(快速、直觉的思考)和系统 2(慢速、理性的逻辑推理),一个具备实用价值的 AI 智能体必须具备"系统 2"的能力------它在输出答案前,需要先规划、执行、观察结果、反思,如果发现结果不达标,还要能主动"退回"到之前的步骤重新尝试。

这种非确定性的、动态收敛的认知过程,在数学和拓扑学上,最自然的表达载体就是"图(Graph)"

辉夜姬万岁!

下面这个图也很好讲解了我们Agent的思考、行动循环,这种动态策略,非常适合以图为载体!

在图的思想中,我们不再把 AI 应用看作是一个"输入->处理->输出"的单向管道(Pipeline),而是将其看作一个持续演进的状态机(Evolving State Machine)

  • 系统的生命周期被表示为一个图(Graph)

  • 每一个具体的计算步骤(大模型调用、API 调用、人工审核)都是图中的一个节点(Node)

  • 节点之间的流转关系由**边(Edge)**决定。

  • 最关键的是,允许存在反向边(Backward Edges),使系统能够顺畅地形成反馈环路(Cycles)。

通过图的思想,我们得以将复杂的认知闭环(如自我纠错、多轮对话、动态工具调用)转化为直观的拓扑结构,从而大大降低了智能体系统的构建和维护成本。

二、 核心辨析:图(Graph)、DAG、FSM 的本质差异

实际上很多初学者在学习的时候会搞错这三者的关系和区别

2.1 智能体图(Agent Graph) vs 有向无环图(DAG)

许多开发者会产生疑问:DAG 也是图,为什么不能用来做智能体编排?

  • 物理拓扑的限制(无环 vs 有环)

    DAG 的核心约束在于 Acyclic(无环) 。如果你的智能体需要进行**"运行测试 ->发现 Bug ->回到修改步骤"**的循环,DAG 引擎在编译期就会因检测到"循环依赖"而报错。

  • 执行轨迹与图拓扑的混淆

    在实际运行中,智能体不管是执行 ReAct 还是 Reflection,其时间线上的执行轨迹(Execution Trace)确实是单向无环的(时间不能倒流)。但我们在代码中编排的是静态拓扑结构(Topology)。要让静态拓扑在运行时动态地反复执行某段逻辑,其拓扑结构必须允许成环。

  • 如何用 DAG 勉强实现循环(其代价是什么)

    在 DAG 框架中,要实现循环,只能通过节点内部单体化(Monolithic Loop)------将"编写、运行、分析、修复"的所有逻辑塞进一个巨大的、无法拆分的 Node 内部。这导致该节点变成了一个不可复用、无法插拔和无法插入人工审核的"黑盒"。

2.2 智能体图(Agent Graph) vs 有限状态机(FSM)

在传统业务开发中,状态机(如 Spring StateMachine)常用于订单状态流转等场景。那么,它和智能体图又有什么区别?

  • 状态载体的演进(离散值 vs 共享上下文)

    • FSM 的状态通常是离散且有限的枚举值(如 ORDER_INIT、PAYING、PAID)。

    • Agent Graph 的状态是一个丰富且持续演进的共享上下文对象(State Schema),它不仅记录了当前处于哪个节点,更记录了多轮对话历史、工具调用日志、代码草稿、多模态输入等全量信息。

  • 驱动力差异(确定性事件 vs 语义路由)

    • FSM 的状态跳转依赖硬编码的确定性事件(如接收到 ON_PAYMENT_SUCCESS 事件则跳转至 PAID)。

    • Agent Graph 的跳转依赖大模型的语义化路由(Semantic Routing)。大模型阅读全局状态,通过逻辑推理动态决定下一步是去 tools 还是 END,甚至在面对未知的异常时也能做出鲁棒的跳转。

  • 状态爆炸的规避

    在 FSM 中,如果想为"NullPointer"、"SyntaxError"、"TimeOut"等 10 种不同的代码错误分别定义不同的修复路径,你必须创建 10 个独立的状态和复杂的跳转分支,导致状态爆炸(State Explosion)。而在 Agent Graph 中,图的物理节点只有 3 个(Generate、Test、Evaluate),具体的错误细节被作为数据封装在共享状态中,由 LLM 在节点内直接阅读并自我适配。

2.3 核心差异对比矩阵

|------------|-----------------------|-------------------------|--------------------------------|
| 维度 | 有向无环图 (DAG) | 有限状态机 (FSM) | 智能体图 (Agent Graph) |
| 拓扑结构 | 严格单向,绝对无环 | 有向,支持有限循环 | 有向,天然支持复杂的环路与回溯 1, 2 |
| 状态传递 | 管道流式传递(上游输出即下游输入) | 仅存储当前处于哪个离散状态 | 读写全局共享的演进上下文(State Schema) |
| 路由决策机制 | 确定性的条件分支(If/Else) | 基于事件触发(Event-Triggered) | LLM 语义推理决策 + 规则决策混合驱动 |
| 典型应用场景 | ETL 数据处理、静态串行推理、多分支分发 | 订单生命周期管理、审批工作流 | 动态纠错 Agent、ReAct 闭环、多 Agent 协作 |

三、 图驱动 AI 编排的五大核心要素

无论是使用 Python 的 LangGraph 还是 Java 的 Spring AI Alibaba Graph,其底层都建立在以下五个核心要素之上

3.1 状态(State)------ 唯一事实源

状态是图的骨架。它是贯穿整个图生命周期的全局共享对象 。

  • 合并策略(Reducer / KeyStrategy) :状态中的不同字段可以配置不同的合并策略。例如,存储对话历史的 messages 字段应该采用 Append(追加) 策略,而存储最新生成的代码 code 字段应该采用 Replace(覆盖) 策略。

3.2 节点(Nodes)------ 原子计算单元

节点是具体的执行步骤。每个节点通常是一个函数或一个类,它接收当前的 State,执行计算(如调用大模型、查询数据库、调用工具),然后返回一个字典或 Map,用于增量更新全局状态。节点之间不直接通信,全部通过读写 State 进行间接协同。

3.3 边(Edges)------ 控制流导轨

边定义了节点之间的连接关系。

  • 普通边(Normal Edges):确定性的单向连接。如 A -> B,执行完 A 必定执行 B。

  • 条件边(Conditional Edges):动态连接。其绑定了一个判定函数(Router),根据当前 State 的值动态决定下一步流向 。

3.4 持久化与检查点(Checkpointing & Memory)

检查点机制是图框架极具工程价值的特性。它通过在每个节点执行完毕后,将当前 State 的快照序列化并存入数据库(如 SQLite、Postgres、Redis)。

  • 短效记忆(Thread Memory):支持在同一线程内实现多轮对话。

  • 状态回放与时间旅行(Time Travel):开发者可以随时将状态回滚到历史的某一个检查点,便于调试。

  • 断点续传:如果程序中途因网络或欠费中断,可以从最后一个检查点直接恢复执行,而无需从头运行。

3.5 人工干预(Human-in-the-Loop)

在真实的企业级场景中,有些操作(如发送扣款邮件、合并代码到主分支)必须由人类审批。图框架通过支持中断(Interrupt)机制实现了这一点。图可以在进入某个敏感节点前自动挂起,释放当前线程,等待人类在 UI 界面上修改状态(如修改生成的邮件内容)并点击"批准"后,图再反序列化并继续执行。

四、 实战一:基于 LangGraph (Python) 构建 SQL 自我纠错智能体

在这个实战中,我们将使用 LangGraph 构建一个 SQL 生成、执行与自我纠错智能体

  • 场景:用户输入自然语言提问,SQL_Generator 生成 SQL 语句,SQL_Executor 模拟执行。如果执行报错,将错误信息丢回 SQL_Generator 重新生成,最多尝试 3 次。
python 复制代码
import operator
from typing import TypedDict, Annotated, List, Dict, Any
from langgraph.graph import StateGraph, END
from langgraph.checkpoint.memory import MemorySaver

# 1. 定义图的全局状态
class SQLAgentState(TypedDict):
    question: str
    sql_query: str
    execution_result: str
    error_message: str
    iteration_count: int
    is_success: bool

# 2. 定义节点一:SQL 生成器
# 真实场景下这里会调用 ChatOpenAI 等大模型并配合 System Prompt
def sql_generator_node(state: SQLAgentState) -> Dict[str, Any]:
    question = state["question"]
    error_message = state.get("error_message", "")
    iteration = state.get("iteration_count", 0)
    
    print(f"\n=== [Node: SQL_Generator] 正在生成第 {iteration + 1} 次 SQL ===")
    
    # 模拟大模型根据报错进行自我纠错的逻辑
    if error_message:
        print(f"-> 收到上一次的执行报错: {error_message}")
        # 大模型吸取教训,生成了正确的 SQL
        sql = "SELECT user_id, name FROM users WHERE age > 18;"
    else:
        # 初次尝试,故意生成一个写错表名的 SQL (users_wrong_table)
        sql = "SELECT user_id, name FROM users_wrong_table WHERE age > 18;"
        
    print(f"-> 生成的 SQL 语句: {sql}")
    return {
        "sql_query": sql,
        "iteration_count": iteration + 1,
        "error_message": "" # 尝试新 SQL 前清空错误信息
    }

# 3. 定义节点二:SQL 执行器
def sql_executor_node(state: SQLAgentState) -> Dict[str, Any]:
    sql_query = state["sql_query"]
    print(f"\n=== [Node: SQL_Executor] 正在执行 SQL ===")
    
    # 模拟数据库执行
    if "users_wrong_table" in sql_query:
        err = "Table 'db.users_wrong_table' doesn't exist"
        print(f"-> 执行失败: {err}")
        return {"execution_result": "", "is_success": False, "error_message": err}
    else:
        result = "[{'user_id': 1, 'name': 'Alice'}, {'user_id': 2, 'name': 'Bob'}]"
        print(f"-> 执行成功! 得到结果: {result}")
        return {"execution_result": result, "is_success": True, "error_message": ""}

# 4. 定义条件路由规则(决定是继续循环还是结束)
def should_continue_router(state: SQLAgentState) -> str:
    if state["is_success"]:
        return "success"
    if state["iteration_count"] >= 3:
        print("\n=== [Router] 已达到最大重试次数,被迫终止 ===")
        return "abort"
    print("\n=== [Router] 发现报错,决定打回 Generator 重新生成 ===")
    return "retry"

# 5. 装配状态图
workflow = StateGraph(SQLAgentState)

# 注册节点
workflow.add_node("sql_generator", sql_generator_node)
workflow.add_node("sql_executor", sql_executor_node)

# 建立边关系
workflow.set_entry_point("sql_generator")
workflow.add_edge("sql_generator", "sql_executor")

# 建立条件边(核心闭环)
workflow.add_conditional_edges(
    "sql_executor",
    should_continue_router,
    {
        "success": END,
        "abort": END,
        "retry": "sql_generator" # 重新指向生成器节点,形成有环图
    }
)

# 6. 配置内存检查点以便支持时间旅行和对话管理
memory = MemorySaver()
app = workflow.compile(checkpointer=memory)

# 7. 测试运行
if __name__ == "__main__":
    initial_state = {
        "question": "查询所有大于18岁的用户ID和姓名",
        "sql_query": "",
        "execution_result": "",
        "error_message": "",
        "iteration_count": 0,
        "is_success": False
    }
    
    # 必须指定 thread_id 才能启动 Checkpointer 机制
    config = {"configurable": {"thread_id": "sql_test_thread_1"}}
    
    final_output = app.invoke(initial_state, config)
    
    print("\n====== 最终图执行结果 ======")
    print(f"是否成功: {final_output['is_success']}")
    print(f"最终 SQL: {final_output['sql_query']}")
    print(f"执行结果: {final_output['execution_result']}")

五、 实战二:基于 Spring AI Alibaba Graph (Java) 构建 SQL 自我纠错智能体

对于企业级 Java 开发者而言,Spring AI Alibaba Graph 提供了与 Spring 框架完美契合的、类型安全的有环图编排底座。我们将采用相同的"SQL 纠错"业务场景,使用 Java 语言进行工业级标准的实现。

5.1 完整 Java 代码实现

A. 引入 POM 依赖

确保在你的 Spring Boot 项目中引入了 Spring AI Alibaba 相关的 Graph 组件依赖:

XML 复制代码
<dependency>
    <groupId>com.alibaba.cloud.ai</groupId>
    <artifactId>spring-ai-alibaba-graph</artifactId>
    <version>1.0.0-SNAPSHOT</version> <!-- 请使用最新的快照或发布版本 -->
</dependency>

B. 定义节点动作(Node Actions)

在 Java 中,每一个节点需要实现 NodeAction 接口。

java 复制代码
package com.example.ai.graph;

import com.alibaba.cloud.ai.graph.OverAllState;
import com.alibaba.cloud.ai.graph.action.NodeAction;
import java.util.HashMap;
import java.util.Map;

// 1. SQL 生成节点
public class SqlGeneratorNode implements NodeAction {
    @Override
    public Map<String, Object> apply(OverAllState state) throws Exception {
        String question = state.value("question", String.class).orElse("");
        String errorMessage = state.value("error_message", String.class).orElse("");
        Integer iteration = state.value("iteration_count", Integer.class).orElse(0);

        System.out.printf("\n=== [Node: SQL_Generator] 正在生成第 %d 次 SQL ===\n", iteration + 1);

        String sql;
        if (errorMessage != null && !errorMessage.isEmpty()) {
            System.out.printf("-> 收到上一次的执行报错: %s\n", errorMessage);
            // 纠错后生成正确的 SQL
            sql = "SELECT user_id, name FROM users WHERE age > 18;";
        } else {
            // 故意写错表名
            sql = "SELECT user_id, name FROM users_wrong_table WHERE age > 18;";
        }

        System.out.printf("-> 生成的 SQL 语句: %s\n", sql);

        Map<String, Object> delta = new HashMap<>();
        delta.put("sql_query", sql);
        delta.put("iteration_count", iteration + 1);
        delta.put("error_message", ""); // 准备执行,清空报错信息
        return delta;
    }
}
java 复制代码
package com.example.ai.graph;

import com.alibaba.cloud.ai.graph.OverAllState;
import com.alibaba.cloud.ai.graph.action.NodeAction;
import java.util.HashMap;
import java.util.Map;

// 2. SQL 执行节点
public class SqlExecutorNode implements NodeAction {
    @Override
    public Map<String, Object> apply(OverAllState state) throws Exception {
        String sqlQuery = state.value("sql_query", String.class).orElse("");
        System.out.println("\n=== [Node: SQL_Executor] 正在执行 SQL ===");

        Map<String, Object> delta = new HashMap<>();
        if (sqlQuery.contains("users_wrong_table")) {
            String err = "Table 'db.users_wrong_table' doesn't exist";
            System.out.printf("-> 执行失败: %s\n", err);
            delta.put("execution_result", "");
            delta.put("is_success", false);
            delta.put("error_message", err);
        } else {
            String result = "[{'user_id': 1, 'name': 'Alice'}, {'user_id': 2, 'name': 'Bob'}]";
            System.out.printf("-> 执行成功! 得到结果: %s\n", result);
            delta.put("execution_result", result);
            delta.put("is_success", true);
            delta.put("error_message", "");
        }
        return delta;
    }
}

C. 定义条件路由规则(Edge Action)

路由需要实现 EdgeAction 接口,返回下一步指向的节点 ID 字符串。

java 复制代码
package com.example.ai.graph;

import com.alibaba.cloud.ai.graph.OverAllState;
import com.alibaba.cloud.ai.graph.action.EdgeAction;

public class SqlAgentRouter implements EdgeAction {
    @Override
    public String apply(OverAllState state) throws Exception {
        Boolean isSuccess = state.value("is_success", Boolean.class).orElse(false);
        Integer iterationCount = state.value("iteration_count", Integer.class).orElse(0);

        if (isSuccess) {
            System.out.println("\n=== [Router] 执行成功,结束图流程 ===");
            return "success";
        } else if (iterationCount >= 3) {
            System.out.println("\n=== [Router] 已达到最大重试次数,被迫终止 ===");
            return "abort";
        } else {
            System.out.println("\n=== [Router] 发现报错,决定打回 Generator 重新生成 ===");
            return "retry";
        }
    }
}

D. 状态图配置与运行(Spring Boot Configuration)

在 Spring Boot 中装配有环图,并利用 Service 进行触发。

java 复制代码
package com.example.ai.graph;

import com.alibaba.cloud.ai.graph.*;
import com.alibaba.cloud.ai.graph.strategy.ReplaceStrategy;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;

@Configuration
public class SqlAgentGraphConfig {

    @Bean
    public CompiledGraph sqlAgentGraph() throws Exception {
        // 1. 定义初始的共享状态骨架及字段合并策略
        OverAllState stateSchema = new OverAllState();
        stateSchema.registerKeyAndStrategy("question", new ReplaceStrategy());
        stateSchema.registerKeyAndStrategy("sql_query", new ReplaceStrategy());
        stateSchema.registerKeyAndStrategy("execution_result", new ReplaceStrategy());
        stateSchema.registerKeyAndStrategy("error_message", new ReplaceStrategy());
        stateSchema.registerKeyAndStrategy("iteration_count", new ReplaceStrategy());
        stateSchema.registerKeyAndStrategy("is_success", new ReplaceStrategy());

        // 2. 初始化 StateGraph
        StateGraph stateGraph = new StateGraph(stateSchema);

        // 3. 注册节点
        stateGraph.addNode("sql_generator", new SqlGeneratorNode());
        stateGraph.addNode("sql_executor", new SqlExecutorNode());

        // 4. 设置默认起始与中间普通边
        stateGraph.addEdge(StateGraph.START, "sql_generator");
        stateGraph.addEdge("sql_generator", "sql_executor");

        // 5. 注册条件边以实现闭环控制
        stateGraph.addConditionalEdges(
            "sql_executor",
            new SqlAgentRouter(),
            Map.of(
                "success", StateGraph.END,
                "abort", StateGraph.END,
                "retry", "sql_generator" // 重新打回到生成节点
            )
        );

        // 6. 编译图(在生产中,可以配置持久化数据库作为 Checkpointer)
        return stateGraph.compile();
    }
}

E. 测试触发组件

java 复制代码
package com.example.ai.graph;

import com.alibaba.cloud.ai.graph.CompiledGraph;
import com.alibaba.cloud.ai.graph.OverAllState;
import com.alibaba.cloud.ai.graph.RunnableConfig;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;

@Service
public class SqlAgentService {

    @Autowired
    private CompiledGraph sqlAgentGraph;

    public void executeSqlAgent() throws Exception {
        // 1. 初始化入参
        Map<String, Object> inputs = new HashMap<>();
        inputs.put("question", "查询所有大于18岁的用户ID和姓名");
        inputs.put("sql_query", "");
        inputs.put("execution_result", "");
        inputs.put("error_message", "");
        inputs.put("iteration_count", 0);
        inputs.put("is_success", false);

        // 2. 启动图并执行
        Optional<OverAllState> resultStateOpt = sqlAgentGraph.invoke(
            inputs, 
            RunnableConfig.builder().build()
        );

        if (resultStateOpt.isPresent()) {
            OverAllState finalState = resultStateOpt.get();
            System.out.println("\n====== Java 最终图执行结果 ======");
            System.out.println("是否成功: " + finalState.value("is_success", Boolean.class).orElse(false));
            System.out.println("最终 SQL: " + finalState.value("sql_query", String.class).orElse(""));
            System.out.println("执行结果: " + finalState.value("execution_result", String.class).orElse(""));
        }
    }
}

六、两大主流图框架技术选型指南

6.1 适用场景与定位

  • LangGraph (Python)

    • 定位:AI 实验室、敏捷原型开发、AI 原生公司。

    • 选型理由:如果你的团队大部分是算法工程师,且整个技术栈重度依赖 Python 生态,LangGraph 无疑是第一选择。它更新极快,能第一时间适配学术界最新的 Agent 论文模型。

  • Spring AI Alibaba Graph (Java)

    • 定位:企业级系统、金融系统、高并发后台、微服务集群。

    • 选型理由:如果你的系统是大型分布式 Java 架构,需要极高的系统稳定性、强类型安全保护以及无缝接入 Spring 容器。Spring AI Alibaba Graph 可以将 AI Agent 直接作为微服务节点(配合 Nacos 注册中心)进行分布式集群调度,提供更可靠的线程安全控制和工业级事务持久化支持。

6.2 状态管理与并发控制

  • LangGraph

    • 状态定义基于 Python 的 TypedDict,通过 asyncio 进行异步并发处理。由于 Python 存在全局解释器锁(GIL),在高吞吐量的多线程环境下,往往需要依靠多进程或复杂的异步架构。
  • Spring AI Alibaba Graph

    • 状态由强类型的 OverAllState 承载,支持显式的类型转换(如 state.value("key", Class))。运行基于 JVM 强大的多线程并发模型,可以轻松支撑数万级线程高并发的智能体流转,性能非常稳健。

总结

大模型时代的应用开发,已经告别了"一行代码、一个 API 调通全部"的蛮荒期。智能体在落地企业真实业务时,必然会走向复杂化与网状化。

图(Graph)作为 AI 编排的一等公民,既保留了传统 FSM 的控制边界和可预测性,又融入了大模型语义推理的非确定性与容错能力。

无论是 Python 生态下的 LangGraph 还是 Java 生态下的 Spring AI Alibaba Graph,都在向我们传递同一个核心的工程设计哲学:

不要试图去写一段庞大、臃肿且不透明的单体 Agent 代码,而应该用"图"的思想,将逻辑解耦为单一职责的节点(Nodes),将记忆与上下文收拢在全局状态(State)中,通过优雅的有环路径(Cycles)和智能路由(Conditional Edges),让智能体在循环与反思中自我收敛、优雅进化。

相关推荐
程序员cxuan7 小时前
为每个任务配一套 harness:Claude Code 里的动态工作流
人工智能
程序员cxuan7 小时前
Claude Fable 5 来了
人工智能·后端·程序员
云边云科技_云网融合7 小时前
云边云科技亮相 2026 WOD 制造业数智化博览会 云网融合赋能制造焕新
人工智能·科技·安全·制造
biter down7 小时前
从 0 到 1 搭建 Python 接口自动化测试框架(博客系统实战)
开发语言·python
Σίσυφος19007 小时前
激光三角 光平面标定-多高度误差分析
人工智能·计算机视觉·平面
JS菌7 小时前
手写一个 AI Agent 全栈项目:从沙箱执行到子智能体的完整实现
前端·人工智能·后端
lqqjuly7 小时前
前沿算法深度解析(二)
人工智能·算法·机器学习
Bode_20027 小时前
基于大数据分析的全生命周期质量追溯质量评估体系落地方案
大数据·人工智能