有向图的状态转换

LangGraph4j : 底层本质上是一个 类型安全的状态机 + 有向图执行引擎

有向图:

  • State : 状态 : 整个图运行时候的上下文,,所有节点共享同一个状态对象,节点之间不传参数,,而是只修改State
  • Node : 节点 :每个节点的执行逻辑,,是个Function<S,S> ,就是传入一个 State,执行完成之后返回一个新的State
  • Edge : 边 ,,分为,有条件的边,,无条件边,,有条件边(根据状态决定要去哪里,通过这个状态去判断接下来要干什么)
  • Graph : 图 : 把所有的节点连接起来,,是一个执行器

LangGraph4j,是状态驱动执行,执行流程由状态决定,,,也叫显示状态驱动模型

为什么要用State: 因为可以将State状态序列化到数据库中,,如果系统崩了,可以重新加载

ai的agent,,天然就是一个有向图,就是 ai思考 ---》 决策 ---》 调用工具 ---》 调用工具是否成 ---》 再思考,, 就是一个天然的 有向图循环

LangGraph4j极简版实现:

业务计算(Node节点的执行逻辑)流程控制决策(Edge的决策逻辑)分离开来,,,Node负责计算,,Edge负责决策,, 这样职责清晰,,满足单一职责原则SRP,,,

llm,,传入action,,根据router的判断逻辑,去判断要 调工具,,还是直接回复,,

Action有很多种状态,,比如结束状态,工具调用状态,默认执行状态,,抛出异常的状态,,重试的状态,根据这些状态,,去执行不同的逻辑,

java 复制代码
package com.cj.graph02;


public enum Action {


    CALL_TOOL, // 工具调用
    CONTINUE, // 下一个
    END, // 结束
    RETRY, // 重试
    ERROR;// 错误
}
java 复制代码
package com.cj.graph02;

public class AgentState {


    public String input;

    public String result;

    // 当前节点输出的动作信号,,, 后面根据这个action去判断要走哪里
    public Action action;

    // 模拟对话轮数
    public int step=0;


    // 新增错误信息
    public Exception error;


    // 重试次数
    public int retryCount = 0;

    // 最大重试次数
    public int maxRetry = 3;


    @Override
    public String toString() {
        return "AgentState{" +
                "input='" + input + '\'' +
                ", result='" + result + '\'' +
                ", action=" + action +
                ", step=" + step +
                ", error=" + error +
                ", retryCount=" + retryCount +
                ", maxRetry=" + maxRetry +
                '}';
    }
}
java 复制代码
public interface Node <S>{

    /**
     *  修改state,, 设置action
     * @param state
     * @return
     */
    S execute(S state);
}
java 复制代码
package com.cj.graph02;

import java.util.HashMap;
import java.util.Map;
import java.util.function.Function;

public class MiniLangGraph <S extends AgentState>{

    // 所有的节点
    private final Map<String,Node<S>> nodes = new HashMap<>();

    // 条件路由
    private final Map<String, Function<S,String>> routers = new HashMap<>();

    //入口
    private String entryPoint;


    public void addNode(String name,Node<S> node){
        nodes.put(name,node);
    }


    public void addRouter(String nodeName,Function<S,String> router){
        routers.put(nodeName,router);
    }


    public void setEntryPoint(String name){
        this.entryPoint = name;
    }



    public S run(S state){
        String current = entryPoint;

        while (current != null){
            System.out.println("当前节点:"+current);
            Node<S> node = nodes.get(current);

            try {
                // 只计算
                state  =node.execute(state);
            } catch (Exception e) {
                System.out.println("节点异常:"+e.getMessage());


                //
                state.error = e;
                state.action = Action.ERROR;


            }


            Function<S, String> router = routers.get(current);

            current = router.apply(state);
        }

        return state;
    }




}

测试:

java 复制代码
package com.cj.graph02;

public class Main {


    public static void main(String[] args) {


        MiniLangGraph<AgentState> graph = new MiniLangGraph<>();

        graph.addNode("llm",(state)->{
            System.out.println("llm thinking...");



            if (state.error != null && state.retryCount >=state.maxRetry){
                state.result = "工具多次调用失败,无法完成请求";
                state.action = Action.END;
                return state;
            }

            if (state.input.contains("天气")){
                state.action = Action.CALL_TOOL;
            }else{
                state.result = "普通回答";
                state.action = Action.END;
            }


            return state;

        });


        graph.addRouter("llm",state -> {
                switch (state.action){
                    case CALL_TOOL:
                        return "tool";
                    case END:
                        return null;
                    default:
                        throw new RuntimeException("未知action");
                }
        });



        graph.addRouter("tool",state->{

            // 把重试交给router去做,,
            if (state.action == Action.ERROR){

                if (state.retryCount < state.maxRetry){
                    state.retryCount++;
                    System.out.println("重试次数:"+state.retryCount);


                    state.action = Action.RETRY;

                    return "tool";
                }else{
                    System.out.println("超过最大重试次数");

                    state.action = Action.END;

                    return "llm"; // 交给llm 判断多次失败,,走哪里
                }

            }

            return null;

        });


        graph.addNode("tool",(state)->{

            // todo 执行失败
            state.action = Action.ERROR;
            state.error = new Exception("test error");
            System.out.printf("执行了tools ,,retryCount为" + state.retryCount);

            return state;
        });

        graph.setEntryPoint("llm");
        AgentState state = new AgentState();
        state.input="帮我查天气";
        AgentState finalState = graph.run(state);

        System.out.println("finalState = " + finalState);


    }
}

状态模式:state pattern : 允许一个对象在其内部状态改变的时候,,改变他的行为,,对象看起来似乎修改了他的类,,

比如说,,两个状态来回切换,,对应了两种不同的行为

java 复制代码
public interface State {
    String init();

    String reply(String input);
}
java 复制代码
public class ConnectedState implements State{


    @Override
    public String init(){
        return "hello ,i am cc";
    }


    @Override
    public String reply(String input){
        if (input.endsWith("?")){
            return "yes";
        }
        return "hehe";
    }
}
java 复制代码
public class DisconnectedState implements State{

    @Override
    public String init(){
        return "bye";
    }


    @Override
    public String reply(String input){
        return "";
    }
}
java 复制代码
public class BotContext {

    private State state;

    public String chat(String input){
        if ("hello".equals(input)){
            state = new ConnectedState();
            return state.init();
        }else if("bye".equals(input)){
            state = new DisconnectedState();
            return state.init();
        }

        return state.reply(input);
    }
}

测试:

java 复制代码
public class Main {


    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        BotContext bot = new BotContext();

        while (true){
            System.out.print("> ");
            String input = scanner.nextLine();
            String output = bot.chat(input);
            System.out.println(output.isEmpty()?"(no reply)":"< "+output);
        }
    }
}

这里面用到的设计模式:

  • 状态模式 State pattern : 改变这个状态,,他会执行不同的东西,
  • 策略模式 Strategy pattern : Node下面有很多种执行策略
  • 命令模式 command pattern : Node也可以理解成一个可执行对象,,Node就是一个带上下文的Command
  • 责任链模式 chain of responsibility : 根据状态不同,,不断的往下面传递
  • 模版方法 template method : 在执行的时候调用Function<S,S>,这里是模板方法
  • ioc控制反转 inversion of control,,, 原本应该是Node去控制下一个Node节点是谁,,现在把控制流程抽离了出来,,Node不知道谁会调用它,,Executor去控制流程
相关推荐
IMdive2 小时前
OpenHarmony鸿蒙远程数据库连接应用开发指南
数据库·华为·harmonyos
筵陌2 小时前
MySQL事务管理(上)
数据库·mysql
数据知道2 小时前
PostgreSQL:详解 orafce 拓展插件的使用
数据库·postgresql
xj198603192 小时前
MySql-9.1.0安装详细教程(保姆级)
数据库·mysql
·云扬·2 小时前
【MySQL】主从复制:原理、作用与实现方法
数据库·mysql
数据知道2 小时前
PostgreSQL:详解 MySQL数据迁移,如何将数据平滑迁移到PostgreSQL
数据库·mysql·postgresql
番茄去哪了2 小时前
在Java中操作Redis
java·开发语言·数据库·redis
JiaHao汤2 小时前
一文掌握 SQL:数据定义、操作与查询完整教程
数据库·sql
白太岁2 小时前
Redis:(3) Lua 与 Redis、基于连接池的 Facade 模式封装
数据库·c++·redis·lua·外观模式