ocr viewer 是一個小型內嵌 HTTP server,以瀏覽器友好的 UI 渲染歷史評審會話。無外部依賴——直接讀 OCR 每次評審寫到磁碟的 JSONL 檔案。
ocr viewer # binds localhost:5483
ocr viewer --addr :3000 # bind all interfaces on port 3000
ocr viewer --addr 0.0.0.0:8080
Server 在前台跑——Ctrl+C 停止。會話每次請求時惰性掃描,另一終端裡跑的評審一旦 JSONL 出現就顯示。
Host 頭。通配綁定(:3000)需設 OCR_VIEWER_ALLOWED_HOSTS 才能從 LAN 存取。| URL | 內容 |
|---|---|
/ | 磁碟上有會話的所有 repo 清單 |
/r/{repo} | 單一 repo 的會話清單,最新在前 |
/r/{repo}/{sessionID} | 單一會話完整詳情 |
最有用的頁面,顯示:
| 任務類型 | 何時出現 |
|---|---|
plan_task | 跑了 plan 階段(檔案 ≥ 50 行) |
main_task | 每個檔案。主評審迴圈。 |
review_filter_task | 跑了評審後評論過濾流程 |
memory_compression_task | active+compress 區超過 60%/80% 預算 |
re_location_task | code_comment 無法錨定,回退重新定位 |
每條泳道是任務卡片的水平條帶——每個 LLM 往返一張,按任務型別著色。
在終端輸出打開一條評論,在 viewer 定位該檔,沿 main_task 泳道往下找包含該 code_comment 的卡片。卡片的 Response 顯示模型推理。
無評論的檔案只有當模型主動呼叫 task_done 才是成功評審。泳道以錯誤卡片結束 = 偽裝成靜默的失敗。
memory_compression_task 泳道顯示每次壓縮輪。Response 窗格有摘要;被壓縮的 XML 在該輪 JSONL 的 messages 中。
~/.opencodereview/sessions/
└── <path-encoded-repo-path>/
└── <session-id>.jsonl
JSONL 每行一個事件:llm_request、llm_response、tool_call 等。行是 append-only——不完整的 JSONL 表示會話被中斷,viewer 渲染已寫入的部分。
JSONL 轉錄包含發給 / 從 LLM 收到的一切(含 diff 中任何程式碼),完全存在你的 ~/.opencodereview/ 內。OCR 不會上傳到任何地方。
每條泳道對應一種任務型別,出現在不同時機:
plan_task——diff ≥ 50 行才出現。看它產出的指引是否合理。main_task——每個檔案都有。主要評審迴圈,最常看的。review_filter_task——評審後過濾。如果評論數大幅減少,就是它在工作。memory_compression_task——超過 60%/80% 預算時出現。看摘要保留/丟棄了什麼。re_location_task——錨定失敗的回退。如果頻繁出現,可能是 diff 格式異常。每個任務卡片是一次 LLM 往返的完整記錄:請求號、模型、token、時長、Response(含 thinking)、Tool calls + 結果。除錯時看:
JSONL 每行一個事件,append-only——不完整的 JSONL 表示會話被中斷。Viewer 會渲染已寫入的部分。實務意義:如果看到不完整的會話,不要慌——可能是 Ctrl+C 中斷或 runner 超時。已寫入的評論仍然是有效的。
Step 1 — 啟動 viewer
ocr viewer
Step 2 — 瀏覽器打開 localhost:5483
→ 看到 repo 列表
Step 3 — 點進目標 repo
→ 看到會話清單,最新在前
Step 4 — 點進目標會話
→ 看到五條泳道
Step 5 — 找到 main_task 泳道中的錯誤卡片
→ 發現模型在第 12 輪報 tool execution error
Step 6 — 檢查 JSONL 原始資料
cat ~/.opencodereview/sessions/.../session.jsonl | jq '.tool_call.error'
預期產出
Task Card #12 (main_task):
Model: claude-opus-4-6
Tool: file_read → Error: file not found
Response: "I'll proceed with the available context..."
→ Agent continued despite error, produced 1 comment
| 錯誤訊息 | 診斷 | 修復 |
|---|---|---|
檔案顯示零評論 | 模型沒主動呼叫 task_done 或有錯誤 | 看 main_task 泳道是否以錯誤卡片結束 |
start_line: 0 / end_line: 0 | 錨定失敗 | 評論是真的,只是沒自動放置 |
DNS-rebinding 防護阻擋存取 | 從 LAN 存取需要設定允許主機 | 設 OCR_VIEWER_ALLOWED_HOSTS |
延伸閱讀:架構(五種任務型別) · 工具 · CLI 參考
你的團隊抱怨「OCR 這次審出來的評論特別淺」,主管要你給出證據而不是感覺。你要用 Viewer 從一個 session 裡復盤:模型用了多少工具、走了幾輪、壓縮幾次、過濾刪了什麼、為何某些檔案靜默——把「品質異常」拆成可檢視的數據。
ocr session list | head -10 # 找「品質異常」的那次
ocr viewer # 瀏覽器開 localhost:5483 → 目標 repo → 目標 session
jq -r 'select(.type=="tool_call") | .data.tool' < session.jsonl \
| sort | uniq -c | sort -rn
6 file_read ← 讀檔多
3 code_search ← 搜尋多
1 code_comment ← 評論少(異常!6 次讀檔只產 1 條評論)
jq -r 'select(.type=="llm_request") | .data.task' < session.jsonl | sort | uniq -c
# 看 REVIEW_FILTER_TASK 幾次、MEMORY_COMPRESSION_TASK 幾次
session f3a9c2: 6 檔
每檔平均 9 次工具呼叫(基準 4)
壓縮 3 次(基準 0-1)→ 大檔 + 長迴圈 → 摘要損失
filter 移除 4 條評論(基準 0-1)
→ 推論: 檔案過大 + 壓縮頻繁 → 早期發現被摘要掉 → 評論變少變淺
「品質異常」是抽象抱怨,Viewer + JSONL 把它變成可測量的管線數據。不看泳道,你只能猜「模型變笨了」;看了數據,你會發現多半是壓縮太頻繁或 filter 太嚴格——這兩個才是可調的(--max-tokens、--max-tools)。復盤的目的不是責怪,是定位「該調哪個旋鈕」。
預期產出
根因: 6 檔都是 400+ 行大檔,壓縮 3 次吃掉早期脈絡
修正: 每檔 --max-tokens 從 58,888 → 150,000
復審: 評論 1→5 條,filter 移除降至 1 條
JSONL 每行一個事件,append-only——中斷只是「停在某一行」。Viewer 渲染「已寫入的部分」,已寫入的評論仍然有效。這特性讓 JSONL 變成審計軌跡:任何一次評審的完整經過都可重放——誰、何時、對哪檔、用了哪個工具、回了什麼、產了什麼評論。比「看輸出報告」更接近真相。
# 你看到 session 的 main_task 泳道「乾淨地結束」:
# 有工具呼叫 + task_done → 你判定「這檔審得沒問題」
# 但細節藏在一張卡片裡:
# Tool call: file_read → Error: file not found
# Response: "I'll proceed with the available context..."
# → 模型其實讀不到它想讀的檔,只憑 diff 殘缺脈絡硬審
# 泳道看起來「完成」了,實際上是「帶傷完成」
「完成」分兩種:乾淨完成(工具都成功)與帶傷完成(工具失敗但模型繼續)。泳道結尾的 task_done 兩者都會出現。防禦:不只看「有無 task_done」,還要看卡片裡的錯誤徽章與「Response 是否 mention 錯誤」。養成習慣:對「重要檔」檢查它的每張卡片有沒有 error badge。
| 症狀 | 可能原因 | 解決方案 |
|---|---|---|
| 某檔 main_task 泳道只有 1 張卡片就結束 | 模型快速判斷沒問題就 task_done(低 Recall 或模型偷懶) | 對照該檔複雜度;若檔大卻審太快,檢查 prompt/模型 |
| re_location_task 泳道塞滿卡片 | 錨定頻繁失敗 | 檢查 diff 格式;改善 existing_code 唯一性 |
| memory_compression 比預期多 | 大檔/長迴圈觸發多次壓縮 | 調高 --max-tokens;或拆檔審 |
| viewer 連不上 localhost:5483 | viewer 沒在前台跑,或 port 被占用 | 確認 ocr viewer 在跑;換 --addr :3000 |
| 從 LAN 連不上 | DNS-rebinding 防護阻擋 | 設 OCR_VIEWER_ALLOWED_HOSTS |
tool_call 事件結構)。