在AI驱动的应用开发中,工作流引擎是串联复杂业务逻辑的核心。LangGraph4j作为Java生态中的轻量级工作流框架,能够帮助开发者高效构建可编排、可扩展的AI任务流程。本文将通过一个完整的项目改造案例,深入讲解如何使用LangGraph4j实现从Demo到生产级工作流的跃迁。

快速上手:LangGraph4j工作流Demo实践

在开始项目改造之前,我们先通过一个简单的Demo熟悉LangGraph4j的核心概念。首先,需要在项目中引入相关依赖:



  org.bsc.langgraph4j
  langgraph4j-core
  1.6.0-rc2

参考官方文档,我们可以快速搭建一个最简工作流。下图展示了工作流的基本结构:

工作流构建通常包含三个关键步骤:

  • 定义状态:状态是工作流中数据流转的载体,通常使用一个类或Map来表示。例如,我们可以定义一个包含用户输入、中间结果和最终输出的状态类。
  • 定义请求节点和响应节点:节点是工作流中的执行单元,每个节点负责处理特定的业务逻辑,并从状态中读取或写入数据。
  • 定义工作流结构:通过连接节点,形成有向无环图(DAG),LangGraph4j会自动管理节点的执行顺序和状态传递。

以下是状态定义的核心代码:

// Define the state for our graph
class SimpleState extends AgentState {
    public static final String MESSAGES_KEY = "messages";
    // Define the schema for the state.
    // MESSAGES_KEY will hold a list of strings, and new messages will be appended.
    public static final Map> SCHEMA = Map.of(
        MESSAGES_KEY, Channels.appender(ArrayList::new)
    );
    public SimpleState(Map initData) {
        super(initData);
    }
    public List messages() {
        return this.>value("messages")
        .orElse( List.of() );
    }
}

请求节点和响应节点的实现如下:

// Node that adds a greeting
class GreeterNode implements NodeAction {
    @Override
    public Map apply(SimpleState state) {
        System.out.println("GreeterNode executing. Current messages: " + state.messages());
        return Map.of(SimpleState.MESSAGES_KEY, "Hello from GreeterNode!");
    }
}
// Node that adds a response
class ResponderNode implements NodeAction {
    @Override
    public Map apply(SimpleState state) {
        System.out.println("ResponderNode executing. Current messages: " + state.messages());
        List currentMessages = state.messages();
        if (currentMessages.contains("Hello from GreeterNode!")) {
            return Map.of(SimpleState.MESSAGES_KEY, "Acknowledged greeting!");
        }
        return Map.of(SimpleState.MESSAGES_KEY, "No greeting found.");
    }
}

最后,将节点组装成工作流:

public class SimpleGraphApp {
    public static void main(String[] args) throws GraphStateException {
        // Initialize nodes
        GreeterNode greeterNode = new GreeterNode();
        ResponderNode responderNode = new ResponderNode();
        // Define the graph structure
        var stateGraph = new StateGraph<>(SimpleState.SCHEMA, initData -> new SimpleState(initData))
        .addNode("greeter", node_async(greeterNode))
        .addNode("responder", node_async(responderNode))
        // Define edges
        .addEdge(START, "greeter") // Start with the greeter node
        .addEdge("greeter", "responder")
        .addEdge("responder", END)   // End after the responder node
        ;
        // Compile the graph
        var compiledGraph = stateGraph.compile();
        // 打印出工作流可视化编排---文本绘图方法能力
        GraphRepresentation graph = stateGraph.getGraph(GraphRepresentation.Type.MERMAID, "demo");
        System.out.println(graph.toString());
        // Run the graph
        // The `stream` method returns an AsyncGenerator.
        // For simplicity, we'll collect results. In a real app, you might process them as they arrive.
        // Here, the final state after execution is the item of interest.
        for (var item : compiledGraph.stream( Map.of( SimpleState.MESSAGES_KEY, "Let's, begin!" ) ) ) {
            System.out.println( item );
        }
    }
}

执行Demo后,可以看到工作流按照预期顺序运行:

提示:在Python、JavaScript或TypeScript中,类似的工作流框架(如LangChain或Temporal)也遵循相似的节点-状态模式,但LangGraph4j在Java生态中提供了更原生的集成体验。

项目改造思路:从AI生成网站场景出发

理解了基础Demo后,我们将其应用到实际项目中。以AI生成代码网站场景为例,用户输入一段提示词后,系统需要完成以下复杂流程:

  1. 用户输入提示词 → Agent通过工具调用从不同渠道获取图片素材
  2. 内容图片:从Pexels网页搜索
  3. 插画图片:从undraw抓取
  4. 文本绘图并上传到COS(对象存储)
  5. AI生成或通过MCP服务设计Logo等图片
  6. 提示词增强:将图片内容关联到原始提示词
  7. 智能路由Agent选择模式生成网站(原生HTML、多文件、Vue工程)
  8. 利用搜索到的图片和确认的模式生成网站
  9. 文件保存、项目构建与打包

这个流程涉及多个外部服务调用和条件分支,非常适合使用工作流进行编排。在Go或C++项目中,类似的编排通常需要手动管理状态机,而LangGraph4j提供了声明式的解决方案。

为了加速开发,我们可以使用AI生成工作流结构。以下是一个提示词示例:

帮我生成 LangGraph4j 工作流的代码
## 工作流的流程描述
// ... 补充具体的流程
## 要求
先生成基础的工作流结构代码,每个工作节点中只输出一句信息就够了,不用真正实现具体的业务逻辑。
## 参考信息
官方文档:@https://langgraph4j.github.io/langgraph4j/core/low_level/
示例工作流实现:@https://github.com/langgraph4j/langgraph4j-examples/blob/main/langchain4j/adaptive-rag/src/main/java/dev/langchain4j/adaptiverag/AdaptiveRag.java

生成的简单工作流图代码如下:

import lombok.extern.slf4j.Slf4j;
import org.bsc.langgraph4j.CompiledGraph;
import org.bsc.langgraph4j.GraphRepresentation;
import org.bsc.langgraph4j.GraphStateException;
import org.bsc.langgraph4j.NodeOutput;
import org.bsc.langgraph4j.action.AsyncNodeAction;
import org.bsc.langgraph4j.prebuilt.MessagesState;
import org.bsc.langgraph4j.prebuilt.MessagesStateGraph;
import java.util.Map;
import static org.bsc.langgraph4j.StateGraph.END;
import static org.bsc.langgraph4j.StateGraph.START;
import static org.bsc.langgraph4j.action.AsyncNodeAction.node_async;
/**
 * 简化版网站生成工作流应用 - 使用 MessagesState
 */
@Slf4j
public class SimpleWorkflowApp {
    /**
     * 创建工作节点的通用方法
     */
    static AsyncNodeAction> makeNode(String message) {
        return node_async(state -> {
            log.info("执行节点: {}", message);
            return Map.of("messages", message);
        });
    }
    public static void main(String[] args) throws GraphStateException {
        // 创建工作流图
        CompiledGraph> workflow = new MessagesStateGraph()
        // 添加节点
        .addNode("image_collector", makeNode("获取图片素材"))
        .addNode("prompt_enhancer", makeNode("增强提示词"))
        .addNode("router", makeNode("智能路由选择"))
        .addNode("code_generator", makeNode("网站代码生成"))
        .addNode("project_builder", makeNode("项目构建"))
        // 添加边
        .addEdge(START, "image_collector")                // 开始 -> 图片收集
        .addEdge("image_collector", "prompt_enhancer")    // 图片收集 -> 提示词增强
        .addEdge("prompt_enhancer", "router")             // 提示词增强 -> 智能路由
        .addEdge("router", "code_generator")              // 智能路由 -> 代码生成
        .addEdge("code_generator", "project_builder")     // 代码生成 -> 项目构建
        .addEdge("project_builder", END)                  // 项目构建 -> 结束
        // 编译工作流
        .compile();
        log.info("开始执行工作流");
        GraphRepresentation graph = workflow.getGraph(GraphRepresentation.Type.MERMAID);
        log.info("工作流图: \n{}", graph.content());
        // 执行工作流
        int stepCounter = 1;
        for (NodeOutput> step : workflow.stream(Map.of())) {
            log.info("--- 第 {} 步完成 ---", stepCounter);
            log.info("步骤输出: {}", step);
            stepCounter++;
        }
        log.info("工作流执行完成!");
    }
}

自定义状态与工作流上下文设计

LangGraph4j官方提供的状态类默认是MessagesState消息列表结构,但在我们的场景中,需要维护的状态包含多个字段(如用户输入、图片URL列表、生成模式等),因此必须自定义一个WorkflowContext状态上下文类。

为了实现与LangGraph4j工作流图所需的AgentState兼容,我们可以将WorkflowContext对象作为一个key/value存入MessagesState中。在需要时,通过state.data().getKey()获取具体字段。下图展示了状态设计思路:

状态与工作流上下文的映射关系如下:

以下是图片资源对象、图片枚举类以及自定义WorkflowContext上下文类的代码实现:

@Getter
public enum ImageCategoryEnum {
    CONTENT("内容图片", "CONTENT"),
    LOGO("LOGO图片", "LOGO"),
    ILLUSTRATION("插画图片", "ILLUSTRATION"),
    ARCHITECTURE("架构图片", "ARCHITECTURE");
    private final String text;
    private final String value;
    ImageCategoryEnum(String text, String value) {
        this.text = text;
        this.value = value;
    }
    /**
     * 根据 value 获取枚举
     *
     * @param value 枚举值的value
     * @return 枚举值
     */
    public static ImageCategoryEnum getEnumByValue(String value) {
        if (ObjUtil.isEmpty(value)) {
            return null;
        }
        for (ImageCategoryEnum anEnum : ImageCategoryEnum.values()) {
            if (anEnum.value.equals(value)) {
                return anEnum;
            }
        }
        return null;
    }
}
/**
 * 图片资源对象
 */
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class ImageResource implements Serializable {
    /**
     * 图片类别
     */
    private ImageCategoryEnum category;
    /**
     * 图片描述
     */
    private String description;
    /**
     * 图片地址
     */
    private String url;
    @Serial
    private static final long serialVersionUID = 1L;
}
/**
 * 工作流上下文 - 存储所有状态信息
 */
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class WorkflowContext implements Serializable {
    /**
     * WorkflowContext 在 MessagesState 中的存储key
     */
    public static final String WORKFLOW_CONTEXT_KEY = "workflowContext";
    /**
     * 当前执行步骤
     */
    private String currentStep;
    /**
     * 用户原始输入的提示词
     */
    private String originalPrompt;
    /**
     * 图片资源字符串
     */
    private String imageListStr;
    /**
     * 图片资源列表
     */
    private List imageList;
    /**
     * 增强后的提示词
     */
    private String enhancedPrompt;
    /**
     * 代码生成类型
     */
    private CodeGenTypeEnum generationType;
    /**
     * 生成的代码目录
     */
    private String generatedCodeDir;
    /**
     * 构建成功的目录
     */
    private String buildResultDir;
    /**
     * 错误信息
     */
    private String errorMessage;
    @Serial
    private static final long serialVersionUID = 1L;
    // ========== 上下文操作方法 ==========
    /**
     * 从 MessagesState 中获取 WorkflowContext
     */
    public static WorkflowContext getContext(MessagesState state) {
        return (WorkflowContext) state.data().get(WORKFLOW_CONTEXT_KEY);
    }
    /**
     * 将 WorkflowContext 保存到 MessagesState 中
     */
    public static Map saveContext(WorkflowContext context) {
        return Map.of(WORKFLOW_CONTEXT_KEY, context);
    }
}

修改工作流图结构,引入状态并完善简单工作流图(不进行状态流转)的代码如下:

/**
 * 简化版带状态定义的工作流 - 只定义状态结构,不实现具体流转
 */
@Slf4j
public class SimpleStatefulWorkflowApp {
    /**
     * 创建带状态感知的工作节点
     */
    static AsyncNodeAction> makeStatefulNode(String nodeName, String message) {
        return node_async(state -> {
            WorkflowContext context = WorkflowContext.getContext(state);
            log.info("执行节点: {} - {}", nodeName, message);
            // 只记录当前步骤,不做具体的状态流转
            if (context != null) {
                context.setCurrentStep(nodeName);
            }
            return WorkflowContext.saveContext(context);
        });
    }
    public static void main(String[] args) throws GraphStateException {
        // 创建工作流图
        CompiledGraph> workflow = new MessagesStateGraph()
        // 添加节点 - 使用带状态感知的节点
        .addNode("image_collector", makeStatefulNode("image_collector", "获取图片素材"))
        .addNode("prompt_enhancer", makeStatefulNode("prompt_enhancer", "增强提示词"))
        .addNode("router", makeStatefulNode("router", "智能路由选择"))
        .addNode("code_generator", makeStatefulNode("code_generator", "网站代码生成"))
        .addNode("project_builder", makeStatefulNode("project_builder", "项目构建"))
        // 添加边
        .addEdge(START, "image_collector")
        .addEdge("image_collector", "prompt_enhancer")
        .addEdge("prompt_enhancer", "router")
        .addEdge("router", "code_generator")
        .addEdge("code_generator", "project_builder")
        .addEdge("project_builder", END)
        // 编译工作流
        .compile();
        // 初始化 WorkflowContext - 只设置基本信息
        WorkflowContext initialContext = WorkflowContext.builder()
        .originalPrompt("创建一个小楼的个人博客网站")
        .currentStep("初始化")
        .build();
        log.info("初始输入: {}", initialContext.getOriginalPrompt());
        log.info("开始执行工作流");
        // 显示工作流图
        GraphRepresentation graph = workflow.getGraph(GraphRepresentation.Type.MERMAID);
        log.info("工作流图:\n{}", graph.content());
        // 执行工作流
        int stepCounter = 1;
        for (NodeOutput> step : workflow.stream(Map.of(WorkflowContext.WORKFLOW_CONTEXT_KEY, initialContext))) {
            log.info("--- 第 {} 步完成 ---", stepCounter);
            // 显示当前状态
            WorkflowContext currentContext = WorkflowContext.getContext(step.state());
            if (currentContext != null) {
                log.info("当前步骤上下文: {}", currentContext);
            }
            stepCounter++;
        }
        log.info("工作流执行完成!");
    }
}

最佳实践:在设计状态时,建议将所有需要跨节点共享的数据都定义在WorkflowContext中,避免在节点间传递冗余参数。这类似于TypeScript中的Record<string, unknown>模式,但Java的强类型系统能提供更好的编译时检查。

⚙️ 工作节点开发:从Mock到真实业务

在开发真实工作节点之前,先使用Mock假数据来模拟状态流转,验证工作流结构的正确性。

  • 图片收集节点:模拟从Pexels和undraw获取图片URL。
  • 提示词增强节点:将图片描述合并到原始提示词中。
  • 智能路由节点:根据提示词内容选择生成模式(HTML/多文件/Vue)。
  • 代码生成节点:根据模式和图片生成网站代码。
  • 项目构建节点:模拟打包和部署过程。

各节点的Mock实现如下:

@Slf4j
public class ImageCollectorNode {
    public static AsyncNodeAction> create() {
        return node_async(state -> {
            WorkflowContext context = WorkflowContext.getContext(state);
            log.info("执行节点: 图片收集");
            // TODO: 实际执行图片收集逻辑
            // 简单的假数据
            List imageList = Arrays.asList(
                ImageResource.builder()
                .category(ImageCategoryEnum.CONTENT)
                .description("假数据图片1")
                .url("https://www.codefather.cn/logo.png")
                .build(),
                ImageResource.builder()
                .category(ImageCategoryEnum.LOGO)
                .description("假数据图片2")
                .url("https://www.codefather.cn/logo.png")
                .build()
            );
            // 更新状态
            context.setCurrentStep("图片收集");
            context.setImageList(imageList);
            log.info("图片收集完成,共收集 {} 张图片", imageList.size());
            return WorkflowContext.saveContext(context);
        });
    }
}
@Slf4j
public class PromptEnhancerNode {
    public static AsyncNodeAction> create() {
        return node_async(state -> {
            WorkflowContext context = WorkflowContext.getContext(state);
            log.info("执行节点: 提示词增强");
            // TODO: 实际执行提示词增强逻辑
            // 简单的假数据
            String enhancedPrompt = "这是增强后的假数据提示词";
            // 更新状态
            context.setCurrentStep("提示词增强");
            context.setEnhancedPrompt(enhancedPrompt);
            log.info("提示词增强完成");
            return WorkflowContext.saveContext(context);
        });
    }
}
@Slf4j
public class RouterNode {
    public static AsyncNodeAction> create() {
        return node_async(state -> {
            WorkflowContext context = WorkflowContext.getContext(state);
            log.info("执行节点: 智能路由");
            // TODO: 实际执行智能路由逻辑
            // 简单的假数据
            CodeGenTypeEnum generationType = CodeGenTypeEnum.HTML;
            // 更新状态
            context.setCurrentStep("智能路由");
            context.setGenerationType(generationType);
            log.info("路由决策完成,选择类型: {}", generationType.getText());
            return WorkflowContext.saveContext(context);
        });
    }
}
@Slf4j
public class CodeGeneratorNode {
    public static AsyncNodeAction> create() {
        return node_async(state -> {
            WorkflowContext context = WorkflowContext.getContext(state);
            log.info("执行节点: 代码生成");
            // TODO: 实际执行代码生成逻辑
            // 简单的假数据
            String generatedCodeDir = "/tmp/generated/fake-code";
            // 更新状态
            context.setCurrentStep("代码生成");
            context.setGeneratedCodeDir(generatedCodeDir);
            log.info("代码生成完成,目录: {}", generatedCodeDir);
            return WorkflowContext.saveContext(context);
        });
    }
}
@Slf4j
public class ProjectBuilderNode {
    public static AsyncNodeAction> create() {
        return node_async(state -> {
            WorkflowContext context = WorkflowContext.getContext(state);
            log.info("执行节点: 项目构建");
            // TODO: 实际执行项目构建逻辑
            // 简单的假数据
            String buildResultDir = "/tmp/build/fake-build";
            // 更新状态
            context.setCurrentStep("项目构建");
            context.setBuildResultDir(buildResultDir);
            log.info("项目构建完成,结果目录: {}", buildResultDir);
            return WorkflowContext.saveContext(context);
        });
    }
}

将上述节点应用到工作流图中,模拟完整流转:

@Slf4j
public class WorkflowApp {
    public static void main(String[] args) throws GraphStateException {
        // 创建工作流图
        CompiledGraph> workflow = new MessagesStateGraph()
        // 添加节点 - 使用真实的工作节点
        .addNode("image_collector", ImageCollectorNode.create())
        .addNode("prompt_enhancer", PromptEnhancerNode.create())
        .addNode("router", RouterNode.create())
        .addNode("code_generator", CodeGeneratorNode.create())
        .addNode("project_builder", ProjectBuilderNode.create())
        // 添加边
        .addEdge(START, "image_collector")
        .addEdge("image_collector", "prompt_enhancer")
        .addEdge("prompt_enhancer", "router")
        .addEdge("router", "code_generator")
        .addEdge("code_generator", "project_builder")
        .addEdge("project_builder", END)
        // 编译工作流
        .compile();
        // 初始化 WorkflowContext - 只设置基本信息
        WorkflowContext initialContext = WorkflowContext.builder()
        .originalPrompt("创建一个小楼的个人博客网站")
        .currentStep("初始化")
        .build();
        log.info("初始输入: {}", initialContext.getOriginalPrompt());
        log.info("开始执行工作流");
        // 显示工作流图
        GraphRepresentation graph = workflow.getGraph(GraphRepresentation.Type.MERMAID);
        log.info("工作流图:\n{}", graph.content());
        // 执行工作流
        int stepCounter = 1;
        for (NodeOutput> step : workflow.stream(Map.of(WorkflowContext.WORKFLOW_CONTEXT_KEY, initialContext))) {
            log.info("--- 第 {} 步完成 ---", stepCounter);
            // 显示当前状态
            WorkflowContext currentContext = WorkflowContext.getContext(step.state());
            if (currentContext != null) {
                log.info("当前步骤上下文: {}", currentContext);
            }
            stepCounter++;
        }
        log.info("工作流执行完成!");
    }
}

Mock验证通过后,依次开发真实工作节点。每个节点需要对接真实的外部服务(如Pexels API、Undraw API、COS SDK、AI模型API等)。以图片收集节点为例,真实实现需要处理HTTP请求、错误重试和结果缓存。

最后,将真实节点替换到工作流中:

@Slf4j
public class CodeGenWorkflow {
    /**
     * 创建完整的工作流
     */
    public CompiledGraph> createWorkflow() {
        try {
            return new MessagesStateGraph()
            // 添加节点 - 使用完整实现的节点
            .addNode("image_collector", ImageCollectorNode.create())
            .addNode("prompt_enhancer", PromptEnhancerNode.create())
            .addNode("router", RouterNode.create())
            .addNode("code_generator", CodeGeneratorNode.create())
            .addNode("project_builder", ProjectBuilderNode.create())
            // 添加边
            .addEdge(START, "image_collector")
            .addEdge("image_collector", "prompt_enhancer")
            .addEdge("prompt_enhancer", "router")
            .addEdge("router", "code_generator")
            .addEdge("code_generator", "project_builder")
            .addEdge("project_builder", END)
            // 编译工作流
            .compile();
        } catch (GraphStateException e) {
            throw new BusinessException(ErrorCode.OPERATION_ERROR, "工作流创建失败");
        }
    }
    /**
     * 执行工作流
     */
    public WorkflowContext executeWorkflow(String originalPrompt) {
        CompiledGraph> workflow = createWorkflow();
        // 初始化 WorkflowContext
        WorkflowContext initialContext = WorkflowContext.builder()
        .originalPrompt(originalPrompt)
        .currentStep("初始化")
        .build();
        GraphRepresentation graph = workflow.getGraph(GraphRepresentation.Type.MERMAID);
        log.info("工作流图:\n{}", graph.content());
        log.info("开始执行代码生成工作流");
        WorkflowContext finalContext = null;
        int stepCounter = 1;
        for (NodeOutput> step : workflow.stream(
            Map.of(WorkflowContext.WORKFLOW_CONTEXT_KEY, initialContext))) {
            log.info("--- 第 {} 步完成 ---", stepCounter);
            // 显示当前状态
            WorkflowContext currentContext = WorkflowContext.getContext(step.state());
            if (currentContext != null) {
                finalContext = currentContext;
                log.info("当前步骤上下文: {}", currentContext);
            }
            stepCounter++;
        }
        log.info("代码生成工作流执行完成!");
        return finalContext;
    }
}

⚠️ 注意事项:真实节点开发时,务必处理超时和异常情况。LangGraph4j支持节点级别的错误处理,可以在节点执行失败时回滚状态或跳转到补偿节点。在Python的LangChain中也有类似机制,但Java版本通过try-catchCompensatingNode实现。

单元测试与效果验证

工作流开发完成后,编写单元测试是保证质量的关键。测试需要覆盖正常流程、边界条件和异常场景。

@SpringBootTest
class CodeGenWorkflowTest {
    @Test
    void testTechBlogWorkflow() {
        WorkflowContext result = new CodeGenWorkflow().executeWorkflow("创建一个技术博客网站,需要展示编程教程和系统架构");
        Assertions.assertNotNull(result);
        System.out.println("生成类型: " + result.getGenerationType());
        System.out.println("生成的代码目录: " + result.getGeneratedCodeDir());
        System.out.println("构建结果目录: " + result.getBuildResultDir());
    }
    @Test
    void testCorporateWorkflow() {
        WorkflowContext result = new CodeGenWorkflow().executeWorkflow("创建企业官网,展示公司形象和业务介绍");
        Assertions.assertNotNull(result);
        System.out.println("生成类型: " + result.getGenerationType());
        System.out.println("生成的代码目录: " + result.getGeneratedCodeDir());
        System.out.println("构建结果目录: " + result.getBuildResultDir());
    }
    @Test
    void testVueProjectWorkflow() {
        WorkflowContext result = new CodeGenWorkflow().executeWorkflow("创建一个Vue前端项目,包含用户管理和数据展示功能");
        Assertions.assertNotNull(result);
        System.out.println("生成类型: " + result.getGenerationType());
        System.out.println("生成的代码目录: " + result.getGeneratedCodeDir());
        System.out.println("构建结果目录: " + result.getBuildResultDir());
    }
    @Test
    void testSimpleHtmlWorkflow() {
        WorkflowContext result = new CodeGenWorkflow().executeWorkflow("创建一个简单的个人主页");
        Assertions.assertNotNull(result);
        System.out.println("生成类型: " + result.getGenerationType());
        System.out.println("生成的代码目录: " + result.getGeneratedCodeDir());
        System.out.println("构建结果目录: " + result.getBuildResultDir());
    }
}

执行测试后,可以观察工作流的运行效果:

多测试几个不同的提示词,验证工作流的鲁棒性:

延伸思考:除了Java,LangGraph4j的类似思想也可以应用于其他语言。例如,在Go中可以使用workflow包实现轻量级状态机;在C++中可以通过std::variantstd::visit模拟多态节点。但LangGraph4j提供的声明式图结构和内置的状态管理,能显著减少样板代码。

[AFFILIATE_SLOT_1]

总结与展望

本文从LangGraph4j的Demo实践出发,逐步深入到项目级AI工作流改造的全流程。通过自定义WorkflowContext状态上下文、Mock节点验证、真实节点开发以及单元测试,我们构建了一个可扩展的AI生成网站工作流。LangGraph4j不仅简化了复杂业务流程的编排,还提供了与Java生态无缝集成的能力。

在未来的项目中,你可以将类似的工作流模式应用于:

  • 多步骤数据处理管道
  • AI Agent的决策与工具调用
  • 微服务间的编排与补偿

掌握LangGraph4j,就是掌握了一种高效构建AI应用的基础设施能力。

[AFFILIATE_SLOT_2]