Skip to main content

转向和排队

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

概述

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

模式Behavior用例
"immediate" (转向)注入 当前 LLM 轮次中实际上,不要创建该文件,而是使用其他方法。
"enqueue" (排队)在当前轮次完成后排队并处理“在此之后,还要修复测试”

关系图:显示描述的过程的序列图。

转向(即时模式)

转向模块会发送一条消息,该消息直接注入到代理的当前回合中。 代理实时看到消息,并相应地调整其响应 — 这对于修正方向而无需中止过程非常有用。

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

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

const session = await client.createSession({
    model: "gpt-4.1",
    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",
});
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-4.1",
    )

    # 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()
Go
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-4.1",
        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)
    }
}
.NET
using GitHub.Copilot;
using GitHub.Copilot.Rpc;

await using var client = new CopilotClient();
await using var session = await client.CreateSessionAsync(new SessionConfig
{
    Model = "gpt-4.1",
    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"
});
Java
import com.github.copilot.sdk.CopilotClient;
import com.github.copilot.sdk.events.*;
import com.github.copilot.sdk.json.*;

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

    var session = client.createSession(
        new SessionConfig()
            .setModel("gpt-4.1")
            .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();
}

内部转向工作原理

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

注意

指导信息是在当前轮次内尽力而为的。 如果代理已参与了工具调用,在该调用完成后,指令调整将生效,但仍在同一操作序列内。

排队(入队模式)

在当前轮次完成后,排队缓冲区消息将按顺序处理。 每个排队的消息启动其自己的完整处理过程。 这是默认模式 - 如果省略 mode,则 SDK 使用 "enqueue"

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

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

const session = await client.createSession({
    model: "gpt-4.1",
    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
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-4.1",
    )

    # 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()
Go
package main

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

func main() {
    ctx := context.Background()
    client := copilot.NewClient(nil)
    client.Start(ctx)

    session, _ := client.CreateSession(ctx, &copilot.SessionConfig{
        Model: "gpt-4.1",
        OnPermissionRequest: func(req copilot.PermissionRequest, inv copilot.PermissionInvocation) (rpc.PermissionDecision, error) {
            return &rpc.PermissionDecisionApproveOnce{}, nil
        },
    })

    session.Send(ctx, copilot.MessageOptions{
        Prompt: "Set up the project structure",
    })

    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",
    })
}
// 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
.NET
using GitHub.Copilot;
using GitHub.Copilot.Rpc;

public static class QueueingExample
{
    public static async Task Main()
    {
        await using var client = new CopilotClient();
        await using var session = await client.CreateSessionAsync(new SessionConfig
        {
            Model = "gpt-4.1",
            OnPermissionRequest = (req, inv) =>
                Task.FromResult(PermissionDecision.ApproveOnce()),
        });

        await session.SendAsync(new MessageOptions
        {
            Prompt = "Set up the project structure"
        });

        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"
        });
    }
}
// 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
Java
import com.github.copilot.sdk.CopilotClient;
import com.github.copilot.sdk.events.*;
import com.github.copilot.sdk.json.*;

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

    var session = client.createSession(
        new SessionConfig()
            .setModel("gpt-4.1")
            .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
}

队列在内部的工作原理

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

组合转向和排队

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

TypeScript
const session = await client.createSession({
    model: "gpt-4.1",
    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",
});
Python
session = await client.create_session(
    on_permission_request=lambda req, inv: PermissionDecisionApproveOnce(),
    model="gpt-4.1",
)

# 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",
)

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

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

使用排队和转向构建用户界面

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

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.jsmode"enqueue" | "immediate""enqueue"消息传送模式
PythonmodeLiteral["enqueue", "immediate"]"enqueue"消息传送模式
GoModestring"enqueue"消息传送模式
.NETModestring?"enqueue"消息传送模式

传递模式

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

注意

当会话处于空闲状态(未处理)时,这两种模式的行为方式相同 — 消息会立即启动新的轮次。

最佳做法

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

  2. 保留更正指导 - 当代理正在积极执行错误操作时使用 "immediate" ,并且你需要在操作进一步之前对其进行重定向。

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

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

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

  6. 处理引导到队列的回退 — 如果引导消息在操作完成后到达,它将自动移动到队列。 设计 UI 以反映此转换。

另见