桃子桃子快讯
返回首页
工具

DocuSeal 开源 TensorRT Ruby 绑定,在 Rails 单体中跑 GPU 推理

开源电子签名平台 DocuSeal 在 Ruby on Rails 单体中直接运行计算机视觉模型进行表单字段检测,自研并…

2026.08.11 · 周二5 分钟阅读

开源电子签名平台 DocuSeal 在其 Ruby on Rails 单体应用中直接集成了 GPU 推理能力,用于自动检测 PDF 表单上需要填写或签名的字段。整套 AI 工作负载跑在 NVIDIA GPU 实例上的 Ruby Sidekiq 进程中,没有引入 Python 微服务,也没有拆分额外的推理服务。团队还把为这一方案自研的 TensorRT Ruby 绑定以 Apache 2.0 协议开源。

整体架构:Rails 单体内的 GPU 推理

DocuSeal 的 AI 字段检测基于一个计算机视觉模型,可以从公开的 PDF 表单样本中识别出待填写的字段位置。为了不破坏 Rails 单体架构的一致性,推理流水线完全用 Ruby 写就,运行在带有 NVIDIA T4 GPU 的工作节点上,由 Sidekiq 异步任务驱动。前端通过 Redis pub/sub 接收每页的检测结果,再以 Server-Sent Events 推回浏览器,实现流式展示。

TensorRT Ruby 绑定

要在 GPU 上高效运行视觉模型,NVIDIA 提供的官方方案是 TensorRT,但 TensorRT 只暴露 C++ 接口,Ruby 生态此前并没有可用绑定。DocuSeal 用 Rice 写了一个非常薄的 C++ 绑定,只链接前向推理真正需要的几个方法:加载 engine、检查 tensor 元信息、绑定显存、执行前向、同步 CUDA stream。整个绑定收敛到一个 TensorRT::Engine 类,11 个方法构成,安装时依赖系统已装好的 TensorRT 和 CUDA。

三阶段推理流水线

  • 预处理:用 PDFium 渲染 PDF 页面,等比缩放到模型输入分辨率后补齐为正方形,按 ImageNet 均值与方差做归一化,再从 HWC 转为 CHW。图像处理使用 ruby-vips,张量操作使用 Numo(NumPy 的 Ruby 替代)。
  • 前向推理:输入张量按 engine 声明的 dtype 转换,序列化为二进制字符串写入 host buffer,再拷入 GPU VRAM。调用 enqueue 提交 CUDA 流任务后立即返回,retrieve 阻塞等待并读取输出。生产环境中 host buffer 按线程预分配并复用,稳态推理不再产生 Ruby 侧的内存分配,每个 Sidekiq 线程持有独立的 engine 实例。
  • 后处理:对输出做 sigmoid 取置信度,按最高分确定类别,将框从中心格式转角点格式,再反向还原预处理中的缩放与补齐,最后过滤低置信度检测并用非极大值抑制去重,统一归一化到表单构建器使用的 0..1 坐标。每个保留下来的检测生成一个 Field 对象。

异步流水线提升吞吐

预处理和后处理在 CPU 上跑,前向推理在 GPU 上跑,同步执行时 CPU 和 GPU 会互相等待。DocuSeal 把 enqueuestream_synchronize 拆开,让前一页的前向推理在 GPU 上跑的同时,CPU 可以并行渲染并预处理下一页。论文式写法是:把 retrieve 包成 lambda 返回,把同步延迟显式化交给外层页面循环来调度。实测在相同模型与输入下,异步流水线相比同步执行获得约 80% 的吞吐提升。

结果回传与部署

结果流式回传采用 Redis pub/sub 桥接 Sidekiq 工作进程和 Rails Web 进程,Web 端使用 ActionController::Live 的 SSE 控制器把每页字段以 JSON 数组推送给浏览器,用户可以看到实时处理进度。部署侧直接基于 nvcr.io/nvidia/tensorrt 基础镜像构建包含 TensorRT、CUDA 和匹配驱动的 Dockerfile.tensorrt,避免自行维护整套 GPU 运行时依赖。

整体来看,这是一份典型的「不拆微服务、把 AI 留在单体里」的工程实践:围绕一个不到 11 个方法的 Ruby 绑定,加上异步流水线设计和 SSE 流式回传,把原本需要 Python 推理服务的视觉能力完整地嵌入到了 Rails 应用之中,相关绑定代码已在 GitHub 上以 Apache 2.0 开源。

信源