# 에이전트 루프

Copilot CLI가 사용자 메시지를 엔드투엔드로 처리하는 방식: 프롬프트부터 session.idle까지.

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

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

## 아키텍처

![다이어그램: 설명된 프로세스를 보여 주는 그래프 다이어그램](/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)

모델은 각 호출에 대한 **전체 대화 기록** (시스템 프롬프트, 사용자 메시지 및 모든 이전 도구 호출 및 결과)을 확인합니다.

**주요 인사이트:** 이 루프의 각 반복은 이벤트 로그에서 한 `assistant.turn_start` / `assistant.turn_end` 쌍으로 표시되는 하나의 LLM API 호출입니다. 숨겨진 호출이 없습니다.

## 턴이란 무엇인가

**턴**은 단일 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 도구를 사용하여 작업을 완료로 표시하지 않았습니다. 계획 중인 경우 계획을 중지하고 구현을 시작합니다. 작업을 완전히 완료할 때까지 완료되지 않습니다."*

이렇게 하면 도구 사용 루프가 사실상 다시 시작됩니다. 모델은 이 신호를 새로운 사용자 메시지로 간주하고 작업을 계속 수행합니다. 또한 이 유도는 모델에 \*\*\*\* 않도록 지시합니다.

* 열린 질문이 있는 경우 호출하지 마세요. 결정을 내리고 계속 작업하세요.
* 오류가 발생한 경우 호출하지 마세요. 해결해 보세요.
* 남아 있는 단계가 있으면 호출하지 말고 먼저 완료하세요

이렇게 하면 autopilot에서 **2단계 완성 메커니즘** 이 만들어집니다.

1. 모델이 요약과 함께 `task_complete`을 호출 → CLI가 `session.task_complete`을 출력 → 완료
2. 모델이 이를 호출하지 않고 중지됨 → CLI가 유도함 → 모델이 계속 진행되거나 `task_complete`를 호출함

### `task_complete`이(가) 표시되지 않을 수 있는 이유

**대화형 모드**(일반 채팅)에서는 CLI가 `task_complete`을(를) 요청하지 않습니다. 모델은 아예 건너뛸 수 있습니다. 일반적인 원인:

* **대화형 Q\&A**: 모델은 질문에 답변하고 단순히 중지합니다. 완료할 개별 "작업"이 없습니다.
* **모델 재량**: 모델은 작업 완료 신호를 호출하지 않고 최종 텍스트 응답을 생성합니다.
* **중단된 세션**: 모델이 완료 지점에 도달하기 전에 세션이 종료됩니다.

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
```

## 추가 읽기

* [스트리밍 세션 이벤트](/ko/copilot/how-tos/copilot-sdk/features/streaming-events): 모든 이벤트 유형에 대한 전체 필드 수준 참조
* [세션 다시 시작 및 지속성](/ko/copilot/how-tos/copilot-sdk/features/session-persistence): 세션 저장 및 다시 시작 방법
* [후크를 사용한 작업](/ko/copilot/how-tos/copilot-sdk/features/hooks): 루프에서 이벤트 가로채기(권한, 도구)