やってみた 2026年8月5日

AIエージェントの制御プレーンLoopXを使い捨てコンテナで動かし、should_run=trueでも配信を止めるゲート設計を運用目線で見た

XECIN PythonAIエージェント制御プレーンCLI

GitHubのトレンドを眺めていたら、huangruiteng/loopx が一番上に来ていました。説明が「長時間動くAIエージェントのためのローカル制御プレーン。あなたのエージェントランタイムは置き換えない」。ここ最近、社内でも複数のコーディングエージェントを長めのタスクに投げる場面が増えていて、「セッションをまたいで目標や判断をどう保つか」で毎回困っていたので、この一文が気になったんですよね。

私が普段気にするのは、こういう自動化の仕組みを入れたときに「誰が最終判断を握るのか」と「無駄なコンピュート(=課金)をどこで止めるのか」の二点です。LoopXはまさにそこを狙っている書きぶりだったので、使い捨てのコンテナで実際に動かして、その設計思想がコマンドの挙動にちゃんと出ているのかを確かめました。

試した環境

ホストを汚したくないので、検証は使い捨ての Docker コンテナ(python:3.12-bookworm)の中だけで行いました。以下はすべてその中での実測値です。

  • 対象: huangruiteng/loopx v0.4.1(commit 1f7c9ed、MIT)
  • ランタイム: Python 3.12.13
  • 位置づけ: LLMは呼ばない。長時間タスクの「目標・ゲート・todo・クオータ・証跡・引き継ぎ」だけを保つ状態カーネル

まず入れてみたら、依存ゼロは本当だった

セットアップは拍子抜けするほど軽かったです。pyproject.toml を見たら dependencies = []。つまり実行時の外部依存がゼロで、Python標準ライブラリだけで動くということですね。

# すべて使い捨てコンテナ内
git clone --depth 1 https://github.com/huangruiteng/loopx ~/loopx   # 約3秒
cd ~/loopx && pip install .                                          # 約4秒
loopx --version
# -> loopx 0.4.1

制御プレーンと聞くと常駐サーバやDBを想像しますが、LoopXはCLIと、プロジェクト直下に置くファイルだけで完結します。運用に載せるものは依存が少ないほど嬉しいので、ここは素直に好印象でした。

次に loopx doctor。ここで早速ひとつ面白い挙動がありました。入れたてで最新版(0.4.1)のはずなのに、requires_upgrade: True と言われるんです。

- ok: `True`
...
- status: `repair_recommended`
- requires_upgrade: `True`
- current_version: `0.4.1`
- reason: `installed LoopX skills are missing or stale`

理由をたどると installed LoopX skills are missing or stale とあります。パッケージのバージョンが古いのではなく、エージェント側に配る「スキルファイル」がまだ入っていないから、という意味でした。okTrue(コア機能は動く)なのに repair_recommended が同居している。ここは「バージョン最新=準備完了」ではない、という設計を最初に理解しておかないと運用で判断を誤るなと思いました。

loopx connect で状態を作る、そして status がいきなり ok:False

プロジェクトのディレクトリで loopx connect を叩くと、状態ファイル一式が作られます。

loopx connect
# 作られるもの:
#   .loopx/registry.json                                    ← ゴール登録簿
#   .codex/goals/<goal>/ACTIVE_GOAL_STATE.md                ← 状態そのもの(Markdown)
#   ~/.codex/loopx/registry.global.json                     ← 全体レジストリへ同期

状態の実体が Markdown ファイルというのが良くて、あとで中を見せますが、人間が git の差分で追える形になっています。

問題はこの直後です。loopx status を実行したら、いきなり ok: False。しかも「エラー16件」と出ました。何を踏んだのかとログを見ると、これが正直かなり紛らわしかった。

- contract: ok=False, errors=16, warnings=1, checks=5
- error: pip/_internal/network/auth.py:517: credential
- error: pip/_vendor/pygments/formatters/latex.py:503: credential
- error: pip/_vendor/requests/adapters.py:285: credential

pip/_vendor/requests/adapters.py ……これは私のプロジェクトのファイルではありません。LoopXには「公開してよいファイルに秘密情報が紛れていないか」を走査する境界チェックがあるのですが、pip install で入れた場合は走査の起点が site-packages になり、pip自身が同梱しているライブラリのソースまで舐めて、その中の credential という単語に反応していたのでした。要は誤検知(false positive)です。

幸い、走査対象を明示するオプションがありました。プロジェクトだけに絞ると、素直に通ります。

loopx status --goal-id myproject-goal --scan-root .
# -> - ok: `True`
#    - contract: ok=True, errors=0, warnings=2
#    - warning: registry should be gitignored: .loopx/registry.json

エラー16件が0件になりました。残った warning は「.loopx/registry.json と状態ファイルは gitignore すべき」という真っ当な指摘で、これはむしろ親切ですね。ただ、既定の loopx status がインストール場所を走査してしまう挙動は、初見だと確実に驚きます。ここは走査起点を絞る運用を最初に決めておくのが良さそうです。

本命は quota should-run ―「動いてよい」と「配信してよい」は別だった

ここからが、私がこのツールで一番見たかった部分です。ゴールに todo をいくつか積んで(レガシー課金APIの呼び出し箇所を洗い出す、といった中身のある想定タスクにしました)、エージェントを1体登録したうえで、loopx quota should-run を叩きます。これは「次の一手を実際に走らせてよいか」をLoopXが判断するコマンドです。

返ってきたJSONが、設計思想をそのまま表していて唸りました。

{
  "decision": "safe_bypass_user_gate_fallback",
  "should_run": true,
  "normal_delivery_allowed": false,
  "effective_action": "operator_gate_notify",
  "reason": "status or contract health is not ok; skip automatic compute",
  "quota": {
    "compute": 1.0, "window_hours": 24, "slot_minutes": 1,
    "allowed_slots": 1440, "spent_slots": 0,
    "state": "operator_gate"
  },
  "recommended_action": "[P0] Inventory every call site of the legacy billing API"
}

should_run: true なのに normal_delivery_allowed: false。つまり「エージェントは起きてよい、でも本来の配信作業(コードを書く・書き込む・本番を触る)はさせない」という状態です。理由が status or contract health is not ok; skip automatic compute ――さきほどの status の誤検知でヘルスが not ok になっていたことが、そのままクオータ側に波及して、コンピュートを止めていたわけです。

一本の誤検知が、健全性チェック → クオータ制御へと連鎖して「実作業を止める」ところまで効く。これは動かしてみて初めて腹落ちしました。should_run を素朴に「走ってよし」と読むと事故ります。実態は「安全な範囲(読み取りの舵取りだけ)なら一手使ってよい、ただし課金を伴う配信はゲートで止める」です。

では、と思って status のヘルスを(--scan-root . で)直してから、もう一度クオータを聞きました。今度こそ配信が開くかと思いきや――まだ止まっているんです。ただし理由が変わりました。

{
  "status_health_ok": true,
  "should_run": true,
  "normal_delivery_allowed": false,
  "state": "operator_gate",
  "reason": "operator gate blocks gated delivery; safe non-gated steering may continue",
  "open_todo_notify_reason": "open user_gate todo requires owner decision before onboarding_decision"
}

ヘルスは true になったのに、今度は 人間の判断ゲート(operator gate) が開いていない、と。loopx connect の時点で「どのオンボーディングtodoを承認するか」「自律的に進めてよいか(autonomous=yes/no)」というオーナー宛の user_gate todo が自動で立っていて、それに人間が答えるまでは配信させない設計でした。二段構えのゲートだったわけです。

段階止めていた理由配信
最初健全性チェックが not ok(pip誤検知の波及)不許可(読み取りの舵取りのみ)
健全性を修正後人間の承認ゲート(operator gate)が未回答不許可(オーナー判断待ち)

「スケジューラが、有益な遷移が無くなった後も課金を続けてしまう」問題を止めたい、という当初の狙いが、コマンドの挙動としてちゃんと形になっている。運用を考えると、この「止まる方向に倒れる」既定はむしろ安心できます。クオータは24時間で1440スロット(1分刻み)の会計になっていて、spent_slots: 0 から、検証済みの配信の後にしか消費しない(spend_rule=spend_only_after_artifact_validation_writeback)設計でした。

状態はMarkdownが正、盤面は投影

ACTIVE_GOAL_STATE.md の中を見ると、思想が一貫していました。frontmatter付きのMarkdownに、運用契約(Operating Contract)や実行プロファイル、そしてtodoが並びます。todoの機械可読メタはHTMLコメントに逃がしてあって、本文は人間がそのまま読めます。

## Agent Todo
- [ ] [P0] Inventory every call site of the legacy billing API
  <!-- loopx:todo todo_id=todo_128b9f9da528 status=open action_kind=implementation claimed_by=billing-migrator ... -->

ドキュメントいわく「盤面(Kanban)は投影に過ぎず、状態ファイルが正」。ダッシュボードをソース・オブ・トゥルースにしない、という割り切りは、私は好きです。監査で追うのも、結局この1ファイルとレジストリを見れば済みますから。

引っかかった細かい点も正直に書いておきます。まず、loopx start-goal --guided はプレビュー専用で、そのままでは状態を変えません。順序付きの手順(ホスト選択ゲート → エージェントID登録 → todo記述 → refresh-state …)を返すだけで、私はここで一度「あれ、目標文が反映されていない」と戸惑いました。実際、状態ファイルの objective は connect が入れた既定文のままで、私が渡した「課金モジュールの移行」という目標文はプレビューにしか出ていませんでした。目標そのものを固定するには別途 bootstrap --objectiveconfigure-goal を通す必要があります。

もう一つ、コマンドの引数体系が全体で統一されていません。loopx quota should-run--project を受け付けず(cwdから解決)、loopx todo claim--claimed-by が要る、という具合に、サブコマンドごとに流儀が違います。サブコマンドは100個以上あって、usage エラーの一覧を見たときは正直ちょっと圧倒されました。ここは好みが分かれるところですが、手なりで叩けるほど枯れてはいないので、都度 --help を見るのが安全です。

あと、loopx review-packet(引き継ぎ用の要約)を出したら、他が英語なのにこの出力だけ中国語でした(このプロジェクトは中国語READMEも持っています)。内容自体は正しく引き継ぎ情報を出してくれるのですが、英語CLIを使っていて急に言語が切り替わるのは、チームに配るときは一言添えておいたほうが良さそうです。

運用に置くとしたら

短時間触っただけですが、LoopXが何を大事にしているかは、コマンドの挙動からはっきり伝わってきました。「動いてよい」と「配信してよい」を分け、健全性と人間の承認という二段のゲートで、勝手に走って課金を垂れ流さないようにする。最終判断は人間に残す。この方針は、複数のエージェントに長いタスクを任せ始めた組織ほど効いてくると考えています。

一方で、今すぐ全社の標準に据えるかというと、そこは様子見です。引数体系の非統一さ、status の走査起点の初期挙動、出力言語の混在など、チームに配る前に「最初のつまずき」を潰しておきたい箇所がいくつかあります。まずは自分の長時間タスク一つを、この状態ファイル運用に載せてみて、監査のしやすさと「止まってくれる安心感」が実務で釣り合うかを、もう少し実データで見てから広げたいと思います。依存ゼロで撤退も容易なので、試すコスト自体は低いのが良いところです。