하네스(harness)는 모델을 감싸서 실제 작업을 수행하게 만드는 바깥 구조다. 어떤 도구를 주는가, 컨텍스트에 무엇을 넣는가, 결과를 어떻게 검증하는가, 실패하면 어떻게 되돌리는가 — 이 모든 것이 하네스다.
같은 모델이라도 하네스에 따라 결과가 크게 달라진다. 그래서 "어느 모델이 더 좋은가"라는 질문은 절반만 유효하다. 나머지 절반은 모델에게 무엇을 보여 주고 무엇을 할 수 있게 하느냐에 달려 있다.
직접 에이전트를 만들어 보면 이 사실이 금방 체감된다. 처음엔 프롬프트를 고치며 시간을 보낸다. 그러다 어느 순간 깨닫는다. 프롬프트를 아무리 다듬어도, 모델이 테스트를 실행할 수 없으면 코드가 도는지 알 방법이 없다는 걸. 그때부터 작업의 무게중심이 프롬프트에서 하네스로 옮겨 간다.
하네스를 구성하는 네 축
| 축 | 결정할 것 | 흔한 실패 |
|---|---|---|
| 도구 | 어떤 동작을 허용할 것인가, 인터페이스를 어떻게 줄 것인가 | 도구가 너무 많거나, 출력이 너무 장황함 |
| 컨텍스트 | 무엇을 보여 줄 것인가, 언제 잘라낼 것인가 | 전부 넣어 핵심이 묻힘 |
| 검증 | 무엇을 기준으로 완료를 판단하는가 | 모델의 자기 보고에 의존 |
| 복구 | 실패했을 때 어떻게 되돌리는가 | 부분 변경이 남아 상태가 꼬임 |
도구 설계 — 적을수록 좋다
도구를 많이 주면 좋을 것 같지만 반대다. 선택지가 늘수록 잘못된 도구를 고를 확률이 오르고, 설명만으로 컨텍스트를 잡아먹는다. 좋은 도구 세트는 겹치지 않고, 각각이 명확하며, 조합으로 대부분의 작업을 커버한다.
// 나쁜 도구: 너무 많은 일을 하고 출력이 무제한
{
name: "file_operation",
params: { action: "read|write|delete|move", path: "...", content: "..." }
}
// 나은 도구: 분리되고 출력이 제한됨
{
name: "read_file",
params: { path: "string", offset: "number?", limit: "number? (기본 2000줄)" },
returns: "지정 범위의 내용. 잘렸으면 그 사실과 전체 줄 수를 함께 반환"
}
{
name: "search_code",
params: { pattern: "string", glob: "string?", max_results: "number? (기본 50)" },
returns: "파일:줄번호:매칭줄. 결과가 많으면 상위 N개 + 전체 개수"
}
컨텍스트 구성 — 무엇을 넣지 않을 것인가
컨텍스트 창이 커졌다고 다 채우면 안 된다. 긴 입력에서 중간 정보가 흐려지는 현상은 여전하고, 비용과 지연도 비례해 늘어난다. 좋은 하네스는 적극적으로 버린다.
검증 루프 — 하네스의 심장
에이전트가 "완료했습니다"라고 말하는 것과 실제로 동작하는 것은 다른 문제다. 검증 루프가 없는 에이전트는 자기 보고에 의존하게 되고, 그건 신뢰할 수 없다.
# 검증 루프의 기본 구조
while not done and attempts < MAX:
action = model.decide(context)
result = execute(action)
context.append(compact(result))
if action.is_final_claim():
# 모델의 주장을 기계적으로 검증한다
checks = run_verification() # 빌드, 테스트, 린트, 타입체크
if checks.all_passed():
done = True
else:
# 실패 정보를 구체적으로 되돌려 준다
context.append(checks.failures) # 어떤 테스트가 왜 실패했는지
attempts += 1
여기서 중요한 건 검증이 모델 바깥에 있다는 것이다. 테스트 러너, 타입 체커, 린터는 모델과 무관하게 참·거짓을 판정한다. 이 객관적 신호가 있으면 에이전트는 스스로 고칠 수 있다. 없으면 그럴듯한 코드를 쓰고 끝낸다.
- 컴파일·타입 체크 결과
- 단위·통합 테스트 통과 여부
- 린터 오류
- 실제 실행 결과(스크린샷·HTTP 응답)
- 모델의 자기 평가
- "코드가 올바릅니다" 같은 서술
- 다른 모델의 리뷰(유용하지만 결정적이지 않음)
실패 복구와 상태 관리
에이전트는 중간에 실패한다. 이때 작업 디렉터리가 어중간한 상태로 남으면 다음 시도가 더 꼬인다. 복구 전략을 미리 정해 둬야 한다.
- 체크포인트 — 작업 시작 시점의 상태를 기록한다. 버전 관리 시스템이 이미 있다면 그걸 활용하는 게 가장 간단하다.
- 부분 커밋 금지 — 검증을 통과하기 전에는 확정하지 않는다.
- 반복 감지 — 같은 실패를 세 번 반복하면 멈추고 사람에게 넘긴다. 무한 루프는 비용만 태운다.
- 시간·비용 상한 — 토큰이든 시간이든 상한을 두고, 초과 시 현재까지의 결과와 함께 중단한다.
샌드박스와 권한
에이전트에게 실행 권한을 주는 순간 보안 문제가 생긴다. 특히 코드베이스나 외부 데이터에 포함된 지시문이 모델의 행동에 영향을 줄 수 있다는 점을 전제해야 한다.
측정 — 하네스를 개선하려면
하네스 개선은 감으로 하면 안 된다. 대표 작업 세트를 만들어 두고, 변경 전후의 성공률·시도 횟수·비용을 비교해야 한다.
| 지표 | 의미 | 개선 신호 |
|---|---|---|
| 작업 성공률 | 검증까지 통과한 비율 | 가장 중요한 지표 |
| 평균 시도 횟수 | 효율성 | 도구·컨텍스트 개선 시 감소 |
| 평균 토큰 사용 | 비용 | 컨텍스트 정리로 감소 |
| 사람 개입 비율 | 자율성 | 복구 전략 개선 시 감소 |
| 잘못된 도구 선택 빈도 | 도구 설계 품질 | 도구 축소·설명 개선 시 감소 |
이 측정이 있으면 "모델을 바꿀까, 하네스를 고칠까"라는 질문에도 답할 수 있다. 같은 모델로 하네스만 바꿔서 성공률이 오른다면, 아직 모델을 바꿀 때가 아니다.
자주 묻는 질문
하네스가 정확히 무엇인가요?
모델을 감싸 실제 작업을 수행하게 만드는 바깥 구조 전체를 말합니다. 도구 정의, 컨텍스트 구성, 검증 루프, 실패 복구, 권한 경계가 포함되며, 같은 모델이라도 하네스에 따라 결과가 크게 달라집니다.
도구는 많이 주는 게 좋지 않나요?
아닙니다. 선택지가 늘면 잘못된 도구를 고를 확률이 오르고 설명만으로 컨텍스트를 소모합니다. 겹치지 않고 각각 명확한 소수의 도구를, 조합으로 대부분의 작업을 커버하도록 설계하는 편이 낫습니다.
검증 루프는 왜 중요한가요?
모델의 자기 보고는 신뢰할 수 없기 때문입니다. 테스트·타입 체크·린터처럼 모델 바깥의 객관적 판정 수단이 있어야 에이전트가 실패를 인지하고 스스로 고칠 수 있습니다. 검증이 없으면 그럴듯한 결과에서 멈춥니다.
컨텍스트는 많이 넣을수록 좋은가요?
아닙니다. 긴 입력에서 중요한 정보가 흐려지고 비용과 지연이 함께 늘어납니다. 작업 정의와 완료 기준을 앞에 두고, 관련 코드만 선별하며, 오래된 도구 출력과 실패 로그는 압축하거나 제거하세요.
에이전트가 같은 실수를 반복합니다. 어떻게 하나요?
최근 행동의 도구·인자 조합을 해시로 비교해 반복을 감지하고, 일정 횟수를 넘으면 중단하거나 접근 방식을 바꾸도록 개입하세요. 동시에 시도 횟수와 비용 상한을 두어 무한 루프를 막아야 합니다.
보안은 어떻게 챙기나요?
작업 디렉터리 밖 접근을 차단하고, 네트워크 아웃바운드를 제한하며, 실행 환경에서 시크릿을 제거하세요. 되돌릴 수 없는 동작에는 승인 게이트를 두고, 외부에서 읽어 들인 콘텐츠는 명령이 아니라 데이터로 취급하도록 경계를 명확히 해야 합니다.

댓글 0