품질을 정하는 건 모델 자체보다 그 주변의 harness다. 앞 편에서 남긴 숙제는 분명했다. agent는 강력하지만 guardrail 없이는 위험하다. 그 숙제를 푸는 단계가 harness engineering이다. 같은 모델을 써도 둘러싼 시스템에 따라 결과가 갈린다.

“LLM 활용의 진화” 시리즈 ④편. ① Prompting · ② Vibe coding · ③ Agents · ④ Harness engineering · ⑤ Open models.

결과가 마음에 안 들면 제일 먼저 떠오르는 해법은 모델을 바꾸는 것이다. 그런데 agent를 실제로 매일 돌려보면 문제 대부분은 모델이 아니라 그 바깥에서 난다. 엉뚱한 context가 들어갔거나, 해서는 안 되는 tool을 쓸 수 있었거나, 결과를 아무도 확인하지 않았거나. 모델을 바꾸면 이 문제들이 그대로 따라온다. 그래서 모델 바깥을 하나의 설계 대상으로 따로 정리해두고 싶었다.

모델을 둘러싼 harness. scope가 정해진 tool, hook, 계층화된 context, verification loop가 같은 모델의 출력을 안전하고 일정하게 만드는 모습

그림 1. 모델을 둘러싼 harness. scope가 정해진 tool, hook, 계층화된 context, verification loop가 같은 모델의 출력을 일정하게 만든다.

harness는 모델을 둘러싼 시스템이다

harness는 모델 바깥의 모든 장치다. 어떤 context를 넣을지, 어떤 tool을 허용할지, 어디서 멈출지, 결과를 어떻게 검증할지. 내가 중요하게 보는 부품은 네 가지다.

요소하는 일비유
Context layering지식을 전사 공통(Global), 도메인별(Domain), repo별(Local)로 나눠 필요한 것만 넣는다신입에게 위키 전체를 한 번에 주지 않는 것
실행되는 지식가이드를 사람이 읽으면 매뉴얼, LLM이 읽으면 system prompt가 되는 형태로 둔다한 곳을 고치면 모두의 agent 행동이 바뀜
Hook특정 행동을 가로채서 교정한다main branch 커밋을 막고 feature branch를 만들게 유도
Verification loop생성 → read-only 비평 → 재생성을 반복한다통과 기준을 코드로 박아둠

prompt engineering과의 차이도 여기서 갈린다. prompt engineering이 모델에게 무엇을 물을지 다듬는 일이라면, harness engineering은 누가 물어도 같은 바닥의 결과가 나오도록 모델 주변을 설계하는 일이다. prompt보다 한 층 위의 시스템 설계다.

내가 쓴 harness 한 조각: MCP self-call

추상적으로만 말하면 감이 안 오니, 직접 써본 패턴 하나를 적는다. 기존 서비스를 외부 agent가 tool로 쓸 수 있게 MCP server [1]를 붙인 적이 있다. 그때 가장 중요하게 잡은 설계 결정은 MCP tool이 business logic을 다시 짜지 않는다는 것이었다. MCP tool은 앱 자신의 REST route를 in-process로 호출(self-call)하고, REST가 single source of truth(SSOT)가 된다. 아래는 그 구조를 단순화한 스케치다.

# MCP tool = 얇은 adapter. logic을 복제하지 않고 자기 REST route를 self-call
async def create_job_tool(topic: str, ...) -> dict:
    principal = current_principal()                  # API key -> workspace 인증
    token = create_access_token(principal)           # 단기 JWT 발급
    # httpx ASGITransport로 자기 앱의 REST를 in-process 호출
    resp = await self_post("/api/jobs", json={...},
                           headers={"Authorization": f"Bearer {token}"})
    return {"job_id": resp["id"]}                     # 과금·검증·trace는 REST가 단독 소유

이 구조가 버티는 이유는 단순하다. 과금 차감이 두 번 일어날 수 없고, business logic이 두 군데로 갈라지지 않고, 검증과 trace의 출처가 하나다. MCP tool이 logic을 따로 복제하면 처음엔 같아 보여도 시간이 지나며 두 경로가 조금씩 어긋난다. self-call로 두면 tool을 몇 개 늘리든 안전장치는 REST 한 곳만 고치면 된다. 모델이 무엇을 하든 안전장치는 harness가 혼자 책임지는 구조다.

hook도 같은 생각에서 나온다. 위험한 기능은 default-OFF flag 뒤에 두고, OFF일 때는 기존 동작과 byte 단위로 같게(byte-identical) 만든다. 새 기능을 켜는 일과 기존 동작을 깨지 않는 일을 서로 분리하는 것이다.

바닥을 올리는 governance

harness engineering의 실제 효과는 팀의 바닥을 올리는 데 있다고 본다. 가장 잘하는 엔지니어의 workflow(lint 규칙, branch 전략, 검증 절차)를 plugin이나 skill로 묶어 배포하면, 누가 작업하든 그 규율이 기본으로 깔린다. 문서로 남긴 규칙은 읽어야 지켜지지만, harness에 넣은 규칙은 읽지 않아도 지켜진다.

또 하나는 dev-prod parity다. 로컬에서 검증한 harness를 그대로 production에 올릴 수 있다. 별도 RAG server를 띄우고 점수를 맞추는 대신, 눈으로 확인한 context가 그대로 실행된다.

뭐가 단단하고 어디서 깨지나

단단한 건 “모델은 바꿀 수 있지만 harness는 남는다”는 점이다. 모델이 바뀌어도 context 구조, hook, 검증 기준은 그대로 옮겨간다. 반대로 harness 없이 모델의 능력에만 기대면, 모델을 바꿀 때마다 품질이 처음부터 다시 흔들린다.

깨지는 지점도 있다.

  • context layering의 경계: 무엇이 Global이고 무엇이 Local인지 잘못 나누면, 필요한 지식이 빠지거나 상관없는 지식이 섞인다. 나누는 기준을 자주 다시 봐야 한다.
  • 실행되는 지식의 drift: 가이드 한 곳을 고치면 모두의 agent가 바뀐다는 건 장점이자 위험이다. 잘못 고친 한 줄도 똑같이 퍼진다.
  • hook의 우회 경로: hook은 특정 경로의 행동만 가로챈다. 같은 결과에 이르는 다른 경로가 있으면 그대로 통과한다. hook 하나로 막았다고 생각하는 순간이 제일 위험하다.
  • verification loop의 기준: 비평하는 쪽도 모델이면 같은 맹점을 공유할 수 있다. 통과 기준 중 코드로 쓸 수 있는 건 코드로 박아야 한다.

내가 harness를 처음부터 다시 짠다면

순서는 이렇게 잡겠다. 먼저 context layering이다. 전사 공통 지식과 팀별·repo별 지식을 나누는 것만으로도 agent가 엉뚱한 걸 참고하는 일이 크게 준다. 그다음 되돌리기 어려운 행동을 가로채는 hook을 하나씩 더한다. 마지막으로 생성 결과를 코드로 확인하는 최소한의 verification loop를 붙인다. 셋 다 처음부터 완벽할 필요는 없다. 다만 이 셋이 없는 상태에서 모델부터 바꾸는 건 순서가 거꾸로다.

control engineering에서 배운 감각도 같은 쪽을 가리킨다. 시스템은 한 번 돌아갈 때가 아니라 매일 돌아갈 때 의미가 있고, 매일 돌아가게 만드는 건 대부분 주변 장치다.

정리

harness engineering은 “더 좋은 모델로 바꾸자”가 아니라 “모델 주변을 설계하자”다. context를 계층화하고, 지식을 실행 가능한 형태로 두고, hook으로 교정하고, 검증을 코드로 박는다. 같은 모델로 더 안전하고 더 일정한 결과를 내는 길이다.

harness를 잘 갖추면 부수 효과가 하나 생긴다. 모든 실행이 규격화된 데이터를 남긴다. 그 데이터로 자기 도메인에 맞는 모델을 직접 길들일 수 있다면 어떨까. 그게 마지막 편인 open models의 주제다.

References

  1. Anthropic, “Model Context Protocol” official documentation