惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

M
MIT News - Artificial intelligence
罗磊的独立博客
Hugging Face - Blog
Hugging Face - Blog
Apple Machine Learning Research
Apple Machine Learning Research
Last Week in AI
Last Week in AI
S
SegmentFault 最新的问题
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
美团技术团队
人人都是产品经理
人人都是产品经理
WordPress大学
WordPress大学
The Cloudflare Blog
IT之家
IT之家
雷峰网
雷峰网
小众软件
小众软件
博客园 - 叶小钗
博客园 - 聂微东
爱范儿
爱范儿
博客园 - 司徒正美
博客园 - 三生石上(FineUI控件)
V
Visual Studio Blog
博客园 - 【当耐特】
V
V2EX
博客园_首页
T
Tailwind CSS Blog

博客园 - 菩提树下的杨过

langchain4j 学习系列(10)-Skill使用示例 利用SWIG实现JAVA调用C/C++代码 LangGraph4j 学习系列(9)-人机协同(human_in_the_loop) LangGraph4j 学习系列(8)-checkpoint检查点 LangGraph4j 学习系列(7)-流式响应 LangGraph4j 学习系列(6)-并行工作流 LangGraph4j 学习系列(4)-SCHEMA和Channel LangGraph4j 学习系列(3)-循环工作流 LangGraph4j 学习系列(2)-条件工作流 LangGraph4j 学习系列(1)-顺序工作流 Agent设计模式学习(基于langchain4j实现)(11) - PlanAndExecute Agent设计模式学习(基于langchain4j实现)(10) - ReACT Agent设计模式学习(基于langchain4j实现)(9) - 人机协同 Agent设计模式学习(基于langchain4j实现)(8) - 非AI智能体 Agent设计模式学习(基于langchain4j实现)(7) - 监督者模式 Agent设计模式学习(基于langchain4j实现)(6) - 组合复杂工作流 Agent设计模式学习(基于langchain4j实现)(5) - 条件工作流 Agent设计模式学习(基于langchain4j实现)(4) - 并行工作流 Agent设计模式学习(基于langchain4j实现)(3) - 循环工作流 Agent设计模式学习(基于langchain4j实现)(2) - 顺序工作流
LangGraph4j 学习系列(5)-Hook勾子
菩提树下的杨过 · 2026-03-01 · via 博客园 - 菩提树下的杨过
public class Node1Action implements NodeAction<AgentState> {
    @Override
    public Map<String, Object> apply(AgentState state) throws Exception {
        System.out.println("current Node: node-1");
        //模拟节点耗时
        Thread.sleep(1000);
        return Map.of("myData", "node1-my-value",
                "node1Key", "node1-value");
    }
}
public class Node2Action implements NodeAction<AgentState> {
    @Override
    public Map<String, Object> apply(AgentState state) throws Exception {
        System.out.println("current Node: node-2");
        //模拟节点耗时
        Thread.sleep(2000);
        return Map.of("myData", "node2-my-value",
                "node2Key", "node2-value");
    }
}
public class HookSampleApplication {

    public static void main(String[] args) throws GraphStateException {
        runSequenceGraphWithOnlyStaticEdges();
        out.println("\n========== 下面使用带条件边的图,Edge Hook 会执行 ==========");
        runGraphWithConditionalEdge();
    }

    /**
     * 纯静态边:只有 Node Hook 会执行,Edge Hook 不会执行
     */
    private static void runSequenceGraphWithOnlyStaticEdges() throws GraphStateException {
        StateGraph<AgentState> sequenceGraph = getSequenceGraph();

        sequenceGraph.addBeforeCallNodeHook((String node, AgentState data, RunnableConfig config) -> {
            out.println("Before calling node: " + node + ", data: " + data.data());
            return CompletableFuture.completedFuture(data.data());
        });

        sequenceGraph.addAfterCallNodeHook((String node, AgentState data, RunnableConfig config, Map<String, Object> lastResult) -> {
            out.println("After calling node: " + node + ", data: " + data.data() + ", lastResult: " + lastResult);
            return CompletableFuture.completedFuture(lastResult);
        });

        sequenceGraph.addWrapCallNodeHook((String node, AgentState data, RunnableConfig config, AsyncNodeActionWithConfig<AgentState> action) -> {
            out.println("Wrap calling node: " + node + ", data: " + data.data());
            long start = System.currentTimeMillis();
            return action.apply(data, config).whenComplete((result, error) -> {
                var ms = System.currentTimeMillis() - start;
                out.println(String.format("node '%s' took %d ms", node, ms));
            });
        });

        sequenceGraph.addBeforeCallEdgeHook((String sourceId, AgentState state, RunnableConfig config) -> {
            out.println("Before calling edge: " + sourceId);
            return CompletableFuture.completedFuture(new Command(state.data()));
        });

        sequenceGraph.addAfterCallEdgeHook((String sourceId, AgentState state, RunnableConfig config, Command lastResult) -> {
            out.println("After calling edge: " + sourceId);
            return CompletableFuture.completedFuture(lastResult);
        });

        sequenceGraph.addWrapCallEdgeHook((String sourceId, AgentState state, RunnableConfig config, AsyncCommandAction<AgentState> action) -> {
            out.println("Wrap calling edge: " + sourceId);
            long start = System.currentTimeMillis();
            return action.apply(state, config).whenComplete((result, error) -> {
                var ms = System.currentTimeMillis() - start;
                out.println(String.format("source-node '%s' took %d ms", sourceId, ms));
            });

        });

        out.println(sequenceGraph.getGraph(GraphRepresentation.Type.MERMAID, "NodeHook Graph", true).content());

        sequenceGraph.compile().invoke(Map.of("test", "test-init-value")).ifPresent(c -> {
            System.out.println(c.data());
        });
    }

    /**
     * 带条件边:从 node-1 经条件边到 node-2,会触发 Edge Hook
     */
    private static void runGraphWithConditionalEdge() throws GraphStateException {
        StateGraph<AgentState> graph = getGraphWithConditionalEdge();

        graph.addBeforeCallNodeHook((String node, AgentState data, RunnableConfig config) -> {
            out.println("Before calling node: " + node + ", data: " + data.data());
            return CompletableFuture.completedFuture(data.data());
        });
        graph.addAfterCallNodeHook((String node, AgentState data, RunnableConfig config, Map<String, Object> lastResult) -> {
            out.println("After calling node: " + node + ", data: " + data.data() + ", lastResult: " + lastResult);
            return CompletableFuture.completedFuture(lastResult);
        });
        graph.addWrapCallNodeHook((String node, AgentState data, RunnableConfig config, AsyncNodeActionWithConfig<AgentState> action) -> {
            out.println("Wrap calling node: " + node + ", data: " + data.data());
            long start = System.currentTimeMillis();
            return action.apply(data, config).whenComplete((result, error) -> {
                var ms = System.currentTimeMillis() - start;
                out.println(String.format("node '%s' took %d ms", node, ms));
            });
        });

        graph.addBeforeCallEdgeHook((String sourceId, AgentState state, RunnableConfig config) -> {
            out.println("Before calling edge: " + sourceId);
            return CompletableFuture.completedFuture(new Command(state.data()));
        });
        graph.addAfterCallEdgeHook((String sourceId, AgentState state, RunnableConfig config, Command lastResult) -> {
            out.println("After calling edge: " + sourceId);
            return CompletableFuture.completedFuture(lastResult);
        });
        graph.addWrapCallEdgeHook((String sourceId, AgentState state, RunnableConfig config, AsyncCommandAction<AgentState> action) -> {
            out.println("Wrap calling edge: " + sourceId);
            long start = System.currentTimeMillis();
            return action.apply(state, config).whenComplete((result, error) -> {
                var ms = System.currentTimeMillis() - start;
                out.println(String.format("source-node '%s' took %d ms", sourceId, ms));
            });
        });

        out.println(graph.getGraph(GraphRepresentation.Type.MERMAID, "NodeHook And EdgeHook Graph", true).content());

        graph.compile().invoke(Map.of("test", "test-init-value")).ifPresent(c -> System.out.println(c.data()));
    }

    public static StateGraph<AgentState> getSequenceGraph() throws GraphStateException {
        return new StateGraph<>(AgentState::new)
                .addNode("node-1", node_async(new Node1Action()))
                .addNode("node-2", node_async(new Node2Action()))
                .addEdge(GraphDefinition.START, "node-1")
                .addEdge("node-1", "node-2")
                .addEdge("node-2", GraphDefinition.END);
    }

    /**
     * 含一条条件边:node-1 通过条件边到 node-2,用于演示 Edge Hook 触发
     */
    public static StateGraph<AgentState> getGraphWithConditionalEdge() throws GraphStateException {
        return new StateGraph<>(AgentState::new)
                .addNode("node-1", node_async(new Node1Action()))
                .addNode("node-2", node_async(new Node2Action()))
                .addEdge(GraphDefinition.START, "node-1")
                .addConditionalEdges("node-1", state -> CompletableFuture.completedFuture("toNode2"), Map.of("toNode2", "node-2"))
                .addEdge("node-2", GraphDefinition.END);
    }
}

运行结果

Before calling node: node-1, data: {test=test-init-value}
Wrap calling node: node-1, data: {test=test-init-value}
current Node: node-1
node 'node-1' took 1002 ms
After calling node: node-1, data: {test=test-init-value}, lastResult: {myData=node1-my-value, node1Key=node1-value}
Before calling node: node-2, data: {node1Key=node1-value, test=test-init-value, myData=node1-my-value}
Wrap calling node: node-2, data: {node1Key=node1-value, test=test-init-value, myData=node1-my-value}
current Node: node-2
node 'node-2' took 1999 ms
After calling node: node-2, data: {node1Key=node1-value, test=test-init-value, myData=node1-my-value}, lastResult: {myData=node2-my-value, node2Key=node2-value}
{node1Key=node1-value, test=test-init-value, node2Key=node2-value, myData=node2-my-value}

========== 下面使用带条件边的图,Edge Hook 会执行 ==========

Before calling node: node-1, data: {test=test-init-value}
Wrap calling node: node-1, data: {test=test-init-value}
current Node: node-1
node 'node-1' took 1001 ms
After calling node: node-1, data: {test=test-init-value}, lastResult: {myData=node1-my-value, node1Key=node1-value}
Before calling edge: node-1
Wrap calling edge: node-1
source-node 'node-1' took 1 ms
After calling edge: node-1
Before calling node: node-2, data: {node1Key=node1-value, test=test-init-value, myData=node1-my-value}
Wrap calling node: node-2, data: {node1Key=node1-value, test=test-init-value, myData=node1-my-value}
current Node: node-2
node 'node-2' took 2000 ms
After calling node: node-2, data: {node1Key=node1-value, test=test-init-value, myData=node1-my-value}, lastResult: {myData=node2-my-value, node2Key=node2-value}
{node1Key=node1-value, test=test-init-value, node2Key=node2-value, myData=node2-my-value}