Skip to content

Latest commit

 

History

History
222 lines (182 loc) · 12.6 KB

File metadata and controls

222 lines (182 loc) · 12.6 KB

前提

ここでは「Pi がモデル選択・agent loop・tool lifecycle・session・usage・cancellation を所有する」ことを、Pi をハーネスにする意味として考える。

長大な入力は Pi の user message に入れない。/rlm -- のような短い入口から外部ストアへ渡す。

既存理解

現行 RLM は次の3層になっている。

  1. RLM の反復ループ
  2. LMHandler のモデル呼び出し
  3. LocalREPL の永続 Python 実行環境 docs/architecture.md:21-41

重要な契約は次のとおり。

  • 長大な入力はモデル履歴ではなく context 変数に置く
  • REPL コードから llm_query() / rlm_query() を呼ぶ
  • answer["ready"] = True で終了する rlm/utils/prompts.py:125-142
  • 子 RLM は独立した環境を持ち、残り timeout/budget を継承する rlm/core/rlm.py:706-864
  • stdout は切り詰め、完全な中間値は REPL 側に残す rlm/utils/prompts.py:138-142

Pi 側には必要な材料が揃っている。

  • createAgentSession() によるインメモリ子セッション
  • system prompt の完全置換
  • customTools
  • tool allowlist
  • token/cost 集計、abort、dispose
  • custom tool からの sub-agent 起動 Pi SDK (https://pi.dev/docs/latest/sdk)
  • extension command、tool lifecycle、terminate: true Pi Extensions (https://pi.dev/docs/latest/extensions)

ローカルでも確認済み。

  • Pi: 0.79.10
  • Node: v24.18.1
  • createAgentSession、defineTool、DefaultResourceLoader、SessionManager を実際に import 可能
  • Pi の要求は Node >=22.19.0 ~/.npm-global/lib/node_modules/@earendil-works/pi-coding-agent/package.json:1-18,94-96

設計案

┌─────────────────────────────────┐ │ │ │ /rlm context.txt -- question │ │ │ └────────────────┬────────────────┘ │ │ │ │ ▼ ┌─────────────────────────────────┐ │ │ │ Pi extension command │ │ │ └─────────────────────────────────┘ │ │ ├───────────────────────────────────────┐ │ │ ▼ ▼ ┌─────────────────────────────────┐ ┌────────────────────────────────┐ │ │ │ │ │ │ │ │ │ PromptStore │ │ PiRlmRunner depth=0 │ │ raw context stays outside model │ │ │ │ │ │ │ └────────────────┬────────────────┘ └────────────────┬───────────────┘ │ │ │ │ ├───────────────────┐ │ │ │ │ ▼ │ ▼ ┌─────────────────────────────────┐ │ ┌────────────────────────────────┐ │ │ │ │ │ │ │ │ │ │ │ Persistent Python REPL worker │ ├──┤ Pi AgentSession │ │ │ │ │ metadata + short question only │ │ │ │ │ │ └─────────────────────────────────┘ │ └────────────────────────────────┘ │ │ │ │ ├───────────────────┼───────────────────┬──────────────────────────────────────┬───────────────────────────────┐ │ │ │ │ │ ▼ │ ▼ ▼ ▼ ┌─────────────────────────────────┐ │ ┌────────────────────────────────┐ ┌─────────────────────────────────┐ ┌─────────────────┐ │ │ │ │ │ │ │ │ │ │ rlm_exec tool │ ├──┤ llm_query() │ ┌──┤ rlm_query() │ │ answer variable │ │ │ │ │ │ │ │ │ │ │ └────────────────┬────────────────┘ │ └────────────────────────────────┘ │ └─────────────────────────────────┘ └────────┬────────┘ │ │ │ │ │ │ │ │ ├───────────────────┘ ┌──────────────────┘ │ │ │ │ ▼ ▼ │ ┌─────────────────────────────────┐ ┌────────────────────────────────┐ ┌─────────────────────────────────┐ │ │ │ │ │ │ │ │ │ One-shot Pi AgentSession │ │ PiRlmRunner depth+1 │ │ Final result returned by handle │◄─────────────┘ │ │ │ │ │ │ └─────────────────────────────────┘ └────────────────────────────────┘ └─────────────────────────────────┘

PiRlmRunner

各 RLM node を一つの AgentSession として生成する。

  • SessionManager.inMemory()
  • RLM 専用 system prompt
  • active tool は rlm_exec だけ
  • AGENTS、skills、通常の Pi extensions、context files は読み込まない
  • root prompt に渡すのは以下だけ
    • context ID -型 -文字数・行数 -短い prefix -短い質問 -depth、残り budget、iteration limit

生の context は session.messages に入れない。

rlm_exec tool

Pi の通常の bash をそのまま使うのではなく、永続 REPL を一つの構造化ツールにする。

rlm_exec({
 code: `
chunks = ...
results = llm_query_batched(prompts)
answer["content"] = combine(results)
answer["ready"] = True
`
})

内部動作:

  • Python worker にコードを送る
  • namespace を turn 間で保持
  • stdout/stderr は上限付きで Pi に返す -完全な変数は worker 内に保持
  • answer.ready が立ったら terminate: true -最終回答本文は tool result に流さず、外部 handle から runner が取得

これなら、1回の tool call 内の for や条件分岐から任意個の sub-call を起動できる。論文の「symbolic recursion」を維持できる。

sub-call

  • llm_query():
    • tools なしの Pi AgentSession -一つの prompt → 一つの最終 assistant text
  • rlm_query():
    • PiRlmRunner(depth + 1) を生成
    • maxDepth 到達時は llm_query() にフォールバック
  • batched variant:
    • Promise.all ではなく共有 semaphore を通す -入力順で結果を返す -親の AbortSignal、deadline、token/cost budget を共有する

実装配置

既存 Python core に Pi 依存を混ぜず、独立パッケージにするのが安全。

integrations/pi-rlm/
├── package.json
├── src/
│   ├── runner.ts
│   ├── prompt-store.ts
│   ├── repl-tool.ts
│   ├── subcall-runtime.ts
│   ├── budget.ts
│   └── extension.ts
├── worker/
│   └── repl_worker.py
└── test/

extension.ts は薄い /rlm UI。中心は SDK からも直接呼べる PiRlmRunner に置く。

採らない案

Python RLM に PiClient(BaseLM) を追加

最小変更ではあるが、Pi はモデル gateway にしかならない。外側のハーネスは依然として Python RLM。今回の目的とは違う。

Pi に rlm_query JSON tool だけ追加

簡単だが、モデルが sub-call を一件ずつ言語化する必要がある。ループ、分岐、大規模 fan-out をコードとして実行できず、論文 (https://www.alphaxiv.org/pdf/2512.24601v3) の Algorithm 2 に近い。

検証方針

PoC の合格条件は明確にできる。

  1. Context isolation 1MB以上の sentinel 入力が Pi の session messages/provider payload に混入しない。
  2. Symbolic recursion 一つの rlm_exec 内のループから N 個の sub-call が発生する。
  3. Persistent REPL turn 1 で作った変数を turn 2 で参照できる。
  4. Depth semantics maxDepthで one-shot call に切り替わる。
  5. Batch semantics 順序維持、並列実行、concurrency 上限を確認する。既存契約は tests/test_rlm_query.py:76-254 が参考になる。
  6. Shared limits 親子合算の token/cost、deadline、abort が子へ伝播する。
  7. External final answer root model の一回の出力上限を超える回答を、REPL の変数から返せる。
  8. 実モデル smoke direct Pi が context limit で扱えない synthetic S-NIAH を Pi-RLM が解けることを確認する。

論点

最大のリスクは REPL の安全性。現行 LocalREPL も security boundary ではないと明記されている(docs/architecture.md:112-131)。最初は trusted local PoC、外部入力を扱う段階で worker を Docker に隔離するのが妥当。

結論として、実装可能で、Pi native にする価値もある。最小PoCは「外部 context・Pi root session・永続 Python REPL・one-shot/recursive Pi sub-session」の4点に絞るのが正しい。