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.parquet、large_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该变量的作用会在两个层面生效:
装载脚本层面:在 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 同样遵循此分支逻辑。
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_URL、PART_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_ID和R2_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批量执行;执行环境仅注入PATH、AWS_ACCESS_KEY_ID、AWS_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=1 | R2_ACCESS_KEY_ID、R2_SECRET_ACCESS_KEY |
| 下载工具 | requests(ThreadPoolExecutor 并行) | s5cmd sync批量同步 |
| 大表脚本 | download-large-fixtures-http.py | download-large-fixtures.py |
| 小表脚本 | download-small-fixtures-http.py | download-small-fixtures.py |
| 完整性校验 | 本地/远端 ETag 比对 + 3 次重试 | ETag 全量校验 + 指数退避重试 |
fixtures 的目录布局与文件清单
ui/fixtures目录按用途拆分为多个子目录与脚本,整体布局如下(依据仓库实际内容):
small-fixtures/:JSONL 小表数据(如json_inference_examples.jsonl、chat_inference_examples.jsonl、boolean_metric_feedback_examples.jsonl等),通过load_fixtures.sh以INSERT ... 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,共编排四类服务:
clickhouse:
clickhouse:lts镜像,暴露 8123(HTTP)与 9000(Native)端口,提供健康检查;gateway:TensorZero gateway 服务,挂载
./config为只读配置目录,连接到tensorzero_ui_fixtures数据库,并设置TENSORZERO_CLICKHOUSE_URL与TENSORZERO_POSTGRES_URL;postgres / gateway-postgres-migrations:Postgres 数据库与迁移执行服务(
gateway --run-postgres-migrations);fixtures:核心装载容器,构建自 ui/fixtures/Dockerfile,挂载当前目录到
/fixtures,并透传TENSORZERO_DOWNLOAD_FIXTURES_WITHOUT_CREDENTIALS、R2_ACCESS_KEY_ID、R2_SECRET_ACCESS_KEY等环境变量,最终执行:bash -c "cd /fixtures && ./load_fixtures.sh"其健康检查为检查
/load_complete.marker文件是否生成,start_period: 300s、retries: 72,意味着容器有约 6 分钟窗口完成装载。
fixtures容器依赖 gateway、postgres 与 migrations 全部健康后才启动,保证目标数据库 schema 已就绪。
load_fixtures.sh 装载流程
load_fixtures.sh 是整条流水线的核心,依次执行:
- 幂等保护:若已存在
/load_complete.marker,直接退出 0,避免重复装载; - 全表清空:动态查询
system.tables,排除视图、.inner.*内部表、TensorZeroMigration与ConfigSnapshot,批量执行TRUNCATE TABLE,保证每次装载都是干净快照; - 下载小表:按
TENSORZERO_DOWNLOAD_FIXTURES_WITHOUT_CREDENTIALS选择 HTTP 或 s5cmd 脚本; - 导入 JSONL:用
clickhouse-client ... INSERT INTO <Table> FORMAT JSONEachRow < file逐表导入,覆盖JsonInference、ChatInference、BooleanMetricFeedback、FloatMetricFeedback、CommentFeedback、DemonstrationFeedback、ModelInference、ChatInferenceDatapoint、JsonInferenceDatapoint、DynamicEvaluationRun、DynamicEvaluationRunEpisode,并插入DeploymentID记录; - 回填 InferenceEvaluationRuns:从
TagInference中的推理标签(tensorzero::evaluation_run_id、tensorzero::evaluation_name、tensorzero::dataset_name)与各反馈表聚合出评估运行元数据,包含 metric 名称、value_type、function_type(chat/json)等。脚本注释说明这镜像了migration_0049中的回填逻辑,因为clean_start=true的新建数据库会跳过该迁移; - 下载并导入大表:除非设置
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等待写入可见; - 校验并落盘标记:执行 check-fixtures.sh 校验,成功后
touch /load_complete.marker,供 Compose 健康检查使用。
check-fixtures.sh 校验逻辑
check-fixtures.sh 对每张表执行两类校验:
- 行数匹配:将数据库中
SELECT count()的结果与所有源文件(JSONL 按非空行计数、Parquet 用 pyarrow 读取num_rows)的合计行数比对; - 重复 id 检查:对
FloatMetricFeedbackByTargetId、BooleanMetricFeedbackByTargetId按id分组,找出count() > 1的重复项。
任一环节不匹配都会以非零退出码失败,确保 fixture 数据与源文件严格一致。
编写与上传新 fixtures 的规范
README 明确要求:大 fixtures 不应提交进仓库,而是走“上传 R2 + 本地清单登记”的流程。
新增 Parquet 大表
- 将新 fixture 放入
./large-fixtures; - 运行
./upload-large-fixtures.sh上传; - 在
download-large-fixtures.py中登记新文件(实际清单维护在 download_fixtures_consts.py 的LARGE_FIXTURES列表中)。
upload-large-fixtures.sh 的实现很简洁:cd到large-fixtures后执行
aws s3 --endpoint-url <R2_S3_ENDPOINT_URL> sync . s3://tensorzero-fixtures --checksum-algorithm CRC32即把本地目录整体同步到 R2 桶。
新增 / 更新 JSONL 小表
小表的更新遵循版本后缀约定:
- 本地更新文件内容;
- 用版本后缀重命名(例如
model_inference_examples_v2.jsonl); - 运行
./upload-small-fixtures.sh上传; - 更新
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),仅供参考