概述
当会话正在主动处理一个轮次时,传入消息可以通过mode字段以两种方式之一传递MessageOptions。
| 模式 | Behavior | 用例 |
|---|---|---|
"immediate" (转向) | 注入 当前 LLM 轮次中 | 实际上,不要创建该文件,而是使用其他方法。 |
"enqueue" (排队) | 在当前轮次完成后排队并处理 | “在此之后,还要修复测试” |

转向(即时模式)
转向模块会发送一条消息,该消息直接注入到代理的当前回合中。 代理实时看到消息,并相应地调整其响应 — 这对于修正方向而无需中止过程非常有用。
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",
});
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()
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)
}
}
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"
});
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();
}
内部转向工作原理
- 消息将添加到运行时的
ImmediatePromptProcessor队列中 - 在当前轮次中的下一个 LLM 请求之前,处理器将消息注入到会话中
- 代理将引导消息视为新用户消息并调整其响应
- 如果在转向消息被处理之前该轮操作已完成,则该轮会自动移入常规队列,以便在下一轮处理中使用
注意
指导信息是在当前轮次内尽力而为的。 如果代理已参与了工具调用,在该调用完成后,指令调整将生效,但仍在同一操作序列内。
排队(入队模式)
在当前轮次完成后,排队缓冲区消息将按顺序处理。 每个排队的消息启动其自己的完整处理过程。 这是默认模式 - 如果省略 mode,则 SDK 使用 "enqueue"。
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
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()
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
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
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
}
队列在内部的工作原理
- 该消息作为
itemQueue被添加到该会话的QueuedItem中 - 当前轮完成且会话进入空闲状态时,
processQueuedItems()将运行 - 条目按 FIFO 顺序出队——每条消息都会触发一次完整的代理执行轮次
- 如果该轮次结束时有待处理的引导消息,则将其移到队列前端
- 处理将一直持续到队列为空,然后会话发出空闲事件
组合转向和排队
可以在单个会话中同时使用这两种模式。 引导会影响当前轮次,而排队消息等待自己的轮次:
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",
});
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.js | mode | "enqueue" | "immediate" | "enqueue" | 消息传送模式 |
| Python | mode | Literal["enqueue", "immediate"] | "enqueue" | 消息传送模式 |
| Go | Mode | string | "enqueue" | 消息传送模式 |
| .NET | Mode | string? | "enqueue" | 消息传送模式 |
传递模式
| 模式 | Effect | 在活动回合期间 | 空闲期间 |
|---|---|---|---|
"enqueue" | 排队等待下一轮 | 在 FIFO 队列中等待 | 立即启动新轮次 |
"immediate" | 注入到当前回合 | 在下一次 LLM 调用之前注入 | 立即启动新轮次 |
注意
当会话处于空闲状态(未处理)时,这两种模式的行为方式相同 — 消息会立即启动新的轮次。
最佳做法
-
默认为排队 - 对大多数消息使用
"enqueue"(或省略mode)。 这是可预测的,不会有中断正在进行的工作的风险。 -
保留更正指导 - 当代理正在积极执行错误操作时使用
"immediate",并且你需要在操作进一步之前对其进行重定向。 -
保持引导消息简洁 — 代理需要快速了解方向调整。 长而复杂的转向消息可能会混淆当前上下文。
-
不要过度转向 - 多次快速转向可能会降低驾驶表现。 如果需要显著更改方向,请考虑中止轮次并重新开始。
-
在 UI 中显示队列状态 - 显示排队消息数,以便用户知道挂起的内容。 监听空闲事件以清除显示。
-
处理引导到队列的回退 — 如果引导消息在操作完成后到达,它将自动移动到队列。 设计 UI 以反映此转换。
另见
- 构建你的第一个由 Copilot 提供支持的应用:设置会话并发送消息
- 自定义代理和子代理编排:使用作用域内工具定义专用代理
- 会话挂钩:响应会话生命周期事件
- 会话恢复和持久性:重启后恢复会话