# 引导和排队

两种交互模式允许用户在代理已在工作时发送消息：引导会在当前轮次进行中重定向代理，而排队会先缓冲消息，待当前轮次完成后再按顺序处理。

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

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

## 概述

当会话正在主动处理一个轮次时，传入消息可以通过 `mode` 上的 `MessageOptions` 字段以两种方式之一进行传递。

| 模式                 | Behavior            | 用例                    |
| ------------------ | ------------------- | --------------------- |
| `"immediate"` （转向） | 注入到**当前** LLM 轮次中   | 实际上，不要创建该文件，而是使用其他方法。 |
| `"enqueue"` （排队）   | 在当前轮次完成**后**进行排队和处理 | “在此之后，还要修复测试”         |

![关系图：显示描述的过程的序列图。](/assets/images/help/copilot/copilot-sdk/features-steering-and-queueing-diagram-0.png)

## 转向（即时模式）

引导功能会发送一条消息，该消息直接注入到代理的当前轮次中。 代理实时看到消息，并相应地调整其响应 — 这对于修正方向而无需中止过程非常有用。

<div class="ghd-codetabs">
<div class="ghd-codetab" data-lang="typescript" data-label="TypeScript"><div class="ghd-codetab-fallback-label" role="heading" aria-level="3">TypeScript</div>

```typescript
import { CopilotClient } from "@github/copilot-sdk";

const client = new CopilotClient();
await client.start();

const session = await client.createSession({
    model: "gpt-5.4",
    onPermissionRequest: async () => ({ kind: "approve-once" }),
});

// Start a long-running task
const msgId = await session.send({
    prompt: "Refactor the authentication module to use sessions",
});

// While the agent is working, steer it
await session.send({
    prompt: "Actually, use JWT tokens instead of sessions",
    mode: "immediate",
});
```

</div>

<div class="ghd-codetab" data-lang="python" data-label="Python"><div class="ghd-codetab-fallback-label" role="heading" aria-level="3">Python</div>

```python
from copilot import CopilotClient, PermissionDecisionApproveOnce

async def main():
    client = CopilotClient()
    await client.start()

    session = await client.create_session(
        on_permission_request=lambda req, inv: PermissionDecisionApproveOnce(),
        model="gpt-5.4",
    )

    # Start a long-running task
    msg_id = await session.send(
        "Refactor the authentication module to use sessions",
    )

    # While the agent is working, steer it
    await session.send(
        "Actually, use JWT tokens instead of sessions",
        mode="immediate",
    )

    await client.stop()
```

</div>

<div class="ghd-codetab" data-lang="go" data-label="Go"><div class="ghd-codetab-fallback-label" role="heading" aria-level="3">Go</div>

```golang
package main

import (
    "context"
    "log"
    copilot "github.com/github/copilot-sdk/go"
    "github.com/github/copilot-sdk/go/rpc"
)

func main() {
    ctx := context.Background()
    client := copilot.NewClient(nil)
    if err := client.Start(ctx); err != nil {
        log.Fatal(err)
    }
    defer client.Stop()

    session, err := client.CreateSession(ctx, &copilot.SessionConfig{
        Model: "gpt-5.4",
        OnPermissionRequest: func(req copilot.PermissionRequest, inv copilot.PermissionInvocation) (rpc.PermissionDecision, error) {
            return &rpc.PermissionDecisionApproveOnce{}, nil
        },
    })
    if err != nil {
        log.Fatal(err)
    }

    // Start a long-running task
    _, err = session.Send(ctx, copilot.MessageOptions{
        Prompt: "Refactor the authentication module to use sessions",
    })
    if err != nil {
        log.Fatal(err)
    }

    // While the agent is working, steer it
    _, err = session.Send(ctx, copilot.MessageOptions{
        Prompt: "Actually, use JWT tokens instead of sessions",
        Mode:   "immediate",
    })
    if err != nil {
        log.Fatal(err)
    }
}
```

</div>

<div class="ghd-codetab" data-lang="dotnet" data-label=".NET"><div class="ghd-codetab-fallback-label" role="heading" aria-level="3">.NET</div>

```csharp
using GitHub.Copilot;
using GitHub.Copilot.Rpc;

await using var client = new CopilotClient();
await using var session = await client.CreateSessionAsync(new SessionConfig
{
    Model = "gpt-5.4",
    OnPermissionRequest = (req, inv) =>
        Task.FromResult(PermissionDecision.ApproveOnce()),
});

// Start a long-running task
var msgId = await session.SendAsync(new MessageOptions
{
    Prompt = "Refactor the authentication module to use sessions"
});

// While the agent is working, steer it
await session.SendAsync(new MessageOptions
{
    Prompt = "Actually, use JWT tokens instead of sessions",
    Mode = "immediate"
});
```

</div>

<div class="ghd-codetab" data-lang="java" data-label="Java"><div class="ghd-codetab-fallback-label" role="heading" aria-level="3">Java</div>

```java
import com.github.copilot.CopilotClient;
import com.github.copilot.rpc.*;

try (var client = new CopilotClient()) {
    client.start().get();

    var session = client.createSession(
        new SessionConfig()
            .setModel("gpt-5.4")
            .setOnPermissionRequest(PermissionHandler.APPROVE_ALL)
    ).get();

    // Start a long-running task
    session.send(new MessageOptions()
        .setPrompt("Refactor the authentication module to use sessions")
    ).get();

    // While the agent is working, steer it
    session.send(new MessageOptions()
        .setPrompt("Actually, use JWT tokens instead of sessions")
        .setMode("immediate")
    ).get();
}
```

</div>

</div>

### 内部转向工作原理

1. 消息将添加到运行时的 `ImmediatePromptProcessor` 队列中
2. 在当前轮次中的下一个 LLM 请求之前，处理器将消息注入到会话中
3. 代理将引导消息视为新用户消息并调整其响应
4. 如果在处理引导消息之前轮次已完成，则该消息会自动移到下一个轮次的常规队列中

> \[!NOTE]
> 引导消息会在当前轮次内尽力而为。 如果代理已执行了工具调用，则引导在该调用完成后生效，但仍在同一轮次内。

## 排队（入队模式）

排队操作将缓冲消息，以便在当前轮次结束后按顺序处理。 每个排队的消息都会启动其自己的完整轮次。 这是默认模式 - 如果省略 `mode`，则 SDK 使用 `"enqueue"`。

<div class="ghd-codetabs">
<div class="ghd-codetab" data-lang="typescript" data-label="TypeScript"><div class="ghd-codetab-fallback-label" role="heading" aria-level="3">TypeScript</div>

```typescript
import { CopilotClient } from "@github/copilot-sdk";

const client = new CopilotClient();
await client.start();

const session = await client.createSession({
    model: "gpt-5.4",
    onPermissionRequest: async () => ({ kind: "approve-once" }),
});

// Send an initial task
await session.send({ prompt: "Set up the project structure" });

// Queue follow-up tasks while the agent is busy
await session.send({
    prompt: "Add unit tests for the auth module",
    mode: "enqueue",
});

await session.send({
    prompt: "Update the README with setup instructions",
    mode: "enqueue",
});

// Messages are processed in FIFO order after each turn completes
```

</div>

<div class="ghd-codetab" data-lang="python" data-label="Python"><div class="ghd-codetab-fallback-label" role="heading" aria-level="3">Python</div>

```python
from copilot import CopilotClient, PermissionDecisionApproveOnce

async def main():
    client = CopilotClient()
    await client.start()

    session = await client.create_session(
        on_permission_request=lambda req, inv: PermissionDecisionApproveOnce(),
        model="gpt-5.4",
    )

    # Send an initial task
    await session.send("Set up the project structure")

    # Queue follow-up tasks while the agent is busy
    await session.send(
        "Add unit tests for the auth module",
        mode="enqueue",
    )

    await session.send(
        "Update the README with setup instructions",
        mode="enqueue",
    )

    # Messages are processed in FIFO order after each turn completes
    await client.stop()
```

</div>

<div class="ghd-codetab" data-lang="go" data-label="Go"><div class="ghd-codetab-fallback-label" role="heading" aria-level="3">Go</div>

```golang
// Send an initial task
session.Send(ctx, copilot.MessageOptions{
    Prompt: "Set up the project structure",
})

// Queue follow-up tasks while the agent is busy
session.Send(ctx, copilot.MessageOptions{
    Prompt: "Add unit tests for the auth module",
    Mode:   "enqueue",
})

session.Send(ctx, copilot.MessageOptions{
    Prompt: "Update the README with setup instructions",
    Mode:   "enqueue",
})

// Messages are processed in FIFO order after each turn completes
```

</div>

<div class="ghd-codetab" data-lang="dotnet" data-label=".NET"><div class="ghd-codetab-fallback-label" role="heading" aria-level="3">.NET</div>

```csharp
// Send an initial task
await session.SendAsync(new MessageOptions
{
    Prompt = "Set up the project structure"
});

// Queue follow-up tasks while the agent is busy
await session.SendAsync(new MessageOptions
{
    Prompt = "Add unit tests for the auth module",
    Mode = "enqueue"
});

await session.SendAsync(new MessageOptions
{
    Prompt = "Update the README with setup instructions",
    Mode = "enqueue"
});

// Messages are processed in FIFO order after each turn completes
```

</div>

<div class="ghd-codetab" data-lang="java" data-label="Java"><div class="ghd-codetab-fallback-label" role="heading" aria-level="3">Java</div>

```java
import com.github.copilot.CopilotClient;
import com.github.copilot.rpc.*;

try (var client = new CopilotClient()) {
    client.start().get();

    var session = client.createSession(
        new SessionConfig()
            .setModel("gpt-5.4")
            .setOnPermissionRequest(PermissionHandler.APPROVE_ALL)
    ).get();

    // Send an initial task
    session.send(new MessageOptions().setPrompt("Set up the project structure")).get();

    // Queue follow-up tasks while the agent is busy
    session.send(new MessageOptions()
        .setPrompt("Add unit tests for the auth module")
        .setMode("enqueue")
    ).get();

    session.send(new MessageOptions()
        .setPrompt("Update the README with setup instructions")
        .setMode("enqueue")
    ).get();

    // Messages are processed in FIFO order after each turn completes
}
```

</div>

</div>

### 排队的内部工作原理

1. 消息作为 `itemQueue` 添加到会话的 `QueuedItem` 中
2. 当前轮完成且会话进入空闲状态时，`processQueuedItems()` 将运行
3. 条目按 FIFO 顺序出队 — 每条消息都会触发一次完整的代理执行轮次
4. 如果轮次结束时有引导信息待处理，则将其移到队列前面
5. 处理将一直持续到队列为空，然后会话发出空闲事件

## 组合转向和排队

可以在单个会话中同时使用这两种模式。 当排队的消息等待自己的轮次时，引导操作会影响当前轮次：

<div class="ghd-codetabs">
<div class="ghd-codetab" data-lang="typescript" data-label="TypeScript"><div class="ghd-codetab-fallback-label" role="heading" aria-level="3">TypeScript</div>

```typescript
const session = await client.createSession({
    model: "gpt-5.4",
    onPermissionRequest: async () => ({ kind: "approve-once" }),
});

// Start a task
await session.send({ prompt: "Refactor the database layer" });

// Steer the current work
await session.send({
    prompt: "Make sure to keep backwards compatibility with the v1 API",
    mode: "immediate",
});

// Queue a follow-up for after this turn
await session.send({
    prompt: "Now add migration scripts for the schema changes",
    mode: "enqueue",
});
```

</div>

<div class="ghd-codetab" data-lang="python" data-label="Python"><div class="ghd-codetab-fallback-label" role="heading" aria-level="3">Python</div>

```python
session = await client.create_session(
    on_permission_request=lambda req, inv: PermissionDecisionApproveOnce(),
    model="gpt-5.4",
)

# Start a task
await session.send("Refactor the database layer")

# Steer the current work
await session.send(
    "Make sure to keep backwards compatibility with the v1 API",
    mode="immediate",
)

# Queue a follow-up for after this turn
await session.send(
    "Now add migration scripts for the schema changes",
    mode="enqueue",
)
```

</div>

</div>

## 在转向和排队之间进行选择

| 情景              | Pattern             | 为什么 |
| --------------- | ------------------- | --- |
| 代理正在沿着错误的路径前进   |                     |     |
| **转向**          | 重定向当前轮次而不丢失进度       |     |
| 你想到代理应该执行的事情    |                     |     |
| **排队**          | 不会中断当前工作；运行下一步      |     |
| 代理即将出错          |                     |     |
| **转向**          | 在错误发生之前进行干预         |     |
| 您想要链式执行多个任务     |                     |     |
| **排队**          | FIFO 排序可确保可预测的执行    |     |
| 你想要将上下文添加到当前任务  |                     |     |
| **转向**          | 代理将其合并到其当前推理中       |     |
| 你想要对不相关的请求进行批处理 |                     |     |
| **排队**          | 每项都通过清晰的上下文获得其完整的轮次 |     |

## 构建具有引导和排队功能的 UI

下面是用于生成支持这两种模式的交互式 UI 的模式：

```typescript
import { CopilotClient, CopilotSession } from "@github/copilot-sdk";

interface PendingMessage {
    prompt: string;
    mode: "immediate" | "enqueue";
    sentAt: Date;
}

class InteractiveChat {
    private session: CopilotSession;
    private isProcessing = false;
    private pendingMessages: PendingMessage[] = [];

    constructor(session: CopilotSession) {
        this.session = session;

        session.on((event) => {
            if (event.type === "session.idle") {
                this.isProcessing = false;
                this.onIdle();
            }
            if (event.type === "assistant.message") {
                this.renderMessage(event);
            }
        });
    }

    async sendMessage(prompt: string): Promise<void> {
        if (!this.isProcessing) {
            this.isProcessing = true;
            await this.session.send({ prompt });
            return;
        }

        // Session is busy — let the user choose how to deliver
        // Your UI would present this choice (e.g., buttons, keyboard shortcuts)
    }

    async steer(prompt: string): Promise<void> {
        this.pendingMessages.push({
            prompt,
            mode: "immediate",
            sentAt: new Date(),
        });
        await this.session.send({ prompt, mode: "immediate" });
    }

    async enqueue(prompt: string): Promise<void> {
        this.pendingMessages.push({
            prompt,
            mode: "enqueue",
            sentAt: new Date(),
        });
        await this.session.send({ prompt, mode: "enqueue" });
    }

    private onIdle(): void {
        this.pendingMessages = [];
        // Update UI to show session is ready for new input
    }

    private renderMessage(event: unknown): void {
        // Render assistant message in your UI
    }
}
```

## API 参考

### 消息选项

| 语言      | 领域     | 类型                                | 默认          | Description |
| ------- | ------ | --------------------------------- | ----------- | ----------- |
| Node.js | `mode` | `"enqueue" \| "immediate"`        | `"enqueue"` | 消息传送模式      |
| Python  | `mode` | `Literal["enqueue", "immediate"]` | `"enqueue"` | 消息传送模式      |
| Go      | `Mode` | `string`                          | `"enqueue"` | 消息传送模式      |
| .NET    | `Mode` | `string?`                         | `"enqueue"` | 消息传送模式      |

### 传递模式

| 模式            | Effect  | 在活动轮次期间         | 空闲期间    |
| ------------- | ------- | --------------- | ------- |
| `"enqueue"`   | 排队等待下一轮 | 在 FIFO 队列中等待    | 立即启动新轮次 |
| `"immediate"` | 注入到当前回合 | 在下一次 LLM 调用之前注入 | 立即启动新轮次 |

> \[!NOTE]
> 当会话处于空闲状态（未处理）时，这两种模式的行为方式相同 — 消息会立即启动新的轮次。

## 最佳做法

1. **默认为排队** - 对大多数消息使用 `"enqueue"` （或省略 `mode`）。 这是可预测的，不会有中断正在进行的工作的风险。

2. **保留引导以进行更正** - 当代理正在主动做错误的事情，并且你需要在它进一步操作之前将其重定向时，请使用 `"immediate"`。

3. **保持引导消息简洁** — 代理需要快速了解方向调整。 长而复杂的转向消息可能会混淆当前上下文。

4. **不要过度转向** - 多次快速转向可能会降低驾驶表现。 如果需要显著更改方向，请考虑中止轮次并重新开始。

5. **在 UI 中显示队列状态** - 显示排队消息数，以便用户知道挂起的内容。 监听空闲事件以清除显示。

6. **处理引导到队列回退** - 如果引导消息在轮次完成后到达，它将自动移到队列中。 设计 UI 以反映此转换。

## 另见

* [构建你的第一个由 Copilot 提供支持的应用](/zh/copilot/how-tos/copilot-sdk/getting-started)：设置会话并发送消息
* [自定义代理和子代理编排](/zh/copilot/how-tos/copilot-sdk/features/custom-agents)：使用作用域内工具定义专用代理
* [会话挂钩](/zh/copilot/how-tos/copilot-sdk/hooks/hooks-overview)：响应会话生命周期事件
* [会话恢复和持久性](/zh/copilot/how-tos/copilot-sdk/features/session-persistence)：重启后恢复会话