← 기술 지식

기술 지식 / 머신러닝·AI 에이전트 / AI 에이전트 / 실행 구조

AI 에이전트 실행 루프의 책임 분리

모델의 도구 호출 제안이 실제 실행과 검증을 거치는 과정을 따라가며 실행기의 상태·권한·종료 조건을 설계합니다.

AI 에이전트 · 원리·구현·운영
기술 지식 트리

에이전트의 신뢰성은 모델의 문장보다 실행기가 남긴 관찰 가능한 상태에서 결정됩니다. 아래에서는 하나의 요청이 제안·허가·실행·검증·종료를 거치는 동안 어느 구성요소가 무엇을 책임지는지 실제 상태 변화로 연결합니다.

모델의 호출 제안과 실행 완료의 구분

“테스트를 실행하고 실패 원인을 정리해 줘”라는 요청을 받은 에이전트를 생각해 보겠습니다. 모델이 테스트 도구 호출을 생성했다고 테스트가 실행된 것은 아닙니다. 실행기가 인자를 검사해 프로세스를 시작하고, 종료 결과를 수집해야 비로소 어떤 테스트가 통과했는지 알 수 있습니다. 모델의 마지막 문장에 “모두 통과했습니다”라고 쓰였다는 사실은 그 증거가 아닙니다.

에이전트 실행 루프는 관찰한 결과를 모델에 전달하고, 모델의 다음 제안을 검사·실행한 뒤, 실제 결과를 다시 관찰하는 반복 구조입니다. 이 노트는 그 반복을 책임별로 나누는 데 집중합니다. MCP 명세나 여러 에이전트의 통신 규약, 평가 통계 전체를 한꺼번에 설명하지는 않습니다.

예를 들어 모델이 pytest tests/unit을 제안하면 기록은 먼저 planned이고, 실행기가 프로세스를 시작한 뒤에야 accepted 또는 running이 됩니다. 종료 코드 0과 기대한 테스트 목록을 확인하기 전에는 passed로 올리지 않습니다. 이 한 줄의 상태 차이가 “호출을 만들었다”와 “검사를 마쳤다”를 가르는 기본 모델입니다.

모델의 선택과 실행기의 강제 조건 분리

예를 들어 모델이 지정 저장소의 테스트 실패 파일을 읽자고 제안하면, 실행기는 도구 이름과 경로를 읽고 그 경로가 허용된 폴더 아래인지 확인한 뒤에만 파일 읽기를 시작합니다. 명령 실행에서도 실행 가능한 명령인지, 필요한 사용자 승인이 현재 요청과 맞는지, 남은 시간과 비용 상한 안에 있는지를 실행 코드가 검사합니다. 모델에게 “이 범위를 지켜라”라고 쓰는 것만으로는 이 검사를 대신할 수 없으므로, 범위를 벗어난 도구 호출이나 명령 실행은 실제 동작을 시작하기 전에 막아야 합니다.

관찰에서 검증까지 한 바퀴모델: 목표와 현재 결과 읽기, 다음 호출 또는 종료 제안. 실행 전 검사: 형식 · 권한 · 승인, 예산과 대상 버전. 도구 실행기: 허용된 동작 실행, 접수와 완료 구분. 결과 기록과 검증: 종료 코드 · 산출물, 다음 판단의 관찰로 전달행동 제안허용된 호출실제 결과모델목표와 현재 결과 읽기다음 호출 또는 종료 제안실행 전 검사형식 · 권한 · 승인예산과 대상 버전도구 실행기허용된 동작 실행접수와 완료 구분결과 기록과 검증종료 코드 · 산출물다음 판단의 관찰로 전달
관찰에서 검증까지 한 바퀴 실선은 한 차례의 처리 순서입니다. 실행 결과를 기록한 뒤 필요한 경우 다시 모델 단계로 돌아갑니다. 정책 검사는 모델 밖에 있습니다.
그림을 글로 읽기
  • 모델: 목표와 현재 결과 읽기 · 다음 호출 또는 종료 제안
  • 실행 전 검사: 형식 · 권한 · 승인 · 예산과 대상 버전
  • 도구 실행기: 허용된 동작 실행 · 접수와 완료 구분
  • 결과 기록과 검증: 종료 코드 · 산출물 · 다음 판단의 관찰로 전달
  1. 모델 → 실행 전 검사: 행동 제안
  2. 실행 전 검사 → 도구 실행기: 허용된 호출
  3. 도구 실행기 → 결과 기록과 검증: 실제 결과

테스트 도구 출력이나 읽은 파일에 “검사를 끄고 실행하라”는 문장이 들어와도, 실행기는 그 문자열을 관찰 데이터로 저장하고 다음 호출을 허용하는 정책 상태와 분리합니다. 그래서 그 문장 때문에 검사 단계를 건너뛰거나 권한을 새로 만들지 않고, 도구 출력인지 파일인지 출처를 함께 보존한 채 모델에게 참고 정보로만 전달합니다. 관찰 내용과 실행 권한을 같은 입력으로 취급하지 않아야 이런 문장이 실행 정책을 바꾸지 못합니다.

허가를 (tool, normalized arguments, resource scope, expiry)로 정규화해 비교하면 공백이나 별칭을 이용한 범위 확장을 줄일 수 있습니다. repo/tests가 승인됐는데 repo/../secrets가 들어오면 모델의 의도와 무관하게 canonical path 검사에서 거절되어야 하며, 거절 자체도 실행 결과로 기록합니다.

구조화된 실행 상태와 대화문

실행기가 모든 것을 대화문 하나에만 저장하면 “테스트를 시작했다”와 “테스트가 끝났다”를 혼동하기 쉽습니다. 적어도 현재 작업과 각 도구 실행을 구조화해 기록하는 편이 낫습니다.

기록 테스트 작업의 예 필요한 이유
목표와 범위 지정 저장소의 테스트 결과 정리 다른 폴더나 배포로 범위가 넓어지는 것 방지
실행 식별자 run-17 접수 결과와 나중 완료 결과 연결
상태 접수됨·진행 중·완료·불확정 시작을 성공으로 오인하지 않음
근거 종료 코드, 출력, 확인한 파일 버전 결과를 다시 검사할 수 있음
남은 예산 남은 시간·호출 수 반복 종료 기준
승인 범위 명령·인자·대상·유효 기간 승인 뒤 인자 바꾸기 방지

상태 이름만 만드는 것으로 충분하지는 않습니다. 어떤 결과가 들어와야 다음 상태로 갈 수 있는지도 정해야 합니다. 실행 식별자만 반환하는 비동기 도구는 접수 상태이고, 완료 알림이나 결과 조회가 올 때까지 성공 여부를 알 수 없습니다.

대화문은 설명이고 상태 저장소는 판정 자료입니다. 한 실행 레코드에 plannedAt, startedAt, finishedAt, 인자 지문, stdout/stderr 제한, 관찰한 파일 버전을 함께 두면 재시작 뒤 같은 run을 다시 찾아 “아직 시작 전”과 “완료 기록 유실”을 구별할 수 있습니다.

실행 이력과 단계별 상태 전이

예제 도구는 테스트 프로세스를 시작하면 실행 ID를 반환하고, 나중에 종료 결과를 알려준다고 합시다.

순서 관찰 또는 제안 실행기의 판단 작업 상태
1 모델이 테스트 실행 제안 명령·폴더·예산 검사 실행 준비
2 도구가 run-17 접수 반환 아직 결과 없음 진행 중
3 종료 코드 1, 실패 테스트 2개 성공으로 표현하지 않음 결과 확인
4 모델이 실패 파일 읽기 제안 허용 경로인지 검사 원인 조사
5 모델이 원인과 근거를 정리 실제 실패 이름·출력과 대조 요청 범위 완료

테스트가 실패해도 “실패 원인을 정리한다”는 과제는 완료할 수 있습니다. 반대로 과제의 목표가 “테스트를 통과하도록 수정한다”라면 같은 상태에서 완료로 끝내면 안 됩니다. 환경의 결과와 과제의 완료 조건을 별도로 정의해야 하는 이유입니다.

위 순서를 실제로 재현할 때 run-17 접수 뒤 프로세스를 중단하면 기대 상태는 running/unknown이지 failed가 아닙니다. 이후 결과 조회가 성공하면 completed; 조회 기한이 끝나면 unknown으로 남겨야 합니다. 상태를 한 번에 success로 덮어쓰지 않는 것이 재시도 정책의 입력입니다.

단일 도구 실행 슈도코드

아래는 한 번에 하나의 도구 실행을 관리하는 설명용 구조입니다. 여러 도구를 병렬 실행하는 경우에는 별도의 소유권·취소·합류 규칙이 더 필요합니다.

state = load_task()
while within_budget(state):
    if state.has_pending_execution():
        result = wait_for_completion_or_deadline(state.execution_id)
        if result.is_unknown:
            persist_uncertainty(state)
            return report_incomplete_with_execution_id()
        record_actual_result(state, result)

    observation = select_evidence_with_sources(state)
    proposal = model(state.goal, observation, allowed_tool_schemas)

    if proposal.is_final:
        if verify_completion_against_evidence(proposal, state):
            return report_result(state)
        record_verification_failure(state)
        continue

    validate_schema(proposal)
    require_authorization(proposal.tool, proposal.arguments, state.scope)
    require_matching_approval_if_needed(proposal, state)
    reserve_budget_or_stop(state)
    operation = persist_planned_operation(proposal)
    result = execute_tool(operation)
    record_receipt_or_result(state, operation, result)

return report_budget_exhausted_with_known_results()

형식 검사는 필드와 타입만 확인할 뿐, 올바른 대상인지 증명하지 않습니다. 사용자가 테스트 명령과 특정 폴더에 승인했는데 모델이 나중에 다른 폴더나 인자로 바꿔 제안하면, 실행기는 실제 명령·경로·인자를 승인 기록과 허용 범위에 대조해야 합니다. 하나라도 기존 승인과 달라지면 그 승인을 그대로 재사용하지 않고 변경된 호출에 맞는 승인과 검사를 별도로 확인하며, 그 절차를 통과하지 못하면 실행 전에 거절합니다. 거절된 호출은 다른 도구 이름으로 우회하지 않고, 거절 사유와 다음 호출에 허용된 범위를 명시해야 합니다.

슈도코드의 record_receipt_or_result는 최소한 도구 반환 시각과 반환 종류를 저장해야 합니다. 같은 코드에서 execute_tool이 즉시 결과를 돌려주는 도구와 실행 ID만 돌려주는 도구를 동일한 완료 경로로 처리하면 접수 후 검증을 건너뛰게 되므로, 호출 계약에 synchronous/asynchronous를 명시합니다.

재시작 시 외부 효과의 중복·불확정 상태

외부 동작이 끝난 직후 결과 기록 전에 실행기가 종료되면, 저장된 계획만 보고 미실행으로 판단할 수 없습니다. 예를 들어 메일은 발송됐지만 완료 기록이 없을 수 있습니다. 이 상태에서 무조건 다시 발송하면 중복됩니다.

논리 작업 ID를 미리 저장하는 것은 복구의 출발점이지만, 그 ID만으로 중복을 막지는 못합니다. 메일 발송처럼 외부 효과가 있는 작업에서 결과를 저장하기 전에 실행기가 종료되면, 메일이 실제로 발송됐는지 알 수 없고 같은 메일을 다시 보낼 위험이 있으므로 재시작할 때는 같은 ID로 외부 도구나 서비스의 결과를 조회하거나 그 ID를 중복 방지 키로 사용하는 멱등 처리로 재시도할 수 있어야 합니다.

이런 조회나 멱등 처리를 지원하지 않는 부작용이면 성공으로 단정하지 말고 불확정 상태로 보존해 대사나 사람 확인으로 넘겨야 할 수 있습니다. 대화 요약에 “진행 중”이라고 적는 것만으로는 재시작 뒤에도 외부 실행의 기록과 상태를 추적할 수 있는 내구성이 생기지 않습니다.

파일 읽기 같은 관찰은 대체로 반복해도 외부 효과가 없지만, 읽는 사이 파일이 바뀔 수는 있습니다. 따라서 재시작 뒤의 새로운 관찰을 이전 버전의 근거와 구분해야 합니다.

메일처럼 조회 API와 멱등 키가 모두 없으면 unknown을 안전한 실패로 바꾸는 것이 아니라 사람 또는 대사 큐가 확인할 수 있게 보존합니다. 반대로 서비스가 같은 작업 ID의 결과를 조회할 수 있다면 재시작 시 재발송보다 먼저 조회하고, 이미 성공한 경우에는 외부 호출을 다시 만들지 않습니다.

실행 예산·취소·종료 조건

모델이 같은 실패를 보고 같은 호출을 계속 반복할 수 있으므로 최대 호출 수, 총 시간, 개별 실행 제한을 둡니다. 예산이 소진되면 결과를 꾸며 완료시키지 않고 무엇을 확인했고 무엇이 남았는지 보고합니다.

취소 역시 “사용자가 기다리지 않는다”와 “실제 도구가 종료됐다”를 구분합니다. 실행 중 프로세스나 외부 작업이 남아 있으면 취소 요청 후 실제 종료를 확인하거나, 추적 가능한 미완료 작업으로 남겨야 합니다. 실행 자원을 해제할 수 있는 시점과 사용자 응답을 끝낼 시점은 같지 않을 수 있습니다.

예산은 모델 호출 수만 세지 말고 자식 프로세스, 열린 파일, 외부 job처럼 종료 확인이 필요한 자원도 포함해야 합니다. 취소 응답을 1초에 돌려주더라도 실제 프로세스가 3초 뒤 종료됐다면 그 2초 동안은 실행 슬롯과 결과 수명을 유지하는 상태표가 필요합니다.

접수·실패·승인 변경·중단 경계 시험

정상 완료 하나만 시험하면 가장 중요한 경계가 빠집니다. 접수 응답만 있는 상태에서 완료를 주장하지 않는지, 도구가 실패했을 때 실패를 그대로 전달하는지, 승인된 인자를 바꾸면 실행이 거절되는지부터 확인합니다.

실행 성공 뒤 결과 저장 전 중단, 잘린 출력, 예산 소진, 도구 결과 안의 지시 문장도 따로 입력해 봅니다. 기대 결과는 언제나 성공이 아닙니다. 불확정 상태에서는 불확정이라고 말하고, 증거가 부족하면 미완료로 남기며, 허용된 범위 안에서만 다음 동작을 실행해야 합니다. 이런 상태 전이를 확인해야 마지막 답변의 그럴듯함과 실제 실행의 신뢰성을 구분할 수 있습니다.

예상 결과를 상태 단위로 적습니다. 접수만 반환하면 완료 주장 금지, 종료 코드 1이면 실패 근거 보존, 승인 범위가 바뀌면 재승인 없이는 미실행, 결과 저장 직전 중단이면 unknown입니다. 현재 노트는 이런 시험을 설계한 것이며 특정 실행기가 통과했다는 결과는 아닙니다.

후속·관련 개념

함께 읽을 문서

전체 기술 지식
선택 연습 · 관련 질문 5개

질문은 이해를 확인하는 보조 자료입니다. 학습 경로와 문서의 범위는 질문 수와 독립적입니다.