评测架构

August 17, 2026 · View on GitHub

English

评测架构

HumanClawBench 只有一套 single-episode evaluation implementation。公开的 run 命令选择一个 episode、val100、fullval 或自定义列表,然后 dispatcher 把它们调度为相互独立的内部 rollout subprocess;不存在第二套 agent loop 或 metric implementation。

最短的推荐阅读顺序:

  1. main.py:命令入口和分发。
  2. evaluation/evaluator.py: 对象构造、episode loop 和 artifact 生命周期。
  3. agent/planner.py:planner、可选 verifier 和最终 action 选择。
  4. motion/runner.py:带条件的 motion generation 和 autoregressive motion state。
  5. envs/find_nav_interact_env.pyenvs/half_physics_env.py: task semantics、Habitat 和物理执行。
  6. evaluation/trajectory.pyevaluation/video.pyevaluation/metrics/episode.py: 三条输出分支。

1. 命令入口流程

flowchart TD
    CLI["humanclaw-bench"] --> MAIN["main.main()"]
    MAIN --> PARSE["main._build_parser()<br/>解析一个 subcommand"]
    PARSE --> CMD{"command"}

    CMD -->|run| SELECT["解析 episode 集合和可见 GPU<br/>workers_per_gpu -> 总并发"]
    SELECT --> BATCH["batch.run_batch()"]
    BATCH --> LIST["list_episode_specs()<br/>one / val100 / fullval / 自定义列表"]
    LIST --> RESUME["resume / 分配当前负载最低的 GPU"]
    RESUME --> VERIFY["一次性验证全部 motion weights"]
    VERIFY --> SPAWN["subprocess.Popen()<br/>内部 rollout 命令"]

    SPAWN --> PROFILE["config.load_config()<br/>完整 release profile"]
    PROFILE --> EV["HCFindNavInteractEvaluator(config)"]
    EV --> CHECK["check_config_valid()<br/>HSSD + model config + rollout 数量"]
    CHECK --> EVAL["evaluate_main()"]
    EVAL --> RUN["run_episode_rollouts()"]
    RUN --> ONE["run_rollout()<br/>唯一的 episode loop"]

    SPAWN --> WAIT["轮询直到全部 episode child 退出"]
    WAIT -->|仅 --metrics| AGG["aggregate_metric_files()<br/>metrics_summary.json"]
    WAIT --> SUMMARY["completed / skipped / failed 数量"]

    CMD -->|render| DELAYED["render_saved_trajectory()<br/>仅赋 pose + rasterization"]
    CMD -->|render-batch| DELAYED_BATCH["render_saved_batch()<br/>隔离的 render subprocess"]

pyproject.toml 中的 console script 直接指向 main.main()run_batch() 从 canonical .json.gz shard 读取 episode identity,并为每个 episode 启动 同一个内部 rollout 命令。--gpus auto 遵守 CUDA_VISIBLE_DEVICES;显式 GPU ID 和 --workers-per-gpu 只影响调度,不改变评测语义。

2. Evaluator 的构造

flowchart TD
    START["HCFindNavInteractEvaluator.evaluate_main()"] --> EP["episodes.load_episode()<br/>选择 scene / episode / target"]
    EP --> INST["apply_instruction_version()<br/>需要时附加 Sit goal"]
    INST --> MODEL["vlm.factory.build_model()<br/>OpenAI-compatible 或 filesystem queue"]
    MODEL --> ENV["HCFindNavInteractEnv()<br/>构造 Habitat + human + 所需 sensors"]
    ENV --> MOTION["MotionSkillRunner()<br/>reset 时 lazy load weights"]
    MOTION --> AGENT["PSVEgoAgent()<br/>prompt v4 + verifier v3"]
    AGENT --> ROLLOUTS["run_episode_rollouts()"]
    ROLLOUTS --> ROLLOUT["run_rollout(episode, rollout_index)"]

    ROLLOUT --> ARTEFACTS["清理该确定性输出目录中<br/>先前 evaluator 生成的 artifact"]
    ARTEFACTS --> VIDEO{"--video?"}
    VIDEO -->|是| WRITER["RolloutVideoWriter + exo sensor sink"]
    VIDEO -->|否| RESET
    WRITER --> RESET["agent.reset() + _reset_env_for_rollout()"]
    RESET --> MRESET["motion.reset()<br/>加载 weights + 固定初始 body state"]
    MRESET --> ERESET["env.reset()<br/>放置 human + 首张 ego render"]
    ERESET --> TRAJ["_new_trajectory_recorder()<br/>捕获准确 reset state"]
    TRAJ --> METRIC{"--metrics?"}
    METRIC -->|是| MREC["_new_metric_recorder()"]
    METRIC -->|否| LOOP["closed-loop decision steps"]
    MREC --> LOOP

HCFindNavInteractEpisode 是标准化的 task record:包含 instruction、scene、 target category/instance、start pose 和最大 step 数,不包含 simulator 或 rollout 结果。HCFindNavInteractEnv 持有 live scene;MotionSkillRunner 持有 motion model state;PSVEgoAgent 持有 planner state;evaluator 管理它们的生命周期 并完成连接。

这些模块之间传递的主要 value 有意保持紧凑并带类型:

Value产生者使用者含义
HCFindNavInteractEpisodeload_episode()agent、environment、recorder不可变的 task、target、scene、spawn 和 step limit。
HalfPhysicsObservationenv.reset() / env.step()evaluator当前 realized ego RGB;不会向 VLM 暴露隐藏 simulator state。
PlannerResultagent.act()evaluatorParsed planner/verifier record,以及唯一权威的 final SkillCall
GeneratedMotionmotion.generate()trajectory recorder、environmentWorld-frame 75-D SMPL-X 表示的 15 个 requested pre-physics frame。
info["body_state"]env.step()trajectory recorderHalfPhysics 实际实现的对应 15 个 humanoid pose。
info["object_states"]env.step()trajectory recorder每个 dynamic object 的 frame-aligned pose。
info["metric_frames"]metric 模式下的 env.step()metric recorder仅内存共享的 contact rows;绝不写 raw trace。
historyevaluator _history_item()下一次 planner call只保留有界 semantic/action context;不复制图像和 raw provider payload。

Environment reward 始终返回 0.0;评测由 VLM closed loop 和最终论文指标 驱动,不使用 RL reward signal。

3. 一个 closed-loop decision step

flowchart TD
    OBS["当前 HalfPhysicsObservation.head_rgb"] --> FIND{"metrics enabled?"}
    FIND -->|是| SEM["env.metric_find_observation()<br/>统计这张图像中的 target pixels"]
    FIND -->|否| ACT["agent.act(image, history, [])"]
    SEM --> ACT

    ACT --> PROMPT["planner._plan_skill_prompt()<br/>goal + 有界 semantic history"]
    PROMPT --> CALL1["_call_with_retries(percept_mid_low)<br/>同一 state,最多 5 次"]
    CALL1 --> PROPOSE["_chooser_action()<br/>planner JSON -> proposed SkillCall"]
    PROPOSE --> ROUTE["_verifier_prompt()"]
    ROUTE --> NEED{"该 action 是否有 verifier route?"}
    NEED -->|否| ACCEPT["synthetic accept<br/>没有第二次 provider call 或 log"]
    NEED -->|是| CALL2["_call_with_retries(verifier)<br/>同一图像,最多 5 次"]
    CALL2 --> FINAL["_verifier_action()<br/>接受或替换 proposed action"]
    ACCEPT --> NORMAL["_normalize_verifier_plan()"]
    FINAL --> NORMAL
    NORMAL --> RESULT["PlannerResult.action<br/>唯一权威 final SkillCall"]

    RESULT --> LOG["_write_step_vlm_records()<br/>每个逻辑 stage 一份 JSON"]
    LOG --> MGATE{"metrics enabled?"}
    MGATE -->|是| MDEC["PaperMetricRecorder.record_decision()"]
    MGATE -->|否| STOP{"final action 是 Stop?"}
    MDEC --> STOP

    STOP -->|是| COMMIT["env.step(stop)<br/>提交当前 pose 并结束"]
    STOP -->|否| GEN["motion.generate(skill, condition)"]
    GEN --> BEFORE["trajectory.record_before()<br/>generated pre-physics body chunk"]
    BEFORE --> ESTEP["env.step(body chunk)"]
    ESTEP --> AFTER["trajectory.record_after()<br/>realized human + dynamic objects"]
    AFTER --> MOGATE{"metrics enabled?"}
    MOGATE -->|是| MMOTION["metric_recorder.record_motion()"]
    MOGATE -->|否| HISTORY["append minimal _history_item()"]
    MMOTION --> HISTORY
    COMMIT --> HISTORY
    HISTORY --> DONE{"Stop 或达到 max steps?"}
    DONE -->|否| OBS2["最终 realized ego frame"]
    OBS2 --> OBS
    DONE -->|是| FINISH["_finalize_rollout_artifacts()"]

    CALL1 -. 连续 5 次 provider / JSON error .-> PFALLBACK["Walk forward slow<br/>跳过 verifier"]
    PFALLBACK --> NORMAL
    CALL2 -. 连续 5 次 provider / JSON error .-> FAILOPEN["接受 planner proposal"]
    FAILOPEN --> NORMAL

Planner call 每一步都会发生。Verifier v3 只在 Walk、Stop、Climb upstairs、 第一次 turn-for-sit 或 stop-after-sit 对应 route 上调用。其他 skill 直接通过 内存中的 synthetic accept 使用 planner proposal。无论哪种情况,唯一被执行的 都是 PlannerResult.action;若 verifier 替换了 action,原 proposal 绝不会执行。

Retry 不会重置 Habitat 或 motion state。Planner 连续 5 次仍没有有效 JSON 时, 执行一次 Walk<forward><slow>,再根据得到的新 observation 规划。由于同一 provider path 刚连续失败 5 次,这个 fallback 不再调用 verifier。Verifier 连续 5 次失败时,则接受已有的有效 planner proposal 并继续。

4. 从一个 SkillCall 到下一张 ego image

flowchart TD
    SKILL["final SkillCall"] --> COND["conditioning.condition_args()<br/>skill-specific numeric tensor"]
    COND --> DENOISE["solver.denoise()<br/>30 次 midpoint integration"]
    DENOISE --> STATE["15 x 219 generated motion state"]
    STATE --> XB["state_to_xb() + decanonicalize_xb()<br/>15 x 75 world-frame SMPL-X"]
    XB --> RECANON["canonicalize_from_world()<br/>最后 5 帧成为下一段 history"]
    XB --> ENVSTEP["HalfPhysicsEnv.step()"]

    ENVSTEP --> PARSE["_parse_motion_action()<br/>Y-up 75D -> HalfPhysics pose"]
    PARSE --> FRAMES["_step_runtime_motion()<br/>遍历 15 帧"]
    FRAMES --> HP["hp.hp_step()<br/>一个 requested 30 Hz frame"]
    HP --> SUB["120 Hz 下 4 个 Bullet substep<br/>root command + gravity + PJSC + contacts"]
    SUB --> READ["_read_agent_state()<br/>realized human pose"]
    READ --> OBJECTS["_record_object_state()<br/>每个 dynamic object pose"]
    OBJECTS --> CONTACT_GATE{"metrics enabled?"}
    CONTACT_GATE -->|是| CONTACTS["每帧一次共享 contact query"]
    CONTACT_GATE -->|否| VIDEO_GATE{"video enabled?"}
    CONTACTS --> VIDEO_GATE
    VIDEO_GATE -->|是| RGBALL["每帧 ego + exo render -> ffmpeg"]
    VIDEO_GATE -->|否| RGBFINAL["只渲染最终 ego frame"]
    RGBALL --> NEXT["observation + info"]
    RGBFINAL --> NEXT

两份 trajectory 有意记录边界两侧:trajectory_before.npz 是 generator 请求的 motion;trajectory_after.npz 是 Habitat 在 gravity/contact 后实际得到的 pose。 后者同时包含 dynamic-object pose,所以 delayed video 和 disturbance analysis 不需要再次 physics rollout。

5. Finalization 与输出分支

run_rollout()finally 中 finalization,所以 provider call 失败时仍可留下 有用的 partial replay。顺序如下:

flowchart TD
    END["episode loop 退出"] --> UNLOAD["motion.unload()<br/>释放 GPU model tensor"]
    UNLOAD --> VCLOSE["video_writer.close()<br/>仅在启用时"]
    VCLOSE --> MAT["trajectory.materialize()<br/>before/after 各 concatenate 一次"]
    MAT --> SUCCESS{"metrics enabled 且 rollout 成功?"}
    SUCCESS -->|是| MF["PaperMetricRecorder.finalize()<br/>success + collision + disturbance + jerk + cost"]
    MF --> MW["写 metrics.json"]
    SUCCESS -->|否| TW["trajectory.write()"]
    MW --> TW
    TW --> REPLAY["trajectory_before.npz<br/>trajectory_after.npz<br/>replay_manifest.json"]
    REPLAY --> CLOSE["env.close()<br/>释放 Habitat"]

Baseline 路径始终写 per-call VLM record 和 replay bundle。--video 只增加 frame sink 与两份 MP4;--metrics 只增加 semantic/contact collection 和一份 最终 metrics.json。两个 flag 相互独立,可以同时使用。

6. 逐函数职责

入口、episode 选择与调度

文件与函数职责
main._build_parser()定义公开 subcommand 与 flag,不执行评测。
main.main()分发命令,将参数转换为 evaluator 或 batch config。
config.load_config()加载并验证完整、不继承的 experiment profile。
batch._load_episode_subset()对照 full validation 验证 val100.json 等透明子集。
batch._rollout_complete()只检查当前 flag 要求的最终 artifact,实现 --resume
batch._least_loaded_device()在指定 GPU 间平衡 live subprocess。
batch.run_batch()选择 episode、一次验证 weights、启动 rollout subprocess,并按需聚合 metrics。
episodes.list_episode_specs()直接从 scene shard 列出 canonical episode identity。
episodes.load_episode()选择并验证一个 shard entry,返回 HCFindNavInteractEpisode
episodes.apply_instruction_version()为支持 interaction 的 category 添加最终 Sit instruction。
HCFindNavInteractEvaluator.check_config_valid()在重计算前检查 HSSD、model config、rollout 数量和 profile/model compatibility。
HCFindNavInteractEvaluator.evaluate_main()构造 episode、VLM adapter、environment、motion runner 和 agent。
_clear_generated_rollout_artifacts()主动复用 rollout 目录时,只删除已知 evaluator output。
rollout_dir()构造确定性的 episode/rollout 输出路径。
run_episode_rollouts()对选定 episode 运行相互独立的 rollout index。
run_rollout()持有唯一 closed-loop evaluation loop 和全部 runtime cleanup。
_reset_env_for_rollout()先 reset motion、转换 seed pose,再把 physical environment reset 到同一 pose。
_new_trajectory_recorder()捕获准确 reset-time human/object state 和 replay metadata。
_new_metric_recorder()仅在请求时 lazy import 并构造 metric stack。
_history_item()只保留下次 prompt 所需的 semantic planner/action field。
_write_step_vlm_records()每个逻辑 planner/verifier stage 写一份小型 prompt/final-response JSON。
_finalize_rollout_artifacts()关闭 video、共享一次 trajectory materialization、写 replay 并关闭 Habitat。

Agent 与模型调用

文件与函数职责
vlm.factory.build_model()选择 direct OpenAI-compatible 或 filesystem-queue adapter。
OpenAICompatibleModel.respond()发出一次 multimodal chat request,并暴露 provider token usage。
FilesystemQueueModel.respond()与持有 credential 的外部 worker 交换一次 request/response。
prompts.resolve_prompt_version() / render()选择并组合一个 self-contained planner prompt version。
verifiers.resolve_verifier_version()加载 self-contained verifier,并检查其 interface。
HumanClawBenchPSVPlanSkillPlanner._call_with_retries()在不重置 episode 的情况下,原地重试当前 planner/verifier stage,最多 5 次。
verifiers.v3.route_prompt() / render()只选择并组合 proposed action 所需的 verifier route。
PSVEgoAgent.reset() / act()面向 benchmark 的轻量 planner wrapper。
planner.reset()Episode 开始时清空 current plan 和 turn-for-sit state。
_plan_skill_history()构造有界 semantic history,不重复 image、raw provider text 或 token record。
_plan_skill_prompt()根据 goal、当前 step 和 history 渲染指定版本 prompt。
_call()附加 ego image、调用一次 VLM、snapshot usage,并把 transport/JSON error 保存在 stage result 中。
_chooser_action() / _clamp_*()把 action ID/name 转换为落在支持数值范围内的 SkillCall
_planner_from_plan_skill() / _skiller_from_plan_skill()将单一 planner response 拆成 planning field 和 executable action field。
_apply_plan_skill_output()更新有界 mid-level plan/horizon state。
_gate_turn_for_sit_verifier()一个 sequence 中只为第一次 preparatory turn 调用 turn-for-sit verifier。
_verifier_prompt()选择并渲染 verifier-v3 route;不适用时返回空。
_verifier_action()将 accept/replace verdict 转换为最终 executable action。
_normalize_verifier_plan()在稳定 verifier record 中同时保存 proposed/final action。
_combined_raw_plan()生成供 environment/history 使用的小型 planner/verifier reasoning view。
_update_turn_for_sit_state()只携带下一次 sitting transition 所需的 approval state。
planner.act()调度以上函数,返回含唯一权威 final action 的 PlannerResult

Motion generation

文件与函数职责
checkpoints.load_skill_models()验证固定文件、重建准确 base variant、挂接每个 ControlNet,并移动到 device。
motion.networks.*.forward()实现固定 MotionDiT/skill-control forward;不含训练或 checkpoint 选择逻辑。
MotionSkillRunner.load_skills() / unload()Lazy load 请求的 skill set,并在结束后释放 tensor。
MotionSkillRunner.reset()加载五帧 seed,初始化 episode/world reference transform。
conditioning.condition_args()把公开 skill condition 转换为各 network 的训练输入 convention。
solver.denoise()用 midpoint flow solver 从 Gaussian noise 生成 15 个 future frame。
state_to_xb() / state_to_joints()从 219-D model state 提取 SMPL-X body parameter/joint。
decanonicalize_xb() / decanonicalize_joints()将 canonical model output 放入累积的 episode world frame。
canonicalize_from_world()把最后五个 generated world frame 转为下一 skill call 的 model history。
MotionSkillRunner.generate()连接 condition、denoising、world placement 和 history update,返回 GeneratedMotion

Environment 与 HalfPhysics

文件与函数职责
HCFindNavInteractEnv.reset()只在 metric 模式解析 target instance,reset HalfPhysics、应用 lighting,并返回首张 ego view。
_resolve_target_refs() / _assign_target_semantic_ids()将 dataset goal 匹配到 live Habitat instance,并为 metric-only semantic render 标注。
metric_find_observation()在当前 VLM 图像中统计 target semantic pixel。
metric_target_geometry()计算 Nav metric 的最终 body-to-target AABB distance。
is_pelvis_target_contact()将 pelvis contact 匹配到准确 target mesh instance。
HCFindNavInteractEnv.step()将显式 Stop 路由到 _stop_step();其他 motion chunk 进入 HalfPhysicsEnv.step()
HalfPhysicsEnv._build_runtime()加载指定 backend/asset,并只构造一次 Habitat 与 humanoid。
_build_simulator()只创建当前 flag 所需 sensor,并加载 physical scene。
_load_agent()按需加载 URDF、SMPL-X mapping、collision body、friction、link ID 和 metric name。
HalfPhysicsEnv.reset() / _reset_runtime_pose()放置准确 seed pose,并渲染已启用的初始 observation。
_parse_motion_action()验证 generated chunk,将 75-D Y-up motion 转为 simulator pose layout。
_step_runtime_motion()推进每个 generated frame,记录 realized human/object state,并只采集启用的 observation。
hp.hp_step()将一个 requested 30 Hz pose delta 转为四个带 gravity、capped drive、PJSC 和 contact 的 120 Hz Bullet substep。
hp.approximate_*hp.quat_*_shortest_arc_joint_velocity()计算 root/joint drive,避免 quaternion long-arc velocity error。
hp._apply_*gravity()按配置的 substep timing 应用 human-root gravity 和持续 movable-object gravity。
hp._ensure/_update/_zero_pjsc_joint_motors()在一帧内创建、推进并关闭 per-joint position-correction motor。
_read_agent_state() / _record_object_state()将 live Habitat state 转回 frame-aligned replay 坐标。
_update_cameras()将 ego 和可选 exo camera 绑定到 realized humanoid pose。

Replay、video 与 metrics

文件与函数职责
build_replay_metadata()固定 episode、physics、camera、asset path/hash 和 coordinate-frame contract。
TrajectoryRecorder.record_before()追加 generated 75-D pre-physics chunk 及其 action boundary。
TrajectoryRecorder.record_after()为同一批 frame 追加 realized human 和全部 dynamic-object pose。
TrajectoryRecorder.materialize()只 concatenate 一次 record,并在 metric/serialization 间共享。
TrajectoryRecorder.write()写两份 compressed NPZ 和带 hash 的 replay manifest。
RolloutVideoWriter.append() / close()将同步 ego/exo RGB 直接送入两个 H.264 pipe,并安全关闭。
PaperMetricRecorder.record_decision()根据当前 image/decision pair 更新 Find、active-stop 和 token usage。
PaperMetricRecorder.record_motion()让 collision、disturbance 和 final-frame Sit contact 复用同一 30 Hz contact stream。
PaperMetricRecorder.finalize()计算 terminal Nav/Interact、disturbance、Motion Jerk、cost 和已累计的 collision。
claims_target_visible()对 planner visible-state text 应用 target-name/negation 规则。
body_to_target_aabb_distance()计算 NavSR 的最终 3D body-point 到 target-AABB 最小距离。
UsageTracker.record() / summary()标准化 provider usage,或明确标记 fallback token estimate。
CollisionTracker.record_reset() / record_step()一次记录 reset penetration,随后从其他 metric 共用的 post-physics 30 Hz rows 分类 fixed contact。
DisturbanceTracker.record_step() / finalize()在线跟踪有时间顺序的 affected object,并计算 realized path length。
root_rigid_motion_jerk()在论文有效时间尺度上评价 generated pre-physics root-rigid motion。
aggregate_metric_files()将完成的 per-episode metric 文件聚合为完整表格字段。
render_saved_trajectory()单独恢复 saved post-physics pose 并 rasterize video,不调用 VLM、motion generation 或 physics step。
render_saved_batch()在隔离的 Habitat/OpenGL subprocess 中执行 delayed render job。

私有 JSON、array-shape、quaternion 和 path helper 支持上述函数,但不引入额外 evaluation stage。

Half-Physics frame contract

envs/half_physics/hp.py 是唯一 production backend。一个 generated 30 Hz frame 被解析为四个 120 Hz Bullet substep。Generated root x/z velocity 在从零 计数的 substep 0 和 2 赋值;contact 可在 substep 1 和 3 改变它。Root angular velocity 只在 substep 0 前赋值一次。Vertical root velocity 始终由 gravity/contact 驱动。

每个 joint drive 和 root angular drive 都限制为每个 generated frame 30 度。 Quaternion sign change 在限制前转换为 shortest arc。PJSC expected-position trajectory 使用未限制的 velocity,所以该安全限制不会减慢其 target。两个 shoulder 的固定 PJSC gain 为 0.03,两个 wrist 为 0.1。Habitat 对 Python 可见的 所有 human child link 应用 friction 0.4;pelvis base 不由 Habitat per-link API 暴露。humanclaw.physics_config.json 中的 friction_coefficient: 1.0 有意表示 scene/default 值,不是 human-link override。

每个真实 Bullet substep 前,movable object 都收到 mass * gravity。Backend、 command schedule、PJSC gain、human friction 和重命名后的 physics config 都写入 replay_manifest.json,便于 delayed diagnosis 查看准确 runtime contract。

--video 构造 exo sensor 并渲染每个 physics frame。Frame 直接进入两个 ffmpeg pipe,因此内存有界,且不产生 frame 文件。

独立的 render 命令是快速 delayed-video 路径。它读取 trajectory_after.npz,恢复已记录的人体和全部 dynamic object,更新两台 camera,并在不推进 physics 的情况下 rasterize。render-batch 为每个任务启动 一个 renderer subprocess,使 OpenGL context 保持隔离,同时在指定 GPU 上并行。 该路径不写 PNG,也不执行 VLM、motion generation、contact、semantic 或 metric 工作。

--metrics 构造 semantic sensor,并在每个 realized 30 Hz pose 上启用一次共享 contact query。Find 使用与当前 VLM image 对齐的 semantic map。Collision、 Interact 和 disturbance 使用相同的 in-memory contact rows,并立即丢弃。Episode 结束时,trajectory array 只为 Motion Jerk 和 NPZ serialization materialize 一次; collision 不做 terminal replay 或第二轮 contact pass。

输出边界

Evaluator 为每个逻辑 VLM stage 写一份包含最终 attempt 的 JSON;之前的 retry attempt 只在内存中用于 token accounting:

stepNNN_percept_mid_low.json
stepNNN_verifier.json        # 仅 verifier 确实被调用时

并且只写一套 replay bundle:

trajectory_before.npz        # generated motion + 准确 initial state
trajectory_after.npz         # physics-resolved human + 全部 dynamic objects
replay_manifest.json         # replay 参数、asset hash、NPZ hash

不会生成 combined episode log、duplicate trajectory、raw contact trace、semantic map、per-step metric、frame directory 或 status log。Step 之间保留的 in-memory history 只包含构造下一次 prompt 所需字段。

可选输出保持紧凑:

metrics.json                 # 仅 --metrics
ego.mp4, exo.mp4             # 仅 --video
metrics_summary.json         # 多 episode --metrics

配置边界

configs/paper_fullval_v1.json 完整且不继承,固定 episode、prompt version、 motion skill、weight manifest、initial seed、HalfPhysics implementation、rendering 和所有 metric threshold。Model endpoint、credential 和 model-specific output limit 放在单独的 model config 中。