展示 HN:Rex,一个用于科学工作流的并行函数式语言

Hacker News Top 工具

摘要

Rex 是一个静态类型、纯函数式的工作流语言,专为科学计算和数据处理设计,支持并行执行、内容寻址存储和 Docker 隔离。

<a href="https:&#x2F;&#x2F;peterkelly.github.io&#x2F;rex&#x2F;" rel="nofollow">https:&#x2F;&#x2F;peterkelly.github.io&#x2F;rex&#x2F;</a>
查看原文
查看缓存全文

缓存时间: 2026/08/22 01:28

peterkelly/rex 来源:https://github.com/peterkelly/rex # 🦖 Rex 许可证 文档 (https://peterkelly.github.io/rex/) crates.io (https://crates.io/crates/rex) Rex(Rush (https://rush.cloud/) 表达式 的简称)是一种静态类型的纯函数式工作流语言。它专为科学计算和数据处理而设计:Rex 中的工作表现为对不可变值的纯转换,而类型化的工具模块将计算密集型任务委托给外部程序。工作流运行时连接了四个在处理科学数据时特别有用的理念:- 一种真正的函数式语言使控制流、数据流、复用和错误处理成为一个小巧而富有表现力的语言的一部分,而不是 YAML、shell 和应用程序特定配置的混合体。- 内容可寻址存储通过其 BLAKE3 (https://en.wikipedia.org/wiki/BLAKE_(hash_function)) 哈希值来标识每个存储的输入和输出制品。文件和目录树是不可变值,因此中间制品无需共享文件名或可变工作目录即可在工具间传递。- 类型化的工具 API 暴露领域概念,如视频编解码器、PDF 结构、图像操作和输出格式。Rex 程序构建有效的工具请求,而不是组装 shell 命令字符串。- 隔离的 Docker 执行可以在一个全新的、锁定的容器中运行每个工具调用,该容器仅包含其声明的输入。相同的工作流也可以使用本地安装的工具,以实现更快的开发循环。这些特性共同使工作流定义简洁、可检查且易于并行执行。它们还在分析逻辑与执行它的操作系统进程之间创造了清晰的边界。Rex 作为 LLM 生成工作流的目标也很有用:静态类型提供快速、高信号的反馈,纯代码更易于检查,而封闭的工具边界严格限制了生成的程序可以要求主机执行的操作。有关语法和验证建议,请参见 LLM 指南。> 项目状态: main 分支包含向 > Rex v4 推进的进行中的工作,目前版本为 3.9.x。rex-workflow 是新增的且正在 > 积极开发中。核心 Rex 语言较早的生产版本 > 可在 talo/rex (https://github.com/talo/rex) 获取。## 为什么为工作流选择函数式语言?许多工作流系统始于有向无环图,并逐渐发展出自己的表达式语法、模板、条件、循环和插件模型。Rex 则从一种小巧的通用语言开始。它提供了 Hindley-Milner 类型推断、代数数据类型、记录、模式匹配、参数多态、类型类、高阶函数、递归和模块。这对于科学和数据处理工作很重要,因为真正的管道很少保持为静态的命令序列。它们需要在队列上运行分析、对观测结果进行分组、根据元数据进行分支、保留特定领域的故障信息、组合多个工具以及打包可复用的方法。这些操作在函数式程序中是很自然的:rex let observations = [3.0, -1.0, 12.0, 7.0, 20.0], selected = filter (\value -> value >= 0.0) observations, normalized = map (\value -> value / 20.0) selected in foldl (\total value -> total + value) 0.0 normalized Rex 使用严格求值,但表达式和函数是纯的:它们的含义不依赖于语言中隐藏的可变状态。这给了求值器在不让用户管理线程、future、锁、async/await 语法或回调图的情况下并发运行独立异步调用的自由。顺序依赖性通过将一个结果传递给下一个来表达;独立的工作在源代码中保持独立。纯度也提高了可审查性。函数的参数描述了它可以使用的数据,其结果类型描述了它可以产生的内容,而代数数据类型可以枚举所有预期的结果。工具模块通过返回普通的类型化值来保留此模型,例如:rex Result Media FF.FfmpegError Result Q.PdfOutput Q.QpdfError Result P.TextFile P.PopplerError 预期的无效请求和工具进程失败因此可以在工作流内匹配和处理。存储失败、执行器失败和其他基础设施问题仍然是求值错误,使领域失败与运行时本身的失败保持区别。### 静态类型早期捕获工作流接线错误工具选项由记录和代数数据类型表示,而不是非结构化的映射。一旦哈希值被包装在语义制品类型中,Image 就无法意外地被提供给需要 Media 的地方;编解码器选项不会与图像操作混淆;多文件结果必须按其方式处理。原始导入的哈希仍需由工作流正确分类,如果存储的字节不是有效的输入,工具会报告错误。编译器在启动昂贵的外部进程之前捕获结构接线错误。当工作流由软件生成或修改时,类型尤其有价值。LLM 或其他程序可以提议一个 Rex 工作流,运行解析器和类型检查器,并使用精确的诊断信息在执行任何工具之前修复它。### 函数式组合超越 DAG 文件扩展Rex 工作流可以将重复逻辑提炼为函数,定义领域类型,使用 map 和折叠转换集合,使用递归处理层次数据,并在工具边界之间保留结构化错误。其结果是一个可以随分析增长的程序,而不是最终需要外部模板语言的配置文件。## 内容可寻址数据当制品是值而不是可变位置时,科学工作流更容易推理。rex-workflow 包含一个内容可寻址存储,其中每个对象都以其字节的 BLAKE3 哈希值命名。该数据模型有两种对象类型:- blob 是不透明的字节序列:图像、视频、PDF、表格、模型、日志或任何其他文件。- tree 是从名称到 blob 或 tree 条目的确定性编码映射。tree 表示目录和多文件数据集、提取的图像集合和嵌套结果。每个 tree 条目记录其类型、哈希和大小。tree 可以包含其他 tree,因此一个根哈希标识一个完整的不可变目录层次结构。大小是累积的,这意味着引用另一个 tree 的条目包含其包含的所有内容的总大小。text host file or directory | | rex store import v BLAKE3 blob/tree hash | | typed Rex values and tool calls v new blob/tree hashes | | rex store export v host file or directory 此模型提供了几个有用的属性:- 稳定标识。 相同的字节总是产生相同的哈希,无论其原始文件名或机器如何。- 不可变性。 现有输入无法被覆盖。转换创建一个新对象并返回一个新的哈希。- 去重。 写入已存在的内容解析到现有地址,而不是创建第二个逻辑对象。- 明确的交接。 工具消费一个精确的对象并返回其输出的精确标识。不会对读取的是路径的哪个版本产生疑问。- 自然的组合。 一个工具的输出哈希可以立即被另一个工具使用,无需导出并重新导入中间文件。- 可移植的存储。 相同的 API 可以使用本地文件系统存储、内存存储或云托管的 S3 存储桶。CAS 刻意与函数式语言对齐:创建新制品不会改变旧制品,并且使用相同内容调用 put 会返回相同的值。目录更新通过沿着更改的路径创建新的 tree 来表示,类似于 Git 的对象模型。内容寻址是缓存和来源的重要基础,但它不是魔法。哈希标识字节;它本身不记录哪个工作流、参数、工具版本或容器产生了它们。同样,即使输入是不可变的,外部工具也可能是不确定的。Rex 使制品边界显式化,以便可以严格地构建缓存和来源,而不是从可变路径推断。### Rex 程序可用的存储操作内置的 std.storage 模块直接暴露不可变数据:rex import std.storage (*); let report = put_string "analysis complete", files = dict_from_entries [("report.txt", (Blob, report))], result_tree = put_tree files in result_tree 程序可以使用 put_stringput_bytesput_treeget_stringget_bytesget_tree。共享的 std.artifacts 模块通过 MediaImagePdfJsonFile 用语义意义包装哈希;特定工具的结果类型在需要时添加操作元数据。rex import std.artifacts (Pdf); fn as_pdf (content: Hash) -> Pdf = Pdf { content = content }; 构造一个制品类会对 CAS blob 进行分类,但不会检查其字节。消费工具仍然负责验证存储的内容是否具有支持的表示。## 工具是类型化的能力Rex 不向工作流程序暴露通用的 shell 命令。相反,宿主注册模块,其函数和类型描述支持的操作。当前的工作流目录包含:| Rex 模块 | 运行时程序 | 选定的功能 ||—|—|—|| tools.ffmpeg | FFmpeg, FFprobe | 转码和重混媒体、提取音频或帧、创建缩略图、连接、混音、分段、打包 HLS/DASH、探测元数据、检查数据包和帧、以及查询功能 || tools.gnuplot | Gnuplot | 从内联曲线、误差条、带、条、直方图、热图、矢量、标签、点云、路径、曲面和注释渲染类型化图表 || tools.graphviz | Graphviz dot | 使用类型化属性、声明的节点、二元边、端口、标签、默认值和嵌套子图渲染语义化的有向或无向图 || tools.imagemagick | ImageMagick | 生成和转换图像、批量转换、识别、比较、合成、拼贴、提取像素以及查询格式和功能 || tools.qpdf | QPDF | 检查 PDF、计算页面数、导出结构化 JSON、转换或线性化、合并/拆分页面以及应用叠加或底图 || tools.poppler | pdfinfopdftotextpdftocairopdfimages | 解析 PDF 元数据、提取文本和单词几何形状、渲染页面、提取图像以及检查嵌入图像 |狭窄的功能涵盖了常见操作,而 FFmpeg 和 ImageMagick 还提供了类型化的通用渲染 API,用于复杂滤镜图、多输入、有序图像操作和多输出。在底层,模块将每个请求编译为一个 ToolExecutionPlan。计划只能引用:- 封闭工具目录中的一个程序;- 由类型化模块生成的字面参数;- 声明的 CAS blob 或 tree 输入;- 声明的输出槽位和输出类型;以及- 可选的来自 CAS 的标准输入。Rex 工作流不能选择可执行路径、容器镜像、主机挂载、工作目录或任意 Docker 选项。这些仍然是宿主策略。这种分离对于不受信任或生成的工作流很有用,因为添加新操作是一个明确的 Rust API 决策,而不是对 shell 访问的意外扩展。工具输出也在边界处检查。执行器知道是期望单个文件、编号序列、目录还是 tree。它在将输出递归导入 CAS 之前拒绝符号链接和特殊文件。## 本地和 Docker 执行同一个类型化的工具计划可以由不同的宿主选择后端执行。工作流本身不会改变。### 本地进程本地执行是默认的:sh rex --store-path ./store run workflow.rex --inputs inputs.json 它创建一个临时工作区,具体化声明的 CAS 输入,运行主机上安装的目录化可执行文件,并导入声明的输出。这在开发期间很方便,并使用该机器上安装的编解码器、委托、字体和其他功能。它不是一个操作系统沙箱:该进程仍然具有主机用户的权限和环境。### 隔离的 Docker 容器Docker 执行保持工作流 API 相同,同时将每个工具调用放在一个全新的容器中:sh rex --store-path ./store run workflow.rex \ --inputs inputs.json \ --tool-executor docker 提供的容器执行器故意限制性很强。对于每次调用,它:- 仅具体化计划声明的 CAS 输入;- 以只读方式挂载 /work/inputs,以读写方式挂载 /work/outputs;- 使用大小受限的 noexec,nosuid,nodev tmpfs 作为 /work/tmp;- 不挂载 CAS、仓库、当前目录、主目录、Docker 套接字、系统字体或任意主机路径;- 禁用网络、Linux 功能、权限提升和健康检查;- 使用只读的镜像根、PID 限制以及主机用户的数字 UID 和 GID;- 提供无头 C 区域设置、UTC 时区、临时主目录和受控的缓存位置;- 限制执行时间和捕获的 stdout/stderr;- 在完成、超时或取消时显式删除容器。捆绑的工具镜像进一步缩小了可用的表面。FFmpeg API 不暴露网络源、捕获设备或硬件加速。ImageMagick 包含深度防御策略,禁用网络、桌面/捕获/打印、间接路径、MSL 和 MVG 编码器,同时保留文档化的无头格式。容器对于隔离和可重复性都很有用。它们使工具运行时、库、编解码器、委托、字体、区域设置和操作系统环境成为显式的部署选择,而不是工作者的未记录属性。DockerToolImages::new 需要带有摘要限定的镜像引用,CLI 镜像覆盖需要摘要,除非明确允许可变标签。该仓库当前使用标签 rex-tool-ffmpeg:localrex-tool-gnuplot:localrex-tool-graphviz:localrex-tool-imagemagick:localrex-tool-qpdf:localrex-tool-poppler:local 在本地构建开发镜像。执行器使用 --pull=never,因此评估工作流永远不会联系注册表或作为副作用重建镜像。发布的多平台镜像和发布摘要锁定是未来的工作;今天,生产用户如果需要受控的镜像供应链,应提供并固定自己的镜像。有关完整的隔离契约、镜像覆盖、构建详细信息和集成测试覆盖,请参见 工具镜像文档。## 快速入门您需要一个较新的 Rust 工具链。容器配置需要安装 Buildx 的 Docker;对于本地执行,请改为在主机上安装相关的工具套件。从工作区根目录构建工作流二进制文件并创建本地存储:sh cargo build -p rex-workflow mkdir -p store 构建并检查六个本机架构的 Docker 工具镜像:sh target/debug/rex tools build target/debug/rex tools inspect 运行一个无输入的示例,该示例使用 ImageMagick 生成 PNG 渐变:sh target/debug/rex --store-path ./store run \ rex-workflow/examples/imagemagick/generate_gradient.rex \ --tool-executor docker JSON 结果包含生成图像的 BLAKE3 content 哈希。其形式为:json { "Ok": { "SingleImage": [ { "content": "" } ] } } 当需要离开工作流系统时,将该 blob 导出回传统文件:sh target/debug/rex --store-path ./store store export \ gradient.png 要处理现有文件,请先导入:sh target/debug/rex --store-path ./store store import photo.jpg 该命令会打印内容哈希。

相似文章