{"meta":{"title":"引导和排队","intro":"两种交互模式允许用户在代理已在工作时发送消息：引导会在当前轮次进行中重定向代理，而排队会先缓冲消息，待当前轮次完成后再按顺序处理。","product":"GitHub Copilot","breadcrumbs":[{"href":"/zh/copilot","title":"GitHub Copilot"},{"href":"/zh/copilot/how-tos","title":"操作方法"},{"href":"/zh/copilot/how-tos/copilot-sdk","title":"Copilot SDK"},{"href":"/zh/copilot/how-tos/copilot-sdk/features","title":"功能"},{"href":"/zh/copilot/how-tos/copilot-sdk/features/steering-and-queueing","title":"引导和排队"}],"documentType":"article"},"body":"# 引导和排队\n\n两种交互模式允许用户在代理已在工作时发送消息：引导会在当前轮次进行中重定向代理，而排队会先缓冲消息，待当前轮次完成后再按顺序处理。\n\n<!-- markdownlint-disable GHD046 GHD005 -->\n\n<!-- Suppressed: GHD046 (outdated release terminology), GHD005 (hardcoded data variable) -->\n\n## 概述\n\n当会话正在主动处理一个轮次时，传入消息可以通过 `mode` 上的 `MessageOptions` 字段以两种方式之一进行传递。\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可以在单个会话中同时使用这两种模式。 当排队的消息等待自己的轮次时，引导操作会影响当前轮次：\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| 语言      | 领域     | 类型                                | 默认          | 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| 模式            | Effect  | 在活动轮次期间         | 空闲期间    |\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 提供支持的应用](/zh/copilot/how-tos/copilot-sdk/getting-started)：设置会话并发送消息\n* [自定义代理和子代理编排](/zh/copilot/how-tos/copilot-sdk/features/custom-agents)：使用作用域内工具定义专用代理\n* [会话挂钩](/zh/copilot/how-tos/copilot-sdk/hooks/hooks-overview)：响应会话生命周期事件\n* [会话恢复和持久性](/zh/copilot/how-tos/copilot-sdk/features/session-persistence)：重启后恢复会话"}