“더 나은 프롬프트는 하나의 답변을 개선하지만, 더 나은 하네스(Harness)는 모든 실행을 개선한다.”
에이전트가 추론 능력을 갖추었음에도 제약 조건을 잊어버리거나, 도구 선택을 잘 못 하거나, 무한 루프를 돈다면 이는 모델만의 문제가 아니다. 모델을 둘러싼 환경이 명확히 정의되지 않았기 때문이다.
이 글은 코딩, 리서치, 고객 지원, 운영 에이전트에 즉시 적용할 수 있는 실용적인 하네스 구축 가이드이다.
단순히 거대한 프롬프트를 하나 더 만드는 작업이 아니다. 에이전트를 위한 일종의 운영체제를 구축하는 과정이다.
하네스 엔지니어링이 왜 중요한가?
모델은 확률적 추론을 제공하고, 하네스는 그 추론을 통제된 실행으로 바꾼다.
[ 모델 (MODEL) ]
└─> 다음 행동을 제안함
│
▼
[ 하네스 (HARNESS) ]
├─> 컨텍스트 선택
├─> 도구 권한 부여
├─> 상태 저장
├─> 증거 수집
├─> 제한 사항 강제
└─> 실패로부터 복구
프롬프트는 시스템에 들어가는 입력일 뿐이며, 시스템 전체가 아니다.
유용한 하네스를 만들기 위해서 여러개의 서비스나 복잡한 멀티 에이전트 Swarm이 필요한 것은 아니다. 아래에 언급한 6가지 역할만 명시적으로 처리해 주면 된다.
1. 요청을 계약(Contract)으로 변환하기
자연어 요청은 유연하지만, 프로덕션 환경의 작업은 유연해서는 안된다. 모델이 행동에 나서기 전, 하네스는 요청을 경계가 명확한 작업 객체로 번역해야 한다.
task_id: 기능_02
goal: 분석 대시보드에 CSV 내보내기 기능 추가
inputs:
- issue.md
- repository
- design/export-flow.png
constraints:
- 공개 API를 유지할 것
- 데이터베이스 스키마를 변경하지 말 것
- 새로운 의존성 패키지를 추가하지 말 것
deliverable:
type: pull_request
done_when:
- 테스트 통과
- 타입 체크 통과
- 내보낸 CSV가 샘플 데이터와 일치
- UI 스크린샷 리뷰 통과
escakate_when:
- 스키마 변경이 필요한 것으로 보일 때
- 동일한 이유로 테스트가 3회 실패할 때
- 요청된 동작이 기존 제품 규칙과 충돌할 때
이런 컨트랙은 암묵적 작업 대체를 방지한다. 컨트랙이 없으면 에이전트는 멋대로 더 쉬운 버전의 문제를 풀어버린 뒤 자신있게 성공을 말할 수 있다.
또한 컨트랙은 하네스가 객관적으로 평가할 기준을 제공한다. “좋아 보인다.”는 종료 조건이 될 수 없지만, “4가지 검사를 모두 통과했다.”는 명확한 종료 조건이 된다.
2. 컨텍스트를 무작정 던져두지 말고 컴파일 하기
흔히 하는 실수는 전체 대화 내역, 모든 도구의 실행 결과, 프로젝트 전체 문서, 1000줄짜리 지시문. 파일을 통째로 주입하는 것이다.
컨텍스트 양이 늘어난다고 해서 이해도가 높아지지 않는다. OpenAI의 실용적인 규칙은 간단했다. “에이전트에게 매뉴얼을 주지 말고 지도를 주라” 엔트로픽 역시 컨텍스트의 신호 대 잡음비를 높게 유지하고 필요한 정보만 적시에 가져오는 방식을 권장한다.
function buildContext(task, state) {
return [
load(“AGENTS.md”), //요약된 프로젝트 지도
load(task.relevantProductSpec), //작업별 제품 규칙
load(task.relevantArchitecture), //로컬 아키텍처 경계
summarize(state.completedSteps), //압축된 수행 이력
state.openRisks, //잔여 위험 요소
state.currentArtifacts //전체 생성된 아티팩트
];
}
정보 제공은 점진적 공개 방식을 활용한다.
AGENTS.md -> 아키텍처 인덱스 -> 제품 규칙 -> 작업별 세부 가이드 -> 정확한 파일 및 증거 자료
에이전트에게 지식이 어디에 존재하는지 위치를 알려준다. 도구는 해당 지식이 실제로 관련이 있을 때만 더 깊은 자료를 조회한다. 대화 기록이 데이터베이스가 되어서는 안되며, 시스템 프롬프트가 서류함 역할을 해서는 안된다.
3. 모델과 도구 사이에 게이트웨이 두기
모델은 액션을 요청할 수 있지만, 그 액션이 유효한지, 허용되는지, 실행하기에 안전한지 결정하는 것은 하네스다.
async function handleToolRequest(request, run) {
validateSchema(request);
const decision = policy.authorize({
tool: request.name,
args: request.args,
task: run.contract,
risk: classifyRisk(request)
});
if (decision === "deny") {
return observation("permission_denied");
}
if (decision === "approval_required") {
return pauseForHumanApproval(request);
}
const result = await sandbox.execute(request);
return normalizeObservation(result);
}
모든 도구에는 다음 요소가 필수적으로 포함되어야 한다.
- 1가지 명확한 목적
- 모호하지 않은 스키마
- 범위가 지정된 권한 경계
- 예측 가능한 성공 응답
- 구조화된 실패 응답
- 타임아웃 설정
도구 실행 결과는 무제한 텍스트 출력이 아니라, 모델이 추론할 수 있는 구조화된 관찰 결과를 반환해야 한다.
{
“status”: “failed”,
“tool”: “run_test”,
“reason”: 2 snapshot mismatches”,
“evidence”: [
“artifacts/homr-mobile-before.png”,
“artifacts/homr-mobile-after.png”
],
“retryable”: true
}
잘 설계된 도구는 모델이 추측해야 하는 결정의 개수를 줄여준다. 반대로 잘못 설계된 도구는 모든 액션을 또다른 추론 문제로 만들어 버린다.
4. 메모리를 영구 상태로 외부화하기
장시간 구동되는 에이전트는 결국 컨텍스트 제한에 도달하거나, 다운되거나, 재시작되거나, 다른 에이전트에게 작업을 넘기게 된다. 핵심 상태가 대화 스크립트 안에만 존재한다면 실행 구조는 매우 취약해진다.
작업 상태는 모델의 외부 영구 스토리지에 지속화해야 한다.
{
"task_id": "feature_042",
"status": "verifying",
"current_step": "mobile_visual_check",
"completed": [
"implementation",
"unit_tests",
"desktop_visual_check"
],
"decisions": [
"reuse existing export endpoint",
"preserve current date format"
],
"artifacts": [
"export.csv",
"desktop-after.png"
],
"open_risks": [
"mobile toolbar may overflow at 390px"
],
"next_action": "render mobile viewport"
}
메모리는 아래의 4가지 유형으로 명확히 분리하여 저장한다.
FACTS (사실) : 변하지 않는 안정적인 프로젝트 지식
DECISIONS (결정) : 이번 작업을 수행하며 내린 선택들
STATE (상태) : 현재 실행이 위치한 정확한 지점
LESSONS (교훈) : 향후 실행을 바꿔야 할 실패 경험들
이 구분이 중요한 이유는 일시적인 도구 출력은 요약된 후 사라져야 한다. 반면 아키텍처에 관한 결정은 모든 컨텍스트 리셋 속에서도 살아남아야 한다. 반복되는 실패에서 얻은 교훈은 새로운 규칙이나 테스트가 되어야 한다.
메모리는 "모든 채팅 내용을 저장하는 것"이 아니다. 작업을 올바르게 이어가기 위해 필요한 최소한의 정보를 보존하는 것이다.
5. 증거(Evidence)를 완료 관문으로 삼기
모델이 결과물(Artifact)을 만들면, 환경은 그 결과물에 대한 증거를 생성한다. 그리고 하네스는 그 증거가 충분한지 판단한다.
async function verify(artifact, contract) {
const evidence = await Promise.all([
runTests(),
runTypecheck(),
validateOutputSchema(artifact),
renderAndCaptureScreenshots(),
checkScope(contract.constraints)
]);
const failed = evidence.filter(check => !check.passed);
if (failed.length === 0) return { status: "accept", evidence };
if (canRepairLocally(failed)) return { status: "retry", failed };
return { status: "escalate", failed };
}
우선 결정론적(Deterministic) 검사를 먼저 적용한다.
코드(CODE) : 테스트 + 타입 체크 + 린트 + 의존성 규칙
UI : 렌더링 + 스크린샷 + 상호작용 리플레이
리서치(RESEARCH) : 출처 커버리지 + 인용 일치 여부 + 모순 검사
데이터(DATA) : 스키마 + 범위 + 신선도 + 정합성 검증
지원(SUPPORT) : 정책 검사 + PII(개인정보) 검사 + 승인 경계 확인
그 후 인간의 주관적 판단이 필요한 영역에 한해 모델 기반 리뷰어(Model-based reviewer)를 사용한다.
작성자(Maker)와 검증자(Checker)가 정확히 동일한 동기를 가져서는 안 된다. 코드를 작성한 모델이 직접 스스로 검토할 수도 있겠지만, 다른 지침과 신선한 컨텍스트를 가진 독립된 검증자를 속이기는 훨씬 어렵다. 에이전트의 자율성은 증거의 질이 확장될 때만 함께 확장되어야 한다.
6. 실행 기록(Trace)을 남기고 정확한 실패 지점에서 복구하기
트레이스(Trace)가 없으면 실패는 한 편의 이야기에 불과하지만, 트레이스가 있으면 재현 가능한 테스트 케이스가 된다.
{
"run_id": "run_2026_08_29_0142",
"contract_version": "3",
"model_route": "reasoning-large",
"context_sources": ["AGENTS.md", "docs/export.md"],
"tool_calls": 17,
"state_changes": 6,
"verification": {
"passed": 4,
"failed": 1
},
"retries": 1,
"cost_usd": 2.84,
"stop_reason": "human_approval_required",
"rollback_point": "git:9cf31d2"
}
재시도를 수행하기 전 실패의 원인을 먼저 분류해야 한다.
switch (failure.type) {
case "missing_context":
updateProjectMap(failure.source);
break;
case "bad_tool_contract":
improveToolSchema(failure.tool);
break;
case "missing_guardrail":
addPolicyCheck(failure.action);
break;
case "weak_verification":
addRegressionTest(failure.example);
break;
default:
escalateWithEvidence(failure);
}
더 감정적인 문구를 섞은 프롬프트를 써서 동일한 환경을 무작정 다시 실행하면 안된다. 부족한 기능을 수정한 뒤, 정확히 실패한 케이스를 다시 실행하여 수정을 영구화해야 한다.
가장 뛰어난 하네스는 시간이 지날수록 복리로 발전한다(Compound). 즉, 단 하나의 실패가 향후의 모든 실행을 개선하는 방식이다.
실용적인 권한 사다리
모델이 자신의 위험한 행동을 스스로 승인하게 두어서는 안 된다. 제안, 승인, 실행 단계를 철저히 분리해야 한다.
[ 모델의 제안 (MODEL PROPOSES) ]
↓
[ 정책의 승인 (POLICY AUTHORIZES) ]
↓
[ 도구의 실행 (TOOL EXECUTES) ]
↓
[ 하네스의 결과 기록 (HARNESS RECORDS) ]
실용적인 초기 권한 정책 예시는 다음과 같다.
permissions:
read_files:
mode: automatic
write_workspace:
mode: automatic
requires:
- isolated_workspace
- diff_recorded
send_message:
mode: approval_required
requires:
- final_content_preview
deploy_production:
mode: approval_required
requires:
- tests_pass
- rollback_ready
delete_data:
mode: approval_required
requires:
- exact_targets
- recovery_plan
모든 작업에 최대값의 통제 마찰을 부여할 필요는 없다. 공개 문서를 읽는 작업과 고객 기록을 삭제하는 작업이 동일한 승인 절차를 거쳐서는 안 된다. 통제 수준을 액션의 파급력과 일치시켜야 한다.
가장 작은 규모의 유용한 프로젝트 구조
거창한 프레임워크 없이도 아래와 같은 폴더 구조로 하네스의 첫 번째 버전을 구축할 수 있다.
agent-harness/
├── AGENTS.md # 프로젝트 지도 (백과사전이 아님)
├── contracts/
│ └── task.schema.json # 작업 계약 스키마
├── context/
│ ├── architecture.md # 아키텍처 규칙
│ ├── product-rules.md # 제품 규칙
│ └── security.md # 보안 규칙
├── tools/
│ ├── registry.json # 도구 등록부
│ └── permissions.yaml # 권한 정책
├── state/
│ ├── current.json # 현재 실행 상태
│ └── decisions.md # 내린 결정 목록
├── checks/
│ ├── verify.ts # 검증 로직
│ └── regression-cases/ # 회귀 테스트 케이스
├── runs/
│ └── traces.jsonl # 실행 기록
└── lessons/
└── harness-updates.md # 개선 교훈 기록
폴더 이름 자체는 중요하지 않다. 핵심은 책임의 분리다.
구축 순서 가이드
처음부터 멀티 에이전트 스웜으로 시작하지 마라. 스스로의 작업을 증명할 수 있는 가장 작은 루프부터 시작해야 한다.
- 1단계 — '완료' 조건 정의하기: 작업 계약을 작성하고 성공 여부를 결정할 2~3개의 검사를 작성한다.
- 2단계 — 단 하나의 도구 감싸기: 스키마, 타임아웃, 권한 규칙, 구조화된 결과를 부여한다.
- 3단계 — 단 하나의 상태 파일 지속화하기: 완료된 단계, 결정사항, 아티팩트, 잔여 위험, 다음 행동을 저장한다.
- 4단계 — 단 하나의 복구 경로 추가하기: 검사 실패 시 정확한 증거를 반환하고 1회의 제한된 재시도를 허용한다.
- 5단계 — 트레이스 저장하기: 로드된 컨텍스트, 실행된 도구, 변경 사항, 통과된 검사, 정지 이유를 기록한다.
- 6단계 — 반복되는 실패를 인프라로 전환하기: 반복되는 실수는 다음 4가지 중 하나로 변환되어야 한다.
- 더 명확한 지도
- 더 나은 도구
- 더 엄격한 권한
- 새로운 테스트
이 단계까지 완료된 후에야 비로소 더 많은 자율성, 더 많은 도구, 더 많은 에이전트를 추가해야 한다.
하네스 엔지니어링이 아닌 것
- 5,000줄짜리 시스템 프롬프트를 만드는 것이 아니다.
- 연결 가능한 모든 도구를 에이전트에게 쥐여주는 것이 아니다.
- 날것의 대화 스크립트를 영원히 저장해 두고 그것을 메모리라 부르는 것이 아니다.
- 객관적인 수락 기준도 없는 작업에 리뷰어 에이전트를 무작정 붙이는 것이 아니다.
- 확률적인 실행 중 하나가 잘 나올 때까지 무한 재시도를 돌리는 것이 아니다.
- 모든 결정에서 인간을 완전히 배제하는 것이 아니다. (하네스의 목적은 판단이 필요한 곳에 인간의 주의력을 집중시키고 나머지는 자동화하는 것이다.)
핵심 평가 지표
생성된 토큰 수, 도구 호출 횟수, 시작된 작업 수에 연연하지 마라. 당신이 최적화해야 할 단 하나의 지표는 다음과 같다.
이 비율은 하네스가 수행해야 할 본질을 정확히 반영한다. 즉, 출력 과정에서 인간의 동등한 노력을 소모하지 않으면서, 모델의 역량을 유용하고 검토 가능한 결과물로 변환해 내는 능력이다.
패러다임의 전환
- 프롬프트 엔지니어링: "모델에게 무엇을 말해야 할까?"
- 컨텍스트 엔지니어링: "모델이 지금 당장 무엇을 알아야 할까?"
- 하네스 엔지니어링: "모델이 안전하게 행동하고, 결과를 증명하며, 스스로 복구하고 개선되도록 만드는 시스템은 무엇인가?"
모델은 앞으로도 계속 변할 것이다. 하지만 당신의 운영 지식이 복리로 축적되는 곳은 바로 하네스다.
계약을 세우라. 컨텍스트를 컴파일하라. 도구를 제어하라. 상태를 유지를 도모하라. 증거를 요구하라. 그리고 실패를 인프라로 바꾸라. 이것이 뛰어난 모델을 신뢰할 수 있는 에이전트로 만드는 길이다.
참고 자료:
- OpenAI — Harness engineering: leveraging Codex in an agent-first world
- OpenAI — Unrolling the Codex agent loop
- Anthropic — _Effective context engineering for AI agents
- Anthropic — Writing effective tools for AI agents
0 Comments:
댓글 쓰기