Skip to content

AgentSession:コーディング Agent のオーケストレーション層

源码版本v0.73.1

AgentSession は pi-coding-agent の中で一番重い層だ——直接 LLM を走らせるわけでもなく、直接 UI を描くわけでもなく、その中間に挟まる。上に対してはユーザー入力と UI イベントを受け取り、下に対しては pi-agent-coreAgent を駆動してループを走らせる。モデル登録、ツール定義、システムプロンプト構築、コンテキスト圧縮、失敗リトライ、メッセージキューイングは全部ここが管轄する。「汎用 Agent を状態付き・復帰可能・スキルとツール付きのコーディングアシスタントに包む」組み立て工場と捉えればいい。

責務

AgentSession は 4 つをやる:

  1. 組み立て:AgentSessionConfigAgent インスタンス、モデルレジストリ (registry)、ツール定義、拡張ランタイム、システムプロンプトビルダーを詰め、構築時に beforeToolCall/afterToolCall フックとイベント購読を取り付ける。packages/coding-agent/src/core/agent-session.ts:313-330 参照。
  2. 入力受付:prompt(text) が主入り口。/ スラッシュコマンド、ファイル型プロンプトテンプレートの展開、テキストから AgentMessage[] への変換を処理し、下層の Agent に渡す。packages/coding-agent/src/core/agent-session.ts:967-1000 参照。
  3. イベント転送:Agent のイベントストリームを購読し、AgentSessionEvent に包み直して上層 UI に送る。packages/coding-agent/src/core/agent-session.ts:330-335 参照。
  4. キューイングとリトライ:ストリーミング出力中にユーザーが打ち続けた時、streamingBehavior に基づいて steer (差し込み) か followUp (キューイング) に振り分ける。単に捨てたりブロックしたりはしない。packages/coding-agent/src/core/agent-session.ts:1181-1296 参照。

設計動機

なぜ UI に直接 Agent.prompt を呼ばせないのか? コーディングアシスタントは「汎用 Agent が管轄しない」横断ロジックを山ほど必要とするからだ:スラッシュコマンド (/model/settings)、プロンプトテンプレート展開、モデル/API key 検証、コンテキスト超過時の自動圧縮、ツール呼び出し前後の認可とテレメトリ。これを Agent に押し込むと汎用ループが重くなるし、UI 層に置くと print モード、rpc モード、拡張で重複実装する羽目になる。AgentSession を唯一の入り口として切り出し、3 つの実行モードが同じオーケストレーションを共用し、UI はイベントをレンダリングするだけにする。

主要ファイル

コンストラクタでフックと購読を取り付けるのが、全体のオーケストレーションの起点になる:

typescript
// packages/coding-agent/src/core/agent-session.ts:313-335
constructor(config: AgentSessionConfig) {
  // ... config を格納、状態初期化 ...
  this._unsubscribeAgent = this.agent.subscribe(this._handleAgentEvent);
  // ... 略 ...
}

prompt はユーザーのテキストを AgentMessage[] にしてから下層の Agent に渡す:

typescript
// packages/coding-agent/src/core/agent-session.ts:1107-1112
  try {
    await this.agent.prompt(messages);
  } catch (error) {
    // ... 失敗処理 ...
  }

データフロー

ユーザーが TUI エディタで Enter を押す → InteractiveMode.defaultEditor.onSubmitsession.prompt(text):

ストリーミング中にユーザーが打ち続ける時、prompt は無理に押し込まず振り分ける:

境界と失敗

  • スラッシュコマンド衝突:拡張が登録したコマンドはストリーミング中でも即時実行できる (自身で LLM インタラクションを管轄) 。キューには積まれない。コマンドがキュー不可の場合は直接 throw する。packages/coding-agent/src/core/agent-session.ts:1255-1259 参照。
  • モデル/key 欠落:非ストリームパスはモデルと API key を検証し、欠落時は空リクエストを送らず throw する。
  • 圧縮の中止:abort は主ループ、圧縮コントローラ、分岐要約コントローラを同時にキャンセルする。packages/coding-agent/src/core/agent-session.ts:1744-1752 参照。
  • リトライ:Agent.emit() は同期的に _handleAgentEvent を呼ぶ一方、prompt()waitForRetry() する。失敗後はリトライロジックが次ターンを再送するか決める。

小ねた

AgentSession はコーディングアシスタントの「組み立て工場 + イベントバス」だ:Agent を組み立て、イベントを転送し、差し込みをキューイングし、ツールフックを管轄する。上を見れば InteractiveMode/runPrintMode/runRpcMode の 3 つの UI モード、下を見れば pi-agent-core の汎用ループ。組み立ての詳細は createAgentSession 装配、下層のループは 二重 while ループ 参照。