{"meta":{"title":"代理循环","intro":"Copilot CLI 如何端到端处理用户消息：从提示到 session.idle。","product":"GitHub Copilot","breadcrumbs":[{"href":"/zh/enterprise-cloud@latest/copilot","title":"GitHub Copilot"},{"href":"/zh/enterprise-cloud@latest/copilot/how-tos","title":"操作方法"},{"href":"/zh/enterprise-cloud@latest/copilot/how-tos/copilot-sdk","title":"Copilot SDK"},{"href":"/zh/enterprise-cloud@latest/copilot/how-tos/copilot-sdk/features","title":"功能"},{"href":"/zh/enterprise-cloud@latest/copilot/how-tos/copilot-sdk/features/agent-loop","title":"智能体循环"}],"documentType":"article"},"body":"# 代理循环\n\nCopilot CLI 如何端到端处理用户消息：从提示到 session.idle。\n\n<!-- markdownlint-disable GHD046 GHD005 -->\n\n<!-- Suppressed: GHD046 (outdated release terminology), GHD005 (hardcoded data variable) -->\n\n## Architecture\n\n![图示：显示所述过程的图示。](/assets/images/help/copilot/copilot-sdk/features-agent-loop-diagram-0.png)\n\n**SDK** 是一个传输层，它通过 JSON-RPC 将提示发送到 **Copilot CLI**，并将事件呈现回应用。\n**CLI** 是运行智能体工具调用循环的编排器，会持续发起一次或多次 LLM API 调用，直到任务完成。\n\n## 工具使用循环\n\n调用 `session.send({ prompt })`时，CLI 将进入循环：\n\n![图示：显示所述过程的流程图。](/assets/images/help/copilot/copilot-sdk/features-agent-loop-diagram-1.png)\n\n该模型查看每个呼叫 **的完整对话历史记录** -系统提示、用户消息以及所有以前的工具调用和结果。\n\n**关键见解：** 此循环的每个迭代都是一个 LLM API 调用，在事件日志中显示为一 `assistant.turn_start` / `assistant.turn_end` 对。 不存在隐藏调用。\n\n## 轮次 - 基本定义\n\n一个**轮次**指单次 LLM API 调用及其后续影响：\n\n1. CLI 将会话历史记录发送到 LLM\n2. LLM 作出响应（可能包含工具请求）\n3. 如果请求了工具，CLI 会执行它们\n4. `assistant.turn_end` 被触发\n\n单个用户消息通常会导致 **多个轮次**。 例如，像“X 在这个代码库中是如何工作的？”这样的问题。 可能会生成：\n\n| 轮数       | 模型的作用                    | toolRequests? |\n| -------- | ------------------------ | ------------- |\n| 1        | 调用 `grep` 和 `glob` 搜索代码库 |               |\n| ✅ 是的     |                          |               |\n| 2        | 基于搜索结果读取特定文件             |               |\n| ✅ 是的     |                          |               |\n| 3        | 阅读更多文件以获取更深入的上下文         |               |\n| ✅ 是的     |                          |               |\n| 4        | 生成最终文本答案                 |               |\n| ❌ 否→循环结束 |                          |               |\n\n模型决定每个轮次是请求更多工具还是生成最终答案。 每个调用都会看到 **完整的累积上下文** （所有以前的工具调用和结果），因此它可以就它是否有足够的信息做出明智的决定。\n\n## 多轮交互的事件流\n\n![图示：显示所述过程的流程图。](/assets/images/help/copilot/copilot-sdk/features-agent-loop-diagram-2.png)\n\n## 每一轮由谁触发？\n\n| Actor           | 责任                                 |\n| --------------- | ---------------------------------- |\n| **你的应用**        | 通过 `session.send()` 发送初始提示         |\n| **Copilot CLI** | 运行工具使用循环 - 执行工具和将结果馈送回 LLM 以供下一轮使用 |\n| **LLM**         | 决定是否请求工具（继续循环）或生成最终响应（停止）          |\n| **SDK**         | 传递事件;不控制循环                         |\n\nCLI 纯粹是机械性的：“模型要求工具→再次执行→调用模型。\n**模型**是何时停止的决策者。\n\n## `session.idle` 与 `session.task_complete`\n\n这些是两个不同的完成信号，有非常不同的保证：\n\n### `session.idle`\n\n* ```\n            工具使用循环结束时**始终触发**\n  ```\n* **临时**：未保存到磁盘，在会话恢复时不重播\n* 表示：“代理已停止处理，并已准备好接收下一条消息”\n* **将此** 用作可靠的“完成”信号\n\nSDK `sendAndWait()` 的方法等待此事件：\n\n```typescript\n// Blocks until session.idle fires\nconst response = await session.sendAndWait({ prompt: \"Fix the bug\" });\n```\n\n### `session.task_complete`\n\n* **可选输出**：要求模型明确标示这一点\n* **已持久化**：已保存到磁盘上的会话事件日志中\n* 表示：“代理认为整体任务已完成”\n* 包含一个可选的 `summary` 字段\n\n```typescript\nsession.on(\"session.task_complete\", (event) => {\n    console.log(\"Task done:\", event.data.summary);\n});\n```\n\n### Autopilot 模式：CLI 会提示 `task_complete`\n\n在 **Autopilot 模式** （无外设/自主操作）中，CLI 会主动跟踪模型是否已调用 `task_complete`。 如果工具使用循环在没有它的情况下结束，CLI 会注入一条合成的用户消息来提示模型：\n\n> *“你尚未使用 task\\_complete 工具将该任务标记为完成。如果你还在规划，就停止规划，开始执行。在你彻底完成该任务之前，都不能算完成。”*\n\n这实际上会重启工具调用循环——模型会将这一提示视为一条新的用户消息，并继续执行。 该提示还指示模型**不要**过早调用`task_complete`：\n\n* 如果还有未解决的问题就不要调用它 — 做出决定并继续工作\n* 如果遇到错误就不要调用它 — 尝试解决问题\n* 如果还有剩余步骤，请不要调用它——请先完成这些步骤\n\n这将在 Autopilot 中创建**两级完成机制**：\n\n1. 模型使用摘要调用 `task_complete` → CLI 输出 `session.task_complete` → 完成\n2. 模型停止运行且未调用 → CLI 发出提示 → 模型继续运行或调用 `task_complete`\n\n### 为什么 `task_complete` 可能未出现\n\n在**交互模式**（普通聊天）中，CLI 不会提示 `task_complete`。 模型可能会完全跳过它。 常见原因：\n\n* **对话问答**：模型回答问题，只是停止 - 没有离散的“任务”来完成\n* **模型自由裁量权**：模型在不调用任务完成信号的情况下生成最终文本响应\n* **中断的会话**：会话在模型到达完成点之前结束\n\n不管怎样，CLI 都会发出 `session.idle` ，因为它是一个机械信号（循环结束），而不是语义信号（模型认为它已完成）。\n\n### 你应使用哪一种？\n\n| 用例                             | 信号 |\n| ------------------------------ | -- |\n| “等待代理完成处理”                     |    |\n| `session.idle`                 |    |\n| ✅                              |    |\n|                                |    |\n| “了解编码任务何时完成”                   |    |\n| `session.task_complete` （尽力而为） |    |\n| “超时/错误处理”                      |    |\n| `session.idle`                 |    |\n\n*\n\n`session.error`\n✅\n|\n\n## 统计 LLM 调用\n\n事件日志中的对数 `assistant.turn_start` / `assistant.turn_end` 等于进行 LLM API 调用的总数。 不存在用于规划、评估或完成检查的隐藏调用。\n\n要查看会话的轮次计数：\n\n```bash\n# Count turns in a session's event log\ngrep -c \"assistant.turn_start\" ~/.copilot/session-state/<sessionId>/events.jsonl\n```\n\n## 延伸阅读\n\n* [流式处理会话事件](/zh/enterprise-cloud@latest/copilot/how-tos/copilot-sdk/features/streaming-events)：每个事件类型的完整字段级引用\n* [会话恢复和持久性](/zh/enterprise-cloud@latest/copilot/how-tos/copilot-sdk/features/session-persistence)：如何保存和恢复会话\n* [使用挂钩](/zh/enterprise-cloud@latest/copilot/how-tos/copilot-sdk/features/hooks)：截获循环中的事件（权限、工具）"}