에이전트 루프
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쌍으로 보입니다. 숨겨진 호출은 없습니다.
여러 도구 호출
세션 생성과 재개 시allowMultipleToolCalls를 지정할 수 있습니다. 생략하거나 true로 지정하면 기본 동작이 유지됩니다. 모델은 한 번의 응답에서 여러 도구를 요청할 수 있으며, 런타임은 핸들러를 병렬로 실행할 수 있습니다.
false를 지정하면 프로바이더에 응답당 최대 1개의 도구 호출을 요청합니다. 이는 프로바이더에 대한 힌트이며 실행을 보장하지는 않습니다. 프로바이더가 여러 호출을 반환한 경우에도 런타임은 기존 실행 정책으로 모두 처리합니다.
Turn(턴)이란 무엇인가
Turn은 LLM API 호출 1회와 그 결과를 의미합니다.- CLI가 대화 이력을 LLM에 전송합니다
- LLM이 응답합니다(도구 요청을 포함할 수도 있음)
- 도구 요청이 있으면 CLI가 실행합니다
assistant.turn_end가 발행됩니다
모델은 각 턴에서 “도구를 더 사용할지” “최종 답변으로 진행할지”를 판단합니다. 각 호출은 누적된 전체 컨텍스트(과거 도구 호출과 결과 포함)를 볼 수 있어 충분한 정보가 있는지 판단할 수 있습니다.
다중 턴에서의 이벤트 흐름
각 턴을 누가 시작하는가
CLI의 동작은 기계적입니다(“모델이 도구 요청 → 실행 → 다시 모델 호출”). 정지 판정의 의사결정자는 모델입니다.
session.idle과 session.task_complete의 차이
이 두 가지는 완료 시그널이지만, 보장 내용은 크게 다릅니다.
session.idle
- 도구 사용 루프 종료 시에 반드시 발행됩니다
- **휘발적(ephemeral)**입니다. 디스크에 영속화되지 않고, 세션 재개 시 재생되지 않습니다
- 의미: “에이전트는 처리를 멈추고, 다음 메시지를 받을 준비가 되었다”
- 신뢰할 수 있는 “완료” 시그널로는 이쪽을 사용합니다
sendAndWait()이 대상 메시지가 시작된 후 도착하는 이 이벤트를 대기합니다.
다른 요청에서 먼저 발생한 session.idle은 완료로 취급하지 않습니다.
Autopilot 모드에서는 session.idle 대신 대상 requestId를 가진 session.autopilot_stopped를 대기합니다.
session.task_complete
- 선택적 발행(모델이 명시적으로 시그널한 경우에만)
- 영속화됩니다(세션 이벤트 로그에 저장됩니다)
- 의미: “에이전트 스스로 전체 태스크가 달성되었다고 판단했다”
- 임의의
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를 호출하지 말 것”도 포함됩니다.
- 미해결 질문이 있으면 호출하지 않고 판단하여 작업을 계속합니다
- 에러에 부딪혔을 뿐이라면 호출하지 않고 해결을 시도합니다
- 남은 작업이 있으면 호출하지 않고 먼저 완료시킵니다
- 모델이 summary와 함께
task_complete를 호출 → CLI가session.task_complete를 발행 → 완료 - 모델이 호출하지 않고 정지 → CLI가 유도 → 모델이 계속하거나
task_complete를 호출
task_complete가 나오지 않는 이유
interactive mode(일반 채팅)에서는 CLI가 task_complete를 유도하지 않습니다. 모델이 생략하는 경우가 있습니다. 주요 이유는 다음과 같습니다.
- 대화형 Q&A: 질문에 답하고 종료할 뿐, 이산적인 “완료 태스크”가 없습니다
- 모델 판단:
task_complete를 호출하지 않고 최종 텍스트를 반환합니다 - 중단된 세션: 모델이 완료 지점에 도달하기 전에 세션이 종료됩니다
session.idle을 발행합니다. 이는 의미론적 시그널(모델이 완료라고 판단)이 아니라 기계적 시그널(루프 종료)이기 때문입니다.
어느 쪽을 사용해야 하는가
LLM 호출 횟수 세기
이벤트 로그상의assistant.turn_start / assistant.turn_end 쌍의 개수는 LLM API 호출 총수와 일치합니다. 계획, 평가, 완료 확인의 숨겨진 호출은 없습니다.
세션의 턴 수 확인 예: