Skip to content

[scripts][eval] ローカル LLM を使う live 系の評価スクリプトが、マシン全体の重い処理の枠を自分で取らない #649

Closed

Description

@Kewton

背景

ローカルの LLM(LM Studio・Ollama)を使う重いテストや評価が重なると、マシンのメモリが足りなくなります。利用者の決定(2026-10-10)で、重い処理はマシン全体で同時に 1 本だけにします。cargo のテストは、各 worktree の cargo の設定(テストのバイナリの runner)で、枠を取るスクリプト(with-heavy-lock)を通るようにしました。ただし、Python の評価スクリプトは cargo を通らないので、今は「with-heavy-lock python3 … で起動する」という決まりに頼っています。

問題

ローカル LLM や実際の provider を使う live 系の評価は、自分では枠を取りません。直接起動すると、ほかの重い処理と重なります。共通の Ollama POST だけを保護しても、評価が子プロセスで起動する実 product の LLM 呼び出しは覆えません。

着手前調査では live 評価の CLI 入口が 5 本見つかりました。LM Studio 専用の Python 評価入口は現行コードにはなく、将来 provider が増えても CLI 境界で保護できる構成にします。

対処の方向(着手前調査に基づく案)

  • 枠の取得・解放・待ち時間の記録を、共通の context manager に集約します。次の live CLI の呼び出し全体を、ローカル LLM や実際の provider を呼ぶ前から包みます。
    • goal_verify live campaign:旧 runner と v3/v4、および contract で指定した live smoke。
    • goal_verify の旧 blind review:実ローカル reviewer の評価。
    • goal_verify recovery-v4:実 product を起動する recovery smoke。
    • 通常の matrix 評価:dry-run を除く実行全体。cloud-only の実 provider 評価も含めます。
    • preflight:明示的な live-provider-smoke を指定した処理だけ。
  • 枠の場所は HEAVY_LOCK_DIR で変えられるようにし、既定は参照に記した wrapper と同じにします。atomic な mkdir、PID と起動情報の記録を同じ形で使います。別方式の lock に置き換えません。
  • 正の PID を読み取れ、持ち主のプロセスが消滅したことを確認できた lock を回収します。PID の記録前の隙間、壊れた PID、権限不足を「持ち主がいない」と推定して消しません。取得に失敗したまま live 処理を始めません。
  • HEAVY_LOCK_HELD が非空なら再取得しません。自分で取得した場合は子へ引き継ぎ、自分が取得した枠だけを解放します。正常終了・例外・中断時の子プロセスと解放順を確認します。
  • 待った時間を monotonic clock で測り、評価 run の専用メタデータ JSONL に invocation ごとに追記します。通常評価では既存の extras_json にも関連づけます。入れ子の待ち時間はその invocation 自身の待ちであり、外側 wrapper の待ち時間とは区別します。
  • 可変の待ち時間を frozen manifest、raw 記録、hash ledger、既存 runtime event に混ぜません。TSV の列や既存 schema_version を変更せず、実行時間・provider timeout・既存 scheduler 待ち時間の計測も維持します。
  • 決定的な評価・テスト、dry-run、通常 preflight、モデル一覧の GET には枠を取りません。Ollama 互換の固定応答 server を使う deterministic recovery/confirmatory も対象外です。デモ録画と任意 command の汎用 launch は今回の範囲に含めません。
  • CLI の取得呼び出し追加は計 30〜50 行程度を見込みます(既存行の再インデントを除く)。共通実装は 160〜240 行、新規テストは 260〜420 行程度が目安です。
  • 既存の frozen source/exact-SHA/証跡の確認は残します。新しいコードで live 採取する際は新しい contract と run を用い、共通実装も source/hash の対象へ含めます。旧契約や歴史的証跡を修正して通しません。

受け入れ条件

  • 対象の live 評価を 2 本同時に起動すると、2 本目は 1 本目が終わるまで待ちます。既存 wrapper を使うほかの処理とも同じ枠で排他されます。
  • 全世代の live campaign、旧 blind review、recovery smoke、非 dry-run matrix、明示的 provider smoke の入口で取得することをテストで固定します。実 product の子プロセス経由の LLM 呼び出しも取得範囲に含めます。
  • 消滅した持ち主の回収、生存/不明な持ち主の保護、入れ子での取得省略、子への引き継ぎ、自分の枠だけの解放、正常・例外・中断時の扱いをテストで固定します。stale の回収と複数待機者の競合も確認し、未確認の排他保証を成功扱いにしません。
  • テストは既存の unittest 形式に合わせ、一時ディレクトリと無害な子プロセス/mock を使います。実 LLM・実 provider・マシン共通の本番枠・秘密ファイルへ依存しません。
  • 枠を待った時間と入れ子の区別を評価の記録に残します。再開でも過去の待ち時間記録を上書きせず、既存の manifest/raw/ledger の整合性、TSV header、schema を維持します。
  • 決定的な評価・テストには取得処理や枠待ちを足しません。dry-run と通常 preflight も枠を待ちません。既存の実行時間・timeout・採点の意味を変えません。
  • 新規 focused test と対象 Python の Ruff を実行し、結果を証跡に残します。既存 CI が新規テストを自動実行するとみなさず、この Issue の検証ゲートに focused test を明示します。
  • 既存の検証・受入・証跡・リリースゲートを緩めていません。honest-failure 方針を維持し、歴史的な証跡を書き換えません。

対象ファイル

  • scripts/eval_lib/heavy_lock.py(新規:共通の枠取得・解放・待ち時間記録)
  • tests/eval/test_heavy_lock.py(新規:排他・互換性・stale・入れ子・入口配線の unittest)
  • scripts/eval-goal-verify-live.py(既存:全 live campaign 世代の入口)
  • scripts/eval-goal-verify-blind.py(既存:実ローカル reviewer の入口)
  • scripts/eval-goal-verify-recovery-v4.py(既存:実 product recovery smoke の入口)
  • scripts/eval-run.py(既存:非 dry-run matrix の取得と既存 extras への関連づけ)
  • scripts/eval-preflight.py(既存:明示的 provider smoke だけの取得)

参照

  • 調査基準:develop 9047eb35631ef468a3cdd4dc491e6cf19e166def。fetch 後の origin/develop と一致を確認済み。
  • 枠を取るスクリプト:/Volumes/SSD_NX/tmp/heavy-lock/with-heavy-lock:14、:24、:25、:33、:34、:38。既定の lock は /Volumes/SSD_NX/tmp/heavy-lock/slot.d、内部に pid と cmd を記録します。
  • 枠の説明:/Volumes/SSD_NX/tmp/heavy-lock/README.md:17。
  • 決定の記録:workspace/tmp/1007/wave1/ledger.md(本調査では内容を変更していません)。
  • 着手前調査:workspace/tmp/1007/wave1/results/H-22/report.md。
  • 本文更新案:workspace/tmp/1007/wave1/issue-bodies/649-after1.md。

根拠

  • live campaign の入口と schema 分岐:scripts/eval-goal-verify-live.py:67、:101。Ollama POST:scripts/eval_lib/goal_verify_live.py:226、:262、:597。v3/v4 の provider:scripts/eval_lib/goal_verify_live_v3.py:147、:447 と scripts/eval_lib/goal_verify_live_v4.py:114、:440。
  • 実 product 経由の LLM:scripts/eval_lib/goal_verify_baseline_product_v3.py:40、:122。recovery の既定 baseline:scripts/eval_lib/goal_verify_recovery_live_v4.py:77、:118、:668。
  • 旧 blind review の実 reviewer:scripts/eval_lib/goal_verify_blind.py:130、:192。
  • 通常評価の dry-run 終了と実行:scripts/eval-run.py:156、:162、:178、:186、:699。
  • preflight の実 smoke と一覧 GET の違い:scripts/eval-preflight.py:97、:111、:119、:122。実 provider POST:scripts/eval_lib/models.py:143、:172、:184。
  • 決定的 recovery と confirmatory:scripts/eval_lib/goal_verify_recovery_deterministic_smoke.py:385、:992、:1014、scripts/eval_lib/goal_verify_recovery_deterministic_pair.py:825、:858、scripts/eval_lib/goal_verify_recovery_confirmatory.py:230。
  • schema を維持できる既存 extras:scripts/eval_lib/run_summary.py:231、:465、:476、:495(調査基準版)。frozen manifest と raw の検証:scripts/eval_lib/goal_verify_live.py:93、:97、:548。
  • 既存テストの形式:tests/eval/test_goal_verify_live.py:9、:75 と tests/eval/test_eval_run_dry.py:13、:64。現行 CI の eval テスト指定:scripts/ci.sh:28〜:30。

関連(参考)

DONE: H-22 #649 対象 7 ファイル

Activity

  1. added a commit that references this issue on Oct 12, 2026
  2. Kewton commented on Oct 12, 2026

    @Kewton
    OwnerAuthor

    PR #660 で merge しました(develop 966e351、CI・acceptance success)。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions