# 代理循环

Copilot CLI 如何端到端处理用户消息：从提示到 session.idle。

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

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

## Architecture

![图示：显示所述过程的图示。](/assets/images/help/copilot/copilot-sdk/features-agent-loop-diagram-0.png)

**SDK** 是一个传输层，它通过 JSON-RPC 将提示发送到 **Copilot CLI**，并将事件呈现回应用。
**CLI** 是运行智能体工具调用循环的编排器，会持续发起一次或多次 LLM API 调用，直到任务完成。

## 工具使用循环

调用 `session.send({ prompt })`时，CLI 将进入循环：

![图示：显示所述过程的流程图。](/assets/images/help/copilot/copilot-sdk/features-agent-loop-diagram-1.png)

该模型查看每个呼叫 **的完整对话历史记录** -系统提示、用户消息以及所有以前的工具调用和结果。

**关键见解：** 此循环的每个迭代都是一个 LLM API 调用，在事件日志中显示为一 `assistant.turn_start` / `assistant.turn_end` 对。 不存在隐藏调用。

## 轮次 - 基本定义

一个**轮次**指单次 LLM API 调用及其后续影响：

1. CLI 将会话历史记录发送到 LLM
2. LLM 作出响应（可能包含工具请求）
3. 如果请求了工具，CLI 会执行它们
4. `assistant.turn_end` 被触发

单个用户消息通常会导致 **多个轮次**。 例如，像“X 在这个代码库中是如何工作的？”这样的问题。 可能会生成：

| 轮数       | 模型的作用                    | toolRequests? |
| -------- | ------------------------ | ------------- |
| 1        | 调用 `grep` 和 `glob` 搜索代码库 |               |
| ✅ 是的     |                          |               |
| 2        | 基于搜索结果读取特定文件             |               |
| ✅ 是的     |                          |               |
| 3        | 阅读更多文件以获取更深入的上下文         |               |
| ✅ 是的     |                          |               |
| 4        | 生成最终文本答案                 |               |
| ❌ 否→循环结束 |                          |               |

模型决定每个轮次是请求更多工具还是生成最终答案。 每个调用都会看到 **完整的累积上下文** （所有以前的工具调用和结果），因此它可以就它是否有足够的信息做出明智的决定。

## 多轮交互的事件流

![图示：显示所述过程的流程图。](/assets/images/help/copilot/copilot-sdk/features-agent-loop-diagram-2.png)

## 每一轮由谁触发？

| Actor           | 责任                                 |
| --------------- | ---------------------------------- |
| **你的应用**        | 通过 `session.send()` 发送初始提示         |
| **Copilot CLI** | 运行工具使用循环 - 执行工具和将结果馈送回 LLM 以供下一轮使用 |
| **LLM**         | 决定是否请求工具（继续循环）或生成最终响应（停止）          |
| **SDK**         | 传递事件;不控制循环                         |

CLI 纯粹是机械性的：“模型要求工具→再次执行→调用模型。
**模型**是何时停止的决策者。

## `session.idle` 与 `session.task_complete`

这些是两个不同的完成信号，有非常不同的保证：

### `session.idle`

* ```
            工具使用循环结束时**始终触发**
  ```
* **临时**：未保存到磁盘，在会话恢复时不重播
* 表示：“代理已停止处理，并已准备好接收下一条消息”
* **将此** 用作可靠的“完成”信号

SDK `sendAndWait()` 的方法等待此事件：

```typescript
// Blocks until session.idle fires
const response = await session.sendAndWait({ prompt: "Fix the bug" });
```

### `session.task_complete`

* **可选输出**：要求模型明确标示这一点
* **已持久化**：已保存到磁盘上的会话事件日志中
* 表示：“代理认为整体任务已完成”
* 包含一个可选的 `summary` 字段

```typescript
session.on("session.task_complete", (event) => {
    console.log("Task done:", event.data.summary);
});
```

### Autopilot 模式：CLI 会提示 `task_complete`

在 **Autopilot 模式** （无外设/自主操作）中，CLI 会主动跟踪模型是否已调用 `task_complete`。 如果工具使用循环在没有它的情况下结束，CLI 会注入一条合成的用户消息来提示模型：

> *“你尚未使用 task\_complete 工具将该任务标记为完成。如果你还在规划，就停止规划，开始执行。在你彻底完成该任务之前，都不能算完成。”*

这实际上会重启工具调用循环——模型会将这一提示视为一条新的用户消息，并继续执行。 该提示还指示模型**不要**过早调用`task_complete`：

* 如果还有未解决的问题就不要调用它 — 做出决定并继续工作
* 如果遇到错误就不要调用它 — 尝试解决问题
* 如果还有剩余步骤，请不要调用它——请先完成这些步骤

这将在 Autopilot 中创建**两级完成机制**：

1. 模型使用摘要调用 `task_complete` → CLI 输出 `session.task_complete` → 完成
2. 模型停止运行且未调用 → CLI 发出提示 → 模型继续运行或调用 `task_complete`

### 为什么 `task_complete` 可能未出现

在**交互模式**（普通聊天）中，CLI 不会提示 `task_complete`。 模型可能会完全跳过它。 常见原因：

* **对话问答**：模型回答问题，只是停止 - 没有离散的“任务”来完成
* **模型自由裁量权**：模型在不调用任务完成信号的情况下生成最终文本响应
* **中断的会话**：会话在模型到达完成点之前结束

不管怎样，CLI 都会发出 `session.idle` ，因为它是一个机械信号（循环结束），而不是语义信号（模型认为它已完成）。

### 你应使用哪一种？

| 用例                             | 信号 |
| ------------------------------ | -- |
| “等待代理完成处理”                     |    |
| `session.idle`                 |    |
| ✅                              |    |
|                                |    |
| “了解编码任务何时完成”                   |    |
| `session.task_complete` （尽力而为） |    |
| “超时/错误处理”                      |    |
| `session.idle`                 |    |

*

`session.error`
✅
|

## 统计 LLM 调用

事件日志中的对数 `assistant.turn_start` / `assistant.turn_end` 等于进行 LLM API 调用的总数。 不存在用于规划、评估或完成检查的隐藏调用。

要查看会话的轮次计数：

```bash
# Count turns in a session's event log
grep -c "assistant.turn_start" ~/.copilot/session-state/<sessionId>/events.jsonl
```

## 延伸阅读

* [流式处理会话事件](/zh/enterprise-cloud@latest/copilot/how-tos/copilot-sdk/features/streaming-events)：每个事件类型的完整字段级引用
* [会话恢复和持久性](/zh/enterprise-cloud@latest/copilot/how-tos/copilot-sdk/features/session-persistence)：如何保存和恢复会话
* [使用挂钩](/zh/enterprise-cloud@latest/copilot/how-tos/copilot-sdk/features/hooks)：截获循环中的事件（权限、工具）