对比目的:以 LangGraph
0.x(仓库langgraph/libs/源码副本)、Dify1.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
| 维度 | 量化对比 |
|---|---|
| 项目定位 | 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 + 引擎层统一治理 |
| 维度 | 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 | 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) |
| 维度 | LangGraph | Dify | agent-management |
|---|---|---|---|
| 严格拓扑 | 否(节点可跳回前序) | 是 | DAG 模式:是;AGENTIC 模式:否 |
| 重访节点 | 允许(受 recursion_limit 限制) |
不允许(顶层图) | DAG 模式:不允许;AGENTIC 模式:允许(MAX_VISITS=50 兜底) |
| 静态 vs 动态 | 运行时动态:节点可通过 Command(goto=...) 跳转任意节点 |
静态(编译期固定) + Agent 内部 ReAct 动态 | DAG 模式:静态;AGENTIC 模式:运行时 Planner 动态决策 |
| 维度 | 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(订阅) |
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)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 的关键差异:
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=...) 提升为引擎层全局决策。
| 维度 | 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 都没有这种"按节点阻塞性能分级路由"的设计。
| 维度 | 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 隔离——不同节点同名输出共存,前驱读取时按拓扑逆序匹配 |
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 隔离设计避免了覆盖问题,但下游必须显式指定从前驱哪个节点取值,无自动合并能力。
| 维度 | 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 节点强制。这让本仓库的下游节点输入解析有统一预期。
| 边类型 | 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 决策 |
# 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)
关键能力:
Send 列表 → 每个 Send 携带独立 state,并发执行后通过 reducer 合并回主 state双模式分支(backend/src/main/java/com/agent/management/engine/executor/ConditionExecutor.java:18-31):
ConditionRuleEvaluator 评估,无 LLM 调用,支持 22 种操作符(= / != / contains / startsWith / matches 正则 / in / isEmpty / lengthGt 等)评估顺序(ConditionExecutor.java:53-97):按 conditions 数组顺序逐个评估,第一个命中的分支胜出;全未命中走最后一个隐含 ELSE 分支。
分支标识:branch-{index},与前端 ConditionNode.vue:32 严格对齐。
与 LangGraph 的差异:
branch-X sourceHandle 由引擎路由| 维度 | 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 总访问数 + 同节点访问计数(仅展示用,不强制) |
关键差异:
graph.add_edge("B", "A") 即可| 维度 | 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。
| 维度 | 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 都具备的能力,本仓库目前不支持嵌套工作流调用。
| 维度 | 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 工具调用 |
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=...) 恢复时:
checkpoint_pending_writes 中保存了中断前的写入_reapply_writes_to_succeeded_nodes(_loop.py:736)恢复已完成节点的写入(不重复执行)interrupt() 会跳过(基于 interrupt id 匹配,返回 resume 值)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.javabackend/src/main/java/com/agent/management/engine/hermes/HermesBridgeClient.java容错设计:
ClarifyHooks.java:80-83)clarify_id 唯一索引去重(防 TOCTOU,第 76-79 行)clarifyWaitExecutor(避免耗尽 worker)| 项目 | HITL 触发位置 | 设计哲学 |
|---|---|---|
| LangGraph | 任意节点(声明式) + 节点内(命令式) | 给开发者完全控制权——可在任意节点暂停,可携带任意中断值 |
| Dify | 专用 Human Input 节点 + Agent 内部 ask_human | 给业务用户可视化配置——表单字段、超时、用户操作按钮 |
| 本仓库 | 仅 Hermes Agent 内部 clarify 工具 | 与 Hermes Agent 深度耦合——LLM 自主决定何时需要询问 |
本仓库 AskFor 是最受限的——只能在 Hermes Agent 节点内触发,普通 LLM / Condition / Knowledge 节点无法发起 HITL。但优点是与 Agent 推理循环深度集成,LLM 可基于上下文智能决定是否需要询问。
| 维度 | 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 小时) |
双轨 SSE 设计(backend/src/main/java/com/agent/management/engine/SseEventBus.java):
emitter 断线时事件仍缓存到总线GET /stream 重新订阅EXECUTION_TIMEOUT_MINUTES = 1440L),匹配 AskFor 长挂机场景事件总线 + emitter 同步发布(WorkflowEngine.java:247-257):保证缓存与推送一致。
LangGraph 把 stream 模式作为观察图执行的不同视角:
values:每个 superstep 后的完整 state 快照updates:每个 task 的增量写入(默认)messages:LLM token 级流(专门优化)custom:开发者自定义事件debug:task 调度 + checkpoint 保存事件这种"多视角并发观察"是 LangGraph 的独特设计,让调试、监控、用户展示可以共用同一套事件流。
| 维度 | 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 |
核心位置: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 未完成的写入
关键设计:
parent_config 形成链表,支持时间旅行_reapply_writes_to_succeeded_nodes)| 项目 | 持久化目的 | 设计哲学 |
|---|---|---|
| LangGraph | 可恢复 + 可回溯——每个 superstep 都可重放、可分叉 | 给开发者完全控制权——时间旅行是调试和分支实验的核心能力 |
| Dify | 可恢复 + 多租户隔离 | 给平台运营方管理多租户工作流状态 |
| 本仓库 | 可恢复 + 可观测(Visit 表) + HITL 持久化(Pause 表) | 给企业内部场景足够的可追溯性,但不允许"回到过去重新选择" |
本仓库缺失:时间旅行和 fork 分支。本仓库的 Visit 表记录每次节点访问快照(含 iterSeq / fromVisitUuid / variablesJson / outputJson / decisionJson),但只能查看不能"回到 visit #3 重新执行"。
| 维度 | 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 嗅探) |
节点级 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)。
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 处理。
AGENTIC 模式不走 failStrategy——节点重试用尽仍失败时,统一交 Planner 决策:
与 LangGraph 的对比:
error_handler_node 显式处理错误| 维度 | 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 |
解析顺序(backend/src/main/java/com/agent/management/engine/NodeInputResolver.java:84-162):
data.inputs[i].mapping = {sourceNodeId, sourceField, sourcePath},支持 JSON Path 嵌套取值[-_] 与大小写同名(穿透 envelope.data)normalizeForMatch(s) = s.replaceAll("[-_]","").toLowerCase()与 LangGraph 的关键差异:
user_name vs userName)可自动匹配| 项目 | 哲学 | 优点 | 缺点 |
|---|---|---|---|
| LangGraph | 显式 Schema + Reducer 合并 | 类型安全、并发安全、可推理 | 配置成本高,需要开发者熟悉 Schema 注解 |
| Dify | variable_pool 共享 + 显式 selector | 配置直观、调试方便 | 后写覆盖、无隔离、变量污染风险 |
| 本仓库 | nodeId 隔离 + 四级模糊匹配 | 用户配置成本低、容错性强 | 解析顺序依赖拓扑,模糊匹配可能产生歧义 |
| 类别 | 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-* | 无(仅基础节点) |
| 项目 | 哲学 | 优点 | 缺点 |
|---|---|---|---|
| LangGraph | 极简核心 + 用户自定义 | 灵活、无限制 | 用户必须自己实现复杂节点(如 RAG 检索) |
| Dify | 丰富内置 + 插件扩展 | 业务用户即开即用、生态丰富 | 节点类型膨胀、维护成本高 |
| 本仓库 | 硬编码 + Hermes 深度集成 | 强一致 envelope、Hermes 集成度高 | 节点类型少、扩展需改 Java 代码 |
本仓库缺失的关键节点类型(Dify 有):
本仓库独有的节点设计:
| 项目 | 核心问题 | 解决方案 | 取舍 |
|---|---|---|---|
| LangGraph | "如何让 LLM 应用像图算法一样可编程、可恢复、可观察?" | Pregel BSP + Channel + Reducer + Checkpoint | 通用性强,但学习曲线陡;不给业务用户开箱即用的能力 |
| Dify | "如何让业务用户无需写代码就能构建 LLM 应用?" | 可视化画布 + 31 种节点 + 插件生态 + 多形态应用 | 易用性强,但部署复杂、扩展受节点类型限制 |
| 本仓库 | "如何在企业内部构建可控、可观测、容错的 AI 工作流?" | Java 单体 + 强一致 envelope + 双模式调度 + Hermes 深度集成 | 强一致性好,但节点类型少、无子图、无迭代 |
| 项目 | 扩展点 | 扩展方式 |
|---|---|---|
| LangGraph | 节点函数 + Channel + Reducer | 开发者写 Python 函数即可 |
| Dify | 插件 + 节点类型 + Provider | 通过 plugin_daemon 注册第三方插件 |
| 本仓库 | NodeExecutor 实现 | 实现 Java 接口并注册到 Spring 容器 |
| 项目 | 循环实现 | 优点 | 缺点 |
|---|---|---|---|
| LangGraph | 图结构原生支持 | 表达力最强、最直观 | 需要 recursion_limit 兜底,调试复杂 |
| Dify | 专用节点(iteration / loop) | 业务用户可配置 | 顶层图无循环,跨节点循环不可能 |
| 本仓库 | AGENTIC 模式 + Planner 决策 | LLM 自主决定跳转,灵活 | 决策依赖 LLM 质量,有不确定性 |
| 项目 | 持久化目的 | 抽象层级 |
|---|---|---|
| LangGraph | 可恢复 + 可回溯 + 可分叉 | 完整 Checkpoint 抽象(时间旅行) |
| Dify | 可恢复 + 多租户 | DB 全量序列化 |
| 本仓库 | 可恢复 + 可观测 + HITL 持久化 | Visit + Pause + Run 三表 |
| 能力 | 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) | 是 |
| 节点类型 | 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(独有) |
| # | 建议 | 参考来源 | 价值 |
|---|---|---|---|
| 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 决策路由"能力,是本仓库的核心竞争力 |
| # | 建议 | 参考来源 | 价值 |
|---|---|---|---|
| 5 | 迭代节点 | Dify iteration(数组迭代子图) | 处理批量数据场景(如多文档摘要、多候选生成) |
| 6 | 子工作流嵌套 | Dify child GraphEngine + LangGraph subgraph | 工作流复用与模块化 |
| 7 | Code 节点 | Dify code(dify-sandbox) | 让用户写少量代码做数据转换 |
| 8 | 问题分类器 / 参数提取器节点 | Dify question-classifier / parameter-extractor | 减少 LLM 节点的 prompt 工程负担 |
| # | 建议 | 参考来源 | 风险 |
|---|---|---|---|
| 9 | 时间旅行(Checkpoint 历史) | LangGraph get_state_history + fork |
改动大、收益有限(企业内部场景少有"回到过去重新选择"需求) |
| 10 | Reducer 自动合并 | LangGraph Channel + BinaryOperatorAggregate | 与当前 nodeId 隔离设计冲突,需重新设计变量系统 |
| 11 | Send map-reduce | LangGraph Send 对象 | 当前条件节点互斥分支设计深入,改为 fan-out 影响面大 |
| # | 特色 | 不要改的原因 |
|---|---|---|
| 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 更轻量(虽然隔离强度弱一些) |
D:\projects\agent-management\langgraph\libs\langgraph\langgraph\graph\state.py —— StateGraph / CompiledStateGraphD:\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_interruptD:\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 / RetryPolicyD:\projects\agent-management\langgraph\libs\checkpoint\langgraph\checkpoint\base\__init__.py —— BaseCheckpointSaver / CheckpointD:\projects\agent-management\langgraph\libs\prebuilt\langgraph\prebuilt\chat_agent_executor.py —— create_react_agentdify-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 断线重连(独有)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 的节点丰富度,而应:
interrupt_before/after 设计三者并非竞争关系,而是互补参考:LangGraph 提供图原语的天花板,Dify 提供业务节点丰富度的标杆,本仓库在企业内部场景下找到了自己的"强一致性 + 智能调度"独特定位。