
2026/10/02 4:24
「耐性のある Pi」
RSS: https://news.ycombinator.com/rss
要約▶
Japanese Translation:
Pi コミュニティは、既存のコーディングエージェントを置き換えることなく、長期的な実行を耐性のあるエージェントを構築するために設計された実験的な TypeScript フレームワークである Pi Durable を、Pi 1.0 とともに公開しました。Pi Durable は堅牢なハネスとして機能し、最小主義と可塑性というコア原則を含む元のアグエントとの間でコードを共有しており、約 15,000 ラインのコードで構成されています。メモリ保存(SQLite、JSONL)、実行環境(Node、Bun、Cloudflare Durable Object)、並列 LLM 対話を実行するためのマシーンといった重要なインフラストラクチャを提供します。特に重要なのは、クラッシュを生き残るために各ステップをチェックポイント化して耐性を確保しており、エージェントは最後の状態から再開し、不安全なツールは失敗後に自動的に再実行をスキップする一方、安全なものはそうでないことです。このフレームワークは、トランスクリプトの履歴に基づいてフォークされ互いにブロックせずに多くの同時対話を許可することで並行性をサポートします。開発者は、「owned conversations」を使用して最小限のロジックでサブアグエントを構築でき、これは自分自身のコストをカウントし、再起動後も持続します。アプリケーションの状態は、トランスクリプトと一緒にtyped JSON ドキュメントを通じて維持され、フォークまたは再起動後にも一貫性が確保されます。さらに、背景でのコンパクションにより古いメッセージが要約され、コンテキストの制限を管理でき、手動コンパクションおよびハンドオフのオプションがあります。マルチプレイヤー対応の基盤として、コミットされた状態更新を使用して複数のクライアントがリアルタイム対話を見守りおよび操舵することを可能にします。開発者は、Pi チェックアウト内の
packages/durable ディレクトリにある例を使用してすぐに構築を開始することが推奨されており、将来的なイテレーションでは手動コンパクションのような機能が洗練される可能性があります。本文
Pi 1.0 リリースと「Pi Durable」の新パッケージ発表:堅牢なエージェント開発の新たな基盤
Earendil と Pi コミュニティは、Pi 1.0を正式リリースしました。長年の開発・保守・強化の結果、Pi はさらに発展するにふさわしい確かな基盤となりました。同時に、Pi の進化も止まることはありません。
リリースに合わせて、実験的な新パッケージ「Pi Durable」も発表します。
- 特徴: あらゆる場所で実行可能で、長時間稼働し続ける設計(堅牢性)と、柔軟なエージェント開発のためのフレームワーク。
- 目的: 皆様と共に、最高レベルの堅牢なハネス(架台・枠組み)を構築することを願っています。
なぜ Pi Durable なのか?
コーディングエージェントである Pi はもともと、一人の人間の操作下でターミナル上で動作するように設計されています。Pi 1.0 の長所である「プロセス停止後の確認と再開」機能は変わっていません。
しかし、Earendil はこの技術をあらゆる人のニーズに最適化するために「ハネス(Harness)」という新概念を導入しました。
- 要件: 多様なプラットフォームで動作し、無制限の対話長さに対応し、致命的な障害にも耐え、複数人が共同操作できる環境が必要でした。
- 解決策: その答えがPi Durableです。
重要: Pi Durable は Pi コーディングエージェントを置き換えるものではなく、あらゆる「エージェント型アプリケーション(agentic application)」を構築するためのフレームワークです。Pi のコードだけでなく、「最小主義」と「柔軟性」の原則も共有しています。
- メリット: 分野における設計を探求しつつ、Pi の機能や体験に干渉せず進められます。得られた教訓は Pi に還元されます。
ハネス(Harness)とは?
Pi Durable におけるハネスとは、大規模言語モデル(LLM)との対話を並列実行するためのストレージとそのインフラストラクチャそのものです。
- 機能: モデル呼び出しやツール実行環境を備えた包括的なプラットフォームを提供します。
- 構成要素:
- 対話: ユーザーとエージェントのインタラクション(トランスクリプトとして保存)。
- エージェント: LLM+思考レベル+ツールのセット。
- ツール: 実行環境(ノート PC、VM、サンドボックスなど)を通じて作業を行う。
設計理念: エージェント自身がその仕組みを理解できるよう設計されています。
- コード量: ソースコード約 15,000 ライン(GPT で約 15 万トークン/Claude で約 25 万トークン)。
- スキップ可能: 特にストレージバックエンドは約 3,000 ラインのみで、通常はこの部分をスキップできます。
主な機能と特徴
1. あらゆる場所で長時間稼働(Portability & Durability)
- 「どこでも」動作: JavaScript ランタイムが存在する場所ならどこでも稼働可能です。
- 軽量ストレージ: 独自のバックエンド適応スイートを提供。Node.js API を使わないため、Bun や Cloudflare Durable Object などでも動作します。
- SQLite / JSONL などのインターフェースはキーバリューストアや Postgres 等にも容易に実装可能です。
- メモリ効率: SQLite を使用時は、ワーキングセットのみをメモリ保持。残りはディスク保存。コンテキストウィンドウの制限により、何万ものメッセージでも管理可能です。
実行コード例(セットアップ)
import { BACKGROUND_CONTEXT } from "@earendil-works/chord/context"; import { createModels } from "@earendil-works/pi-ai/models"; import { openaiProvider } from "@earendil-works/pi-ai/providers/openai"; import { createRegistry, Harness } from "@earendil-works/pi-durable"; import { NodeExecutionEnv } from "@earendil-works/pi-durable/env/node"; import { openNodeSqliteStorage } from "@earendil-works/pi-durable/storage/sqlite/node"; import { CodingTools } from "@earendil-works/pi-durable/tools"; // 初期設定 const context = BACKGROUND_CONTEXT; const models = createModels(); models.setProvider(openaiProvider()); // リージストとツール登録 const registry = createRegistry(); registry.install(CodingTools); // 実行環境構築関数 const env = ({ cwd }: { cwd?: string }) => new NodeExecutionEnv({ cwd: cwd ?? process.cwd() }); // ハネスのオープン(ストレージ、モデル、レジストリ、環境を渡す) const harness = await Harness.open( await openNodeSqliteStorage("./agent.sqlite"), { models, registry, env }, context, ); // ルート対話の作成(再始動後も同一プロセスが維持されます) const root = await harness.root(context, { agent: { model: { provider: "openai", modelId: "gpt-6.1-sol" }, cwd: "/work/repo", }, });
2. クラッシュからの回復(Crash Recovery)
ノート PC の睡眠、コンテナの再デプロイ、メモリ不足など发生时でも、エージェントはプロセス停止を乗り越え、中断した場所から再開できます。
- チェックポイント: 各ステップでチェックポイントを保存します。
- 自動復旧: 新しいプロセスがストレージを開き、完了していないタスクを見つけ、最後のチェックポイントから続行します。
- モデルリクエストは再送信され、中断部分は「中止済み」として記録されます。
- ツールの呼び出しも安全であれば再実行されます(そうでない場合はモデルに報告)。
- 保証された実行:
を使用することで提出が「一度限り」保証されます。クライアントは元の提出を受け取ればよく、再試行の重複は防ぎます。requestId
復旧コード例
const job = { type: "input", content: "Fix the flaky login test", requestId: "job-42", // ユニークな ID を使用 } as const; // 最初のプロセスがここで停止します(ツール呼び出し途中) await root.submit(job, context); // --- 新しいプロセスが起動し、同じストレージを開きます --- const harness = await Harness.open( await openNodeSqliteStorage("./agent.sqlite"), { models, registry, env }, context, ); // resume() で中断した処理から続行 harness.resume(); const root = await harness.root(context); // 同じ job-42 に対して回答が返ってきます const settled = await (await root.submit(job, context)).wait(context);
3. 多数の対話を同時に実行(Parallel Conversations)
1 つのハネス上で複数の対話を並列に動作させ、互いにブロックしない仕組みです。
- スレッド機能: Slack チャンネル(親対話)とスレッド(フォークされた子対話)のように動作します。
- 独立性: 片方の対話が中断しても他への影響はありません。所有者の管理に基づいて中止の影響範囲が決定されます。
フォーク対話のコード例
// 親対話(チャンネル) const channel = await harness.root(context); const question = await channel.submit( { type: "input", content: "@agent why did the deploy fail?" }, context, ); await question.wait(context); // 誰かのスレッドでの返信(所有者なしのフォーク) const thread = await channel.fork( answered.answer!, { ownership: { kind: "ownerless" } }, context, ); // 両方の対話を同時に動作させます(ブロックされない) await Promise.all([ thread.submit({ type: "input", content: "@agent can we roll it back?" }, context), channel.submit({ type: "input", content: "@agent who is on call today?" }, context) ]);
4. 拡張機能(Extensions)
すべての機能をモジュール化し、追加部分が堅牢性向上に寄与します。システムプロンプト、ツール、フックなどをバンドルとして管理します。
システムプロンプトセクション
- リクエストごとに拡張機能のセクションから即時再構築され、変更が反映されます。
- トランスクリプト内の変更履歴を記録するため、再起動後もモデルが見ていた内容を正確に復元可能です。
import { defineExtension, section } from "@earendil-works/pi-durable"; const ProjectContext = defineExtension({ name: "project-context", sections: [ // ファイルは背景でロード・監視され、最新のステートがレンダリングされます section("agents_md", (input) => agentsMd.latest(input.env)), section("skills", (input) => skills.latest(input.env)), ], });
ツール
- 安全性: クラッシュ後に再実行されるのは「安全(safe)」と宣言されたツールのみです。
- サブエージェントの構築: ツールは独立した対話を作成でき、小型モデルや独自の指示を与えて回答を待つことができます。これもクラッシュに耐え、コストが独自に計上されます。
import { Type } from "@earendil-works/pi-ai"; import { defineTool } from "@earendil-works/pi-durable"; // 安全なツール(読み取り専用):再実行も OK const searchIssues = defineTool({ name: "search_issues", description: "Search the issue tracker", replay: "safe", execute: async (args, api) => { /* ... */ }, }); // 非安全なツール(デプロイ):再実行は禁止 const deploy = defineTool({ name: "deploy", description: "Deploy a version to production", replay: false, // またはデフォルトの動作 execute: async (args) => { /* ... */ }, });
フック(Hooks)
- リクエスト書き換え、ツール呼び出しブロック/置換、サマリー作成などに対応可能です。
- クラッシュ後の再実行で意思決定を行うフックは「メモ」を通じて決定値を保存し、競合を防ぎます。
- チェーン処理:
→beforeTool
→afterTool
の順序で動作します。onYield
import { hook, ToolTask } from "@earendil-works/pi-durable"; const Approval = defineExtension({ name: "approval", hooks: [ hook(ToolTask, { beforeTool: async (call, api, context) => { if (call.name !== "deploy") return undefined; // メモを使って再実行時の決定値を取得 let approved = await api.memo<boolean>("approval:deploy", context); return approved ? undefined : { block: "Nobody approved the deploy." }; }, }), ], });
5. タスク(Tasks)
ハネスは対話と内蔵タスクを実行します。タスクもチェックポイント、タイマー、待機などの同じマシナリーを利用可能です。
- 所有権: 複数のカードで請求を分担する際、1 つのカードが拒否されると他の支払いも中止され返金されます(原子性)。
import { defineTask } from "@earendil-works/pi-durable"; // 個別の支払いタスク const Payment = defineTask<{ card: string }, { phase: "charge" }, string>({ name: "shop.payment", phases: { charge: async (task, runtime) => { /* ... */ } }, }); // チェックアウト(複数カードの処理) const Checkout = defineTask<{ cards: string[] }, { phase: "pay" }, string>({ name: "shop.checkout", policy: "failFast", // 最初の失敗で中止 phases: { pay: async (task, runtime) => { /* ... */ } }, });
実行制御(フォアグラウンド vs バックグラウンド)
- フォアグラウンド: 対話の現在の作業の一部。Esc で中止されると待機中になります。
- バックグラウンド: 対話には属しますが、現在の作業ではありません。中止しても他のタスクに影響しません(リマインダーなど向け)。
await api.createTask(Checkout, input, { ownership: "task" }, context); // 直前の作業 await api.createTask(Reminder, input, { ownership: "conversation", background: true }, context); // バックグラウンドタスク
6. 圧縮(Compaction)
対話を長期にわたって継続しつつ、サマリー作成の必要をなくします。
- 自動管理: コンテキストが限界に近づくとバックグラウンドで古いメッセージを要約し、次の境界に配置します。
- 手動実行: いつでも
で実行可能です。compact() - リセット:
はハンドオフノートから新しいコンテキストを開始し、古いメッセージも検索可能です。reset()
const harness = await Harness.open(storage, { models, registry, settings: { compaction: { reserveTokens: 16384, // 待機開始閾値 backgroundTokens: 32768, // バックグラウンド要約閾値 }, }, }); // 手動圧縮(エージェント作業中も可能) await root.compact("Keep the names of the failing tests", context);
7. エージェント固有のアプリケーション状態(App State)
アプリケーションの状態(タスクリスト、計画など)を対話と同様に堅牢に管理します。
- ドキュメント化: 状態はドキュメント内に存在し、トランスクリプトと整合性が保たれます。
- フォーク対応: フォーク点における親の値を記録し、一貫性を維持します。
import { defineDoc } from "@earendil-works/pi-durable"; const Todos = defineDoc<{ items: string[] }>({ kind: "app.todos", scope: "conversation", fork: "asOf", // フォーク時の親値を保持 }); // UI がコミットされた状態を購読 const todos = await harness.documentState(Todos, channel.id, context); todos?.subscribe((value) => renderTodos(value?.items ?? []));
8. 柔軟性(Malleability)
動作中のエージェントコードを変更しながら中断することなく更新可能です。
- 動的更新:
で拡張機能を再インストールすると、既存のものが即座に置き換わります。registry.install() - シームレス: 現在の呼び出しは旧コードで完了し、次から新コードが使用されます。
// ディスク上のファイル変更を即座に反映 registry.install(await loadExtension("./ops.ts"));
9. マルチプレイヤー(Multiplayer)
多数のクライアントが同時に同じ対話で作業・観測・操作可能です。
- 同期: コミットされた状態をベースに、変化分のみを受け取ります。
- 舵取り: 任意のクライアントが対話を舵取ったり、フォローアップキューイングしたりできます。
// 現在のビュー取得 const view = await thread.viewState(context); view.subscribe((value) => render(value)); // 遠隔からの舵取り(Busy な時でも実行) await thread.submit( { type: "input", content: "Check the staging logs first", whenBusy: "steer" }, context, );
まとめ:試してみよう
Pi Durable は今日から実験的に利用可能です。API や詳細は随時進化します。
- 学習リソース: README、30 のサンプルコード、小型エージェント例などが提供されています。
- デモ実行: 休暇計画エージェントやコーディングエージェントのデモを実行できます(TypeScript 約 1,300 ライン)。
インストール手順:
npm install @earendil-works/pi-durable @earendil-works/pi-ai @earendil-works/chord
来週、詳細な解説や Slack ボット、GitHub トラージボットなどの実用ツールについても紹介予定です。Pi の生態系と共に成長する新たな基盤を是非体験してください。