메인 콘텐츠로 건너뛰기

에이전트 루프

Copilot CLI가 사용자 메시지를 어떻게 end-to-end로 처리하는지(프롬프트 전송에서 session.idle까지) 설명합니다.

아키텍처

SDK는 트랜스포트 계층입니다. JSON-RPC로 Copilot CLI에 프롬프트를 보내고, 이벤트를 앱에 중계합니다. 에이전트적인 도구 사용 루프를 실행하고, 태스크가 완료될 때까지 1회 이상의 LLM API 호출을 오케스트레이션하는 것은 CLI 쪽입니다.

도구 사용 루프

session.send({ prompt })를 호출하면 CLI는 다음 루프에 진입합니다. 모델은 매 호출마다 전체 대화 이력(system prompt / user message / 지금까지의 모든 도구 호출과 결과)을 참조합니다. 중요: 이 루프의 1회 반복은 LLM API 호출 1회와 완전히 일치하며, 이벤트 로그상에서는 assistant.turn_start / assistant.turn_end의 1쌍으로 보입니다. 숨겨진 호출은 없습니다.

Turn(턴)이란 무엇인가

Turn은 LLM API 호출 1회와 그 결과를 의미합니다.
  1. CLI가 대화 이력을 LLM에 전송합니다
  2. LLM이 응답합니다(도구 요청을 포함할 수도 있음)
  3. 도구 요청이 있으면 CLI가 실행합니다
  4. assistant.turn_end가 발행됩니다
1회의 사용자 메시지에 대해 보통 여러 턴이 발생합니다. 예를 들어 “이 코드베이스에서 X는 어떻게 동작해?” 같은 질문에서는 다음과 같이 진행됩니다.
Turn모델의 동작toolRequests?
1grepglob으로 코드베이스 검색✅ Yes
2검색 결과에 따라 특정 파일 읽기✅ Yes
3더 깊은 문맥을 위해 추가로 읽기✅ Yes
4최종 텍스트 답변 생성❌ No → loop ends
모델은 각 턴에서 “도구를 더 사용할지” “최종 답변으로 진행할지”를 판단합니다. 각 호출은 누적된 전체 컨텍스트(과거 도구 호출과 결과 포함)를 볼 수 있어 충분한 정보가 있는지 판단할 수 있습니다.

다중 턴에서의 이벤트 흐름

각 턴을 누가 시작하는가

ActorResponsibility
Your appsession.send()로 최초 프롬프트를 전송
Copilot CLI도구 사용 루프를 실행하고, 도구 실행 결과를 다음 턴 입력으로 LLM에 반환
LLM도구를 요청하여 계속할지, 최종 응답을 반환하고 종료할지 결정
SDK이벤트를 중계할 뿐. 루프 제어는 하지 않음
CLI의 동작은 기계적입니다(“모델이 도구 요청 → 실행 → 다시 모델 호출”). 정지 판정의 의사결정자는 모델입니다.

session.idlesession.task_complete의 차이

이 두 가지는 완료 시그널이지만, 보장 내용은 크게 다릅니다.

session.idle

  • 도구 사용 루프 종료 시에 반드시 발행됩니다
  • **휘발적(ephemeral)**입니다. 디스크에 영속화되지 않고, 세션 재개 시 재생되지 않습니다
  • 의미: “에이전트는 처리를 멈추고, 다음 메시지를 받을 준비가 되었다”
  • 신뢰할 수 있는 “완료” 시그널로는 이쪽을 사용합니다
SDK의 sendAndWait()은 이 이벤트를 대기합니다.
// session.idle이 발행될 때까지 대기
const response = await session.sendAndWait({ prompt: "Fix the bug" });

session.task_complete

  • 선택적 발행(모델이 명시적으로 시그널한 경우에만)
  • 영속화됩니다(세션 이벤트 로그에 저장됩니다)
  • 의미: “에이전트 스스로 전체 태스크가 달성되었다고 판단했다”
  • 임의의 summary를 포함할 수 있습니다
session.on("session.task_complete", (event) => {
    console.log("Task done:", event.data.summary);
});

Autopilot 모드: CLI가 task_complete를 유도

autopilot mode(headless / autonomous)에서는 CLI가 모델이 task_complete를 호출했는지 추적합니다. 도구 사용 루프가 task_complete 없이 끝나면, CLI는 다음의 합성 사용자 메시지를 삽입해 모델에 유도합니다.
“You have not yet marked the task as complete using the task_complete tool. If you were planning, stop planning and start implementing. You aren’t done until you have fully completed the task.”
이로써 도구 사용 루프가 사실상 재개됩니다. 모델은 이 유도를 새 사용자 메시지로 받고 작업을 계속합니다. 유도에는 “너무 이른 task_complete를 호출하지 말 것”도 포함됩니다.
  • 미해결 질문이 있으면 호출하지 않고 판단하여 작업을 계속합니다
  • 에러에 부딪혔을 뿐이라면 호출하지 않고 해결을 시도합니다
  • 남은 작업이 있으면 호출하지 않고 먼저 완료시킵니다
autopilot에서는 다음의 2단계 완료 메커니즘이 됩니다.
  1. 모델이 summary와 함께 task_complete를 호출 → CLI가 session.task_complete를 발행 → 완료
  2. 모델이 호출하지 않고 정지 → CLI가 유도 → 모델이 계속하거나 task_complete를 호출

task_complete가 나오지 않는 이유

interactive mode(일반 채팅)에서는 CLI가 task_complete를 유도하지 않습니다. 모델이 생략하는 경우가 있습니다. 주요 이유는 다음과 같습니다.
  • 대화형 Q&A: 질문에 답하고 종료할 뿐, 이산적인 “완료 태스크”가 없습니다
  • 모델 판단: task_complete를 호출하지 않고 최종 텍스트를 반환합니다
  • 중단된 세션: 모델이 완료 지점에 도달하기 전에 세션이 종료됩니다
반면 CLI는 항상 session.idle을 발행합니다. 이는 의미론적 시그널(모델이 완료라고 판단)이 아니라 기계적 시그널(루프 종료)이기 때문입니다.

어느 쪽을 사용해야 하는가

Use caseSignal
”에이전트의 처리 종료를 대기”session.idle
“코딩 태스크가 완료되었음을 알기”session.task_complete(best-effort)
“타임아웃/에러 처리”session.idle + session.error

LLM 호출 횟수 세기

이벤트 로그상의 assistant.turn_start / assistant.turn_end 쌍의 개수는 LLM API 호출 총수와 일치합니다. 계획, 평가, 완료 확인의 숨겨진 호출은 없습니다. 세션의 턴 수 확인 예:
# 세션 이벤트 로그 내의 turn 수 카운트
grep -c "assistant.turn_start" ~/.copilot/session-state/<sessionId>/events.jsonl

관련 문서

마지막 수정일 2026년 7월 13일