Cheondi
개발 · AI

AI 에이전트 개발의 현실적인 도입 순서

Agent Loop, 평가, 실행 하네스를 함께 읽고 실제 개발 흐름의 어느 단계부터 적용할지 정리한 기록입니다.

  • #ai
  • #agent-engineering
  • #codex
  • #evals
  • #harness

요즘 AI 개발 글을 읽다 보면 새 모델이나 점수 이야기가 먼저 눈에 들어온다. 나도 처음에는 어떤 모델이 코드를 더 잘 짜는지에 관심이 컸다. 그런데 실제 작업에 붙여서 써보니 모델의 한 번짜리 답변보다 그 답변을 둘러싼 실행 구조가 더 자주 문제를 만들었다.

도구를 어디까지 허용할지, 중간에 실패하면 어디에서 다시 시작할지, 작업이 끝났다는 말을 무엇으로 확인할지가 정해져 있지 않으면 모델이 좋아져도 긴 작업은 불안했다. 그래서 이번에는 Codex의 Agent Loop, Anthropic의 Agent Evals, Vercel AI SDK 7의 HarnessAgent를 한꺼번에 읽었다. 서로 다른 글이지만 실제로는 실행, 평가, 연결이라는 같은 문제의 다른 부분을 다루고 있었다.

Codex Agent Loop — 도구 결과가 다시 입력이 되는 구조

원문에서 말하는 것

OpenAI 글은 Codex CLI 안에서 사용자 입력이 어떻게 모델에 전달되고, 모델이 요청한 도구의 결과가 어떻게 다시 다음 입력으로 이어지는지 설명한다. 한 번의 대화 차례 안에서도 모델 호출과 도구 호출은 여러 번 반복될 수 있다.

단순하게 줄이면 이런 구조다.

사용자 요청
  → 컨텍스트 구성
  → 모델 호출
  → 도구 실행
  → 결과를 컨텍스트에 추가
  → 다시 모델 호출
  → 종료 응답

여기서 하네스는 모델 앞뒤에 붙은 얇은 포장지가 아니다. 프로젝트 지침을 모으고, 대화 기록과 도구 결과를 정리하고, 컨텍스트가 너무 커지지 않게 관리하면서 한 차례를 끝낼 조건도 판단한다. 실제 구현은 openai/codex 저장소에서 더 자세히 볼 수 있다.

내 생각

원문을 읽으며 가장 먼저 든 생각은 “루프를 만드는 것”과 “루프를 믿을 수 있게 만드는 것”은 다르다는 점이었다. 모델과 도구를 번갈아 호출하는 구조 자체는 어렵지 않다. 문제는 도구가 파일을 바꾸거나 외부 상태를 움직이기 시작한 다음이다.

예를 들어 코드 수정까지 끝난 상태에서 빌드가 실패했다고 해보자. 원인이 코드인지, 로컬 환경인지, 원래부터 있던 실패인지 분류하지 않고 다시 루프를 돌리면 수정 범위만 커질 수 있다. 배포 요청을 보낸 뒤 응답을 받지 못했을 때도 곧바로 같은 요청을 반복하면 실제로는 두 번 실행될 수 있다.

그래서 내가 생각하는 Agent Loop의 핵심은 반복 횟수가 아니라 전환 조건이다.

context = collect_read_only_evidence()

while task.has_safe_next_step():
    action = model.next_action(context)
    result = tools.run_within_scope(action)
    context.append(result)

    if result.requires_approval:
        break

has_safe_next_step() 안에는 생각보다 많은 판단이 들어간다. 현재 브랜치가 맞는지, 수정 대상이 분명한지, 같은 외부 작업을 이미 실행하지 않았는지, 실패 원인이 바뀌었는지 같은 조건이다. 모델을 호출하는 코드보다 이 경계를 정하는 코드가 길어질 수도 있다.

업무에서 넣어볼 위치

처음부터 개발 전체를 루프에 맡기기보다는 문제 분석 → 작은 코드 수정 → 로컬 검증 구간에 넣는 게 자연스럽다고 본다. 이 구간은 입력과 결과를 비교하기 쉽고, 잘못됐을 때 로컬 변경을 확인하며 멈출 수 있다.

증상과 관련 코드 수집
  → 실제 호출 경로 정리
  → 수정 범위 제안
  → 제한된 파일 변경
  → 테스트와 빌드
  → diff와 결과 보고

반대로 운영 배포 승인, 고객에게 영향을 주는 데이터 변경, 대상이 불분명한 외부 요청은 기본 루프에서 빼두는 편이 낫다. 자동화를 못 해서가 아니라, 실패했을 때 되돌릴 비용과 판단 책임이 다르기 때문이다.

Agent Evals — 결과보다 과정까지 확인하는 평가

원문에서 말하는 것

Anthropic 글은 에이전트 평가가 일반적인 한 번짜리 응답 평가보다 어려운 이유를 설명한다. 에이전트는 여러 차례 도구를 호출하고 환경 상태를 바꾸며, 같은 목표에도 서로 다른 경로로 도달할 수 있다.

글에서는 평가의 단위를 task, trial, grader, transcript, outcome으로 나눠 설명한다. 여기서 transcript는 도구 호출과 중간 결과를 포함한 실행 기록이고, outcome은 실행이 끝난 뒤 환경에 실제로 남은 상태다. 에이전트가 “완료했다”고 말한 것과 원하는 결과가 만들어진 것은 별개의 사실이다.

또한 한 번의 실행 결과만으로 판단하지 않고 여러 trial을 돌리는 이유도 중요하다. 모델 출력에는 변동이 있기 때문에 한 번 성공했다고 계속 성공한다고 볼 수 없고, 한 번 실패했다고 기능 전체가 불가능하다고 단정하기도 어렵다.

내 생각

이 부분은 기존 개발 테스트와 닮았지만 확인 범위가 더 넓다고 느꼈다. 보통 함수 테스트는 같은 입력에 같은 결과를 기대할 수 있다. 반면 에이전트는 다른 파일을 먼저 읽거나 도구 호출 순서를 바꾸고도 올바른 결과를 만들 수 있다. 모든 실행 경로를 하나로 고정하면 좋은 해결 방법까지 실패로 처리할 수 있다.

그래서 최종 결과와 과정의 안전성을 분리해서 보는 편이 낫다.

평가 층 확인할 것
결과 요구한 상태가 실제로 만들어졌는가
과정 허용된 도구와 범위 안에서 작업했는가
증거 API, 로그, 화면이 같은 결론을 가리키는가
복구 중간 실패 후 안전한 지점에서 재개할 수 있는가

모든 항목을 하나의 점수로 합치면 어디가 나빠졌는지 알기 어렵다. 코드 결과는 맞지만 필요 없는 파일까지 수정했을 수도 있고, 테스트는 통과했지만 실제 화면이 이전 자산을 보고 있을 수도 있다. 이런 차이는 점수 하나보다 층별 결과로 남길 때 고치기 쉽다.

평가 데이터도 거창한 벤치마크부터 만들 필요는 없다고 생각한다. 실제로 반복해서 겪은 실패를 익명화해 작은 시나리오로 쌓는 편이 현실적이다.

task: 변경된 화면이 실제 배포 자산과 일치하는지 확인
graders:
  - build_artifact_matches
  - runtime_page_contains_marker
  - browser_console_has_no_error
evidence:
  - build_sha
  - served_asset_url
  - visible_screen_result

업무에서 넣어볼 위치

내가 먼저 적용하고 싶은 위치는 구현 이후의 검증 구간이다. 테스트 시나리오를 입력으로 주고 코드 테스트, API 응답, 활성 로그, 실제 화면을 서로 다른 grader로 확인한다. 하나가 통과했다고 나머지를 생략하지 않는다.

개발 중에는 빠른 회귀 테스트로 사용하고, 배포 뒤에는 같은 시나리오의 실제 환경 증거를 추가한다. 실패한 사례는 다음 평가 데이터가 된다. 이렇게 모인 데이터가 있어야 모델이나 하네스를 바꿨을 때 “느낌상 좋아졌다”가 아니라 어떤 작업이 나아지고 나빠졌는지 비교할 수 있다.

다만 transcript를 모을 때는 입력 전체를 그대로 저장하지 않아야 한다. 계정, 내부 주소, 요청 값이 섞일 수 있으므로 평가에 필요한 이벤트 종류와 결과만 남기고 민감한 값은 처음부터 제외하는 게 맞다.

HarnessAgent — 공통 인터페이스와 남는 차이

원문에서 말하는 것

Vercel은 AI SDK 7에서 HarnessAgent라는 실험적 추상화를 소개했다. 모델 제공자만 바꾸는 수준을 넘어 Codex, Claude Code, Pi 같은 이미 구성된 에이전트 하네스를 AI SDK의 공통 Agent 인터페이스로 실행하려는 접근이다.

각 하네스는 샌드박스, 지침, 스킬, 도구를 따로 구성하면서도 generatestream 같은 공통 결과 형태로 연결할 수 있다. 중단된 세션이나 차례를 다시 이어가기 위한 세션 기능도 함께 소개된다. 구현과 API의 변화는 vercel/ai 저장소에서 확인할 수 있다.

내 생각

공통 인터페이스가 생기면 애플리케이션에서 특정 하네스의 실행 방법을 직접 아는 코드를 줄일 수 있다. 어떤 작업에는 코드 탐색이 강한 하네스를 쓰고, 다른 작업에는 정해진 도구만 가진 가벼운 실행기를 연결하는 구성도 쉬워진다.

하지만 run()의 모양이 같다고 실행 의미까지 같아지는 것은 아니다. 하네스마다 권한 확인 방식, 컨텍스트 구성, 세션 복구, 도구 호출 의미가 다를 수 있다. 이 차이를 억지로 숨기면 교체는 쉬워 보여도 장애가 났을 때 어느 계층을 확인해야 하는지 모호해진다.

그래서 공통 인터페이스에는 최소 계약만 두고, 하네스별 차이는 어댑터가 명시적으로 드러내는 방향이 좋아 보인다.

interface AgentRunner {
    run(task, allowedTools, checkpoint): EvidenceBundle
}

CodexRunner    -> AgentRunner
OtherRunner    -> AgentRunner
ApprovalPolicy -> runner 밖에서 공통 적용

여기서 EvidenceBundle에는 답변만 들어가면 부족하다. 어떤 도구가 실행됐는지, 수정된 범위가 어디인지, 재개할 체크포인트가 무엇인지 같이 있어야 상위 애플리케이션에서 같은 검증 규칙을 적용할 수 있다.

업무에서 넣어볼 위치

처음부터 운영 핵심 기능을 여러 하네스로 교체 가능하게 만드는 것보다는 반복 조사나 코드 검증을 돕는 내부 도구의 실험 계층에 넣는 게 적당해 보인다.

예를 들면 읽기 전용 코드 분석, 정해진 테스트 실행, 변경 요약 생성처럼 실패해도 사람이 결과를 보고 버릴 수 있는 작업이다. 이 단계에서 공통 입력과 증거 형식을 먼저 맞춘다. 그 다음에야 제한된 파일 수정처럼 부작용이 있는 기능으로 범위를 넓힌다.

하네스 선택과 승인 정책도 분리하고 싶다. 실행기는 작업을 수행하지만, 어떤 도구를 허용할지와 외부 상태 변경을 승인할지는 상위 정책이 결정한다. 그래야 하네스를 바꿔도 안전 기준이 같이 바뀌지 않는다.

현실적인 도입 순서

세 글을 함께 읽고 나니 에이전트 개발의 순서가 조금 더 분명해졌다. 처음부터 자율성을 높이는 것보다 결과를 확인하기 쉬운 구간부터 연결하는 편이 낫다.

읽기 전용 분석
→ 제한된 코드 수정
→ 로컬 검증 자동화
→ 실행 증거 수집
→ 평가 데이터 축적
→ 승인된 배포 단계 연결

첫 단계에서는 코드와 문서를 읽고 근거를 정리하게 한다. 다음 단계에서는 변경 가능한 파일을 미리 제한한다. 검증 결과가 안정적으로 쌓인 뒤에 실행 증거와 평가 데이터를 연결하고, 마지막에야 승인된 배포 구간을 붙인다.

각 단계에서 다음 단계로 넘어가는 기준도 필요하다.

단계 다음 단계로 가기 위한 기준
읽기 전용 분석 실제 코드 경로와 원인 근거를 일관되게 찾음
제한된 수정 지정 범위를 벗어나지 않고 diff를 설명함
로컬 검증 기존 실패와 새 실패를 구분함
증거 수집 결과를 다시 확인할 위치와 식별자를 남김
평가 데이터 반복 실행에서 품질 변화를 비교할 수 있음
배포 연결 승인, 중복 실행 방지, 복구 절차가 분리됨

현재 내가 만들고 싶은 방향은 사람을 빼는 자동화가 아니다. 사람이 모든 명령을 직접 치지 않아도 되지만, 위험한 지점에서는 멈추고 무엇을 확인해야 하는지 보여주는 개발 과정에 가깝다.

Agent Loop는 작업을 이어가는 구조를 만들고, Evals는 그 구조가 제대로 움직였는지 확인하며, HarnessAgent 같은 추상화는 서로 다른 실행기를 같은 제품 안에 연결한다. 세 가지를 이 순서와 경계 없이 한꺼번에 붙이면 편리한 데모는 만들 수 있어도 오래 쓰기 어려울 것 같다.

결국 에이전트가 얼마나 많은 일을 대신하느냐보다, 사람이 다시 확인할 위치와 중간에 끊겼을 때 재개할 증거가 얼마나 분명한지가 먼저다. 당분간은 그 기준을 지키면서 자동화 범위를 한 단계씩 넓혀보려고 한다.


참고한 글과 코드