news 2026/9/15 14:39:13

TensorZero UI 测试数据(Fixtures)指南:从 R2 拉取、加载与编写可复现的开发数据

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
TensorZero UI 测试数据(Fixtures)指南:从 R2 拉取、加载与编写可复现的开发数据

TensorZero UI 测试数据(Fixtures)指南:从 R2 拉取、加载与编写可复现的开发数据

【免费下载链接】tensorzeroTensorZero is an open-source LLMOps platform that unifies an LLM gateway, observability, evaluation, optimization, and experimentation.项目地址: https://gitcode.com/GitHub_Trending/te/tensorzero

TensorZero 是一个开源 LLMOps 平台,其 Web UI(ui/)依赖大量示例数据进行开发、测试与端到端验证。这些示例数据(fixtures)绝大多数集中存放在 ui/fixtures 目录中,并以 Cloudflare R2 对象存储作为远端载体。本文以 ui/fixtures/README.md 为主线,完整讲解 fixtures 的目录结构、无凭证与带凭证两种拉取方式、如何通过 Docker Compose 一键装载进 ClickHouse/Postgres,以及新增与更新 fixtures 的完整工作流,并结合仓库中的下载/上传脚本与装载脚本,给出源码级的实现细节与验证手段。读完本文,你将能独立为 TensorZero UI 搭建一套完整、可复现的本地开发数据环境。

为什么 fixtures 要放在 R2 而不是直接提交到仓库

从 ui/fixtures/README.md 可以看到,TensorZero 采用了一个明确的取舍原则:绝大多数 fixtures 存放在ui/fixtures目录,但部分体积较大的文件除外。所谓“大文件”,主要指数据行数庞大的 Parquet 表,例如large_chat_inference_v2.parquetlarge_json_inference_v2.parquet以及多张大型反馈(feedback)表。这些文件如果直接提交到 Git 仓库,会显著拖慢 clone、pull 与 CI 速度,因此仓库选择将它们托管在 Cloudflare R2 的公开桶中,仅在需要时按需拉取。

这一设计的好处是:

  • 仓库体积保持轻量,普通 fixture 只占用极小空间;
  • 大表数据可以独立演进、版本化(通过文件名后缀),不影响仓库历史;
  • 本地开发、CI、E2E 测试都可以从同一个 R2 桶获取一致的数据快照。

目录内还配套了 docker-compose.yml、load_fixtures.sh、check-fixtures.sh 以及若干download-*.py/upload-*.sh脚本,构成一整套“下载 → 装载 → 校验”的流水线。

拉取 fixtures 的两种方式

方式一:本地开发(无需任何凭证)

面向本地开发场景,README 推荐设置环境变量TENSORZERO_DOWNLOAD_FIXTURES_WITHOUT_CREDENTIALS=1后启动 docker compose:

TENSORZERO_DOWNLOAD_FIXTURES_WITHOUT_CREDENTIALS=1 docker compose -f docker-compose.yml up

该变量的作用会在两个层面生效:

  1. 装载脚本层面:在 load_fixtures.sh 中,脚本会根据该变量选择下载脚本:

    if [ "${TENSORZERO_DOWNLOAD_FIXTURES_WITHOUT_CREDENTIALS:-}" = "1" ]; then uv run ./download-small-fixtures-http.py else uv run ./download-small-fixtures.py fi

    大表(Parquet)部分在 load_fixtures.sh 同样遵循此分支逻辑。

  2. Compose 层面:在 docker-compose.yml 中,该变量被透传给fixtures容器,容器内执行cd /fixtures && ./load_fixtures.sh

无凭证方式的核心是HTTP-only 下载脚本

  • Parquet 大表uv run ./download-large-fixtures-http.py
  • JSONL 小表uv run ./download-small-fixtures-http.py

两个脚本都以 download_fixtures_consts.py 中定义的公共常量(如R2_PUBLIC_BUCKET_URLPART_SIZE = 8388608)为基础,通过requests流式下载,并用concurrent.futures.ThreadPoolExecutor并行拉取所有文件。下载前先通过requests.head获取远端 ETag,与本地文件的 S3/R2 风格 ETag 比对,仅当本地文件缺失或 ETag 不一致时才重新下载,避免重复流量。

需要特别说明的是,HTTP 路径的完整性校验同样严谨:download_file_http在下载完成后会重新计算本地 ETag 并与远端比对,不一致则抛出ETag mismatch并重试(最多 3 次,间隔 1 秒),从 download-large-fixtures-http.py 和 download-small-fixtures-http.py 中可以看到这一逻辑。calculate_etag实现了标准的分块 ETag 计算:文件小于PART_SIZE时直接取 MD5;否则按 8 MiB 分块计算各部分 MD5,再对拼接结果求 MD5 并附上分块数(<md5>-<num_parts>),这与 S3 多段上传的 ETag 约定一致。

方式二:CI / 带 R2 凭证(更快、更可靠)

当需要更快的下载速度与更强的可靠性时,README 建议设置R2_ACCESS_KEY_IDR2_SECRET_ACCESS_KEY两个环境变量,改用 S3 兼容协议下载:

  • Parquet 大表uv run ./download-large-fixtures.py
  • JSONL 小表uv run ./download-small-fixtures.py

带凭证路径的核心工具是s5cmd(一个高性能 S3 兼容 CLI)。以 download-large-fixtures.py 为例,脚本会把所有 fixture 拼接成一批sync s3://tensorzero-fixtures/<file> <dir>/命令,通过s5cmd --endpoint-url <R2_S3_ENDPOINT_URL> run批量执行;执行环境仅注入PATHAWS_ACCESS_KEY_IDAWS_SECRET_ACCESS_KEY(从R2_ACCESS_KEY_ID/R2_SECRET_ACCESS_KEY读取)。下载成功后还会调用verify_etags()逐一校验本地 ETag 与远端一致,任何一步失败都会按指数退避(1s、3s、9s)重试,最多 3 次。s5cmd的架构感知安装(x86_64 使用64bit、aarch64 使用arm64)可以在 ui/fixtures/Dockerfile 中找到。

小表脚本 download-small-fixtures.py 在此基础上多了一个远端文件名到本地文件名的重命名步骤rename_fixtures):因为 R2 上的文件带版本后缀(如model_inference_examples_20260224.jsonl),而本地希望保持稳定的文件名(如model_inference_examples.jsonl),详见 download_fixtures_consts.py 中的SMALL_FIXTURES映射表。

值得注意的是,两个带凭证脚本都会在缺少环境变量时直接抛错,并提示改用 HTTP 版本,避免误用。

两种方式的对比

维度无凭证(HTTP)带凭证(s5cmd)
适用场景本地开发CI / 持续集成
环境变量TENSORZERO_DOWNLOAD_FIXTURES_WITHOUT_CREDENTIALS=1R2_ACCESS_KEY_IDR2_SECRET_ACCESS_KEY
下载工具requests(ThreadPoolExecutor 并行)s5cmd sync批量同步
大表脚本download-large-fixtures-http.pydownload-large-fixtures.py
小表脚本download-small-fixtures-http.pydownload-small-fixtures.py
完整性校验本地/远端 ETag 比对 + 3 次重试ETag 全量校验 + 指数退避重试

fixtures 的目录布局与文件清单

ui/fixtures目录按用途拆分为多个子目录与脚本,整体布局如下(依据仓库实际内容):

  • small-fixtures/:JSONL 小表数据(如json_inference_examples.jsonlchat_inference_examples.jsonlboolean_metric_feedback_examples.jsonl等),通过load_fixtures.shINSERT ... FORMAT JSONEachRow方式导入;
  • large-fixtures/:Parquet 大表数据(如large_chat_inference_v2.parquet等 12 个文件),通过INSERT ... FROM INFILE ... FORMAT Parquet导入;
  • config/:装载时挂载给 gateway 使用的 TensorZero 配置;
  • dynamic_evals/:动态评估相关数据;
  • download-*.py/upload-*.sh:下载与上传脚本;
  • docker-compose*.yml:面向不同场景(默认、E2E、单元测试、UI、代理、config-in-db)的 Compose 编排;
  • load_fixtures.sh/load_fixtures_postgres.sh/check-fixtures.sh:ClickHouse/Postgres 装载与校验脚本。

download_fixtures_consts.py中还完整列出了小表映射与大表清单:小表共 13 项(如model_inference_examples_20260224.jsonl → model_inference_examples.jsonl),大表共 12 个 Parquet 文件(覆盖 Chat/Json 推理、模型推理与四类反馈的 chat/json 变体),这些清单同时被下载脚本与上传脚本复用,是保证远端与本地一致性的单一事实来源。

一键装载:Docker Compose 与 load_fixtures.sh 全流程

Compose 服务编排

ui/fixtures/docker-compose.yml 通过include引入 docker-compose-common.yml,共编排四类服务:

  1. clickhouseclickhouse:lts镜像,暴露 8123(HTTP)与 9000(Native)端口,提供健康检查;

  2. gateway:TensorZero gateway 服务,挂载./config为只读配置目录,连接到tensorzero_ui_fixtures数据库,并设置TENSORZERO_CLICKHOUSE_URLTENSORZERO_POSTGRES_URL

  3. postgres / gateway-postgres-migrations:Postgres 数据库与迁移执行服务(gateway --run-postgres-migrations);

  4. fixtures:核心装载容器,构建自 ui/fixtures/Dockerfile,挂载当前目录到/fixtures,并透传TENSORZERO_DOWNLOAD_FIXTURES_WITHOUT_CREDENTIALSR2_ACCESS_KEY_IDR2_SECRET_ACCESS_KEY等环境变量,最终执行:

    bash -c "cd /fixtures && ./load_fixtures.sh"

    其健康检查为检查/load_complete.marker文件是否生成,start_period: 300sretries: 72,意味着容器有约 6 分钟窗口完成装载。

fixtures容器依赖 gateway、postgres 与 migrations 全部健康后才启动,保证目标数据库 schema 已就绪。

load_fixtures.sh 装载流程

load_fixtures.sh 是整条流水线的核心,依次执行:

  1. 幂等保护:若已存在/load_complete.marker,直接退出 0,避免重复装载;
  2. 全表清空:动态查询system.tables,排除视图、.inner.*内部表、TensorZeroMigrationConfigSnapshot,批量执行TRUNCATE TABLE,保证每次装载都是干净快照;
  3. 下载小表:按TENSORZERO_DOWNLOAD_FIXTURES_WITHOUT_CREDENTIALS选择 HTTP 或 s5cmd 脚本;
  4. 导入 JSONL:用clickhouse-client ... INSERT INTO <Table> FORMAT JSONEachRow < file逐表导入,覆盖JsonInferenceChatInferenceBooleanMetricFeedbackFloatMetricFeedbackCommentFeedbackDemonstrationFeedbackModelInferenceChatInferenceDatapointJsonInferenceDatapointDynamicEvaluationRunDynamicEvaluationRunEpisode,并插入DeploymentID记录;
  5. 回填 InferenceEvaluationRuns:从TagInference中的推理标签(tensorzero::evaluation_run_idtensorzero::evaluation_nametensorzero::dataset_name)与各反馈表聚合出评估运行元数据,包含 metric 名称、value_type、function_type(chat/json)等。脚本注释说明这镜像了migration_0049中的回填逻辑,因为clean_start=true的新建数据库会跳过该迁移;
  6. 下载并导入大表:除非设置TENSORZERO_SKIP_LARGE_FIXTURES=1,否则通过INSERT INTO ... FROM INFILE './large-fixtures/xxx.parquet' SETTINGS input_format_parquet_use_native_reader_v3=0 FORMAT Parquet导入 12 个大表文件,之后sleep 2等待写入可见;
  7. 校验并落盘标记:执行 check-fixtures.sh 校验,成功后touch /load_complete.marker,供 Compose 健康检查使用。

check-fixtures.sh 校验逻辑

check-fixtures.sh 对每张表执行两类校验:

  • 行数匹配:将数据库中SELECT count()的结果与所有源文件(JSONL 按非空行计数、Parquet 用 pyarrow 读取num_rows)的合计行数比对;
  • 重复 id 检查:对FloatMetricFeedbackByTargetIdBooleanMetricFeedbackByTargetIdid分组,找出count() > 1的重复项。

任一环节不匹配都会以非零退出码失败,确保 fixture 数据与源文件严格一致。

编写与上传新 fixtures 的规范

README 明确要求:大 fixtures 不应提交进仓库,而是走“上传 R2 + 本地清单登记”的流程。

新增 Parquet 大表

  1. 将新 fixture 放入./large-fixtures
  2. 运行./upload-large-fixtures.sh上传;
  3. download-large-fixtures.py中登记新文件(实际清单维护在 download_fixtures_consts.py 的LARGE_FIXTURES列表中)。

upload-large-fixtures.sh 的实现很简洁:cdlarge-fixtures后执行

aws s3 --endpoint-url <R2_S3_ENDPOINT_URL> sync . s3://tensorzero-fixtures --checksum-algorithm CRC32

即把本地目录整体同步到 R2 桶。

新增 / 更新 JSONL 小表

小表的更新遵循版本后缀约定:

  1. 本地更新文件内容;
  2. 用版本后缀重命名(例如model_inference_examples_v2.jsonl);
  3. 运行./upload-small-fixtures.sh上传;
  4. 更新download-small-fixtures.py中的文件名映射(实际在 download_fixtures_consts.py 的SMALL_FIXTURES中维护“远端版本名 → 本地稳定名”)。

upload-small-fixtures.sh 内置了JSONL_FILES列表(与SMALL_FIXTURES的键一一对应),逐个用aws s3 cp上传,文件缺失时仅打印警告并跳过。版本后缀 + 稳定本地名的机制,让远端可保留历史版本,而本地消费方始终引用稳定的文件名。

上传凭证注意事项

README 特别提醒:上传需要 R2 凭证,且应使用子 shell 隔离环境变量,避免会话中的临时 AWS 凭证污染上传过程:

(unset AWS_SESSION_TOKEN AWS_CREDENTIAL_EXPIRATION; AWS_ACCESS_KEY_ID="$R2_ACCESS_KEY_ID" AWS_SECRET_ACCESS_KEY="$R2_SECRET_ACCESS_KEY" ./upload-small-fixtures.sh)

关键实现细节与工程要点

  • ETag 即完整性契约:无论 HTTP 还是 s5cmd 路径,都围绕“本地/远端 ETag 比对”做增量下载与完整性校验,calculate_etag同时兼容单块 MD5 与多块拼接两种形态;
  • 单一事实来源:文件清单集中在 download_fixtures_consts.py,下载脚本、上传脚本与校验脚本均以此为准,避免清单漂移;
  • 幂等与健康检查/load_complete.marker既是重复装载的短路开关,也是 Compose 健康检查的判定依据;所有 TRUNCATE 均在装载前执行,保证可重复构建;
  • 兼容云数据库:仓库 CI 中还提供了面向 ClickHouse Cloud 的装载脚本变体(参见 test-clickhouse-cloud.sh),说明同一套 fixture 流水线同样服务于云端环境验证。

小结

TensorZero UI 的 fixtures 体系是一条高度工程化的数据供给流水线:R2 托管大文件、HTTP/s5cmd 双通道下载、ClickHouse/Postgres 幂等装载、行数与重复 id 双重校验、版本后缀管理远端数据演进。无论你是想在本地快速跑起 UI 开发环境,还是为 CI 编写可靠的数据准备步骤,都可以直接复用 ui/fixtures 中的脚本与 Compose 编排,做到“一键拉取、一键装载、一键校验”。

【免费下载链接】tensorzeroTensorZero is an open-source LLMOps platform that unifies an LLM gateway, observability, evaluation, optimization, and experimentation.项目地址: https://gitcode.com/GitHub_Trending/te/tensorzero

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/15 14:38:00

五子棋AI项目实战:基于Qt和C++的界面搭建与事件处理

做五子棋AI这个项目&#xff0c;是我特别推荐给算法入门者的一条练手路线。五子棋规则足够简单&#xff0c;棋盘只有15x15&#xff0c;但搜索空间又不像围棋那么夸张&#xff0c;正好用来讲清楚极大极小搜索和α-β剪枝算法这两块博弈树的核心思想&#xff1b;界面部分用Qt和C来…

作者头像 李华
网站建设 2026/9/15 14:36:24

Tesseract字库训练全流程指南:从OCR原理到Python落地实践

先说一个我自己的真实经历。早几年接了个票据识别的需求&#xff0c;客户给了一批扫描件&#xff0c;字迹清楚、背景干净&#xff0c;我当时直接用tesseract-ocr的chi_sim默认字库跑&#xff0c;心想这还不简单。结果识别率惨不忍睹&#xff0c;数字串错位、某些字体下的汉字直…

作者头像 李华
网站建设 2026/9/15 14:36:02

前端内存泄漏实战指南:闭包、DOM残留与Chrome DevTools定位

1. 这不是理论题&#xff0c;是线上事故的复盘现场“内存泄漏”这四个字在前端团队里&#xff0c;从来不是面试时背诵的八股文&#xff0c;而是凌晨两点告警群里突然炸开的红色消息&#xff1a;“用户侧内存占用持续攀升&#xff0c;30分钟内上涨400MB&#xff0c;页面卡死率上…

作者头像 李华
网站建设 2026/9/15 14:34:59

告别手动复制:文件夹同步备份与FreeFileSync实战指南

1. 文件夹同步备份到底解决什么问题1.1 为什么手动复制根本不是"备份"先说个我自己的教训。早几年我帮朋友整理工作资料&#xff0c;他电脑里有个叫"设计稿最终版"的文件夹&#xff0c;里面堆了几十个版本&#xff0c;什么"最终版_v3""最终…

作者头像 李华