workflow-langgraph-dify-comparison-reference.md 49 KB

DAG 工作流对比:LangGraph vs Dify vs agent-management

对比目的:以 LangGraph 0.x(仓库 langgraph/libs/ 源码副本)、Dify 1.16.0(详见 dify-reference.md、workflow-vs-dify-comparison.md)和本仓库(agent-management)三个项目为坐标,聚焦** DAG 工作流层**的能力差异、设计取舍和演进方向。

对比维度:图模型、调度执行、状态管理、边与分支、循环回环、并行 fan-out、子图、HITL、流式输出、持久化恢复、失败重试、上下文传递、节点扩展、设计哲学。

信息来源:

  • LangGraph 实现:langgraph/libs/langgraph/langgraph/{graph,pregel,channels,func}/**、langgraph/libs/checkpoint/**、langgraph/libs/prebuilt/**
  • Dify 实现:dify/api/core/workflow/**、dify/dify-agent/**、dify/dify-agent-runtime/**
  • 本仓库实现:backend/src/main/java/com/agent/management/engine/**、frontend/src/components/workflow/nodes/**、docs/design/workflow-*.md、docs/plans/workflow-*.md

0. 结论速览

维度 量化对比
项目定位 LangGraph:通用图计算框架(DSL + Pregel 运行时),面向开发者代码编排
Dify:LLMOSS 平台(可视化 + 多形态应用),面向业务用户与 SaaS
本仓库:企业内部 AI 编码助手平台(Java 单体 + 强一致 envelope),面向可控场景
图模型 LangGraph:有向图(原生支持循环),节点 + 边 + 条件边 + Send
Dify:严格 DAG(顶层无环),循环只能由 Agent 节点内部 / iteration / loop 节点承载
本仓库:双模式——DAG 模式严格无环,AGENTIC 模式由 LLM Planner 自主路由(允许任意跳转与重访)
执行模型 LangGraph:Pregel BSP,superstep 内并发,channel 驱动触发
Dify:graphon GraphEngine 线程池,完成即触发后继
本仓库:DAG 模式 Dataflow(入度计数),AGENTIC 模式单线程顺序
状态管理 LangGraph:Schema → Channel 映射,Reducer 二元运算合并多写入
Dify:variable_pool 共享扁平 Map,无合并语义
本仓库:nodeScopedOutputs 按 nodeId 隔离,同名变量互不覆盖
循环支持 LangGraph:原生一等公民(受 Pregel 启发)
Dify:顶层无,由 iteration / loop 节点或 Agent 内部 ReAct 实现
本仓库:DAG 模式无,AGENTIC 模式有(重访 + 任意跳转)
HITL 三者都支持,但机制差异显著(详见 §7)
设计哲学 LangGraph:声明式 Schema + 通用运行时 + 强持久化
Dify:可视化 + 插件化生态 + 多形态应用
本仓库:可视化 + 强一致 envelope + 引擎层统一治理

1. 总体定位差异

维度 LangGraph Dify agent-management
项目类型 通用 LLM 图计算框架(DSL + 运行时) 开源 LLMOSS 平台 企业内部 AI 编码助手平台
主语言 Python(另有 JS/TS SDK) Python + Go(沙箱 / 插件) Java 17 + Spring Boot + Python(Hermes Bridge)
编排入口 代码(StateGraph API) + 可视化(LangGraph Studio) 可视化画布(ReactFlow) 可视化画布(Vue Flow)
应用形态 通用图应用(开发者自定义) Workflow / Chat / AdvancedChat / Completion / AgentChat 五种 Workflow(含 DAG / AGENTIC 两种模式)
部署形态 库 + Server(可选)+ Studio(可选) 11 个 docker-compose 服务 单 JAR + MySQL + Redis
Agent 引擎 create_react_agent 预构建 + 用户自定义图 CotAgentRunner / FunctionCallAgentRunner / dify-agent(pydantic-ai) Hermes Bridge(Python 子进程)+ Spring AI ChatClient
沙箱实现 不提供(开发者自行处理) Go Landlock LSM + tmux + sanitize-pty Python 三层 monkey-patch(路径白名单)
持久化抽象 BaseCheckpointSaver(内存 / SQLite / Postgres) DB 全量序列化(DB-based pause state) DB + WorkflowRunVisit + WorkflowPause

核心差异:

  • LangGraph 追求通用性与可编程性——给开发者一套声明式 API 和强大运行时,让任何图结构(含循环、map-reduce、子图)都能用一套模型表达。
  • Dify 追求生态丰富度与多形态覆盖——给业务用户一个可视化平台 + 多种应用类型,给开发者一个插件机制。
  • 本仓库 追求强一致性与引擎层统一治理——给企业内部场景一个可控、可观测、容错的工作流引擎。

2. 图模型与拓扑结构

2.1 图模型基础

维度 LangGraph Dify agent-management
图类型 有向图(允许循环) 严格 DAG DAG 模式:严格 DAG
AGENTIC 模式:画布仅作参考,运行时由 Planner 决定跳转
节点定义 add_node(name, callable) —— 节点是 Python 可调用对象 JSON 配置 {type, data} + NodeFactory 构造 JSON 配置 {type, data} + NodeExecutor 实现
边定义 add_edge(start, end) 或 add_edge([start1, start2], end)(join) JSON edges[] 数组(source / target / sourceHandle) JSON edges[] 数组(同 Dify)
虚拟节点 START(__start__)、END(__end__) start、end 节点(显式) userInput 节点(入度 0)、output 节点(出度 0,可多个)
环检测 不检测——循环是合法结构,由 recursion_limit(默认 25)兜底 显式禁止(GraphEngine 拒绝环) DAG 模式:DagResolver.resolve() Kahn 算法检测到环直接抛异常(DagResolver.java:108-111)

2.2 拓扑执行约束

维度 LangGraph Dify agent-management
严格拓扑 否(节点可跳回前序) 是 DAG 模式:是;AGENTIC 模式:否
重访节点 允许(受 recursion_limit 限制) 不允许(顶层图) DAG 模式:不允许;AGENTIC 模式:允许(MAX_VISITS=50 兜底)
静态 vs 动态 运行时动态:节点可通过 Command(goto=...) 跳转任意节点 静态(编译期固定) + Agent 内部 ReAct 动态 DAG 模式:静态;AGENTIC 模式:运行时 Planner 动态决策

3. 调度与执行模型

3.1 调度算法对比

维度 LangGraph Dify agent-management
调度模型 Pregel BSP(Bulk Synchronous Parallel) graphon GraphEngine 线程池 DAG 模式:Dataflow(入度计数)
AGENTIC 模式:单线程顺序
核心算法 superstep 循环:Plan → Execute → Update 完成即触发后继(无显式 superstep) pendingInputs: Map<nodeId, AtomicInteger>,归零即调度(WorkflowLevelExecutor.java:248-307)
触发机制 Channel 版本号驱动:节点声明订阅的 channel,channel 更新触发 边驱动:上游完成 → 下游入度减一 边驱动:同 Dify,但支持运行时活跃边过滤(条件分支未选 target 由 tryScheduleSuccessors 移除)
任务发现 PULL(订阅)+ PUSH(Send 主动推送)双模式 仅 PULL(订阅) 仅 PULL(订阅)

3.2 LangGraph Pregel 三阶段循环

PregelLoop.tick() / after_tick()
  ├── Plan:prepare_next_tasks() 决定本步执行哪些 task
  │     ├── 消费 TASKS 通道中的 Send 任务(PUSH)
  │     └── 遍历候选节点,检查 trigger channel 版本号(PULL)
  ├── Execute:PregelRunner 并发执行所有 task(线程池或 asyncio)
  ├── Update:apply_writes() 应用写入,调用 Reducer 合并
  ├── Checkpoint:_put_checkpoint() 持久化(durability 模式可选)
  └── Interrupt 检查:before/after 节点是否命中 HITL

关键位置:

  • langgraph/libs/langgraph/langgraph/pregel/_loop.py:599(tick)
  • langgraph/libs/langgraph/langgraph/pregel/_loop.py:683(after_tick)
  • langgraph/libs/langgraph/langgraph/pregel/_algo.py:392(prepare_next_tasks)
  • langgraph/libs/langgraph/langgraph/pregel/_algo.py:232(apply_writes)
  • langgraph/libs/langgraph/langgraph/pregel/_runner.py:135(PregelRunner)

3.3 本仓库 Dataflow 调度

WorkflowLevelExecutor.executeByLevel()
  ├── 初始化 pendingInputs(每节点入度计数,按 source 去重)
  ├── 调度起点(入度为 0 的节点)到 nodeExecutor 线程池
  ├── 节点完成回调 onNodeCompleted()
  │     ├── 写 records、推 SSE
  │     ├── 激活下游 tryScheduleSuccessors()(递减入度)
  │     └── 归零则调度下游节点
  └── 主线程通过 done: CompletableFuture<Void> 等待完成

关键位置:

  • backend/src/main/java/com/agent/management/engine/WorkflowLevelExecutor.java:319(executeByLevel)
  • backend/src/main/java/com/agent/management/engine/WorkflowLevelExecutor.java:452(onNodeCompleted)
  • backend/src/main/java/com/agent/management/engine/WorkflowLevelExecutor.java:579(tryScheduleSuccessors)

与 LangGraph 的关键差异:

  • LangGraph 以 channel 为中心——节点订阅 channel,channel 更新即触发(数据驱动)
  • 本仓库以 节点为中心——节点有入度计数,入边全到齐即触发(控制驱动)
  • 两者在纯 DAG 场景表现等价,但 LangGraph 的 channel 模型天然支持 Reducer(多写入合并),而本仓库同名变量按 nodeId 隔离(无合并)

3.4 AGENTIC 模式(本仓库独有)

AgenticExecutor.execute()
  while (sortOrder < MAX_VISITS=50):
    ├── 构建 AGENTIC workspace(直接用 currentVars)
    ├── 调用 executor.execute()(含 maxRetries 重试)
    ├── envelope 失败自动升级
    ├── 合并输出到 currentVars
    ├── 持久化 WorkflowRunVisit
    ├── 终止判定(到达 output 节点且成功)
    └── PlannerService.decideNext() 调 LLM 决定下一步

关键位置:

  • backend/src/main/java/com/agent/management/engine/AgenticExecutor.java:104(主循环)
  • backend/src/main/java/com/agent/management/engine/PlannerService.java:136(buildPrompt)

这是本仓库独有的"智能调度而非强依赖 DAG"模式——LangGraph 没有等价物(开发者必须显式写 Command(goto=...)),Dify 明确不支持(详见 dify-reference.md §5)。AGENTIC 模式让 LLM Planner 像一个外部调度器决定节点跳转,相当于把 LangGraph 的节点内 Command(goto=...) 提升为引擎层全局决策。

3.5 并发模型对比

维度 LangGraph Dify agent-management
并发粒度 superstep 内所有 task 并发 GraphEngine 线程池并发 DAG 模式:同层无依赖节点并发;AGENTIC 模式:不并发(单线程)
同步原语 superstep barrier(BSP) 无显式 barrier 入度计数归零(Dataflow)
线程池 ThreadPoolExecutor 或 asyncio graphon min/max workers + 弹性伸缩 双池:nodeExecutor(快节点)+ clarifyWaitExecutor(长阻塞节点,避免 AskFor 等待耗尽 worker)
取消机制 任务失败时 _should_stop_others 决定 graphon 图级策略(Dify 侧不可见) abortFlag(AtomicBoolean)+ inFlightFutures 显式 cancel(true)(WorkflowLevelExecutor.java:686-705)

本仓库线程池隔离是独有亮点:clarifyWaitExecutor 专门承担 agent / smartAction 节点(因 AskFor 可能阻塞数小时),避免长阻塞任务耗尽快节点 worker(WorkflowLevelExecutor.java:106-137)。LangGraph 与 Dify 都没有这种"按节点阻塞性能分级路由"的设计。


4. 状态管理机制

4.1 状态抽象

维度 LangGraph Dify agent-management
状态抽象 TypedDict Schema + Annotated reducer variable_pool 共享 Map WorkflowContext 持有 nodeScopedOutputs + variables
字段语义 三种:①默认 LastValue(覆盖)②Annotated[list, reducer](合并)③自定义 Channel 实例 单一:variable_pool 直接赋值(覆盖) 单一:nodeScopedOutputs 按 nodeId 隔离,同名变量互不覆盖
多写入处理 Reducer 自动合并(如 operator.add 列表拼接) 后写覆盖前写(无合并) 按 nodeId 隔离——不同节点同名输出共存,前驱读取时按拓扑逆序匹配

4.2 Reducer 实现

LangGraph 通过 Channel 实现 Reducer:

# langgraph/libs/langgraph/langgraph/channels/binop.py:65
class BinaryOperatorAggregate(BaseChannel):
    def update(self, values):
        for value in values:
            is_overwrite, overwrite_value = _get_overwrite(value)
            if is_overwrite:
                self.value = overwrite_value          # 强制覆盖
            else:
                self.value = self.operator(self.value, value)  # ★ 应用 reducer
        return True

典型用法:

class State(TypedDict):
    messages: Annotated[list, operator.add]   # 多节点 append 自动合并
    count: Annotated[int, lambda a, b: a + b] # 多节点计数累加

Dify 与本仓库都没有 Reducer 语义——Dify 用 variable_pool 后写覆盖,本仓库用 nodeId 隔离避免覆盖。

关键差异:LangGraph 的 Reducer 让"多节点并行写入同一字段"成为一等公民,是 map-reduce 工作流的基础。本仓库的 nodeId 隔离设计避免了覆盖问题,但下游必须显式指定从前驱哪个节点取值,无自动合并能力。

4.3 节点访问 State

维度 LangGraph Dify agent-management
入参签名 9 种可注入形式:state / config / writer / store / runtime 及其组合 节点类继承 Node,实现 _run() 生成器 NodeExecutor.execute(nodeId, data, context, workspace)
出参形式 dict / Command 对象 / list[Command] / dataclass / pydantic model yield NodeRunResult / StreamCompletedEvent / PauseRequestedEvent 返回 NodeExecutionResult(SUCCESS/FAILED + data)
输出契约 由 Schema 约束(写入字段需在 Schema 中声明) 由 NodeFactory 校验(部分节点强制 declared outputs) 统一 envelope 全节点强制:{status, message, data}(NodeOutputEnvelope.java)

本仓库 envelope 强制是独有亮点:所有 8 种节点类型都必须产出 {status, message, data} 三段式结构,多余字段丢弃。LangGraph 由 Schema 软约束,Dify 仅 Agent v2 节点强制。这让本仓库的下游节点输入解析有统一预期。


5. 边与分支

5.1 边类型对比

边类型 LangGraph Dify agent-management
普通边 add_edge(start, end) JSON edges[] JSON edges[]
多入边 join add_edge([s1, s2], end) —— 编译为 NamedBarrierValue 屏障 默认等待所有入边(隐式 join) 默认等待所有入边(隐式 join)
条件边 add_conditional_edges(src, path_fn, path_map) if-else 节点(通过 sourceHandle 区分分支) condition 节点(通过 sourceHandle 区分分支,branch-0..branch-N)
循环边 add_edge(b, a) 直接支持 顶层不支持(由 iteration/loop 节点承载) DAG 模式:不支持;AGENTIC 模式:通过 Planner 决策实现
动态分发 路径函数返回列表 → 同时路由多节点;或 Send 主动 push 不支持(静态条件边) DAG 模式:不支持;AGENTIC 模式:Planner 决策

5.2 LangGraph 的条件边(动态路由核心)

# langgraph/libs/langgraph/langgraph/graph/state.py:982
def add_conditional_edges(self, source, path, path_map=None):
    path = coerce_to_runnable(path, name=None, trace=True)
    self.branches[source][path.name or "condition"] = BranchSpec.from_path(path, path_map, True)

# langgraph/libs/langgraph/langgraph/graph/_branch.py:192
def _finish(self, writer, input, result, config):
    if not isinstance(result, (list, tuple)): result = [result]
    if self.ends:
        destinations = [r if isinstance(r, Send) else self.ends[r] for r in result]
    else:
        destinations = cast(Sequence[Send | str], result)
    # destinations 可包含多个 Send → 实现 fan-out 并行
    entries = writer(destinations, False)

关键能力:

  • 路径函数返回单个值 → 路由到单节点
  • 路径函数返回列表 → 同时路由到多个节点(fan-out)
  • 路径函数返回 Send 列表 → 每个 Send 携带独立 state,并发执行后通过 reducer 合并回主 state

5.3 本仓库条件节点

双模式分支(backend/src/main/java/com/agent/management/engine/executor/ConditionExecutor.java:18-31):

  • rule 模式:本地 ConditionRuleEvaluator 评估,无 LLM 调用,支持 22 种操作符(= / != / contains / startsWith / matches 正则 / in / isEmpty / lengthGt 等)
  • natural 模式:独立调用 LLM 判断 YES/NO

评估顺序(ConditionExecutor.java:53-97):按 conditions 数组顺序逐个评估,第一个命中的分支胜出;全未命中走最后一个隐含 ELSE 分支。

分支标识:branch-{index},与前端 ConditionNode.vue:32 严格对齐。

与 LangGraph 的差异:

  • 本仓库条件节点是"控制流节点",输出 branch-X sourceHandle 由引擎路由
  • LangGraph 的条件边是"边属性",路径函数返回值映射到目标节点
  • 两者表达力等价,但 LangGraph 的列表返回天然支持 fan-out,本仓库 condition 节点只能选一个分支

6. 循环、并行与子图

6.1 循环支持对比

维度 LangGraph Dify agent-management
顶层图循环 原生支持(受 Pregel 启发的核心特性) 不支持 DAG 模式:不支持;AGENTIC 模式:通过 Planner 重访实现
循环终止 节点路由到 END / 超过 recursion_limit(默认 25,抛 GraphRecursionError)/ 无写入自然终止 N/A DAG 模式:拓扑完成;AGENTIC 模式:到达 output 节点 + Planner 决策 / 超过 MAX_VISITS=50
迭代子图 不需要(图本身可循环) iteration 节点 + 内嵌子工作流 不支持
循环计数 recursion_limit 全局步数 iteration / loop 节点局部步数 AGENTIC 模式:MAX_VISITS=50 总访问数 + 同节点访问计数(仅展示用,不强制)

关键差异:

  • LangGraph 把循环作为图的一等公民——开发者直接 graph.add_edge("B", "A") 即可
  • Dify 和本仓库 DAG 模式拒绝循环,强制通过节点内部逻辑(iteration / loop / Agent ReAct)实现
  • 本仓库 AGENTIC 模式走第三条路——不修改图结构,由 Planner 在运行时决定跳转

6.2 并行 fan-out

维度 LangGraph Dify agent-management
同层并行 superstep 内所有 task 并发 GraphEngine 线程池并发 DAG 模式:是(Dataflow 入度归零即调度);AGENTIC 模式:否(单线程)
Map-Reduce Send 对象原生支持:路径函数返回 [Send(node, arg) for arg in items],每个 Send 独立 state 并发执行,通过 reducer 合并 iteration 节点(数组迭代,is_parallel + parallel_nums 默认 10) 不支持
条件 fan-out 路径函数返回列表 → 多目标并发 不支持(条件分支互斥) 不支持(condition 节点互斥)

LangGraph 的 Send 是核心创新(langgraph/libs/langgraph/langgraph/types.py:704):

class Send:
    """A message or packet to send to a specific node in the graph.
    The sent state can differ from the core graph's state, allowing for
    flexible and dynamic workflow management.
    One such example is a 'map-reduce' workflow."""

这让 map-reduce 工作流(如批量处理列表、并行生成多个候选答案再合并)成为 LangGraph 的一等公民。Dify 和本仓库要实现类似功能,必须通过专门的迭代节点,且无法携带独立 state。

6.3 子图(Subgraph)

维度 LangGraph Dify agent-management
子图抽象 完全支持:子图是编译好的 CompiledStateGraph,作为父图某节点的 bound 运行 支持:_WorkflowChildEngineBuilder 创建隔离的 child GraphEngine 不支持
状态隔离 独立 channel namespace + checkpoint namespace 复用父图 variable_pool N/A
父子通信 Command(PARENT, update=...) 向上通信 共享 variable_pool N/A
嵌套深度 无限制 无限制 N/A

关键 API(langgraph/libs/langgraph/langgraph/pregel/main.py:1076):

def get_subgraphs(self, namespace=None, recurse=False):
    """通过 namespace 路径访问嵌套子图"""

本仓库缺失:子图是 Dify 和 LangGraph 都具备的能力,本仓库目前不支持嵌套工作流调用。


7. HITL(Human-in-the-Loop)

7.1 三者机制对比

维度 LangGraph Dify agent-management
声明式中断 compile(interrupt_before=["node"], interrupt_after=["node"]) 无(仅运行时触发) 无
命令式中断 节点内 interrupt(value) 抛 GraphInterrupt Human Input 节点 / Agent v2 ask_human 工具 AskFor(Hermes Agent 内部 LLM 决定调 clarify 工具)
恢复机制 Command(resume=value) 通过 stream() 传入 用户答题 → 后端写回 → 工作流恢复 HermesBridgeClient.submitClarifyAnswer() → Bridge → 唤醒 Hermes Agent loop
持久化 必须配置 checkpointer PauseStatePersistenceLayer 全量序列化 WorkflowPause 表 + 同步落库(ClarifyHooks.java)
中断粒度 节点级(before/after) 节点级(Human Input)或 Agent 内部 ask_human Agent 内部 clarify 工具调用

7.2 LangGraph HITL 实现

should_interrupt 判断逻辑(langgraph/libs/langgraph/langgraph/pregel/_algo.py:155):

def should_interrupt(checkpoint, interrupt_nodes, tasks):
    # 任意 channel 自上次 interrupt 后被更新过
    any_updates_since_prev_interrupt = any(
        version > seen.get(chan, null_version)
        for chan, version in checkpoint["channel_versions"].items()
    )
    # 且有任务在 interrupt_nodes 列表中
    return [task for task in tasks if task.name in interrupt_nodes] if any_updates_since_prev_interrupt else []

恢复机制:通过 Command(resume=...) 恢复时:

  1. checkpoint_pending_writes 中保存了中断前的写入
  2. _reapply_writes_to_succeeded_nodes(_loop.py:736)恢复已完成节点的写入(不重复执行)
  3. 中断的节点从头重新执行,但 interrupt() 会跳过(基于 interrupt id 匹配,返回 resume 值)

7.3 本仓库 AskFor 机制

1. Hermes Agent 内部 LLM 决定调 `clarify` 工具
2. Python Bridge 推送 `clarify_request` 事件
3. HermesBridgeClient 转发前端 + ClarifyHooks.wrap() 同步落库 WorkflowPause
4. 整个 SSE 读取阻塞,等待用户答题(可能长达数小时)
5. 用户提交答案 → submitClarifyAnswer() → Bridge → 唤醒 Hermes Agent loop

关键位置:

  • backend/src/main/java/com/agent/management/engine/hermes/ClarifyHooks.java
  • backend/src/main/java/com/agent/management/engine/hermes/HermesBridgeClient.java

容错设计:

  • DB 写入失败仅记日志,不阻塞主流程(ClarifyHooks.java:80-83)
  • clarify_id 唯一索引去重(防 TOCTOU,第 76-79 行)
  • 长阻塞节点走独立 clarifyWaitExecutor(避免耗尽 worker)

7.4 三者 HITL 哲学差异

项目 HITL 触发位置 设计哲学
LangGraph 任意节点(声明式) + 节点内(命令式) 给开发者完全控制权——可在任意节点暂停,可携带任意中断值
Dify 专用 Human Input 节点 + Agent 内部 ask_human 给业务用户可视化配置——表单字段、超时、用户操作按钮
本仓库 仅 Hermes Agent 内部 clarify 工具 与 Hermes Agent 深度耦合——LLM 自主决定何时需要询问

本仓库 AskFor 是最受限的——只能在 Hermes Agent 节点内触发,普通 LLM / Condition / Knowledge 节点无法发起 HITL。但优点是与 Agent 推理循环深度集成,LLM 可基于上下文智能决定是否需要询问。


8. 流式输出

8.1 流模式对比

维度 LangGraph Dify agent-management
Stream 模式 values / updates / messages / custom / debug / tasks / checkpoints(可多选) GraphEngine 事件队列 → TaskPipeline → SSE/WebSocket SSE 事件:node_running / node_stream / node_result / visit_recorded / planner_decision / workflow_complete / workflow_error
LLM token 级 messages 模式(专门为 chat 优化) 流式 LLM 输出 node_stream 子类型:thinking / tool_call / tool_result / status / step / tool_progress / todo_update / clarify_request
自定义事件 节点内 writer: StreamWriter 注入 ResponseStreamFilter 过滤 executor 通过 context.getStreamSink().emit(nodeId, kind, content, toolName) 推送
断线重连 通过 thread_id + checkpoint 恢复 无原生支持 SseEventBus 双轨设计 + Last-Event-ID 缓存窗口(默认 1000 条或运行结束保留 1 小时)

8.2 本仓库流式输出亮点

双轨 SSE 设计(backend/src/main/java/com/agent/management/engine/SseEventBus.java):

  • emitter 断线时事件仍缓存到总线
  • 前端可通过 GET /stream 重新订阅
  • 超时延长到 24 小时(EXECUTION_TIMEOUT_MINUTES = 1440L),匹配 AskFor 长挂机场景

事件总线 + emitter 同步发布(WorkflowEngine.java:247-257):保证缓存与推送一致。

8.3 LangGraph 流式输出哲学

LangGraph 把 stream 模式作为观察图执行的不同视角:

  • values:每个 superstep 后的完整 state 快照
  • updates:每个 task 的增量写入(默认)
  • messages:LLM token 级流(专门优化)
  • custom:开发者自定义事件
  • debug:task 调度 + checkpoint 保存事件

这种"多视角并发观察"是 LangGraph 的独特设计,让调试、监控、用户展示可以共用同一套事件流。


9. 持久化与恢复

9.1 持久化抽象

维度 LangGraph Dify agent-management
持久化层 BaseCheckpointSaver 抽象(内存 / SQLite / Postgres) DB 全量序列化 WorkflowRunVisit + WorkflowPause + WorkflowRun
持久化粒度 每个 superstep 自动持久化(durability 模式可选) 节点完成时持久化 节点 visit 完成时持久化
时间旅行 完全支持:get_state_history 通过 parent_config 形成链表,可回溯任意历史状态 不支持 不支持(仅当前状态可查)
Fork 分支 支持:从历史 checkpoint 分叉创建新分支 不支持 不支持
断线恢复 通过 thread_id 加载最新 checkpoint,pending_writes 自动重放 通过 pause_state 表恢复 通过 WorkflowPause 表恢复 AskFor UI

9.2 LangGraph Checkpoint 数据结构

核心位置:langgraph/libs/checkpoint/langgraph/checkpoint/base/__init__.py:92

class Checkpoint(TypedDict):
    v: int                                  # 版本号
    id: str                                 # 单调递增的唯一 ID(UUID6)
    ts: str                                 # ISO8601 时间戳
    channel_values: dict[str, Any]          # 所有 channel 的当前值
    channel_versions: ChannelVersions       # channel → 版本号映射
    versions_seen: dict[str, ChannelVersions]  # ★ 节点 → 已见 channel 版本(防重复触发)
    updated_channels: list[str] | None      # 本步更新的 channel 列表

class CheckpointTuple(NamedTuple):
    config: RunnableConfig
    checkpoint: Checkpoint
    metadata: CheckpointMetadata            # source / step / parents / run_id
    parent_config: RunnableConfig | None    # ★ 父 checkpoint(形成历史链)
    pending_writes: list[PendingWrite]      # ★ 当前 step 未完成的写入

关键设计:

  • 版本号驱动:channel 每次更新递增版本号,节点对比"已见版本"和"当前版本"判断是否应被触发
  • 历史链:parent_config 形成链表,支持时间旅行
  • pending_writes:保存中断前的写入,恢复时重放(_reapply_writes_to_succeeded_nodes)

9.3 三者持久化哲学差异

项目 持久化目的 设计哲学
LangGraph 可恢复 + 可回溯——每个 superstep 都可重放、可分叉 给开发者完全控制权——时间旅行是调试和分支实验的核心能力
Dify 可恢复 + 多租户隔离 给平台运营方管理多租户工作流状态
本仓库 可恢复 + 可观测(Visit 表) + HITL 持久化(Pause 表) 给企业内部场景足够的可追溯性,但不允许"回到过去重新选择"

本仓库缺失:时间旅行和 fork 分支。本仓库的 Visit 表记录每次节点访问快照(含 iterSeq / fromVisitUuid / variablesJson / outputJson / decisionJson),但只能查看不能&#34;回到 visit #3 重新执行"。


10. 失败处理与重试

10.1 失败策略对比

维度 LangGraph Dify agent-management
节点级失败策略 节点级 error_handler 指定错误处理节点 error_strategy: none / fail-branch / default-value DAG 模式:failStrategy(abort / skip);AGENTIC 模式:交 Planner 决策
重试策略 RetryPolicy(可叠加多个) retry_config: max_retries + retry_interval(默认 3 次 + 100ms) RetryPolicy:runtime retry(重新推理) + format retry(仅重组 JSON)双层
超时策略 TimeoutPolicy(节点级) HTTP 节点私有超时(max_connect/read/write_timeout) 全局 EXECUTION_TIMEOUT_MINUTES = 1440L(24 小时,针对 AskFor 长挂机)
失败取消 _should_stop_others 决定是否取消其他任务 graphon 图级策略(不可见) DAG 模式:triggerAbort() CAS 取消所有在飞节点;AGENTIC 模式:不取消,交 Planner
错误嗅探 由 retry_policy / error_handler 显式处理 OutputFailureOrchestrator(Agent v2 专属) Hermes Error Pattern(内置中文错误模式数据模型 + SSE 嗅探)

10.2 LangGraph 的错误处理

节点级 error_handler(langgraph/libs/langgraph/langgraph/graph/_node.py:91):

@dataclass(slots=True)
class StateNodeSpec:
    runnable: StateNode
    retry_policy: RetryPolicy | Sequence[RetryPolicy] | None
    cache_policy: CachePolicy | None
    is_error_handler: bool = False
    error_handler_node: str | None = None
    ...

节点失败时,引擎自动调度 error_handler_node 处理错误(传入 error 信息作为 state)。

10.3 本仓库双层重试

Runtime retry vs Format retry(backend/src/main/java/com/agent/management/engine/RetryPolicy.java):

层级 字段 含义
runtime runtimeMaxRetries 运行异常(LLM 调用失败、限流、超时)时重新执行完整推理
format formatMaxRetries 格式错误(JSON 解析失败、字段缺失、类型不匹配)时仅重组 JSON,不重跑业务

Hermes 专用降级(RetryPolicy.forHermes()):未显式配置时 runtimeMaxRetries 强制降为 1(Hermes 单次重跑成本极高)。

envelope 失败自动升级(WorkflowLevelExecutor.java:481-496):executor 返回 SUCCESS 但 envelope.status=400(如 Hermes Agent 把 LLM 429 错误描述当作成功结果返回)时,引擎自动升级为 NodeExecutionResult.failed,按 failStrategy 处理。

10.4 AGENTIC 模式的失败处理

AGENTIC 模式不走 failStrategy——节点重试用尽仍失败时,统一交 Planner 决策:

  • Planner prompt 会带上失败原因
  • Planner 可建议"切换到其他节点绕过"或"再次访问但说明理由"
  • 失败不再是工作流的终止信号,而是 Planner 的输入信号

与 LangGraph 的对比:

  • LangGraph 通过 error_handler_node 显式处理错误
  • 本仓库 AGENTIC 模式通过 Planner 智能处理错误
  • 两者都让"失败"不再是硬终止,但本仓库的 Planner 模式更灵活(LLM 可基于上下文判断如何绕过),LangGraph 的 error_handler 更确定(开发者预先指定处理逻辑)

11. 上下文传递

11.1 寻址方式对比

维度 LangGraph Dify agent-management
寻址模型 Schema 字段——节点读写 Schema 声明的字段,Channel 自动合并 variable_pool 直接寻址 + variable_prefixes 前缀解析 四级模糊匹配 fallback + 显式 mapping + envelope.data 穿透
工作区隔离 Channel 隔离(每字段独立 channel) 共享 variable_pool(无显式隔离) NodeWorkspace 仅含可达前驱(反向 BFS 计算,避免无关变量污染)
变量合并 Reducer 自动合并(如 operator.add) 后写覆盖 同名按 nodeId 隔离,不合并
全局变量 通过 Schema 共享字段 sys.dialogue_count / conversation_id / user_id / app_id / workflow_id / workflow_run_id / timestamp 初始 inputs 扁平注入 + 运行级 key

11.2 本仓库四级模糊匹配

解析顺序(backend/src/main/java/com/agent/management/engine/NodeInputResolver.java:84-162):

  1. 显式 mapping:data.inputs[i].mapping = {sourceNodeId, sourceField, sourcePath},支持 JSON Path 嵌套取值
  2. 初始输入:扁平形式直接命中
  3. 隐式关联 fallback(按拓扑逆序,最近前驱优先):
    • 3.1 精确同名
    • 3.2 忽略 [-_] 与大小写同名(穿透 envelope.data)
    • 3.3 JSON 嵌套路径精确同名
    • 3.4 JSON 嵌套路径模糊同名
  4. 归一化函数:normalizeForMatch(s) = s.replaceAll("[-_]","").toLowerCase()

与 LangGraph 的关键差异:

  • LangGraph 要求开发者显式声明 Schema 字段,无隐式匹配
  • 本仓库通过四级模糊匹配降低用户配置成本——LLM 生成的字段名轻微差异(如 user_name vs userName)可自动匹配
  • 但模糊匹配也带来歧义风险(多个前驱都有同名字段时按拓扑顺序选最近)

11.3 三者上下文传递哲学

项目 哲学 优点 缺点
LangGraph 显式 Schema + Reducer 合并 类型安全、并发安全、可推理 配置成本高,需要开发者熟悉 Schema 注解
Dify variable_pool 共享 + 显式 selector 配置直观、调试方便 后写覆盖、无隔离、变量污染风险
本仓库 nodeId 隔离 + 四级模糊匹配 用户配置成本低、容错性强 解析顺序依赖拓扑,模糊匹配可能产生歧义

12. 节点类型与扩展机制

12.1 节点类型对比

类别 LangGraph Dify agent-management
节点抽象 Python 可调用对象(无类型区分) NodeFactory 按 type+version 注册 NodeExecutor 接口实现
节点类型数 无限制(开发者自定义) 31 种(含辅助/占位)+ 插件扩展 硬编码 8 种(userInput / llm / skill / agent / smartAction / condition / knowledgeRetrieval / output)
扩展机制 写一个函数 + add_node 插件化(plugin_daemon) 实现新 NodeExecutor + 注册
预构建节点 create_react_agent / ToolNode / ValidationNode iteration / loop / if-else / question-classifier / parameter-extractor / template-transform / variable-aggregator / http-request / code / human-input / trigger-* 无(仅基础节点)

12.2 三者节点设计哲学

项目 哲学 优点 缺点
LangGraph 极简核心 + 用户自定义 灵活、无限制 用户必须自己实现复杂节点(如 RAG 检索)
Dify 丰富内置 + 插件扩展 业务用户即开即用、生态丰富 节点类型膨胀、维护成本高
本仓库 硬编码 + Hermes 深度集成 强一致 envelope、Hermes 集成度高 节点类型少、扩展需改 Java 代码

本仓库缺失的关键节点类型(Dify 有):

  • iteration / loop:迭代子图(不支持)
  • HTTP Request:独立 HTTP 调用节点(由 smartAction 近似)
  • Code:沙箱化代码执行(不支持)
  • template-transform:Jinja2 渲染(内嵌于各节点)
  • variable-aggregator:多分支变量合并(由 envelope 自动聚合)
  • question-classifier / parameter-extractor:LLM 驱动的结构化处理(不支持)

本仓库独有的节点设计:

  • skill:技能节点(与 Hermes Skill 系统深度集成)
  • smartAction:智能操作节点(LLM 自主决定动作)
  • condition 双模式:rule(本地评估)+ natural(LLM 判断)

13. 设计哲学对比

13.1 三种根本不同的设计取向

项目 核心问题 解决方案 取舍
LangGraph "如何让 LLM 应用像图算法一样可编程、可恢复、可观察?" Pregel BSP + Channel + Reducer + Checkpoint 通用性强,但学习曲线陡;不给业务用户开箱即用的能力
Dify "如何让业务用户无需写代码就能构建 LLM 应用?" 可视化画布 + 31 种节点 + 插件生态 + 多形态应用 易用性强,但部署复杂、扩展受节点类型限制
本仓库 "如何在企业内部构建可控、可观测、容错的 AI 工作流?" Java 单体 + 强一致 envelope + 双模式调度 + Hermes 深度集成 强一致性好,但节点类型少、无子图、无迭代

13.2 三种不同的"扩展点"

项目 扩展点 扩展方式
LangGraph 节点函数 + Channel + Reducer 开发者写 Python 函数即可
Dify 插件 + 节点类型 + Provider 通过 plugin_daemon 注册第三方插件
本仓库 NodeExecutor 实现 实现 Java 接口并注册到 Spring 容器

13.3 三种不同的"循环哲学"

项目 循环实现 优点 缺点
LangGraph 图结构原生支持 表达力最强、最直观 需要 recursion_limit 兜底,调试复杂
Dify 专用节点(iteration / loop) 业务用户可配置 顶层图无循环,跨节点循环不可能
本仓库 AGENTIC 模式 + Planner 决策 LLM 自主决定跳转,灵活 决策依赖 LLM 质量,有不确定性

13.4 三种不同的"持久化哲学"

项目 持久化目的 抽象层级
LangGraph 可恢复 + 可回溯 + 可分叉 完整 Checkpoint 抽象(时间旅行)
Dify 可恢复 + 多租户 DB 全量序列化
本仓库 可恢复 + 可观测 + HITL 持久化 Visit + Pause + Run 三表

14. 综合能力矩阵

14.1 DAG 工作流核心能力

能力 LangGraph Dify agent-management (DAG) agent-management (AGENTIC)
严格 DAG 调度 是 是 是 画布作参考
顶层图循环 是 否 否 是(通过 Planner)
同层并行 是(BSP) 是(线程池) 是(Dataflow) 否(单线程)
Map-Reduce fan-out 是(Send) 是(iteration) 否 否
条件分支 是(路径函数) 是(if-else 节点) 是(condition 节点) 是(Planner 决策)
节点重访 是 否 否 是
子图 是 是 否 否
Reducer 自动合并 是 否 否 否
时间旅行 是 否 否 否
HITL 节点级 是(before/after) 是(Human Input 节点) 否 否
HITL Agent 内部 是(interrupt) 是(ask_human) 是(AskFor clarify) 是(AskFor clarify)
断线重连 是(thread_id + checkpoint) 否 是(SseEventBus + Last-Event-ID) 是
统一 envelope 否(Schema 软约束) 否(仅 Agent v2) 是(全节点强制) 是
统一 failStrategy 否(节点级 error_handler) 否(散落各节点) 是(abort/skip) 否(交 Planner)
LLM 错误模式嗅探 否 否 是(Hermes Error Pattern) 是
长阻塞节点线程池隔离 否 否 是(clarifyWaitExecutor) 是

14.2 节点类型覆盖

节点类型 LangGraph Dify agent-management
大模型节点 用户自定义 llm(含 prompt_template / context / memory / vision / structured_output) llm(含 systemPrompt / userPrompt / inputs / outputs / failStrategy)
Agent 节点 create_react_agent 预构建 agent / agent-v2 agent(Hermes Bridge)
条件分支 路径函数 if-else condition(rule + natural 双模式)
迭代子图 不需要(图本身可循环) iteration / loop 不支持
子工作流 子图 child GraphEngine 不支持
HTTP 请求 用户自定义 http-request 由 smartAction 近似
代码执行 用户自定义 code(dify-sandbox) 不支持
模板转换 用户自定义 template-transform(Jinja2) 内嵌于各节点
变量聚合 Reducer 自动处理 variable-aggregator envelope 自动聚合
知识检索 用户自定义 knowledge-retrieval knowledgeRetrieval
问题分类 用户自定义 question-classifier 不支持
参数提取 用户自定义 parameter-extractor 不支持
人工介入 节点级 interrupt human-input(运行中暂停) AskFor(仅 Agent 内部)
触发器 不适用(库) trigger-schedule/webhook/plugin 仅同步 API
工具调用 ToolNode tool(plugin 化) smartAction / skill
技能节点 不适用 不适用 skill(独有)

15. 对本项目的演进建议

15.1 短期低成本高收益(建议优先)

# 建议 参考来源 价值
1 HITL 扩展到任意节点 LangGraph interrupt_before/after + Dify Human Input 节点 当前 AskFor 仅限 Hermes Agent 内部,扩展到 LLM / Condition 节点可大幅提升交互能力
2 HTTP Request 节点 Dify http-request 节点 当前 smartAction 过重(必须经 LLM),轻量 HTTP 调用是常见需求
3 节点级 error_handler LangGraph error_handler_node 让 failStrategy 更细粒度——失败时可路由到错误处理节点而非整体 abort
4 保留 AGENTIC 模式作为差异化优势 本仓库独有 LangGraph 和 Dify 都没有"全局 Planner 决策路由"能力,是本仓库的核心竞争力

15.2 中期中等成本

# 建议 参考来源 价值
5 迭代节点 Dify iteration(数组迭代子图) 处理批量数据场景(如多文档摘要、多候选生成)
6 子工作流嵌套 Dify child GraphEngine + LangGraph subgraph 工作流复用与模块化
7 Code 节点 Dify code(dify-sandbox) 让用户写少量代码做数据转换
8 问题分类器 / 参数提取器节点 Dify question-classifier / parameter-extractor 减少 LLM 节点的 prompt 工程负担

15.3 长期慎选(高成本)

# 建议 参考来源 风险
9 时间旅行(Checkpoint 历史) LangGraph get_state_history + fork 改动大、收益有限(企业内部场景少有"回到过去重新选择"需求)
10 Reducer 自动合并 LangGraph Channel + BinaryOperatorAggregate 与当前 nodeId 隔离设计冲突,需重新设计变量系统
11 Send map-reduce LangGraph Send 对象 当前条件节点互斥分支设计深入,改为 fan-out 影响面大

15.4 应坚持的本仓库特色(不盲目学 LangGraph / Dify)

# 特色 不要改的原因
1 AGENTIC 模式(全局 Planner 决策路由) 是本仓库核心差异化能力,LangGraph 和 Dify 都没有
2 统一 envelope 全节点强制 是强一致性的基础,LangGraph 和 Dify 都没做到
3 统一 failStrategy(abort/skip) 引擎层统一治理,比 Dify 散落各节点更可控
4 四级模糊匹配 fallback 用户配置成本低,LangGraph 显式 Schema 太重
5 长阻塞节点线程池隔离 AskFor 长挂机场景下避免 worker 耗尽,是工程级优化
6 Hermes Error Pattern 错误嗅探 中文 LLM 错误模式识别,是本地化优势
7 双线程池(nodeExecutor + clarifyWaitExecutor) 工程级隔离,比 LangGraph / Dify 的单一池更稳健
8 SseEventBus 双轨 + Last-Event-ID 断线重连 长挂机场景必备,Dify 没有原生支持
9 condition 节点 rule + natural 双模式 rule 模式无 LLM 调用成本,比 LangGraph 纯函数和 Dify 纯节点更灵活
10 skill 节点与 Hermes 深度集成 企业内部技能复用场景刚需
11 Hermes Bridge Python 子进程隔离 比直接在 Java 进程内调用 Python 更安全
12 三层 monkey-patch 沙箱 比 Dify Landlock LSM 更轻量(虽然隔离强度弱一些)

16. 关键文件索引

LangGraph(源码副本路径)

  • D:\projects\agent-management\langgraph\libs\langgraph\langgraph\graph\state.py —— StateGraph / CompiledStateGraph
  • D:\projects\agent-management\langgraph\libs\langgraph\langgraph\graph\_branch.py —— BranchSpec 条件边
  • D:\projects\agent-management\langgraph\libs\langgraph\langgraph\graph\_node.py —— StateNode 协议
  • D:\projects\agent-management\langgraph\libs\langgraph\langgraph\pregel\main.py —— Pregel 类
  • D:\projects\agent-management\langgraph\libs\langgraph\langgraph\pregel\_loop.py —— PregelLoop(tick / after_tick)
  • D:\projects\agent-management\langgraph\libs\langgraph\langgraph\pregel\_algo.py —— prepare_next_tasks / apply_writes / should_interrupt
  • D:\projects\agent-management\langgraph\libs\langgraph\langgraph\pregel\_runner.py —— PregelRunner(并发执行)
  • D:\projects\agent-management\langgraph\libs\langgraph\langgraph\channels\binop.py —— BinaryOperatorAggregate(Reducer)
  • D:\projects\agent-management\langgraph\libs\langgraph\langgraph\channels\named_barrier_value.py —— NamedBarrierValue(join 同步)
  • D:\projects\agent-management\langgraph\libs\langgraph\langgraph\types.py —— Send / Command / Interrupt / RetryPolicy
  • D:\projects\agent-management\langgraph\libs\checkpoint\langgraph\checkpoint\base\__init__.py —— BaseCheckpointSaver / Checkpoint
  • D:\projects\agent-management\langgraph\libs\prebuilt\langgraph\prebuilt\chat_agent_executor.py —— create_react_agent

Dify(详见 dify-reference.md)

  • dify/api/core/workflow/** —— 工作流引擎
  • dify/dify-agent/src/agenton/compositor/** —— 单 Agent 层组合
  • dify/dify-agent-runtime/** —— Agent 沙箱(Go Landlock)

本仓库

  • backend/src/main/java/com/agent/management/engine/DagResolver.java —— DAG 解析 + Kahn 拓扑排序
  • backend/src/main/java/com/agent/management/engine/WorkflowLevelExecutor.java —— DAG Dataflow 调度
  • backend/src/main/java/com/agent/management/engine/AgenticExecutor.java —— AGENTIC 自主决策(独有)
  • backend/src/main/java/com/agent/management/engine/PlannerService.java —— LLM Planner(独有)
  • backend/src/main/java/com/agent/management/engine/NodeOutputEnvelope.java —— 统一 envelope(独有)
  • backend/src/main/java/com/agent/management/engine/NodeInputResolver.java —— 四级模糊匹配(独有)
  • backend/src/main/java/com/agent/management/engine/RetryPolicy.java —— 双层重试(独有)
  • backend/src/main/java/com/agent/management/engine/executor/ConditionExecutor.java —— rule + natural 双模式(独有)
  • backend/src/main/java/com/agent/management/engine/hermes/ClarifyHooks.java —— AskFor 落库(独有)
  • backend/src/main/java/com/agent/management/engine/hermes/HermesBridgeClient.java —— Hermes 通信
  • backend/src/main/java/com/agent/management/engine/SseEventBus.java —— 双轨 SSE 断线重连(独有)

17. 总结

LangGraph、Dify 和本仓库(agent-management)代表了三种根本不同的 DAG 工作流设计取向:

  • LangGraph 是"通用图计算框架"——以 Pregel BSP 为核心,把图算法的成熟模型(channel、reducer、superstep、checkpoint)引入 LLM 应用开发。它的优势在于表达力(任意图结构、循环、map-reduce、子图、时间旅行),劣势在于学习曲线陡峭、对业务用户不友好。

  • Dify 是"LLMOSS 平台"——以可视化画布为核心,通过 31 种节点 + 插件生态覆盖多种应用形态。它的优势在于易用性和生态丰富度,劣势在于顶层图严格 DAG(循环只能通过专用节点实现)、无统一失败策略、无统一 envelope。

  • 本仓库是"企业内部 AI 编码助手平台"——以 Java 单体 + 强一致 envelope + Hermes 深度集成为核心。它的优势在于引擎层统一治理(统一 envelope、统一 failStrategy、四级模糊匹配、双线程池隔离、Hermes Error Pattern 嗅探)和独有的 AGENTIC 模式(全局 Planner 决策路由),劣势在于节点类型少(8 种)、无迭代 / 子图 / Code / HTTP 等高级节点、无时间旅行能力。

关键结论:本仓库不应盲目模仿 LangGraph 的图原语(Reducer、Send、子图)或 Dify 的节点丰富度,而应:

  1. 保持 AGENTIC 模式作为核心竞争力——这是 LangGraph 和 Dify 都没有的差异化能力
  2. 坚持统一 envelope 和强一致性设计——这是企业内部场景的刚需
  3. 补齐关键缺失节点(HTTP、Code、iteration、子工作流)作为优先演进方向
  4. HITL 能力扩展到任意节点——借鉴 LangGraph 的 interrupt_before/after 设计

三者并非竞争关系,而是互补参考:LangGraph 提供图原语的天花板,Dify 提供业务节点丰富度的标杆,本仓库在企业内部场景下找到了自己的"强一致性 + 智能调度"独特定位。