# エージェント ループ

Copilot CLI がユーザー メッセージをエンド ツー エンドで処理する方法: プロンプトから session.idle。

<!-- markdownlint-disable GHD046 GHD005 -->

<!-- Suppressed: GHD046 (outdated release terminology), GHD005 (hardcoded data variable) -->

## Architecture

![図: 説明されたプロセスを示すグラフ図。](/assets/images/help/copilot/copilot-sdk/features-agent-loop-diagram-0.png)

**SDK** はトランスポート層であり、JSON-RPC 経由でプロンプトを **Copilot CLI** に送信し、イベントをアプリに表示します。
**CLI** は、エージェント ツール使用ループを実行するオーケストレーターであり、タスクが完了するまで 1 つ以上の LLM API 呼び出しを行います。

## ツール使用ループ

`session.send({ prompt })`を呼び出すと、CLI はループに入ります。

![図: 説明されたプロセスを示すフローチャート。](/assets/images/help/copilot/copilot-sdk/features-agent-loop-diagram-1.png)

このモデルでは、各呼び出しの **会話履歴 (** システム プロンプト、ユーザー メッセージ、および以前のすべてのツール呼び出しと結果) が表示されます。

**主要な分析情報:** このループの各イテレーションは、イベント ログ内の 1 つの `assistant.turn_start` / `assistant.turn_end` ペアとして表示される、1 つの LLM API 呼び出しです。 非表示の呼び出しはありません。

## ターン — その定義

**ターン**は、単一の LLM API 呼び出しとその結果です。

1. CLI は会話履歴を LLM に送信します。
2. LLM が応答する (場合によってはツール要求あり)
3. ツールが要求された場合、CLI によって実行されます
4. `assistant.turn_end` が送出される

通常、1 つのユーザー メッセージは **複数のターン**になります。 たとえば、"X はこのコードベースでどのように機能しますか?" のような質問です。 生成される可能性があります:

| 回転                             | モデルの動作                         | toolRequests? |
| ------------------------------ | ------------------------------ | ------------- |
| 1                              |                                |               |
| `grep`と`glob`を呼び出してコードベースを検索する |                                |               |
| ✅ はい                           |                                |               |
| 2                              | 検索結果に基づいて特定のファイルを読み取ります        |               |
| ✅ はい                           |                                |               |
| 3                              | より深いコンテキストのためにより多くのファイルを読み取ります |               |
| ✅ はい                           |                                |               |
| 4                              | 最終的なテキスト回答を生成します               |               |
| ❌ いいえ → ループが終了する               |                                |               |

モデルは、各ターンで、より多くのツールを要求するか、最終的な回答を生成するかを決定します。 各呼び出しでは **、蓄積されたコンテキスト全体** (以前のすべてのツール呼び出しと結果) が表示されるため、十分な情報が得られたかどうかに関する情報に基づいて判断できます。

## 複数ターンにわたる対話のイベントフロー

![図: 説明されたプロセスを示すフローチャート。](/assets/images/help/copilot/copilot-sdk/features-agent-loop-diagram-2.png)

## 各ターンをトリガーするのは誰ですか？

| 俳優              | 責任                                              |
| --------------- | ----------------------------------------------- |
| **お客様のアプリ**     | を使用して最初のプロンプトを送信します。 `session.send()`           |
| **Copilot CLI** | ツール使用ループを実行します。ツールを実行し、結果を次のターンの LLM にフィードします   |
| **Llm**         | ツールを要求するか (ループを続ける)、または最終的な応答を生成するか (停止) を決定します |
| **SDK**         | イベントをそのまま通します。ループは制御しません                        |

CLI は単に機械的です。つまり、「モデルがツールを要求する → 実行する → もう一度モデルを呼び出す」です。
**モデル**は、停止するタイミングの意思決定者です。

## `session.idle` と `session.task_complete`

これらは、非常に異なる保証を持つ 2 つの異なる完了信号です。

### `session.idle`

* ツール使用ループが終了したときに**常に出力されます**
* **エフェメラル**: ディスクに永続化されず、セッションの再開時に再生されない
* 意味: "エージェントは処理を停止し、次のメッセージの準備ができています"
* **これを** 信頼できる "完了" 信号として使用する

SDK の `sendAndWait()` メソッドは、このイベントを待機します。

```typescript
// Blocks until session.idle fires
const response = await session.sendAndWait({ prompt: "Fix the bug" });
```

### `session.task_complete`

* **必要に応じて出力:** モデルに明示的に通知する必要があります
* **永続化**: ディスク上のセッション イベント ログに保存
* 意味: "エージェントは、タスク全体が満たされたと見なします"
* 省略可能な `summary` フィールドを含む

```typescript
session.on("session.task_complete", (event) => {
    console.log("Task done:", event.data.summary);
});
```

### 自動操縦モード: CLI が `task_complete` を促す

**オートパイロット モード** (ヘッドレス/自律操作) では、CLI はモデルが `task_complete` を呼び出したかどうかをアクティブに追跡します。 ツール使用ループがそれなしで終了した場合、CLI はモデルを微調整する合成ユーザー メッセージを挿入します。

> *"task\_complete ツールを使用して、タスクを完了としてまだマークしていません。計画していた場合は、計画を中止し、実装を開始します。タスクが完全に完了するまでは完了しません。*

これにより、ツール使用ループが効果的に再開されます。モデルはナッジを新しいユーザー メッセージとして認識し、動作を続けます。 また、ナッジは、\*\*\*\* を途中で呼び出`task_complete`モデルに指示します。

* オープンな質問がある場合は呼び出さないでください。意思決定を行い、作業を続ける
* エラーが発生した場合は呼び出さないでください。解決してみてください
* 残りの手順がある場合は呼び出さないでください。最初に完了してください

これにより、オートパイロットに **2 レベルの完了メカニズム** が作成されます。

1. モデルが概要付きで`task_complete`を呼び出す → CLI が`session.task_complete`を出力する → 完了
2. モデルがそれを呼び出さずに停止する → CLI が促す → モデルが継続するか、`task_complete` を呼び出す

### `task_complete`が表示されない理由

**対話型モード**（通常のチャット）では、CLI は`task_complete`の入力を促しません。 モデルはそれを完全にスキップする可能性があります。 一般的な理由:

* **会話型 Q\&A**: モデルは質問に回答し、単に停止します。完了する個別の "タスク" はありません
* **モデルの裁量**: モデルは、タスク完了シグナルを呼び出さずに最終的なテキスト応答を生成します
* **中断されたセッション**: モデルが完了ポイントに達する前にセッションが終了する

CLI は、セマンティック信号ではなく、機械的なシグナル (ループが終了) であるため、 `session.idle` を出力します (モデルでは完了と見なされます)。

### どちらを使用すべきか

| 利用シーン                              | 信号 |
| ---------------------------------- | -- |
| "エージェントの処理が完了するまで待ちます"             |    |
| `session.idle`                     |    |
| ✅                                  |    |
|                                    |    |
| "コーディング タスクがいつ完了したかを把握する"          |    |
| `session.task_complete` (ベストエフォート) |    |
| "タイムアウト/エラー処理"                     |    |
| `session.idle`                     |    |

*

`session.error`
✅
|

## LLM 呼び出しの数え方

イベント ログ内の `assistant.turn_start` / `assistant.turn_end` ペアの数は、LLM API 呼び出しの合計数と同じです。 計画、評価、または完了チェックの非表示の呼び出しはありません。

セッションのターン数を調べるには:

```bash
# Count turns in a session's event log
grep -c "assistant.turn_start" ~/.copilot/session-state/<sessionId>/events.jsonl
```

## 詳細については、次を参照してください。

* [ストリーミング セッション イベント](/ja/copilot/how-tos/copilot-sdk/features/streaming-events): すべてのイベントの種類に対するフィールド レベルの完全な参照
* [セッションの再開と永続化](/ja/copilot/how-tos/copilot-sdk/features/session-persistence): セッションの保存と再開方法
* [フックを使った作業](/ja/copilot/how-tos/copilot-sdk/features/hooks): ループ内のイベントをインターセプトする (アクセス許可、ツール)