# 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`,归零即调度(`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 等待完成
```
**关键位置**:
- `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:
```python
# 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
```
**典型用法**:
```python
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 的条件边(动态路由核心)
```python
# 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`):
```python
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`):
```python
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`):
```python
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`
```python
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),但只能查看不能"回到 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`):
```python
@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 提供业务节点丰富度的标杆,本仓库在企业内部场景下找到了自己的"强一致性 + 智能调度"独特定位。