Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
5189a2f
feat(tools): add baseline summary, report fill, run compare and rank …
Oct 25, 2026
1e44a08
fix(tools): count every decision without an executable action as a no-op
Oct 25, 2026
ae7108b
feat(demo): 流式读取层 demo/reader(题号归一 + 大文件保护)
Oct 26, 2026
6e66cb3
feat(demo): 聚合层委托 tools/batch_summary + Sai 逐题对照列
Oct 26, 2026
53a7fe3
feat(demo): 官方榜/Sai 逐题分快照 + 价格表 + 刷新脚本
Oct 26, 2026
78adf8b
docs(demo): 落地重建计划(stage+memory 批次 × 官方 harness 对照)
Oct 26, 2026
aa86436
feat(demo): 对照汇总(compare_summary)+ 满分浮点容差
Oct 26, 2026
e4b774a
feat(demo): 单页工作台骨架 + 总览视图(重写 demo 前端)
Oct 26, 2026
e71a977
feat(demo): 官方对照视图 + 机制视图
Oct 26, 2026
6b97585
feat(demo): 轨迹页 + 双轨同步播放器
Oct 26, 2026
9fa6cd6
feat(demo): 发布闸门——导出到内网共享目录 + 诚实披露红线自检
Oct 26, 2026
cedf2ad
feat(demo): 重做首页视觉 + 官方 Sai 双边轨迹对照
Oct 26, 2026
d38423e
fix(demo): 对照页左侧加载我们的逐题数据(原来传了 null)
Oct 26, 2026
75ea77b
docs(demo): 补实施后记(计划偏差与实施期发现的两个真 bug)
Oct 26, 2026
5e185bf
feat(demo): 官方参考轨迹补帧映射(12 题 · 2298 张帧)
Oct 26, 2026
e1a4ca3
feat(demo): 修首页崩溃 + 首页简化放大 + 双播放器 + 机制泳道
Oct 26, 2026
4a4018b
fix(demo): 逐步归因对齐到「一次观察 = 一帧 = 一次动作」
Oct 26, 2026
ce2e0ac
feat(demo): 首页按反馈精简
Oct 26, 2026
8e905fd
feat(demo): 「官方对照」改为「轨迹浏览」,按 OSW2 showcase 布局重做三栏
Oct 26, 2026
8538906
fix(demo): Tandem 轨迹无图 + 整宽排版 + 读数移到播放器下
Oct 26, 2026
75429a5
feat(demo): 首页重排为「简介 → 机制 → 结果」滚动页
Oct 26, 2026
238dab7
fix(demo): 修首页「view is not a function」+ 加渲染冒烟测试
Oct 26, 2026
9d80308
test(demo): 渲染冒烟的 JS stub 拆成独立文件(避开 ruff 行长)
Oct 26, 2026
6533374
fix(demo): 机制段左侧吸顶列滚下去就空了 + 规划原文取不到
Oct 26, 2026
9a13590
feat(demo): 接入 OSW v2.1 全量基线(c10+c20 合并 108 题)
Oct 27, 2026
fb81e46
feat(demo): 机制段改成可交互流程图(不再是纯文字)
Oct 27, 2026
12d68fb
fix(demo): 修「首页没有内联的总览数据」+ 构建脚本加 --site-only 快路径
Oct 27, 2026
545762b
feat(demo): 首页按子任务循环重画机制段(流程图+粘性卡片+真实case泳道)
Oct 27, 2026
79f109c
refactor(demo): 拆前端超长模块(browser.js / views.css)
Oct 27, 2026
e6044a6
fix(demo): 修流程图出框/压字 + 补真实子任务与动作级候选 + 删真实case泳道
Oct 27, 2026
5e6e4af
docs(demo): 记下机制段流程图的视觉 smoke 链路(本机无浏览器时的替代方案)
Oct 27, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,4 @@ results/
demo/site/
*.log
infra/config.json
demo/.cache/
229 changes: 79 additions & 150 deletions demo/README.md
Original file line number Diff line number Diff line change
@@ -1,164 +1,93 @@
# demo/ — OSWorld2 轨迹回放与对比站点
# demo/ — OSWorld 2.1 工作台

把 `rjob_logs/<run_id>/` 的结果目录变成一个**静态站点**:逐步回放每道 OSW2
任务的桌面轨迹与模型操作,并横向对比多个 run 的**成本 / 耗时 / 准确率**。
把**评测结果目录**变成一个**单页工作台**:左侧任务轨、中间双轨同步轨迹播放器、
右侧机制 / 评估 / 成本面板;对比对象是官方 leaderboard 的 **harness 型条目**
(`Sai w/ Claude Opus 5` 等),并用其 HF 公开参考 run 的逐题轨迹做同题对照。

demo 层与 harness 完全解耦:**不 import `tandem` / `tandem_eval`,不调用模型,
不碰 VM**。唯一输入是 `docs/RESULT_DIR_CONTRACT.md` 描述的结果目录契约。
## 边界(硬约束)

## 用法
demo 层与 harness **完全解耦**:不 import `tandem` / `tandem_eval`、不调模型、不碰 VM。
唯一输入是 `docs/RESULT_DIR_CONTRACT.md` 描述的结果目录契约 + `demo/data/` 里的
官方数据快照。`tests/demo/test_site.py` 有一条断言专门守这条边界。

```bash
# 构建全部 OSW2 run(默认输出 demo/site)
python demo/build_site.py --runs-root rjob_logs --out demo/site --benchmark osworld2
运行时只用标准库(Pillow 可用于构建期缩放帧)。站点**零外部资源加载**,离线可开。

# 只构建指定 run
python demo/build_site.py --runs-root rjob_logs --out demo/site --run rjob_logs/osw2-20-s1
## 目录

# 本地预览(file:// 也能开,但证据抽屉需要 http 才能拉取完整 JSON)
python -m http.server 8899 --directory demo/site
```
demo/
├── reader/ 读取层:结果目录 → 站点数据契约(纯标准库,全流式)
│ ├── runs.py 发现 run、读 run 级元数据
│ ├── episodes.py 题级终态(分数/停止原因/评测来源/域)
│ ├── events.py 流式解析 events.jsonl → 步骤(plan/决策/核验/replan)
│ ├── llm.py 流式解析 llm_calls.jsonl → usage + 原文(丢 base64)
│ ├── traj.py 流式解析 traj.jsonl / a11y_digests.jsonl → 帧索引
│ ├── paths.py 题号归一 + 集群路径脱敏
│ ├── viewmodel.py episode view model(站点唯一数据契约)
│ └── aggregate.py 逐题行与汇总(**委托 tools/batch_summary.py**,口径不分叉)
├── web/ 站点源码(构建时拷进 demo/site/)
│ ├── index.html
│ └── assets/{app.css,app.js,boot.js,views/*.js,player/*.js}
├── fetch_sai_frames.py 抓官方参考 run 的帧(流式解 tar,跳过 mp4;缓存进 .cache/)
├── data/ vendored 官方数据
│ ├── official/leaderboard.json 官方榜快照(带 source_url / fetched_at)
│ ├── official/sai_tasks.json Sai 逐题 mean/first/best(HF 134 次干净运行)
│ ├── official/sai_traj/ 对比子集的 Sai 轨迹摘要
│ ├── official/compare_subset.json 对比子集(12 题 / 4 组)
│ └── prices.json 挂牌价(带来源与快照日期)
├── build_site.py 构建脚本(结果目录 → 站点)
├── export_share.py 发布闸门:导出到内网共享目录 + 诚实披露红线自检
├── sync_official.py 刷新官方数据快照
└── frames.py 帧缩放(构建期,唯一保留的旧模块)
```

产物:
## 用法

```
site/
├── index.html # 总览:run 卡片 + 成本×准确率散点 + 缩略图网格 + 可排序对比表
├── compare.html # 两个 run 的并排对比(选择器切换 + 差值)
├── task/<run_id>/<task_id>.html # 单题轨迹回放
└── assets/
├── player.css / player-v2.css # 样式
├── player.js # 内核:共享工具 + 索引页排序
├── player-compare.js # 对比页模块
├── player-detail.js # 任务页详情渲染(规划/排序/动作/验证)
├── player-fail.js # 0 步任务的失败态(没有轨迹时讲清原因)
├── player-task.js # 任务页:泳道时间轴 / 需求章节 / 步骤回放
├── frames/<run_id>/<task_id>/ # 缩放后的帧(step_NN.jpg)+ thumb.jpg
└── evidence/<run_id>/<task>.json # 完整原始证据(事件全文 + LLM 请求/响应)
```
```bash
# 构建(默认数据源 = stage+memory 批次)
python3 demo/build_site.py --out demo/site --benchmark osworld21

## 页面能看什么
# 本地预览
python3 -m http.server 8899 --directory demo/site
# → http://localhost:8899/

### 总览页

- 每个 run 一张卡(已出分 / 未出分 / 均分 / 总耗时 / LLM 调用数 / 输入输出与思考
token / 快模型排序次数 / 失败分类);
- **成本 × 准确率散点**:横轴 token 用量(平方根刻度),纵轴官方评分,气泡半径 ∝
耗时;绿=有分、红=零分,未出分的题单独排在底部一排(不计入均分);
- **任务缩略图网格**(无帧的题显示"未进入决策步"占位);
- 逐题对比表——点表头按分数、耗时、token 排序。

### 对比页(`compare.html`)

任选两个 run 并排:逐题的分数 / token / 耗时 / 步数 / LLM 调用 / 快模型排序,以及
Δ差值(绿=更优、红=更差)。当前数据里 jev 与 kev 在任务 001 上可直接对照。

### 任务页

- **读数带**:官方评分、token 用量、真实用时、步数与快模型排序次数;
- 左侧执行前 / 执行后双帧对照;
- **需求清单 · 逐条核验**:从结构化字段(`verification.requirement_checks` +
`task_requirements` 抽取结果)得到每条需求的终态结论与"在第几步被核验",
点条目直接跳到那一步;
- 右侧是这一步的完整模型操作链:
1. **规划**:当前目标、预期观察、路由、任务状态、动作提案;
2. **候选与排序**:快模型两阶段(操作级 → 目标级)softmax 分数与相对强度条、
margin、排序延迟;
3. **编译后动作**:kind / action_type / 参数 / 目标 id / 描述;CLI 路由附命令全文;
4. **执行回执与独立验证**:verifier 裁决、状态变化、证据、逐条需求核验、
stdout/stderr;
5. **终态双确认**:planner 声称完成后的终态核验全文;
- **真实时钟轨迹**:四条泳道(KIMI 规划 / 排序服务 / 动作与观察 / KIMI 验证),
横轴是**实测**时间——由 `llm_calls[].time`、`traj[].wall_time`、
`ranking.latency_ms` 还原,不做比例重建、不估算未记录的时间;点泳道条跳到该步;
- 底部步骤时间轴(每步一个 chip,颜色=验证结论,标注 gui/cli 路由),
支持键盘 ←/→ 翻步、`#step=N` 深链;
- 右上角「运行证据」抽屉按需拉取完整原始 JSON。

### 0 步任务(失败态)

本批次有 9 题在 import 期即死——0 步 / 0 次模型调用 / 0 帧。这类页面不画轨迹,
而是直接讲清楚「为什么没有轨迹」:

- 失败态卡片:终止原因(`exception_type` / `stop_reason`)、耗时、
终止前的模型调用次数与 role 分布;
- **调用数也是 0 ⇒ 失败发生在模型被调用之前**(环境 / 依赖 / 配置),
与「模型跑了但没做对」是两回事;
- 右侧「执行摘要」列出状态、评分口径、决策步、快模型次数、调用分布、用时。

例:任务 017 的模型调用是 `task_requirements × 1 + plan_step × 3`(规划连续三次
非法 JSON 后抛 ValueError);任务 020 是 0 次调用(环境问题)。

## 数据契约(只读这些)

| 文件 | 用途 |
|------|------|
| `summary.json` | run 级汇总(已出分 / 未出分 / 均分) |
| `code_version.json` | harness 版本、协议版本、git describe、镜像 digest、run_spec(selector 后端) |
| `episodes/manifest.resolved.json` | task_id → episode_dir 映射 |
| `episodes/<dir>/task.json` | 任务指令、snapshot |
| `episodes/<dir>/episode.json` | status / score / stop_reason / exception_type |
| `episodes/<dir>/timing.json` | wall_seconds |
| `episodes/<dir>/evaluation.json` | 官方评分 |
| `episodes/<dir>/events.jsonl` | planner / gui / cli / verified / terminal 事件(含 ranking 全量输入输出) |
| `episodes/<dir>/traj.jsonl` | 每步观察、回执与 wall_time |
| `episodes/<dir>/llm_calls.jsonl` | LLM 请求/响应、usage 与响应落盘时刻 |
| `episodes/<dir>/screenshots/step_NN.png` | 逐步截图(帧来源) |

### 步号语义(契约 §步号语义,已写进 docs/RESULT_DIR_CONTRACT.md)

- 截图索引 0 基,且是**动作执行后**的屏幕;`traj.step_idx = 截图索引 + 1`;
- 动作步 s 的「执行后」帧 = `step_{s:02d}.png`,「执行前」帧 = `step_{s-1:02d}.png`;
第 0 步没有执行前帧(初始观察只在 `obs_stage`,结束即清);
- 验证事件落在动作的**下一步骤**桶,故动作步 s 的裁决取自 s+1 桶。

### 时间轴是怎么还原的

| 泳道 | 区间 | 数据来源 |
|------|------|----------|
| `plan` | 上一步结束 → 慢模型规划调用结束 | `llm_calls[].time`(响应落盘时刻) |
| `rank` | 规划结束 → +排序延迟 | `ranking.latency_ms`(服务端实测) |
| `act` | 排序结束 → 本步观察采集时刻 | `traj[].wall_time` |
| `verify` | 观察时刻 → 验证调用结束 | `llm_calls[].time` |

LLM 调用按事件顺序归属步骤(实测序列恒为 `plan* → 观察 → verify → plan* → …`)。
**没有 LLM 调用痕迹的步骤不画段**——没有数据就不画条。

## 诚实披露纪律

1. 只搬运**模型公开输出**,不生成任何虚构的"思考字幕";
2. `failed_unscored` 一律显示「未出分」,**不折算 0 分、不计入均分**;
3. 排序服务分数始终标注 **softmax,非校准概率**;
4. 成本以 **token 用量 + 调用次数** 表达(成本代理指标),本仓库没有慢模型计价信息,
不臆造货币单价;
5. 截图为**逐步离散帧**,非连续录屏;初始观察帧与终态动作后的帧不随结果目录保留,
页面如实标注缺失原因;
6. 展示载荷中的长文本会被截断并标注字数,**完整原文在「运行证据」抽屉里**;
7. 集群挂载路径(`/mnt/…`、账号名)发布前统一替换为占位符;VM 内的
`/home/user/…` 属任务语义,保留。

## 代码

| 文件 | 行数 | 职责 |
|------|-------|------|
| `adapter.py` | 272 | 结果目录 → 文档(逐题轨迹、成本、耗时、路径清洗) |
| `timeline.py` | 253 | 真实时钟时间轴 + 需求章节 + 结构化需求抽取 |
| `metrics.py` | 106 | 成本/耗时/准确率聚合与多 run 对齐 |
| `frames.py` | 65 | 帧缩放(Pillow,仅构建期) |
| `render.py` | 206 | 任务页渲染 + 展示载荷/证据载荷分离 |
| `pages.py` | 282 | 总览页与对比页渲染(散点图服务端生成 SVG) |
| `build_site.py` | 138 | 编排 + CLI |
| `player/player*.js` | 60–276 | 播放器(内核 / 对比 / 详情 / 任务页四块,零第三方依赖) |
| `player/player*.css` | 112–215 | 样式 |

依赖:Python 标准库 + Pillow(**仅构建期**用于缩放截图;缺失时退化为原样拷贝 PNG)。
页面本身零第三方依赖、零外部请求。

## 测试
# 刷新官方数据(联网)
python3 demo/sync_official.py

```bash
pytest tests/demo -q # 15 个单测:合成结果目录 → 文档 → 站点
ruff check src tests demo # demo 也在 lint 范围内
# 抓官方参考帧(联网,约 10 分钟;结果缓存进 demo/.cache/,不进 git)
python3 demo/fetch_sai_frames.py

# 发布到内网共享目录(红线不过就拒绝导出)
python3 demo/export_share.py --site demo/site --out /path/to/shared/dir
```

单测不依赖 `rjob_logs/` 里的真实 run,CI 上可独立运行。
## 页面能看什么

- **总览 `#/`**:Hero 大数字(结论先行,数字滚动动画)→ 按域分行的**战绩热力矩阵** →
**终态解剖**堆叠条 → **官方 harness 榜单条**(我们的行插在 harness 行里)→
成本×分数散点(带轴与网格,虚线只画官方已发布值);**首屏可折叠披露条**。
- **官方对照 `#/compare`**:对比子集 12 题的逐题 Δ、三处口径声明,
以及**双边轨迹播放器**——同一进度点上并排看我们的帧与官方 Sai 的帧、动作与 reward
(Sai 的帧来自其 HF 公开参考 run,流式解包只取 PNG)。
- **机制 `#/mechanism`**:快慢双模各干什么 + 失败模式解剖(完成却零分 / 墙钟耗尽 / 空转 / 异常)。
- **轨迹 `#/task/<run>/<task>`**:左任务轨、中间双轨播放器(上轨慢模型原文 + 下轨快模型排序 + 帧序列)、
右机制面板(plan→act→verify→replan 闭环、需求逐条核验、成本流水)。

## 诚实披露红线(`export_share.py` 逐条断言,不过就拒绝导出)

1. 未出分的题显示「未出分」,**不折算 0**;
2. 虚线只画**官方已发布**参照值,不生成我们的预测分/目标分;
3. 美元成本标注快照日期与底座型号;
4. 快模型(本地 GPU)成本单列第二轴,不折美元;
5. release / 题集 / 步数预算差异出现在每处对比旁;
6. 慢模型原文只搬运 `llm_calls.jsonl` 的真实内容;
7. 排序分数恒标 **softmax,非校准概率**;
8. 帧是离散帧不是录屏,缺失如实标注;
9. 失败与口径在首屏披露(未出分、墙钟主约束、另一代码树实验);
10. 集群路径与凭据不进站点。

## 数据源

唯一数据源是 stage+memory 批次(`execution_mode=stage`,500 步上限 / 4h 墙钟,
慢模型 MiniMax-M3.1-Flash `reasoning_effort=max`,快模型 JEV,**原生官方评测器**)。
**不使用** `osw21-500-jevfull` / kev 臂的结果。
Loading
Loading