PostHog 沙箱化 Agent Evals 运行指南:本地 Docker 与远程 Modal 的完整实战
【免费下载链接】posthog:hedgehog: PostHog is the leading platform for building self-driving products. Our developer tools – AI observability, analytics, session replay, flags, experiments, error tracking, logs, and more – capture all the context agents need to diagnose problems, uncover opportunities, and ship fixes. Steer it all from Slack, web, desktop, or the MCP.项目地址: https://gitcode.com/GitHub_Trending/po/posthog
PostHog 的 Agent Eval 评测体系在独立评测(eval harness)中运行真实编码 Agent,让其在沙箱内操作种子化的 Hedgebox 项目并对其行为打分。本指南围绕仓库内 running-evals.md 的完整实战说明展开,结合 eval_harness 源码 深入讲解如何准备评测机器、配置环境变量,并在本地 Docker 沙箱与远程 Modal 沙箱两种运行时之间做出正确选择。读完本文,你将掌握从零跑通第一个 eval case、正确配置凭证与网络、以及快速诊断各类 setup 失败的完整能力。
两种沙箱运行时:架构与选择依据
独立评测 harness 的设计核心是"主次分离":无论选择哪个沙箱 provider,评测数据库、Django 服务器、LLM 网关、MCP 服务器、Temporal、personhog、案例(case)的团队与数据设置、以及最终的评分,全部运行在本地执行hogli evals的这台机器上;provider 改变的仅仅是"编码 Agent 的沙箱运行在哪里"。
- 远程 Modal(推荐):适用于多案例的沙箱化评测。远程沙箱并行运行,不消耗本地 Docker 内存,通常能显著缩短整体运行时间。当 Modal 凭证和具备 Funnel 能力的 tailnet 可用时优先选择。
- 本地 Docker:适用于单个案例的冒烟测试(smoke test)、离线工作,或 Modal 凭证 / Funnel tailnet 不可用的场景。
从 providers.py 源码可见两种策略的默认并发能力差异:Docker 策略default_max_sandboxes = 4,而 Modal 策略默认为None(无上限),这一差异直接影响后续--max-sandboxes参数的使用策略。
共享设置:所有运行的前置条件
在 flox shell 中运行
评测必须从仓库根目录、在 flox shell 内执行。personhog 的构建依赖 flox 提供的 Rust 工具链、pkg-config和 OpenSSL。如果当前没有激活 flox shell,不要进入交互式激活,而是把整条命令包进flox activate:
flox activate -- bash -c "hogli evals --list"日常运行统一使用hogli evals命令。它会自动加载仓库根目录的.env、.env.local、.env.development和.env.services文件,且已存在的 shell 环境变量优先。个人凭证应保存在已被 gitignore 的.env.local中,或直接放在 shell 环境里。
如果绕过 hogli 直接调用底层模块入口python -m products.posthog_ai.eval_harness.harness,则只加载.env文件,其余凭证需要自行 export。这也解释了为什么 env_preflight.py 的报错信息中会提示"把变量放进.env.local并通过hogli evals运行"。
必需的环境变量
所需的变量取决于选中的套件(suite)类型与运行时:
| 选择 | 必需变量 |
|---|---|
| 所有套件 | BRAINTRUST_API_KEY、LLM_GATEWAY_ANTHROPIC_API_KEY |
| 任何沙箱化套件 | 以上变量之外,还需SANDBOX_JWT_PRIVATE_KEY |
| Codex 运行时 | 以上变量之外,还需LLM_GATEWAY_OPENAI_API_KEY |
其中SANDBOX_JWT_PRIVATE_KEY用于签发沙箱内 Agent 的 API 令牌。本地开发用的沙箱 JWT 私钥随仓库根目录的 .env.example 提供,通常会在仓库初始化时被复制进.env。该密钥仅供本地开发使用,绝不能用于生产环境。
Preflight(预检)会在启动数据库或任何服务之前,报告每一个缺失的变量。BRAINTRUST_API_KEY其实是 Braintrust 评测引擎自身的required_env(),而非核心 harness 变量;沙箱化套件则额外要求SANDBOX_JWT_PRIVATE_KEY与LLM_GATEWAY_ANTHROPIC_API_KEY,one-shot 套件只要求LLM_GATEWAY_ANTHROPIC_API_KEY(直接使用,不经网关)。只有被选中套件对应类型的环境模型才会被检查,因此一次不含沙箱套件的运行不会强索沙箱变量。
发现套件:--list
正式运行前先用--list列出已发现的套件 ID:
hogli evals --list套件发现基于约定而非注册表:内置套件位于products/posthog_ai/evals/ /(如sql、experiments、surveys、product_analytics等),产品自有套件位于products/<product>/evals/。产品归属的套件 ID 形如<product>/<module>::<function>。选择器是子串匹配,未匹配的选择器会在任何资源被分配之前立即失败。
从单套件单案例起步
先跑一个套件、一个案例,让 setup 错误变得廉价且易于诊断:
hogli evals '<product>/eval_example::eval_my_thing' --eval '<case-name>'--eval只运行名称中包含该子串的案例。
one-shot 套件与沙箱标志的约束
纯 one-shot(一次进程内模型调用)的套件不使用沙箱 provider。不要为这类运行传入--provider、--max-sandboxes等沙箱专属标志——preflight 会直接拒绝这些标志而不是静默忽略。cli.py 中通过sandbox_flags_set机制追踪哪些沙箱标志被显式传入,一旦发现当前选择不含沙箱套件便报错,避免"用户以为限制了并发、实际被忽略"的误导。
团队克隆与案例播种的独立并发限制
harness 将"团队克隆 + 案例播种"阶段与沙箱并发分开限制:普通本地机器上该限制为 1,而当设置了CODER或CI环境变量时自动提升到 4。这样托管的开发环境与 CI 环境可以并行准备案例,同时让本地机器的 ClickHouse 内存占用保持保守。团队设置阶段可能产生大型 ClickHouse 拷贝或直接插入,独立限制可以防止 ClickHouse 内存耗尽——即使 Modal 沙箱容量无上限。
本地设置:Docker 沙箱
本地沙箱化评测默认使用 Docker。安装 Docker、启动守护进程并确认 CLI 可访问:
docker info无需手动构建沙箱镜像。每次运行前,harness 会检查posthog-sandbox-base镜像是否最新:当 Dockerfile 或已发布的 Agent 版本发生变化时自动重建。首次 personhog 构建和必要的镜像构建可能耗时数分钟,后续运行会复用其产物(personhog 的首次构建需要编译rust/下的personhog-replica与personhog-routercrate)。
先跑一个案例、一个活跃沙箱:
hogli evals '<suite-selector>' \ --eval '<case-name>' \ --provider docker \ --max-sandboxes 1--provider docker是可选的,因为 Docker 就是默认值。本地默认并发上限是 4 个沙箱,每个沙箱最多配置 16 GB 内存。除非宿主机内存足够支撑目标并发,否则尽量保持较低的--max-sandboxes(在 providers.py 中 Docker 策略的默认上限即为 4,且每个容器默认 16 GB)。
正常 teardown 会移除每个案例的容器并清扫本次运行的残留。如果某个失败案例需要检查容器,用--keep-sandbox-containers重跑,调试完毕后手动删除保留的容器。只有当强制重建镜像本身就是诊断的一部分时,才使用--rebuild-sandbox-image(该参数仅对 Docker 有效,cli.py 会拒绝在非 Docker provider 下使用)。
Notebook 内核沙箱(Docker 专属)
需要额外注意:含 python 或 duckdb 单元格的 notebook 案例,其内核会通过 Temporal workflow 申请第二个沙箱(约 2 GB,与 Agent 的 16 GB 并存)。这一通道仅支持 Docker provider——Modal provider 将SANDBOX_PROVIDER设为MODAL_EVALS,不在内核后端的取值集合中,端口映射与存活检查都会失效。因此含 python/duckdb 单元格的套件请使用--provider docker;纯 SQL 套件不受影响,可在任何 provider 下运行。
远程设置:Modal 沙箱(推荐)
远程沙箱化评测使用专用的posthog-sandbox-evalsModal 应用(与生产环境、本地开发沙箱完全隔离)。首先在 flox 环境内安装modal和tailscaleCLI,并确保两者都在PATH上。
一次性认证
认证 Modal:
modal token new这通常会写入~/.modal.toml。非交互式环境则需同时设置MODAL_TOKEN_ID和MODAL_TOKEN_SECRET。
连接 tailnet(一次性):
tailscale upFunnel 还需要:在 Tailscale 管理控制台的 ACL 中为 tailnet 和当前节点授予funnel节点属性,并为 tailnet 启用 HTTPS 证书。之后 harness 会在运行期间通过 Tailscale Funnel 暴露本地 Django API、LLM 网关和 MCP 服务器,每个服务占用一个 Funnel 公共端口(443、8443、10000)。不要手动设置SANDBOX_API_URL、SANDBOX_LLM_GATEWAY_URL或SANDBOX_MCP_URL——这些由 harness 自行生成。
Funnel 只服务这三个公共端口,所以当 tailnet 节点已占用其中任意一个时,harness 会拒绝启动而不是抢占端口。先释放端口:tailscale funnel --https=443 off,8443与10000同理。
第一个远程案例
用一个远程沙箱验证凭证、Funnel 和远程镜像构建:
hogli evals '<suite-selector>' \ --eval '<case-name>' \ --provider modal \ --max-sandboxes 1首次 Modal 运行会在远端构建评测镜像;后续运行复用该镜像,直到其 Dockerfile、构建上下文或打包的本地 skills 发生变化。保持本地进程和网络连接存活——远程沙箱需要回调到本地运行的服务。Modal 的沙箱回收策略包括按 workflow 终止逐个清理 + 按任务标签(task_id)的全局清扫,中断或崩溃的运行不会让沙箱一直计费到 TTL。
并发:Modal 默认无上限
Modal 并发默认无上限(providers.py 中default_max_sandboxes = None),因此在运行多案例产品域之前,务必主动选择一个有意的--max-sandboxes值:
hogli evals '<product-or-domain>' --provider modal --max-sandboxes 4teardown 与中断后的安全检查
harness 在 teardown 阶段会关闭自己的 Funnel 映射,并终止每个案例的任务标签沙箱、清扫本次运行的残留。但Funnel 配置存在于tailscaled中,生命周期比 harness 进程更长——被SIGKILL杀掉的运行可能让三个端口继续保持公开状态。用tailscale serve status检查,并手动关闭。运行期间 Funnel URL 对任何得知该地址的互联网用户都可达,因此不要分享这些 URL,中断后也不要让运行处于无人值守状态。
诊断 setup 失败
先读 preflight 错误——它会点名缺失的可执行文件、凭证或环境变量,并给出预期修复方式。常见原因:
- 在 flox 之外运行,导致 personhog 的 Rust 构建在工具链或 OpenSSL 依赖上失败;
- 本地模式下 Docker 守护进程已停止;
- 远程模式下缺少
~/.modal.toml,或MODAL_TOKEN_ID/MODAL_TOKEN_SECRET不完整; tailscaled未运行、节点未登录,或 tailnet 缺少funnel节点属性 / HTTPS 证书,导致 Funnel 无法提供三个远程可达的服务;- Funnel 公共端口(
443、8443、10000)已被其他服务占用,harness 拒绝覆盖; - 向 one-shot 套件传入了 provider 标志;
- 选择
--agent-runtime codex时遗漏了LLM_GATEWAY_OPENAI_API_KEY(codex 运行时默认模型为gpt-5.5,需要 OpenAI 密钥经 LLM 网关代理调用)。
日志与转录文件
每一次真实评测调用都会把完整 stdout/stderr 镜像到:
products/posthog_ai/eval_harness/logs/harness/<timestamp>_<id>.log终端最后一行是该转录文件的绝对路径,且logs/harness/latest.log指向最新一次运行。eval_harness 的 logs 目录 就是排查入口。--list和参数错误不会生成转录文件。
案例启动后,其 Agent 日志目录中会包含<case>.jsonl、<case>.artifacts.json和<case>.summary.txt,用于更深入的调试——这些原始 Agent 日志通常是查看 Agent 实际做了什么的最快途径。
补充:完整 CLI 参数与运行语义
harness/cli.py 定义了全部参数,与 eval_harness 的 README 中的表格一致:
| 参数 | 含义 |
|---|---|
--eval <substr> | 只运行名称包含该子串的案例 |
--provider {docker,modal} | 沙箱运行位置,默认docker |
--max-sandboxes N | 跨所有套件的活跃沙箱上限 |
--agent-model <model> | Agent 使用的模型,默认 claude 为claude-opus-5、codex 为gpt-5.5,固定以支持跨运行对比 |
--agent-runtime {claude,codex} | 服务模型的运行时,默认claude |
--skill-delivery {bundled,exec} | skill 投递方式,默认bundled;exec会移除沙箱内原生 skills |
--reasoning-effort <effort> | Agent 推理强度,合法值取决于运行时 + 模型 |
--keep-sandbox-containers | 跳过运行结束的 Docker 清扫(仅 Docker) |
--rebuild-sandbox-image | 强制重建posthog-sandbox-base镜像(仅 Docker) |
--create-db | 重建评测测试数据库而非复用 |
--case-timeout <seconds> | Agent 运行预算(最少 1 秒),从案例团队设置完成后开始计时 |
--trials N | 每个案例运行 N 次(Braintrust trials),用于度量随机 Agent 的方差 |
--fail-under <fraction> | 平均分低于该比例(0-1)时以非零码退出 |
--list | 打印发现的套件 ID(含类型)并退出 |
几个关键语义值得注意:
- Agent 模型默认值:
claude运行时默认claude-opus-5,codex运行时默认gpt-5.5,且必须使用裸模型 ID(无anthropic/、openai/前缀),否则 LLM 网关会以 403 拒绝。若模型前缀与运行时不匹配,CLI 会在运行前报错而不是等到运行几分钟后以模糊的网关 403 失败。 - 并发模型:所有选中套件在同一事件循环上并发运行,全局信号量跨套件限制活跃沙箱数;
--max-sandboxes只覆盖"团队设置 + Agent 运行"窗口,日志解析、Braintrust span 构建、trace 发射和评分都在槽位释放后进行。case 的超时也从团队设置完成后才开始,排队不消耗 Agent 预算。Sandbox-only 标志(--provider、--max-sandboxes、--agent-runtime、--skill-delivery、--reasoning-effort、--keep-sandbox-containers、--rebuild-sandbox-image)在无沙箱套件被选中时会被 preflight 拒绝。 - 输出语义:进度行使用稳定标签(
SUITE START、EXPERIMENT START、CASE DONE、EXPERIMENT DONE、SUITE DONE),只有整体运行使用PASS/FAIL;崩溃的套件标记为CRASH并附 traceback,使运行以非零码退出但不影响其他套件。设置EXPORT_EVAL_RESULTS=1可额外在每个实验后向eval_results.jsonl追加一条结构化 JSON 摘要。
结语
从单案例冒烟测试到大规模并行评测,PostHog 的 eval harness 把"基础设施留在本地、沙箱按需伸缩"做到了清晰的分层:Docker 适合快速验证与离线开发,Modal + Tailscale Funnel 则把多案例评测扩展到无本地内存压力的大规模并行。牢记环境变量矩阵与 Funnel 端口的独占约束,善用 preflight 报错与logs/harness/latest.log转录,即可稳定复现、对比与调试每一次 Agent 评测。更深入的架构细节(套件发现约定、新增套件的完整工作流、评分器模式)可继续阅读 eval_harness 的 README 与 writing-evals 技能文档。
【免费下载链接】posthog:hedgehog: PostHog is the leading platform for building self-driving products. Our developer tools – AI observability, analytics, session replay, flags, experiments, error tracking, logs, and more – capture all the context agents need to diagnose problems, uncover opportunities, and ship fixes. Steer it all from Slack, web, desktop, or the MCP.项目地址: https://gitcode.com/GitHub_Trending/po/posthog
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考