본문으로 이동
문서 탐색안쪽 agentic 루프
핵심 개념레퍼런스

안쪽 agentic 루프

while(tool_use) 기본 단위입니다. 한 턴이 어떻게 돌고, 어떤 경로로 끝나는지 설명합니다.

기본 단위

AgenticLoopcore/agent/loop/agent_loop.py에 있습니다. 모든 작업 실행의 엔진이고, 형태는 의도적으로 단순합니다. 모델이 도구를 요청하는 동안 계속 돕니다.

while stop_reason == "tool_use":
    round-entry guards          # round / time / session / cost budget
    context-overflow check      # compact or prune if needed
    response = call_llm(messages, tools)
    run tool calls -> append results -> checkpoint

루프 클래스 본체 옆에 책임별 모듈이 같은 패키지에 나뉘어 있습니다. 물리적 턴의 순서는 agent_loop.py에 그대로 보이고, 입력 준비, 모델 호출 준비, 제공자 호출/재시도, 도구 처리, 관찰/히스토리 정리, 종료 조립은 _phases.py의 여섯 함수가 담당합니다. 시스템 프롬프트와 컨텍스트 위임은 _context.py, 결과 모델과 컨텍스트 고갈 처리는 models.py, 모델 전환은 _model_switching.py, 서브에이전트 알림은 _collaboration_mailbox.py입니다. 예전 단일 파일 core/agent/loop.py는 더 이상 존재하지 않습니다.

턴 사이클

매 라운드는 같은 순서를 밟습니다.

  1. 라운드 진입 가드. 라운드 수, 시간 예산, 세션 예산, 비용 예산을 확인합니다.
  2. 컨텍스트 오버플로 점검. 임계값을 넘으면 압축하거나 정리합니다. 자세한 동작은 컨텍스트 조립을 참고합니다.
  3. LLM 호출.
  4. 모델이 요청한 도구 실행.
  5. assistant 메시지와 tool_result를 히스토리에 붙여 checkpoint한 뒤 다음 라운드로 진입합니다.

각 LLM 샘플링 직전에는 불변 StepSnapshot이 모델 경로, 도구 계획 세대, 예산, 취소 핸들, 추적 상관관계를 고정합니다. 그 응답의 도구 배치는 같은 스냅샷을 사용합니다. 한 물리적 턴의 메시지, 완료 라운드, 재시도, 계획 힌트, 종료 사유는 가변 TurnState가 소유합니다. 따라서 같은 라운드 번호로 재시도해도 샘플링 step ID는 단조 증가합니다.

변경·통신·관리 도구는 assistant tool call을 먼저 strict checkpoint한 뒤 provider call ID와 별개인 logical operation ID와 sampling step을 고정하고, 프로젝트 sessions.db에 receipt를 기록합니다. 재시작은 이 anchor로만 복구하며 provider call ID는 tool use/result 짝맞춤에만 씁니다. 완료 receipt는 PostToolUse까지 끝난 결과를 재생하고, 미완료 receipt는 effect_outcome_uncertain으로 중단합니다. 재시작은 이 call을 receipt 상태로 닫은 뒤에만 모델을 다시 호출합니다. 이는 중복 억제 경계이며 외부 시스템의 exactly-once 보장은 아닙니다. 미해결 이전 step은 geode session effects resolve-effect로 외부 sink 확인 후 해소하며 applied와 not-applied 모두 증거로 남습니다. 개인 인수는 해시하지 않으므로, checkpoint anchor 없는 동일 ID 재진입은 거부합니다.

AgenticLoop turn cycle: round-entry guards, context-overflow check, LLM call, tool execution, and the early-termination paths
한 턴의 사이클과 종료 경로. 가드에 막히면 라운드에 들어가지 않고 종료 사유를 남기고 끝납니다.

라운드 진입 가드

가드조건동작
라운드 한도max_rounds > 0 (0은 무제한)max_rounds로 종료
시간 예산time_budget_s > 0, wall-clock 기준time_budget_expired로 종료
세션 예산기본 세션 상한 2시간 (core/agent/budget.py)임계 직전 HANDOFF_TRIGGERED 훅 1회, 만료 시 하드 스톱
비용 예산cost_budget > 080%에서 1회 경고, 도달 시 cost_budget_exceeded

종료 경로

모든 실행은 AgenticResult.termination_reason 하나로 끝납니다. SoT는 core/agent/loop/models.py입니다.

termination_reason의미
natural모델이 도구 호출 없이 답을 마침
forced_text마무리 단계에서 텍스트 응답을 강제함 (adaptive compute: max_tokens 축소, thinking off)
max_rounds라운드 한도 도달
time_budget_expiredwall-clock 예산 소진
cost_budget_exceeded세션 비용이 예산에 도달
context_exhausted압축과 정리 후에도 컨텍스트가 임계 상태
llm_error복구 불가능한 LLM 호출 실패
model_action_required모델이 외부 조치를 요구하며 종료 신호를 보냄
user_clarification_needed과거 종료 기록을 읽기 위해 유지한 값. 현재는 응답 길이만으로 실행을 멈추지 않습니다.
model_refusal모델이 안전 거절로 응답 (아래 절)
input_blocked입력이 인터셉터에서 차단됨
billing_error결제/쿼터 치명 오류
user_cancelled사용자 취소
convergence_detected진전 없는 반복 감지

기본값은 unknown이며, 정상 경로에서는 나타나지 않습니다.

model_refusal: 거절을 1급 종료로

Fable 5의 안전 분류기는 요청을 거절할 때 HTTP 200 stop_reason: "refusal"을 실어 보냅니다. 본문이 비어 있는 경우도 많습니다. 이를 일반 응답처럼 다루면 빈 답이 조용히 사용자에게 흘러갑니다.

GEODE는 두 지점에서 처리합니다. Anthropic 프로바이더의 normalize_anthropic이 응답의 stop_details를 보존하고, 루프가 이를 termination_reason="model_refusal"로 매핑하며 stop_details.category를 포함한 정직한 메시지를 만듭니다. 같은 경로가 Opus 4.7과 4.8에도 적용됩니다.

발화되는 훅

루프는 의미 있는 경계마다 라이프사이클 이벤트를 발화합니다. 라운드 종료의 TURN_COMPLETED, LLM 호출의 LLM_CALL_STARTED / LLM_CALL_ENDED / LLM_CALL_FAILED / LLM_CALL_RETRIED, 도구 실행의 TOOL_EXEC_STARTED / TOOL_EXEC_ENDED / TOOL_EXEC_FAILED, 승인 게이트의 TOOL_APPROVAL_REQUESTED / GRANTED / DENIED, 컨텍스트의 CONTEXT_CRITICAL CONTEXT_OVERFLOW_ACTION이 대표입니다. 전체 목록과 등록 방법은 훅과 관측성을 참고합니다.

왜 얇은 루프인가

루프는 의도적으로 얇습니다. 시스템에서 가장 많이 테스트되고 가장 적게 바뀌는 코드입니다. 새로운 동작은 도구, 훅, 가드에 둡니다. 그래야 핵심 실행 경로가 예측 가능하고 테스트 가능한 상태로 유지됩니다.

다음