View

728x90
반응형

HN 댓글창이나 레딧 r/LocalLLaMA 스레드를 긁어보면 "I'm tuning my agent harness"라는 말이 심심찮게 나온다. 국내 X에서도 "하네스 새로 짰더니 확실히 다르다"는 글이 점점 늘고 있다. Harness 엔지니어링이라는 용어가 이렇게 자리잡은 이유, 그리고 프로그램을 "실행한다"도 아니고 서비스를 "배포한다"도 아닌데 왜 하필 "돌린다"라고 하는지, 이 단어들이 붙박인 과정은 꽤 흥미롭다.

 

지난주에 겪은 일이 상징적이다. 같은 Claude Sonnet 4.6을 쓰고 비슷하게 MCP 서버를 붙여놨는데, 내 로컬 에이전트는 파일 세 개쯤 읽다가 엉뚱한 경로로 파고드는 반면 동료 에이전트는 같은 작업을 한 번에 끝냈다. 모델이 같은데 결과가 다른 이유를 파보면 대부분 하네스로 귀결된다. 모델이 엔진이라면 하네스는 그 엔진을 달고 실제로 굴러가게 만드는 주변부 전체다. 지난 반년 동안 에이전트 서너 개를 만들어보면서 내린 결론은, 모델 자체보다 주변부 설계가 결과를 훨씬 크게 바꾼다는 것이었다.

 

Harness 엔지니어링의 어원: 모델은 엔진, 하네스는 마구다

 

하네스(harness)라는 단어를 먼저 뜯어보자. 원래 영어로 "마구", 즉 말 목에 씌우는 가죽 장치를 뜻한다. 말이라는 야생 동력을 마차에 연결해서 원하는 방향으로 달리게 만드는 도구다. 자동차 전기 배선을 "wire harness"라고 부르는 것도 같은 계보다. 강력한 동력원을 통제해서 필요한 곳으로 흘려보내는 역할이다.

 

AI 쪽 "하네스"도 정확히 이 의미에서 왔다. 하네스는 AI 에이전트(LLM)가 제대로 돌아가도록 환경을 짜는 레이어다. 여기서 핵심은 "모델은 그대로 두고"라는 부분이다. GPT-5든 Claude Opus 4.7이든 훈련이 끝난 모델은 블랙박스라 가중치를 만질 수 없다. 대신 모델 주변을 어떻게 꾸미느냐가 결과를 가른다. 툴과 메모리, 컨텍스트, 스킬, 권한, 후처리 같은 장치가 모두 하네스 범주에 들어간다.

 

왜 하필 '하네스'라는 단어를 굳이 쓰는가

 

비슷한 단어가 여럿 있다. 스캐폴딩(scaffolding)은 개발 중 임시 구조물 느낌이라 운영 환경과 결이 맞지 않는다. 프레임워크(framework)는 너무 무겁고 정적이며, 래퍼(wrapper)는 단순 포장지 이미지라 루프 개념이 붙지 않는다. 미들웨어(middleware)는 요청-응답 파이프라인 이미지라 에이전트 루프와 어긋난다.

 

하네스가 자리잡은 이유는 동력을 통제해서 전달한다는 복합적인 뉘앙스를 한 번에 담기 때문이다. LLM은 생성형 동력이라 예측하기 어렵고, 풀어놓으면 엉뚱한 방향으로 달린다. 이것을 특정 과업에 맞게 조향하고, 필요한 자원을 공급하고, 결과를 다음 반복으로 되먹이는 주변부가 하네스다. 그래서 Anthropic과 OpenAI 쪽 사람들이 블로그에서 "agent harness"를 쓰기 시작했고, 국내에도 번역 없이 그대로 넘어왔다.

 

Anthropic 공식 블로그 Building effective agents에서도 에이전트 설계를 이야기할 때 모델 튜닝이 아니라 LLM 주변에 어떤 도구와 워크플로우를 둘지를 중심에 놓는다. 주변부 설계가 진짜 싸움터라는 관점이 깔려 있다.

 

'돌린다'라는 표현, 왜 쓰는가

 

 

출처: jenkins-x.io

이 단어가 한국어 개발 커뮤니티에서 AI 에이전트 맥락으로 오기까지 계보가 있다.

 

첫 번째 뿌리는 머신러닝 학습이다. "training을 돌린다"는 표현은 10년 넘게 써왔다. 모델 학습은 한 번의 명령어 실행이 아니라 에폭을 수백 번 반복하면서 로스가 떨어지길 기다리는 작업이다. "실행"보다 "돌린다"가 자연스러운 이유가 이 반복성 때문이다. 두 번째 뿌리는 CI/CD 잡이다. 젠킨스나 GitHub Actions 잡도 "돌린다"라고 부른다. 빌드와 테스트, 배포를 한 바퀴 순회한다는 감각이다.

 

세 번째가 지금 유행하는 에이전트 루프다. AI 에이전트는 전통적인 프로그램처럼 실행하고 종료되는 구조가 아니다. 관찰→판단→행동을 계속 반복하면서 자기가 무엇을 했는지 보고 다음에 무엇을 할지 다시 결정한다. 종료 조건이 선언적이고 비결정적이라 몇 바퀴 돌지는 런타임에 결정된다. "실행"보다 "돌린다"가 더 어울리는 이유다.

 

'돌린다'가 담는 감각

 

이 단어는 루프가 계속 회전한다는 반복성을 담고, 동시에 사람이 한 스텝씩 찍어주지 않아도 에이전트가 스스로 다음 행동을 결정한다는 자율성까지 담는다. 몇 바퀴에 끝날지 모르는 불확정성까지 얹히면 "돈다"라는 표현밖에 남지 않는다. 종료 조건이 충족되면 루프가 자연히 멈춘다. 목표 달성일 수도 있고, 예산 소진일 수도 있고, 에러가 누적돼서 포기하는 경우도 있다.

 

"Claude Code 하네스를 돌린다"고 하면, Claude 모델을 호출해서 에이전트 루프가 자기 컨텍스트를 관리하고 툴을 쓰면서 작업을 마칠 때까지 반복한다는 뜻이다. 프로세스 하나를 실행하는 것이 아니라 작은 자율 시스템 하나를 풀어놓는 것이다.

 

Harness 엔지니어링의 6계층: AI 에이전트 환경 설계 뜯어보기

 

 

출처: khalilstemmler.com

하네스를 처음 만드는 사람이 자주 저지르는 실수가 "툴이나 잔뜩 붙이면 되겠지"이다. 다만 현실은 다르다. 잘 만든 에이전트 환경은 명확한 계층 구조를 가지고 있다. 써보면서 정리한 여섯 개 층을 차례로 살펴본다.

 

시스템 프롬프트·페르소나

 

모든 하네스의 바닥층이다. 에이전트가 누구이고 어떤 원칙으로 판단하고 어떤 출력 포맷을 쓰는지 정한다. 여기가 부실하면 나머지가 다 무너진다. Claude Code의 내장 시스템 프롬프트가 "사용자 요청을 수행하는 엔지니어"라는 정체성을 박아 넣는 것도 이 층에 해당한다.

 

컨텍스트 엔지니어링

 

요즘 가장 빠르게 뜨는 영역이다. Andrej Karpathy가 "context engineering is the new prompt engineering"이라고 한 바로 그 영역이다. 프로젝트 파일과 CLAUDE.md, 메모리, RAG, 사용자 설정을 언제 얼마만큼 주입할지 설계한다. 초반에 CLAUDE.md에 8천 토큰 정도를 때려넣었다가 정작 필요한 파일을 읽을 컨텍스트 여유가 남지 않아 에이전트가 엉뚱한 추측을 하는 장면을 본 적이 있다. 무엇을 넣을지 고르는 것이 기술이지, 많이 넣는 것이 기술은 아니다.

 

툴 세트

 

에이전트가 세상과 상호작용하는 접점이다. MCP(Model Context Protocol) 서버, 로컬 CLI, HTTP API, 데이터베이스 쿼리가 여기 들어간다. 툴 개수뿐 아니라 이름과 인자 스키마까지가 에이전트의 행동 가능 영역을 결정한다. chrome-devtools-mcp 하나를 붙였다가 그 안에 하위 명령이 30개 넘게 달려 들어와, 툴 설명만으로 4000 토큰 가까이 먹어치우는 경우를 본 적이 있다. 그 세션에서 Claude가 자꾸 list_pages만 돌리고 다른 것은 쓰지 않아, 결국 navigate_page와 take_screenshot만 남기고 나머지는 모두 떼어냈다.

 

스킬·서브에이전트

 

특정 과업에 특화된 재사용 가능 모듈이다. Claude Code의 skill 시스템이 대표적이다. "이 상황이면 이 스킬 호출"이 자동으로 매칭되면서 반복 작업 패턴을 캡슐화한다. 서브에이전트는 별도 컨텍스트를 가진 보조 에이전트로, 메인 에이전트가 특정 작업을 위임할 때 사용한다.

 

권한·가드레일

 

자율 에이전트는 돌다가 실수하면 파괴적일 수 있다. rm -rf 같은 것을 돌리기 전에 막거나, 특정 파일 수정 전에 사용자 확인을 받거나, API 호출 빈도를 제한하는 층이다. 하네스의 안전벨트다. 이것이 없으면 차라리 에이전트를 돌리지 않는 편이 낫다.

 

피드백 루프

 

에이전트가 자기 작업 결과를 스스로 검증하고 다음 행동에 반영하는 메커니즘이다. 자동 평가와 테스트 자동 실행, 훅 시스템, 리뷰 서브에이전트 같은 것이 여기에 속한다. 이 층이 있어야 "자율적으로 돈다"는 감각이 실제로 구현된다.

 

여섯 층이 제대로 맞물리면 같은 모델로도 에이전트가 전혀 다른 레벨로 움직인다. 반대로 하나라도 빠지면 어딘가 삐걱거리고, 사용자는 "이 에이전트 멍청하네"라고 느낀다. 실제로는 모델이 멍청한 것이 아니라 주변부가 허술한 경우가 훨씬 많다.

 

실제 사례: Claude Code 하네스는 이렇게 생겼다

 

 

출처: shipyard.build

이론만 보면 감이 잘 오지 않으니 실제 하네스를 살펴본다. Claude Code는 공개된 에이전트 중에 하네스 완성도가 높은 축에 속한다. Anthropic 공식 문서에 나와 있는 구조를 기반으로 본다.

 

터미널에서 claude 명령어로 시작하면 세션이 뜨고 현재 작업 디렉토리가 프로젝트 루트로 자동 잡힌다. 여기부터 이미 하네스가 돌기 시작한다. 프로젝트 루트의 CLAUDE.md와 전역 ~/.claude/CLAUDE.md가 자동으로 세션 컨텍스트에 올라간다. 개발자가 "이 프로젝트는 이런 컨벤션으로 돌아간다"를 적어두면 에이전트가 기본 지식으로 가져간다. memory/ 폴더에 누적된 사용자 선호도 함께 로드된다. 내 블로그 스튜디오 프로젝트의 경우 이 메모리 폴더에 반년 치 피드백 4개 파일이 쌓여 있는데, 새 세션이 시작될 때마다 인덱스가 200줄 아래로 잘려서 올라온다.

 

스킬 디렉토리 쪽도 흥미롭다. ~/.claude/skills/ 또는 프로젝트 로컬 스킬 디렉토리에 정의된 스킬들이 자동으로 목록에 올라가고, 각 스킬은 프론트매터로 "언제 사용하는지" 설명이 붙어 있다. 상황에 매칭되면 에이전트가 Skill 툴로 호출한다. 슈퍼파워스(superpowers) 스킬 팩 같은 커뮤니티 번들을 붙이면 브레인스토밍이나 TDD 워크플로우가 한꺼번에 따라온다.

 

훅 층은 하네스가 에이전트 바깥에서 개입하는 통로다. settings.json의 hooks에 설정된 이벤트 훅이 자동 실행된다. SessionStart는 세션 시작 시 컨텍스트를 주입하고, PostToolUse는 특정 툴 실행 후 검증용으로 쓴다. 내 로컬에선 PostToolUse에 tsc --noEmit를 걸어두고 파일 편집 직후 바로 타입 체커가 돌게 해놨는데, 에이전트가 자기 실수를 한 턴 안에 스스로 인지해서 되돌리는 비율이 체감상 절반 이상 올라갔다.

 

툴 층은 기본으로 Bash, Read, Edit, Write, Grep, Glob, Agent가 붙어 있고, MCP 서버를 붙이면 Chrome DevTools나 Gmail, Drive 같은 외부 툴이 추가된다. 툴 개수보다 이름과 설명, 스키마의 품질이 에이전트 판단력에 더 직결된다. 권한 층은 settings.json의 permissions로 어떤 명령과 파일 접근을 자동 허용하고 어떤 것은 사용자에게 물어볼지 관리한다. 위험한 작업 전에는 사용자 승인이 기본값이다.

 

사용자가 claude라고 치는 순간 이 전체 주변부가 돌기 시작한다. 모델 한 번 호출이 아니라 에이전트 루프 전체가 자기 컨텍스트를 관리하면서 과업이 끝날 때까지 자율적으로 반복한다.

 

하네스를 잘 돌리려면 무엇을 챙겨야 하는가

 

 

출처: dreamstime.com

직접 에이전트 시스템을 설계하면서 삽질한 경험에서 몇 가지를 추려본다.

 

각설하고 툴은 적을수록 좋다. 초보 실수 1위가 MCP 서버를 열 개 넘게 붙이는 것이다. 툴 설명이 컨텍스트 윈도우를 잡아먹고, 에이전트는 비슷한 툴 사이에서 헷갈린다. 한 번은 filesystem MCP와 github MCP, 그리고 Bash의 git CLI를 같이 붙여놨다가 에이전트가 파일 수정을 어디로 보낼지 매번 망설이는 모습을 보고 그중 둘을 뗐다. "이 에이전트가 꼭 해야 하는 작업이 무엇이냐"를 먼저 정의하고 그 작업에 필요한 최소 툴만 남기는 것이 맞다. 나머지는 서브에이전트로 격리한다.

 

컨텍스트 위생은 진짜 체감된다. 모든 파일을 다 읽히지 말고 에이전트가 필요할 때 직접 읽게 유도해야 한다. Read와 Grep이 있는 이유가 그것이다. 미리 다 주입하면 토큰이 낭비되고, 주의가 분산되고, 비용이 폭발한다. 내 경우 블로그 스튜디오의 CLAUDE.md를 8천 토큰에서 2천 토큰 밑으로 줄이고 상세 규칙은 참조 문서로 분리해서 필요할 때만 읽도록 바꿨더니 응답 품질이 눈에 띄게 올라갔고, 사용자 요청당 평균 Read 호출 수도 오히려 줄었다.

 

권한 레이어는 처음부터 설계해야 한다. 나중에 붙이려고 하면 이미 사고가 난 뒤다. 파일 쓰기나 네트워크 호출, 결제, 이메일 발송처럼 되돌리기 어려운 동작은 기본값을 "사용자 확인"으로 잡아야 한다. 에이전트가 자율적으로 돌수록 가드레일이 더 중요해진다.

 

피드백 루프 자동화도 필수다. 에이전트가 쓴 코드가 테스트를 통과하는지, 린트와 타입 체크를 통과하는지 에이전트 스스로 돌리고 실패하면 다시 루프에 넣어야 한다. 사람이 개입해서 "테스트 돌려봐"라고 말하는 순간 에이전트의 자율성이 깨진다. 훅과 후처리 스크립트, 평가 서브에이전트가 모두 이 목적이다.

 

관측성을 포기하면 안 된다. 에이전트가 루프를 도는 동안 무엇을 생각했고 무슨 툴을 어떤 인자로 호출했고 응답이 무엇이었는지 로그로 남겨야 한다. 트레이스 없이 돌리면 디버깅이 사실상 불가능하다. LangSmith, Langfuse, Arize Phoenix 같은 도구를 쓰든 직접 JSON 로그를 남기든 무엇이라도 해야 한다. 돌다가 막히면 로그 없이는 원인을 찾지 못한다.

 

정리하면 에이전트가 잘 동작하느냐 마느냐는 모델의 문제가 아니라 주변부의 문제다. 모델을 바꾸는 것은 마지막 카드다. 그 전에 하네스의 여섯 계층을 돌아보면 대부분 거기서 원인이 나온다. 경험상 "모델이 멍청해서 안 돼"는 열에 아홉이 오진이었고, 실제 원인은 거의 주변부 쪽에 있었다.

 

모델 교체 전 Harness 엔지니어링부터 의심해본다

 

하네스는 환경 설계 레이어다. 모델이 엔진이라면 하네스는 그것을 제어하고 전달하는 주변부다. "돌린다"는 루프의 언어이고, 관찰과 판단과 행동이 비결정적으로 반복되는 자율 시스템이라 단발 실행이 아니라 루프가 맞다. 시스템 프롬프트에서 피드백 루프까지 여섯 계층 중 하나라도 허술하면 전체가 흔들린다. Claude Code는 이 모든 계층이 CLAUDE.md와 스킬, 훅, MCP 서버로 엮여서 돌아가는 실제 사례이고, 그래서 모델을 갈아타기 전에 주변부를 먼저 의심하는 것이 합리적이다.

 

지난달에 블로그 스튜디오에서 돌린 실험 결과를 하나 덧붙인다. settings.json에 쌓여 있던 MCP 서버 17개 중 실제로 6개월 동안 호출된 적이 있는 것은 7개뿐이었다. 나머지 10개를 전부 뗀 뒤 같은 파이프라인을 다시 돌렸더니 평균 응답 시간이 4.2초에서 2.9초로 줄었고, 경로 오판으로 재시도하는 빈도가 체감상 절반 수준으로 떨어졌다. 모델을 갈지 않아도 이 정도 차이가 나는 것이 주변부 설계의 위력이다. 결국 Harness 엔지니어링은 모델 스펙이 아니라 환경 설계로 에이전트를 차별화하는 실전 기술이다.

728x90
반응형
Share Link
reply
«   2026/05   »
1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
31