{"meta":{"title":"ステアリングとキューイング","intro":"エージェントが既に動作している間、ユーザーは 2 つの対話パターンでメッセージを送信できます。 ステアリング は途中ターンでエージェントをリダイレクトし、現在のターンが完了した後に順次処理するためにメッセージ をキューに入 れます。","product":"GitHub Copilot","breadcrumbs":[{"href":"/ja/copilot","title":"GitHub Copilot"},{"href":"/ja/copilot/how-tos","title":"方法"},{"href":"/ja/copilot/how-tos/copilot-sdk","title":"Copilot SDK"},{"href":"/ja/copilot/how-tos/copilot-sdk/features","title":"機能"},{"href":"/ja/copilot/how-tos/copilot-sdk/features/steering-and-queueing","title":"ステアリングとキューイング"}],"documentType":"article"},"body":"# ステアリングとキューイング\n\nエージェントが既に動作している間、ユーザーは 2 つの対話パターンでメッセージを送信できます。 ステアリング は途中ターンでエージェントをリダイレクトし、現在のターンが完了した後に順次処理するためにメッセージ をキューに入 れます。\n\n<!-- markdownlint-disable GHD046 GHD005 -->\n\n<!-- Suppressed: GHD046 (outdated release terminology), GHD005 (hardcoded data variable) -->\n\n## Overview\n\nセッションがターンをアクティブに処理している場合、受信メッセージは、`mode`の `MessageOptions` フィールドを使用して、次の 2 つのモードのいずれかで配信できます。\n\n| モード                    | Behavior                        | 利用シーン                                     |\n| ---------------------- | ------------------------------- | ----------------------------------------- |\n| `\"immediate\"` (ステアリング) | LLMの**現在の**ターンに挿入               | \"実際には、そのファイルを作成しないでください。別のアプローチを使用してください\" |\n| `\"enqueue\"` (キュー)      | 現在のターンが終了した **後** にキューに入れ、処理される | \"この後、テストも修正します\"                           |\n\n![図: 説明されたプロセスを示すシーケンス図。](/assets/images/help/copilot/copilot-sdk/features-steering-and-queueing-diagram-0.png)\n\n## ステアリング (イミディエイト モード)\n\nステアリングは、エージェントの現在のターンに直接挿入されるメッセージを送信します。 エージェントはリアルタイムでメッセージを確認し、それに応じて応答を調整します。ターンを中止せずにコースの修正に役立ちます。\n\n<div class=\"ghd-codetabs\">\n<div class=\"ghd-codetab\" data-lang=\"typescript\" data-label=\"TypeScript\"><div class=\"ghd-codetab-fallback-label\" role=\"heading\" aria-level=\"3\">TypeScript</div>\n\n```typescript\nimport { CopilotClient } from \"@github/copilot-sdk\";\n\nconst client = new CopilotClient();\nawait client.start();\n\nconst session = await client.createSession({\n    model: \"gpt-5.4\",\n    onPermissionRequest: async () => ({ kind: \"approve-once\" }),\n});\n\n// Start a long-running task\nconst msgId = await session.send({\n    prompt: \"Refactor the authentication module to use sessions\",\n});\n\n// While the agent is working, steer it\nawait session.send({\n    prompt: \"Actually, use JWT tokens instead of sessions\",\n    mode: \"immediate\",\n});\n```\n\n</div>\n\n<div class=\"ghd-codetab\" data-lang=\"python\" data-label=\"Python\"><div class=\"ghd-codetab-fallback-label\" role=\"heading\" aria-level=\"3\">Python</div>\n\n```python\nfrom copilot import CopilotClient, PermissionDecisionApproveOnce\n\nasync def main():\n    client = CopilotClient()\n    await client.start()\n\n    session = await client.create_session(\n        on_permission_request=lambda req, inv: PermissionDecisionApproveOnce(),\n        model=\"gpt-5.4\",\n    )\n\n    # Start a long-running task\n    msg_id = await session.send(\n        \"Refactor the authentication module to use sessions\",\n    )\n\n    # While the agent is working, steer it\n    await session.send(\n        \"Actually, use JWT tokens instead of sessions\",\n        mode=\"immediate\",\n    )\n\n    await client.stop()\n```\n\n</div>\n\n<div class=\"ghd-codetab\" data-lang=\"go\" data-label=\"Go\"><div class=\"ghd-codetab-fallback-label\" role=\"heading\" aria-level=\"3\">Go</div>\n\n```golang\npackage main\n\nimport (\n    \"context\"\n    \"log\"\n    copilot \"github.com/github/copilot-sdk/go\"\n    \"github.com/github/copilot-sdk/go/rpc\"\n)\n\nfunc main() {\n    ctx := context.Background()\n    client := copilot.NewClient(nil)\n    if err := client.Start(ctx); err != nil {\n        log.Fatal(err)\n    }\n    defer client.Stop()\n\n    session, err := client.CreateSession(ctx, &copilot.SessionConfig{\n        Model: \"gpt-5.4\",\n        OnPermissionRequest: func(req copilot.PermissionRequest, inv copilot.PermissionInvocation) (rpc.PermissionDecision, error) {\n            return &rpc.PermissionDecisionApproveOnce{}, nil\n        },\n    })\n    if err != nil {\n        log.Fatal(err)\n    }\n\n    // Start a long-running task\n    _, err = session.Send(ctx, copilot.MessageOptions{\n        Prompt: \"Refactor the authentication module to use sessions\",\n    })\n    if err != nil {\n        log.Fatal(err)\n    }\n\n    // While the agent is working, steer it\n    _, err = session.Send(ctx, copilot.MessageOptions{\n        Prompt: \"Actually, use JWT tokens instead of sessions\",\n        Mode:   \"immediate\",\n    })\n    if err != nil {\n        log.Fatal(err)\n    }\n}\n```\n\n</div>\n\n<div class=\"ghd-codetab\" data-lang=\"dotnet\" data-label=\".NET\"><div class=\"ghd-codetab-fallback-label\" role=\"heading\" aria-level=\"3\">.NET</div>\n\n```csharp\nusing GitHub.Copilot;\nusing GitHub.Copilot.Rpc;\n\nawait using var client = new CopilotClient();\nawait using var session = await client.CreateSessionAsync(new SessionConfig\n{\n    Model = \"gpt-5.4\",\n    OnPermissionRequest = (req, inv) =>\n        Task.FromResult(PermissionDecision.ApproveOnce()),\n});\n\n// Start a long-running task\nvar msgId = await session.SendAsync(new MessageOptions\n{\n    Prompt = \"Refactor the authentication module to use sessions\"\n});\n\n// While the agent is working, steer it\nawait session.SendAsync(new MessageOptions\n{\n    Prompt = \"Actually, use JWT tokens instead of sessions\",\n    Mode = \"immediate\"\n});\n```\n\n</div>\n\n<div class=\"ghd-codetab\" data-lang=\"java\" data-label=\"Java\"><div class=\"ghd-codetab-fallback-label\" role=\"heading\" aria-level=\"3\">Java</div>\n\n```java\nimport com.github.copilot.CopilotClient;\nimport com.github.copilot.rpc.*;\n\ntry (var client = new CopilotClient()) {\n    client.start().get();\n\n    var session = client.createSession(\n        new SessionConfig()\n            .setModel(\"gpt-5.4\")\n            .setOnPermissionRequest(PermissionHandler.APPROVE_ALL)\n    ).get();\n\n    // Start a long-running task\n    session.send(new MessageOptions()\n        .setPrompt(\"Refactor the authentication module to use sessions\")\n    ).get();\n\n    // While the agent is working, steer it\n    session.send(new MessageOptions()\n        .setPrompt(\"Actually, use JWT tokens instead of sessions\")\n        .setMode(\"immediate\")\n    ).get();\n}\n```\n\n</div>\n\n</div>\n\n### ステアリングの内部構造\n\n1. メッセージがランタイムの `ImmediatePromptProcessor` キューに追加されます\n2. 現在のターン内の次の LLM 要求の前に、プロセッサはメッセージを会話に挿入します\n3. エージェントは、ステアリング メッセージを新しいユーザー メッセージとして認識し、その応答を調整します\n4. ステアリング メッセージが処理される前にターンが完了すると、次のターンの通常のキューに自動的に移動されます。\n\n> \\[!NOTE]\n> メッセージの転送は、現在のターン内で可能な限りベストを尽くします。 エージェントが既にツール呼び出しにコミットしている場合、その呼び出しが完了した後でも同じターン内でステアリングが有効になります。\n\n## キューイング (エンキュー モード)\n\nメッセージをキューに登録すると、現在のターンが終了した後に順に処理されます。 キューに登録された各メッセージは、独自のフル ターンを開始します。 これは既定のモードです。 `mode`を省略すると、SDK は `\"enqueue\"` を使用します。\n\n<div class=\"ghd-codetabs\">\n<div class=\"ghd-codetab\" data-lang=\"typescript\" data-label=\"TypeScript\"><div class=\"ghd-codetab-fallback-label\" role=\"heading\" aria-level=\"3\">TypeScript</div>\n\n```typescript\nimport { CopilotClient } from \"@github/copilot-sdk\";\n\nconst client = new CopilotClient();\nawait client.start();\n\nconst session = await client.createSession({\n    model: \"gpt-5.4\",\n    onPermissionRequest: async () => ({ kind: \"approve-once\" }),\n});\n\n// Send an initial task\nawait session.send({ prompt: \"Set up the project structure\" });\n\n// Queue follow-up tasks while the agent is busy\nawait session.send({\n    prompt: \"Add unit tests for the auth module\",\n    mode: \"enqueue\",\n});\n\nawait session.send({\n    prompt: \"Update the README with setup instructions\",\n    mode: \"enqueue\",\n});\n\n// Messages are processed in FIFO order after each turn completes\n```\n\n</div>\n\n<div class=\"ghd-codetab\" data-lang=\"python\" data-label=\"Python\"><div class=\"ghd-codetab-fallback-label\" role=\"heading\" aria-level=\"3\">Python</div>\n\n```python\nfrom copilot import CopilotClient, PermissionDecisionApproveOnce\n\nasync def main():\n    client = CopilotClient()\n    await client.start()\n\n    session = await client.create_session(\n        on_permission_request=lambda req, inv: PermissionDecisionApproveOnce(),\n        model=\"gpt-5.4\",\n    )\n\n    # Send an initial task\n    await session.send(\"Set up the project structure\")\n\n    # Queue follow-up tasks while the agent is busy\n    await session.send(\n        \"Add unit tests for the auth module\",\n        mode=\"enqueue\",\n    )\n\n    await session.send(\n        \"Update the README with setup instructions\",\n        mode=\"enqueue\",\n    )\n\n    # Messages are processed in FIFO order after each turn completes\n    await client.stop()\n```\n\n</div>\n\n<div class=\"ghd-codetab\" data-lang=\"go\" data-label=\"Go\"><div class=\"ghd-codetab-fallback-label\" role=\"heading\" aria-level=\"3\">Go</div>\n\n```golang\n// Send an initial task\nsession.Send(ctx, copilot.MessageOptions{\n    Prompt: \"Set up the project structure\",\n})\n\n// Queue follow-up tasks while the agent is busy\nsession.Send(ctx, copilot.MessageOptions{\n    Prompt: \"Add unit tests for the auth module\",\n    Mode:   \"enqueue\",\n})\n\nsession.Send(ctx, copilot.MessageOptions{\n    Prompt: \"Update the README with setup instructions\",\n    Mode:   \"enqueue\",\n})\n\n// Messages are processed in FIFO order after each turn completes\n```\n\n</div>\n\n<div class=\"ghd-codetab\" data-lang=\"dotnet\" data-label=\".NET\"><div class=\"ghd-codetab-fallback-label\" role=\"heading\" aria-level=\"3\">.NET</div>\n\n```csharp\n// Send an initial task\nawait session.SendAsync(new MessageOptions\n{\n    Prompt = \"Set up the project structure\"\n});\n\n// Queue follow-up tasks while the agent is busy\nawait session.SendAsync(new MessageOptions\n{\n    Prompt = \"Add unit tests for the auth module\",\n    Mode = \"enqueue\"\n});\n\nawait session.SendAsync(new MessageOptions\n{\n    Prompt = \"Update the README with setup instructions\",\n    Mode = \"enqueue\"\n});\n\n// Messages are processed in FIFO order after each turn completes\n```\n\n</div>\n\n<div class=\"ghd-codetab\" data-lang=\"java\" data-label=\"Java\"><div class=\"ghd-codetab-fallback-label\" role=\"heading\" aria-level=\"3\">Java</div>\n\n```java\nimport com.github.copilot.CopilotClient;\nimport com.github.copilot.rpc.*;\n\ntry (var client = new CopilotClient()) {\n    client.start().get();\n\n    var session = client.createSession(\n        new SessionConfig()\n            .setModel(\"gpt-5.4\")\n            .setOnPermissionRequest(PermissionHandler.APPROVE_ALL)\n    ).get();\n\n    // Send an initial task\n    session.send(new MessageOptions().setPrompt(\"Set up the project structure\")).get();\n\n    // Queue follow-up tasks while the agent is busy\n    session.send(new MessageOptions()\n        .setPrompt(\"Add unit tests for the auth module\")\n        .setMode(\"enqueue\")\n    ).get();\n\n    session.send(new MessageOptions()\n        .setPrompt(\"Update the README with setup instructions\")\n        .setMode(\"enqueue\")\n    ).get();\n\n    // Messages are processed in FIFO order after each turn completes\n}\n```\n\n</div>\n\n</div>\n\n### キューイングの内部での動作\n\n1. メッセージはセッションの `itemQueue` に追加されます。 `QueuedItem`\n2. 現在のターンが完了し、セッションがアイドル状態になると、 `processQueuedItems()` 実行されます。\n3. 項目は FIFO 順にデキューされます。各メッセージはエージェントのターン全体をトリガーします\n4. ターンが終了したときにステアリング メッセージが保留中であった場合は、キューの先頭に移動されます。\n5. キューが空になるまで処理が続行され、セッションによってアイドル 状態のイベントが生成されます\n\n## ステアリングとキューイングの組み合わせ\n\n両方のパターンを 1 つのセッションで一緒に使用できます。 ステアリングは、キューに入ったメッセージが自分のターンを待機している間、現在のターンに影響します。\n\n<div class=\"ghd-codetabs\">\n<div class=\"ghd-codetab\" data-lang=\"typescript\" data-label=\"TypeScript\"><div class=\"ghd-codetab-fallback-label\" role=\"heading\" aria-level=\"3\">TypeScript</div>\n\n```typescript\nconst session = await client.createSession({\n    model: \"gpt-5.4\",\n    onPermissionRequest: async () => ({ kind: \"approve-once\" }),\n});\n\n// Start a task\nawait session.send({ prompt: \"Refactor the database layer\" });\n\n// Steer the current work\nawait session.send({\n    prompt: \"Make sure to keep backwards compatibility with the v1 API\",\n    mode: \"immediate\",\n});\n\n// Queue a follow-up for after this turn\nawait session.send({\n    prompt: \"Now add migration scripts for the schema changes\",\n    mode: \"enqueue\",\n});\n```\n\n</div>\n\n<div class=\"ghd-codetab\" data-lang=\"python\" data-label=\"Python\"><div class=\"ghd-codetab-fallback-label\" role=\"heading\" aria-level=\"3\">Python</div>\n\n```python\nsession = await client.create_session(\n    on_permission_request=lambda req, inv: PermissionDecisionApproveOnce(),\n    model=\"gpt-5.4\",\n)\n\n# Start a task\nawait session.send(\"Refactor the database layer\")\n\n# Steer the current work\nawait session.send(\n    \"Make sure to keep backwards compatibility with the v1 API\",\n    mode=\"immediate\",\n)\n\n# Queue a follow-up for after this turn\nawait session.send(\n    \"Now add migration scripts for the schema changes\",\n    mode=\"enqueue\",\n)\n```\n\n</div>\n\n</div>\n\n## ステアリングとキューイングの選択\n\n| シナリオ                      | Pattern                      | なぜでしょうか |\n| ------------------------- | ---------------------------- | ------- |\n| エージェントが間違った方向に進んでいる       |                              |         |\n| **ステアリング**                | 進行状況を失うことなく現在のターンをリダイレクトします  |         |\n| あなたは、エージェントが行うべきことを思いついた。 |                              |         |\n| **待ち行列**                  | 現在の作業を中断しません。次に実行            |         |\n| エージェントが間違いを犯しようとしています     |                              |         |\n| **ステアリング**                | 間違いが犯される前に介入する               |         |\n| 複数のタスクを連結する               |                              |         |\n| **待ち行列**                  | FIFO の順序付けにより、予測可能な実行が保証されます |         |\n| 現在のタスクにコンテキストを追加する        |                              |         |\n| **ステアリング**                | エージェントが自身の現在の推論に組み込む         |         |\n| 関連のない要求をバッチ処理する場合         |                              |         |\n| **待ち行列**                  | それぞれが明確なコンテキストで完全なターンを行います   |         |\n\n## ステアリングとキューを使用して UI を構築する\n\n両方のモードをサポートする対話型 UI を構築するためのパターンを次に示します。\n\n```typescript\nimport { CopilotClient, CopilotSession } from \"@github/copilot-sdk\";\n\ninterface PendingMessage {\n    prompt: string;\n    mode: \"immediate\" | \"enqueue\";\n    sentAt: Date;\n}\n\nclass InteractiveChat {\n    private session: CopilotSession;\n    private isProcessing = false;\n    private pendingMessages: PendingMessage[] = [];\n\n    constructor(session: CopilotSession) {\n        this.session = session;\n\n        session.on((event) => {\n            if (event.type === \"session.idle\") {\n                this.isProcessing = false;\n                this.onIdle();\n            }\n            if (event.type === \"assistant.message\") {\n                this.renderMessage(event);\n            }\n        });\n    }\n\n    async sendMessage(prompt: string): Promise<void> {\n        if (!this.isProcessing) {\n            this.isProcessing = true;\n            await this.session.send({ prompt });\n            return;\n        }\n\n        // Session is busy — let the user choose how to deliver\n        // Your UI would present this choice (e.g., buttons, keyboard shortcuts)\n    }\n\n    async steer(prompt: string): Promise<void> {\n        this.pendingMessages.push({\n            prompt,\n            mode: \"immediate\",\n            sentAt: new Date(),\n        });\n        await this.session.send({ prompt, mode: \"immediate\" });\n    }\n\n    async enqueue(prompt: string): Promise<void> {\n        this.pendingMessages.push({\n            prompt,\n            mode: \"enqueue\",\n            sentAt: new Date(),\n        });\n        await this.session.send({ prompt, mode: \"enqueue\" });\n    }\n\n    private onIdle(): void {\n        this.pendingMessages = [];\n        // Update UI to show session is ready for new input\n    }\n\n    private renderMessage(event: unknown): void {\n        // Render assistant message in your UI\n    }\n}\n```\n\n## API リファレンス\n\n### メッセージオプション\n\n| Language | フィールド  | タイプ                               | デフォルト       | Description |\n| -------- | ------ | --------------------------------- | ----------- | ----------- |\n| Node.js  | `mode` | `\"enqueue\" \\| \"immediate\"`        | `\"enqueue\"` | メッセージ配信モード  |\n| Python   | `mode` | `Literal[\"enqueue\", \"immediate\"]` | `\"enqueue\"` | メッセージ配信モード  |\n| Go       | `Mode` | `string`                          | `\"enqueue\"` | メッセージ配信モード  |\n| .NET     | `Mode` | `string?`                         | `\"enqueue\"` | メッセージ配信モード  |\n\n### 配送モード\n\n| モード           | 影響          | アクティブターン中        | 待機中            |\n| ------------- | ----------- | ---------------- | -------------- |\n| `\"enqueue\"`   | 次のターンの順番待ち  | FIFO キューでの待機     | 新しいターンをすぐに開始する |\n| `\"immediate\"` | 現在のターンに挿入する | 次の LLM 呼び出しの前に挿入 | 新しいターンをすぐに開始する |\n\n> \\[!NOTE]\n> セッションがアイドル状態 (未処理)、両方のモードが同じように動作します。メッセージはすぐに新しいターンを開始します。\n\n## ベスト プラクティス\n\n1. **既定ではキューに登録**されます。ほとんどのメッセージに対して `\"enqueue\"` (または省略 `mode`) を使用します。 これは予測可能であり、進行中の作業を中断するリスクはありません。\n\n2. **修正のためにステアリングを予約**する - エージェントが間違ったことを積極的に行っており、先に進む前にリダイレクトする必要がある場合は、 `\"immediate\"` を使用します。\n\n3. **指示メッセージを簡潔に保つ** - エージェントはコースの修正をすばやく理解する必要があります。 長く複雑なステアリング メッセージは、現在のコンテキストを混乱させる可能性があります。\n\n4. **オーバーステアを行わない** - 複数の迅速なステアリング メッセージによってターン品質が低下する可能性があります。 方向を大幅に変更する必要がある場合は、ターンを中止して新たに開始することを検討してください。\n\n5. **UI にキューの状態を表示**する - ユーザーが保留中の内容を把握できるように、キューに登録されたメッセージの数を表示します。 アイドル 状態のイベントをリッスンして、表示をクリアします。\n\n6. **ステアリングからキューへのフォールバックを処理**する - ターンが完了した後にステアリング メッセージが到着すると、自動的にキューに移動されます。 この遷移を反映するように UI を設計します。\n\n## こちらも参照ください\n\n* [初めてのCopilot搭載アプリを構築する](/ja/copilot/how-tos/copilot-sdk/getting-started): セッションを設定してメッセージを送信する\n* [カスタム エージェントとサブエージェント オーケストレーション](/ja/copilot/how-tos/copilot-sdk/features/custom-agents): スコープ付きツールを使用して特殊なエージェントを定義する\n* [セッション フック](/ja/copilot/how-tos/copilot-sdk/hooks/hooks-overview): セッション ライフサイクル イベントに対応する\n* [セッションの再開と永続化](/ja/copilot/how-tos/copilot-sdk/features/session-persistence): 再起動の間にセッションを再開する"}