欢迎光临
我们一直在努力

让数据管道自己写代码:代码组件,正在成为大模型时代的数据处理主入口

让数据管道自己写代码:代码组件,正在成为大模型时代的数据处理主入口

拖拽组件解决了 80% 的标准化搬运。
真正拖住数据团队的,是剩下 20% 说不清、每次都长得不一样的长尾逻辑。
大模型让这 20%,第一次可以规模化。


一、每个团队都有一张"写不进 SQL"的表

一张普通的订单表:order_id、amount、phone、payload、status、create_time。

同步进数仓前,需求是这样的:

  • amount 在源库是分,要转成元,保留两位小数;
  • phone 要脱敏,但只遮中间四位;
  • payload 里的 items 数组,每个商品都要拆成单独一行;
  • status = DELETED 的记录不能进下游;
  • CDC 的 UPDATE 事件,还要处理 __before 里的变更前数据。

没有一个是"把 A 字段搬到 B 字段"。它们有判断、循环、数值精度、嵌套展开。

用 SQL 写,能写,但会写成一篇没人愿意 review 的"SQL 散文";写成 UDF,要发版、要重启;拆成外部服务,链路就断成两截。

问题不在能力,而在位置——管道里缺一个能写代码的组件。


二、代码组件:把"写代码"变成一个节点

在 DataY 的画布上,它就是一个普通转换节点:拖进来、连上上下游、写代码、跑。

不启动独立服务,不引入第二套运行时,调度、重试、监控全部复用管道已有能力。对任务来说,它和 SQL 节点、Join 节点没有区别。

契约只有一个方法

类名必须是 UserScript,不能带 package;方法签名固定为 process(FlowFile, Map),必须返回 FlowFile;一次处理一批数据,而不是一行调一次。

FlowFile 装着三样东西:

  • 数据:JSON 数组 / 对象、CSV、文本、二进制;
  • 属性:库名、表名、事件类型等随数据流动的上下文;
  • 表元数据:字段名与类型,决定下游能否正确建表、映射。

context 提供 log、componentId、componentName,以及执行上下文。

一个真实的清洗脚本

import com.data.job.FlowFile;
import com.data.job.component.javascript.ScriptContext.LogFunction;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Map;

public class UserScript {
public FlowFile process(FlowFile flowFile, Map<String, Object> context) {
LogFunction log = (LogFunction) context.get("log");
JSONArray rows = flowFile.getJsonArray();
if (rows == null) return flowFile; // 其它格式原样透传

JSONArray result = new JSONArray();
for (Object item : rows) {
JSONObject row = (JSONObject) item;

if ("DELETED".equals(row.getString("status"))) continue;

Long amount = row.getLong("amount");
if (amount != null) {
row.put("amount_yuan", BigDecimal.valueOf(amount)
.divide(BigDecimal.valueOf(100), 2, RoundingMode.HALF_UP));
}

String phone = row.getString("phone");
if (phone != null && phone.length() == 11) {
row.put("phone", phone.substring(0, 3) + "****" + phone.substring(7));
}

result.add(row);
}

flowFile.setJsonArray(result);
// 新增字段必须同步元数据,否则下游"看不见"它
flowFile.upsertColumnMeta("amount_yuan", "DECIMAL");

log.info("清洗完成,输出 " + result.size() + " 条");
return flowFile;
}
}

没有注解、没有依赖注入、没有配置文件。它只依赖 FlowFile 和 JDK,却能做到 SQL 很难表达、UDF 又要发版才能做到的事。


三、为什么是 Java?

很多平台的代码组件选 Python 或 JavaScript。DataY 选 Java,不是情怀,是工程约束下的最优解。

同进程,零跨语言开销。 数据不用序列化出进程再反序列化回来,也不用维护第二套运行时。对每天几千万行的任务,少一次跨进程拷贝,就是实打实的吞吐差异。

直接复用已有能力。 JSON 用 fastjson2,金额用 BigDecimal,加密、日期用 JDK 自带工具,还能调用企业内部沉淀的 Java SDK。

编译期挡住一半的错。 字段名写错、类型不匹配、方法签名不对,编译阶段就报错,还带行列号。Python 里把 amount 写成 amout,可能跑一个月才在某个空值分支炸掉。数据管道最怕的不是报错,是静默地算错。

部署零新增。 服务器不用装 Python、不用配 Node、不用额外容器。一个 JAR 起来,组件就能用——这也是 DataY "2 核 4G 跑完整链路"的一部分。

需要说明的是:脚本定位是纯数据处理。字段加工、结构转换、规则判断可以;直连数据库、访问文件、调用外部 HTTP 不行。需要这些能力时,用对应的输入输出组件,让链路边界保持清晰。


四、它是怎么跑起来的

脚本源码 → 内存 javac → 字节码 Map(主类 + lambda/匿名类辅助类)
→ 自定义 ClassLoader → 反射调用 process() → 下游

三个生产里必须处理的细节:

① Spring Boot 可执行 JAR 的类加载。 打成可执行 JAR 后,依赖都在 BOOT-INF/lib,java.class.path 指不到它们。只靠 -classpath 编译,FlowFile、fastjson2 全会"找不到符号"。所以还要通过运行时类加载器索引包内的类,兼容普通目录、JAR 和 fat JAR。

② 辅助类必须整批加载。 脚本里用了 lambda、匿名内部类,javac 会额外生成 UserScript$1。只加载主类,运行时会直接 NoClassDefFoundError。所以编译产物是一张类名到字节码的 Map。

③ 编译一次,反复执行。 脚本实例会被缓存。第一次执行时编译,之后复用同一实例——同一任务实例内,编译开销只发生一次。

小插曲:组件内部的类名至今还叫 JavaScriptComponent,但执行的是标准 Java,页面展示名是「JAVA脚本」。


五、大模型时代:从"兜底"变成"主入口"

到这里,代码组件还只是一个"能力强、门槛也高"的兜底方案。大模型改变了这个等式。

一个反直觉的判断

大模型进管道,最常见的做法是:每来一行数据,就调一次模型。

做语义理解时这是对的,比如从评论里抽问题类型。但拿它做规则化加工——金额换算、字段拆分、条件过滤——会撞上三个硬问题:

  • 成本与行数成正比。一千万行就是一千多万次调用;
  • 结果不稳定。同样的输入,今天和明天可能给出不同格式;
  • 不可审计。出了问题,说不清"这条数据为什么被改成这样"。
  • 而大模型最擅长的,其实不是"处理数据",是写处理数据的代码。

    让模型写一次代码,让代码跑所有行。

    四种做法的分水岭

    做法成本确定性表达力可审计性适合什么
    固定组件 / 字段映射 极低 高 低 高 标准搬运
    手写 SQL 低 高 中(受方言限制) 高 Join、聚合
    每行调用大模型 与行数成正比 低 高(语义) 弱 分类、抽取、打标
    大模型生成代码 一次生成,执行近乎免费 高 高 高(可 review) 长尾规则化转换

    这张表最关键的一列不是成本,是确定性。

    数据管道是生产系统,不是聊天窗口。它可以接受"模型偶尔抽错一个标签",但不能接受"金额今天按分算,明天按元算"。

    代码,就是把一次不确定的智能,固化成一份确定的执行逻辑。

    所以三者不是替代,是分工:SQL / 映射处理标准集合运算;大模型组件处理语义理解;代码组件 + 大模型生成处理规则复杂、SQL 难表达、又必须稳定可复现的长尾逻辑。

    前两者决定管道的下限,后者决定上限。


    六、DataY 已经把它做进了组件

    在「JAVA脚本」的配置面板上,除了代码编辑器,还有一个按钮:AI 助手。

    点进去,只有一句需求输入框、一排示例,以及"上游调试数据"的状态。但"能用"和"可靠"之间,隔着四道工程保障。

    第一道:把契约写进提示词

    模型必须知道这个组件的接口规范。提示词里明确约束:

    • 不许有 package,必须是 public class UserScript;
    • 必须实现 process(FlowFile, Map) 并返回 FlowFile;
    • 新增 / 删除 / 重命名字段,必须同步调用对应的元数据方法;
    • CDC 场景要处理 __before;
    • 只处理认识的数据格式,其它原样透传;
    • 运行在 JDK 17,不许用更高版本语法;
    • 只做纯数据处理,不访问网络、文件、数据库。

    这不是让模型自由发挥,而是先给它一份组件说明书,再让它填实现。

    第二道:把真实字段喂给它

    大模型写数据代码,最常见的翻车不是语法错,是臆造字段名。你说 amount,它写出 total_amount;你说手机号,它写成 mobile。编译能过,跑起来全是空值。

    DataY 会在进入助手时自动拉取上游组件的调试采样——真实字段名、类型、样例值——一并放进提示词,并明确要求:字段必须来自这份元数据,不在其中就提示用户,不许猜。

    这一句话,把"猜字段"变成了"查字段"。

    第三道:让模型自己先编译一遍

    助手挂了一个只读工具 validate_javascript。模型给出最终答案前,可以把代码交给它真实编译一次。

    它复用的就是组件运行时的编译器——不是正则,不是模拟,是货真价实的 javac。失败会把行列号和诊断信息返回,模型据此修正再试。

    理解需求 → 生成脚本 → 真实编译 → 报错?修正重试 → 编译通过 → 输出

    编译器成了模型的"单元测试"。 交付到面前的代码,至少是能编译的。

    第四道:抽取、人工确认、再试跑

    模型回复里常夹着解释和 Markdown。前端按优先级抽取代码:```java 围栏 → 任意围栏 → 从 import / public class 截到最后一个右花括号。

    抽出来不会自动保存。先看预览、看说明,确认后点"应用到编辑器",再照常试跑、看日志、看输出。

    AI 负责起草,人负责签字。

    实际效果

    需求:

    根据 create_time 算出订单小时和是否周末;金额小于 100 算小额,100 到 1000 算中额,超过 1000 算大额;时间解析失败的记录打 time_invalid 标记,但不要丢。

    生成的脚本:

    import com.data.job.FlowFile;
    import com.alibaba.fastjson2.JSONArray;
    import com.alibaba.fastjson2.JSONObject;
    import java.math.BigDecimal;
    import java.time.DayOfWeek;
    import java.time.LocalDateTime;
    import java.time.format.DateTimeFormatter;
    import java.util.Map;

    public class UserScript {
    private static final DateTimeFormatter FMT =
    DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");

    public FlowFile process(FlowFile flowFile, Map<String, Object> context) {
    JSONArray rows = flowFile.getJsonArray();
    if (rows == null) return flowFile;

    JSONArray result = new JSONArray();
    for (Object item : rows) {
    JSONObject row = (JSONObject) item;

    try {
    LocalDateTime time = LocalDateTime.parse(row.getString("create_time"), FMT);
    row.put("order_hour", time.getHour());
    row.put("is_weekend", time.getDayOfWeek() == DayOfWeek.SATURDAY
    || time.getDayOfWeek() == DayOfWeek.SUNDAY);
    } catch (Exception e) {
    row.put("time_invalid", true);
    }

    BigDecimal amount = row.getBigDecimal("amount");
    String level = "未知";
    if (amount != null) {
    if (amount.compareTo(BigDecimal.valueOf(100)) < 0) level = "小额";
    else if (amount.compareTo(BigDecimal.valueOf(1000)) <= 0) level = "中额";
    else level = "大额";
    }
    row.put("order_level", level);

    result.add(row);
    }

    flowFile.setJsonArray(result);
    flowFile.upsertColumnMeta("order_hour", "INTEGER");
    flowFile.upsertColumnMeta("is_weekend", "BOOLEAN");
    flowFile.upsertColumnMeta("time_invalid", "BOOLEAN");
    flowFile.upsertColumnMeta("order_level", "VARCHAR");
    return flowFile;
    }
    }

    这段代码放在两年前,是熟手 Java 工程师半小时的工作量。现在,是一句话加几秒钟。


    七、边界与实践

    场景首选
    表到表同步、字段改名、简单过滤 字段映射 / SQL
    多表关联、聚合、窗口计算 SQL 组件
    语义抽取、分类、打标 大模型组件
    嵌套 JSON 展开、复杂编解码、特殊规则 代码组件
    规则频繁变化、SQL 写不动 代码组件 + AI 生成

    四条建议:

  • 先跑上游,再让 AI 写。 有真实采样,字段名和类型才不会错;
  • 改字段,必须同步元数据。 只改数据不改 _tableMetadata,下游会"看不见";
  • 异常留痕,不要随手吞。 用 log.error 留下现场,能透传就透传;
  • 保持幂等,别写巨型脚本。 一个脚本只做一件事,拆成多个节点才好定位。
  • 底线只有一条:AI 生成的代码,也要 review。 编译器只能保证它"能跑",不能保证它"算对"。


    八、关于 DataY

    前面讲的代码组件,不是演示稿里的截图,而是 DataY 里已经上线的一个普通节点。

    DataY 是一个开源、轻量、一体化的数据平台。设计原则只有一句话:

    数据平台可以很小,功能不打折。

    传统平台动辄十几台机器、上百 G 内存。DataY 的选择是:用一个 JAR,把数据集成、维度建模、任务调度、高性能数仓、智能问数装进去。

    • 数据集成:十余种数据源,拖拽或 JSON 编排,全量 / 增量 / CDC,自动建表;
    • 维度建模:星型 / 雪花模型,三种维度类型,事实表物化落表;
    • 任务调度:Quartz Cron 精准到秒,DAG 编排,master / worker 可分离;
    • 高性能数仓:内嵌 DuckDB,列存 + 向量化,可外挂 Doris / ClickHouse;
    • 智能问数:自然语言 → 指标 → 维度 → 取数,RAG + 统一指标口径;
    • MCP Harness:通过 /mcp/datay,让 AI Agent 直接操作平台对象。

    代码组件就在 datay-core 引擎里,和 StreamJdbcInput、StreamSqlUnit、LlmComponent 一样,用注解注册成普通组件。这意味着:

  • 同源:共享同一个 JVM、同一套 FlowFile 和表元数据,不存在"脚本服务"和"数据管道"两套状态;
  • 同权:出现在任何 ETL 任务里,享受同样的调度、重试、日志、调试能力;
  • 可扩展:要加自己的算子,照同样方式注册即可,不用改画布和调度。
  • AI 代码生成也不是独立产品,而是长在组件配置面板里的一个按钮:读上游采样、守组件契约、调真实编译器、把结果交回编辑器。用完即走,不改变任务的运行方式。

    适合谁?没有专职大数据工程师的中小团队;想用 2 核 4G 快速验证完整链路的团队;需要私有化部署的团队(AI 可指向本地 OpenAI 兼容服务);以及想读源码、看数据平台怎么造的开发者。

    git clone https://github.com/yuyuejia/datay
    cd datay
    mvn clean package -DskipTests
    java -jar datay-web/target/*.jar –DATAY_AI_API_KEY=sk-xxxxxxxxx
    # 浏览器打开 http://localhost:8080,默认账号 admin/admin

    • 产品首页:http://datay.yuyuejia.com.cn/
    • 在线体验:http://datay-demo.yuyuejia.com.cn/
    • GitHub:https://github.com/yuyuejia/datay
    • Gitee:https://gitee.com/yuyuejia/datay

    项目采用 Apache-2.0 协议。如果这篇文章对你有帮助,欢迎点个 Star,或转给正在被"清洗脚本"折磨的同事。

    数据平台可以很小,但数据处理的上限,不该被组件的数量决定。

    赞(0)
    未经允许不得转载:171主机测评 » 让数据管道自己写代码:代码组件,正在成为大模型时代的数据处理主入口
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址