Skip to main content

概述

本指南将帮助您快速创建新的工作流节点。基于SpringBoot3 + LangGraph4j + Hutool技术栈,新的节点系统采用了标准化的结构设计,使得创建节点变得非常简单和规范。

节点创建步骤

1. 创建节点文件夹

langchat-workflow/langchat-workflow-core/src/main/java/cn/langchat/workflow/core/node/ 目录下创建新的文件夹,文件夹名称使用小写格式:
mkdir my-custom-node

2. 创建必需的三个Java文件

每个节点都必须包含以下三个文件:

2.1 节点主类 (XxxNode.java)

这是节点的核心实现类,必须实现 WorkflowNode<T> 接口:
package cn.langchat.workflow.core.node.mycustomnode;

import cn.langchat.workflow.core.WorkflowState;
import cn.langchat.workflow.core.node.common.BaseContext;
import cn.langchat.workflow.core.node.common.CommonParamFields;
import cn.langchat.workflow.core.node.common.WorkflowNode;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import java.util.Map;

/**
 * @author tycoding
 * @since 2025/8/28
 */
@Slf4j
@Component
public class MyCustomNode implements WorkflowNode<MyCustomContext> {

    @Override
    public String getNodeName() {
        return this.getClass().getSimpleName();
    }

    @Override
    public Class<MyCustomContext> getContextType() {
        return MyCustomContext.class;
    }

    @Override
    public Map<String, Object> process(MyCustomContext context, Map<String, Object> state) {
        // 表单校验
        BaseContext.ValidationResult validationResult = BaseContext.validateParams(
                context.getParamSchemas(),
                context.getParams(),
                CommonParamFields.INPUT
        );
        if (!validationResult.isValid()) {
            throw new RuntimeException("表单校验失败: " + validationResult.getFirstError());
        }

        // 获取输入内容
        String input = context.getInput(state);

        // 实现节点业务逻辑
        String result = processCustomLogic(input);

        // 返回处理结果
        return WorkflowState.addStringMessage(state, result, context.getId());
    }

    private String processCustomLogic(String input) {
        // 在这里实现具体的业务逻辑
        return "处理结果: " + input;
    }
}

2.2 节点上下文类 (XxxContext.java)

这是节点的上下文类,继承自 BaseContext
package cn.langchat.workflow.core.node.mycustomnode;

import cn.langchat.workflow.core.node.common.BaseContext;
import lombok.Data;
import lombok.EqualsAndHashCode;

import java.util.Map;

/**
 * @author tycoding
 * @since 2025/8/28
 */
@EqualsAndHashCode(callSuper = true)
@Data
public class MyCustomContext extends BaseContext {

    /**
     * 获取自定义参数值
     */
    public String getCustomParam(Map<String, Object> state) {
        return getParamValue(state, MyCustomParamFields.CUSTOM_PARAM, String.class);
    }

    /**
     * 获取数值参数
     */
    public Integer getNumberParam(Map<String, Object> state) {
        Integer value = getParamValue(state, MyCustomParamFields.NUMBER_PARAM, Integer.class);
        return value != null ? value : 10; // 默认值
    }
}

2.3 节点参数字段枚举 (XxxParamFields.java)

这是节点参数的字段定义,使用枚举形式:
package cn.langchat.workflow.core.node.mycustomnode;

import lombok.Getter;

/**
 * 自定义节点参数字段枚举
 * 定义MyCustomNode特有的表单字段名
 *
 * @author tycoding
 * @since 2025/8/27
 */
@Getter
public enum MyCustomParamFields {

    /**
     * 自定义参数
     */
    CUSTOM_PARAM("customParam", "自定义参数"),

    /**
     * 数值参数
     */
    NUMBER_PARAM("numberParam", "数值参数"),
    ;

    /**
     * 字段名
     */
    private final String fieldName;

    /**
     * 字段描述
     */
    private final String description;

    MyCustomParamFields(String fieldName, String description) {
        this.fieldName = fieldName;
        this.description = description;
    }

    @Override
    public String toString() {
        return fieldName;
    }
}

3. 核心接口和基类说明

3.1 WorkflowNode<T> 接口

所有节点都必须实现这个接口:
public interface WorkflowNode<T extends BaseContext> {
    String getNodeName();
    Class&lt;T&gt; getContextType();
    Map<String, Object> process(T context, Map<String, Object> state);
}

3.2 BaseContext 基类

提供通用的上下文功能:
  • 参数验证
  • 参数值获取
  • 变量替换
  • 基础数据管理

3.3 CommonParamFields 通用字段

定义所有节点共享的参数字段:
public enum CommonParamFields {
    INPUT("input", "输入内容");
}

4. 节点开发规范

4.1 注解使用

  • @Slf4j: 提供日志功能
  • @Component: 使节点被Spring容器管理
  • @Data: Lombok注解,自动生成getter/setter
  • @EqualsAndHashCode(callSuper = true): 继承基类的equals和hashCode

4.2 参数验证

每个节点都应该进行参数验证:
BaseContext.ValidationResult validationResult = BaseContext.validateParams(
    context.getParamSchemas(),
    context.getParams(),
    CommonParamFields.INPUT
);
if (!validationResult.isValid()) {
    throw new RuntimeException("表单校验失败: " + validationResult.getFirstError());
}

4.3 状态管理

使用 WorkflowState 工具类管理工作流状态:
// 添加字符串消息
return WorkflowState.addStringMessage(state, result, context.getId());

// 添加变量
return WorkflowState.addVariable(state, key, value);

5. 高级功能

5.1 自定义参数获取

在Context类中添加自定义方法:
public String getCustomParam(Map<String, Object> state) {
    return getParamValue(state, MyCustomParamFields.CUSTOM_PARAM, String.class);
}

5.2 类型转换

系统自动处理前端组件类型到Java类型的转换:
  • Input/TextArea → String
  • InputNumber → Integer
  • Switch/Checkbox → Boolean
  • Select → String

5.3 变量替换

支持在工作流状态中进行变量替换:
// 自动处理 ${variable} 格式的变量
String result = VariableReplacer.replace(input, state);

6. 完整示例

文件结构

my-custom-node/
├── MyCustomNode.java      # 节点主类
├── MyCustomContext.java   # 节点上下文
└── MyCustomParamFields.java # 参数字段枚举

节点主类 (MyCustomNode.java)

@Slf4j
@Component
public class MyCustomNode implements WorkflowNode&lt;MyCustomContext&gt; {

    @Override
    public String getNodeName() {
        return this.getClass().getSimpleName();
    }

    @Override
    public Class&lt;MyCustomContext&gt; getContextType() {
        return MyCustomContext.class;
    }

    @Override
    public Map<String, Object> process(MyCustomContext context, Map<String, Object> state) {
        // 参数验证
        BaseContext.ValidationResult validationResult = BaseContext.validateParams(
                context.getParamSchemas(),
                context.getParams(),
                CommonParamFields.INPUT
        );
        if (!validationResult.isValid()) {
            throw new RuntimeException("表单校验失败: " + validationResult.getFirstError());
        }

        // 获取参数
        String input = context.getInput(state);
        String customParam = context.getCustomParam(state);
        Integer numberParam = context.getNumberParam(state);

        // 业务逻辑处理
        String result = processBusinessLogic(input, customParam, numberParam);

        // 返回结果
        return WorkflowState.addStringMessage(state, result, context.getId());
    }

    private String processBusinessLogic(String input, String customParam, Integer numberParam) {
        log.info("处理输入: {}, 自定义参数: {}, 数值参数: {}", input, customParam, numberParam);
        return String.format("处理结果: %s + %s + %d", input, customParam, numberParam);
    }
}

7. 测试和验证

7.1 单元测试

为节点创建单元测试:
@SpringBootTest
class MyCustomNodeTest {

    @Autowired
    private MyCustomNode node;

    @Test
    void testProcess() {
        // 创建测试上下文和状态
        MyCustomContext context = new MyCustomContext();
        Map<String, Object> state = new HashMap<>();

        // 设置测试数据
        context.setParams(Map.of("input", "测试输入"));

        // 执行节点
        Map<String, Object> result = node.process(context, state);

        // 验证结果
        assertNotNull(result);
        // 添加更多断言...
    }
}

7.2 集成测试

在工作流中测试节点的完整流程。

8. 注意事项

  1. 命名规范: 类名使用PascalCase,文件夹使用小写
  2. 继承关系: Context类必须继承BaseContext
  3. 接口实现: Node类必须实现WorkflowNode接口
  4. 参数验证: 始终进行参数验证
  5. 异常处理: 合理处理异常情况
  6. 日志记录: 使用日志记录关键操作
  7. 类型安全: 使用泛型确保类型安全

9. 故障排除

节点不生效

  • 检查是否正确添加了 @Component 注解
  • 确认包路径是否正确
  • 验证Spring容器是否正确扫描

参数获取失败

  • 检查ParamFields枚举定义
  • 确认参数名称匹配
  • 验证参数类型转换

状态更新失败

  • 检查WorkflowState工具类使用
  • 确认状态键值正确
  • 验证返回格式

更多资源

  • 查看现有节点实现作为参考
  • 阅读LangGraph4j文档了解工作流概念
  • 参考Spring Boot文档了解依赖注入
  • 查看Hutool工具类文档