하네스 엔지니어링이란? LLM 엔진을 '탈 수 있는 자동차'로 만드는 기술

하네스 엔지니어링이란? LLM 엔진을 '탈 수 있는 자동차'로 만드는 기술

엔진이 아니라 자동차를 만든다는 발상

하네스 엔지니어링(harness engineering)은 대규모 언어모델(LLM)을 실제 제품·서비스로 잇기 위해, 모델을 둘러싼 입력·출력·검증·실행·안전 장치를 하나의 시스템으로 설계하는 일을 뜻한다. 소프트웨어에서 '하네스(harness)'라는 말은 시험용 골격을 뜻하는 '테스트 하네스'에서 왔고, 그 뿌리는 자동차 안의 전선을 하나로 묶는 '배선 하네스'나 높은 곳에서 작업자를 붙들어 주는 '안전벨트(안전 하네스)'처럼 무언가를 붙들어 제어하는 장비를 가리키는 말이다. 공통점은 하나다. 강력하지만 그대로 두면 위험하거나 제구실을 못 하는 것을, 붙들고 이어서 안전하게 쓸 수 있게 만든다는 점이다. 언어모델도 마찬가지다. 모델 자체는 강력한 엔진이지만, 엔진만으로는 자동차가 아니다. 차체와 조향장치, 브레이크, 계기판, 안전벨트가 갖춰져야 비로소 사람이 탈 수 있는 자동차가 된다. 하네스 엔지니어링은 바로 이 '엔진을 자동차로 만드는' 일이다.

핵심을 먼저 못 박자. 하네스 엔지니어링은 '더 똑똑한 모델을 만드는 일'이 아니다. 모델 자체는 그대로 두고, 그 주변을 짜는 일이다. 모델의 가중치를 손대는 파인튜닝(fine-tuning)이나 학습과는 층위가 다르다. 파인튜닝이 엔진 자체의 성능을 끌어올리는 일이라면, 하네스 엔지니어링은 그 엔진을 차체에 얹어 사람이 안전하게 몰 수 있는 자동차로 완성하는 일에 가깝다.

이 개념은 최근 에이전트(agent)와 LLM 애플리케이션이 늘어나면서 부쩍 중요해졌다. 데모에서는 근사하게 돌아가던 프롬프트가 정작 운영 환경에 올리면 무너지는 경험을 많이들 한다. 그 간극을 메우는 공학이 바로 하네스 엔지니어링이다. '프롬프트만 잘 쓰면 된다'는 인식에서 '프롬프트를 감싼 시스템을 잘 지어야 한다'는 인식으로 넘어가는 지점이라고 봐도 좋다. 다만 '하네스 엔지니어링'은 아직 표준으로 굳어진 용어는 아니며, 스캐폴딩(scaffolding)이나 LLM 애플리케이션 엔지니어링으로도 불린다.

왜 모델만으로는 제품이 되지 않는가

이 코스가 내내 강조해 온 사실이 하네스 엔지니어링의 출발점이다. 언어모델은 이해하는 기계가 아니라 확률로 그럴듯한 다음 토큰을 고르는 기계다. 입력이 주어지면 다음에 올 단어의 확률 분포를 계산하고, 그 분포에서 하나를 뽑는다. 이 과정을 반복해 문장을 만든다. 그럴듯한 것을 빠르게 대량으로 생성하는 데는 탁월하지만, 그 결과가 사실인지·정확한지·형식에 맞는지는 스스로 보장하지 못한다. 자신 있게 틀리기도 한다.

제품은 다르다. 결제 API에 넘길 값은 정확한 숫자여야 하고, 데이터베이스에 넣을 결과는 정해진 형식이어야 하며, 사용자에게 나가는 답변은 되돌릴 수 없는 사고를 내지 않아야 한다. 그런데 모델의 출력은 본질적으로 '그럴듯한 텍스트'다. 이 그럴듯한 텍스트를 그대로 시스템의 다음 부품에 흘려보내면, 어딘가에서 반드시 어긋난다.

그래서 모델과 제품 사이에 완충 장치가 필요하다. 모델이 뱉은 그럴듯한 결과를 받아서 형식을 강제하고, 틀린 것을 걸러내고, 실패하면 다시 시도하고, 위험한 행동은 사람에게 넘기는 층. 이 층 전체를 설계하는 일이 하네스 엔지니어링이다. 정리하면, 모델의 능력을 '쓸 수 있는 능력'으로 바꾸는 변환 작업이다.

'초안은 기계, 판단은 사람'이라는 원칙을 시스템 수준으로 확장한 것이라고 봐도 좋다. 사람이 매번 눈으로 검토할 수 없을 만큼 자동화가 늘어나면, 그 검토의 일부를 코드로 짜 넣어야 한다. 그 코드가 하네스다.

모델과 제품을 잇는 파이프라인의 골격

하네스를 하나의 흐름으로 그려 보면 대략 이렇다. 사용자 요청이 들어오면 먼저 입력을 다듬고 필요한 맥락을 붙인다. 검색 증강 생성(RAG)으로 관련 문서를 임베딩(embedding) 기반으로 찾아 프롬프트에 끼워 넣는 단계가 여기 들어간다. 그다음 정돈된 프롬프트를 모델에 보내 출력을 받는다. 받은 출력은 곧바로 쓰지 않고 검증기를 거친다. 형식이 맞는지, 값이 유효한지 확인한다. 통과하면 실제 행동(도구 호출, 저장, 응답)으로 넘어가고, 실패하면 재시도하거나 폴백으로 빠진다.

모델을 제품으로 잇는 데이터 파이프라인

여기서 눈여겨볼 점은, 모델 호출이 파이프라인 전체에서 한 부품에 불과하다는 사실이다. 초보 단계에서는 '프롬프트 → 답변'이 전부처럼 보이지만, 운영 시스템에서는 앞뒤로 전처리와 후처리가 훨씬 길게 붙는다. 실무자들이 흔히 '모델 코드는 10%, 나머지 90%가 하네스'라고 말하는 이유가 여기 있다.

이 파이프라인은 대부분 결정론적 코드로 짜인다. 모델만 확률적이고, 그것을 감싼 껍질은 예측 가능하게 동작해야 한다. 확률적인 심장과 결정론적인 골격을 어떻게 맞물리느냐가 하네스 설계의 핵심 감각이다.

구조화 출력 — 자유 텍스트를 검증 가능한 데이터로

구조화 출력 — 자유 텍스트 응답을 parse해 검증 가능한 스키마(Pydantic) 데이터로 바꾼다

하네스의 첫 번째 기둥은 구조화 출력(structured output)이다. 모델이 자유로운 문장으로 답하게 두는 대신, 정해진 형식—대개 JSON—으로 답하도록 강제한다. 예를 들어 회의록에서 할 일을 뽑는 기능이라면, 모델에게 {"tasks": [{"title": "...", "due": "...", "owner": "..."}]} 같은 스키마를 따르라고 지시한다. 그러면 출력을 프로그램이 곧바로 파싱해 쓸 수 있다.

구조화 출력(JSON 스키마)으로 결과를 검증 가능하게

왜 이것이 결정적인가. 자유 텍스트는 사람이 읽기 좋지만 기계가 검증하기 어렵다. 반면 스키마가 정해진 JSON은 형식 위반을 즉시 잡아낼 수 있다. due 필드가 날짜 형식이 아니거나, 필수 항목이 비어 있거나, 값이 허용 범위를 벗어나면 검증기가 그 자리에서 거부한다. 그럴듯하게 틀린 출력을 다음 단계로 넘기기 전에 걸러내는 그물이 생기는 셈이다.

요즘 주요 모델 API는 이 요구를 더 강하게 뒷받침한다. 응답 형식을 JSON으로 못 박거나, 사전에 정의한 함수 시그니처(도구 호출 스키마)에 맞춰 인자를 채우게 하는 기능이 그것이다. 이렇게 하면 모델이 형식을 어길 확률 자체가 크게 줄어든다. 다만 형식이 맞다고 내용까지 맞는 것은 아니라는 점은 늘 기억해야 한다. 형식 검증은 첫 그물일 뿐, 내용의 사실성은 별도의 장치로 따져야 한다.

디코딩 손잡이 — temperature와 top-p로 출력을 길들이기

하네스 엔지니어링은 모델이 토큰을 뽑는 방식에도 개입한다. 앞서 말했듯 모델은 다음 토큰의 확률 분포를 만든다. 이 분포에서 어떻게 하나를 고를지 정하는 규칙이 디코딩 파라미터다. 온도(temperature)는 분포를 얼마나 평평하게 혹은 뾰족하게 만들지를 조절한다. 값이 낮으면 확률이 높은 토큰에 몰려 보수적이고 일관된 출력이 나오고, 값이 높으면 분포가 평평해져 다양하고 창의적이지만 엉뚱해지기 쉽다.

top-k는 확률 상위 k개 후보만 남기고 나머지를 잘라내는 방식이고, top-p(뉴클리어스 샘플링)는 확률을 큰 순서로 더해 누적이 p에 도달할 때까지의 후보만 남긴다. 예를 들어 top-p를 0.9로 두면, 누적 확률 90%를 채우는 만큼의 후보 안에서만 다음 토큰을 뽑는다. 꼬리 쪽의 이상한 후보를 자연스럽게 배제하는 장치다.

하네스 관점에서 이 손잡이들은 용도에 맞게 조여야 하는 나사다. 구조화된 데이터 추출이나 분류처럼 정확성이 중요한 작업에서는 온도를 낮게 두어 출력을 안정시킨다. 카피 초안이나 아이디어 발산처럼 다양성이 필요한 작업에서는 온도를 조금 올린다. 아래 표는 감각을 잡는 데 참고할 만한 대략적 대비다.

파라미터 하는 일 낮/좁게 높/넓게
temperature 분포의 뾰족함 조절 일관·보수적 다양·엉뚱해지기 쉬움
top-k 상위 k개 후보만 남김 안정적 후보 폭 넓음
top-p 누적확률 p까지 후보 유지 꼬리 제거 강함 다양성 허용
추천 용도 추출·분류·JSON 창작·브레인스토밍

한 가지 주의할 점. 온도를 0에 가깝게 둔다고 출력이 완전히 똑같아지지는 않는다. 실제 서비스에서는 다른 요인들로 미세한 변동이 남을 수 있다. 그래서 '온도를 낮췄으니 검증은 생략해도 된다'는 판단은 위험하다. 디코딩 조정은 어긋날 확률을 줄이는 완화책이지, 검증을 대체하는 보장책이 아니다.

실패를 전제로 설계하기 — 재시도와 폴백

숙련된 하네스는 성공을 가정하지 않는다. 실패를 기본값으로 놓고 설계한다. 모델이 형식을 어긴 JSON을 뱉거나, API가 시간 초과를 내거나, 검증기가 결과를 거부하는 일은 드물지 않게 일어난다. 이때 시스템 전체가 멈추면 제품이 될 수 없다.

실패 시 폴백·재시도 전략

재시도(retry)는 가장 기본적인 대응이다. 형식 검증에 실패하면, 오류 내용을 다시 프롬프트에 담아 '이 부분이 스키마에 맞지 않았으니 고쳐 달라'고 되돌려 보낸다. 이 자기 교정 루프는 외부 검증 신호를 되먹인다는 점에서 Reflexion, 그리고 자기 출력을 스스로 다듬는 Self-Refine 계열 연구가 보여 준 아이디어와 통한다. 모델이 자기 출력을 피드백받아 다듬는 방식이다. 다만 무한히 반복하면 비용과 지연이 쌓이므로, 시도 횟수에 상한을 두어야 한다.

폴백(fallback)은 재시도로도 안 될 때의 대비책이다. 더 강한 모델로 올려 다시 시도하거나, 규칙 기반의 단순한 처리로 우회하거나, 마지막에는 사람에게 넘긴다. 핵심은 '실패했을 때 무엇을 할지'가 코드에 미리 정해져 있어야 한다는 점이다. 폴백 경로가 없는 파이프라인은, 예외 상황이 오는 순간 사용자에게 아무 대답도 못 하고 무너진다.

이 사고방식은 에이전트에서 특히 중요하다. 에이전트는 생각·행동·관찰(ReAct) 루프를 스스로 여러 번 돈다. 한 단계의 작은 오차가 다음 단계의 전제가 되어 어긋남이 누적된다. 단계마다 검증과 재시도, 폴백을 심어 두지 않으면, 길어진 사슬의 끝에서 결과가 크게 빗나간다.

되돌릴 수 없는 행동에는 사람을 세운다

하네스의 마지막 기둥은 안전장치다. 모든 행동을 자동으로 흘려보내서는 안 된다. 파일을 지우거나, 결제를 실행하거나, 외부로 메일을 보내는 행동은 되돌릴 수 없다. 이런 행동 앞에는 사람이 확인하고 승인하는 관문을 세운다. 이것을 사람 개입(human-in-the-loop)이라고 부른다.

위험한 행동은 사람에게 넘기는 안전장치

설계의 요령은 행동을 위험도에 따라 등급을 나누는 것이다. 조회나 요약처럼 되돌릴 수 있고 영향이 작은 행동은 자동으로 통과시킨다. 데이터 변경이나 외부 발송처럼 되돌리기 어려운 행동은 승인 관문을 거치게 한다. 이렇게 나누면 자동화의 효율과 안전을 함께 챙길 수 있다. 모든 것을 사람이 확인하면 자동화의 의미가 없고, 아무것도 확인하지 않으면 사고가 난다.

에이전트가 강력한 실행기이되 아직 감독이 필요한 실행기인 이유가 여기에 있다. 오히려 여러 단계를 자동으로 처리할수록 사람의 감독은 더 중요해진다. 하네스 엔지니어링은 그 감독을 '매번 사람이 지켜보는' 방식이 아니라 '중요한 길목에만 사람을 세우는' 방식으로 구조화한다. Anthropic이 정리한 실효적 에이전트 설계 지침도 같은 결을 강조한다. 자율성을 높이되 통제 가능성을 함께 설계하라는 것이다.

평가와 관측 — 눈에 보이지 않는 시스템을 재는 법

하네스가 잘 작동하는지 어떻게 아는가. 여기서 평가(evaluation)와 관측(observability)이 등장한다. 모델 출력은 정답이 하나로 딱 떨어지지 않는 경우가 많아, 전통적인 단위 테스트만으로는 품질을 재기 어렵다. 그래서 대표적인 입력들을 모아 놓은 평가 세트를 만들고, 시스템을 바꿀 때마다 그 세트에 돌려 성능이 나아졌는지 나빠졌는지 비교한다.

평가 항목은 형식 준수율, 사실성, 거부해야 할 요청을 잘 거부하는지, 지연과 비용 같은 운영 지표까지 폭넓게 잡는다. HELM처럼 언어모델을 여러 축에서 종합적으로 평가하려는 연구 흐름이 이런 다면 평가의 필요성을 잘 보여 준다. 정확도 하나만 보면 놓치는 것이 너무 많다.

관측은 운영 중인 시스템의 내부를 들여다보는 장치다. 어떤 프롬프트가 들어갔고, 모델이 무엇을 반환했으며, 어느 단계에서 재시도가 났고, 어떤 도구가 호출됐는지를 기록으로 남긴다. 이 기록(트레이스)이 있어야 문제가 생겼을 때 원인을 추적하고, 실패 사례를 모아 평가 세트에 다시 반영할 수 있다. 하네스 엔지니어링은 이렇게 만들고-재고-고치는 순환으로 굴러간다.

흔한 오해와 넘기 힘든 벽

가장 흔한 오해는 '프롬프트를 잘 쓰면 하네스가 필요 없다'는 생각이다. 프롬프트 엔지니어링은 모델에게 무엇을 어떻게 지시할지 다루는 기술이고, 하네스 엔지니어링은 그 지시의 결과를 받아 시스템으로 엮는 기술이다. 둘은 대체 관계가 아니라 층이 다른 보완 관계다. 아무리 정교한 프롬프트라도 확률적 출력이라는 본질은 바뀌지 않으니, 그 결과를 검증하고 감싸는 층은 여전히 필요하다.

또 하나. '검증을 촘촘히 하면 모델의 오류를 완전히 없앨 수 있다'는 기대도 지나치다. 하네스는 어긋날 확률을 줄이고, 어긋났을 때의 피해를 줄이는 장치다. 오류를 0으로 만드는 마법이 아니다. 특히 형식 검증은 겉모습만 본다. JSON 형식은 완벽한데 그 안의 날짜가 사실과 다를 수 있다. 형식과 내용은 다른 문제라는 점을 늘 붙들어야 한다.

복잡성의 벽도 실재한다. 재시도, 폴백, 승인 관문, 평가 파이프라인을 다 붙이면 시스템이 무거워지고 비용과 지연이 늘어난다. 그래서 하네스는 처음부터 다 짓는 것이 아니라, 위험이 큰 길목부터 최소한으로 시작해 필요에 따라 늘려 가는 편이 현명하다. Anthropic의 지침이 '가장 단순한 구조로 시작하라'고 조언하는 이유도 여기에 있다. 과잉 설계는 그 자체로 새로운 실패 지점을 만든다.

프롬프트를 코드처럼 다루기 — 하네스 안의 버전 관리

하네스 엔지니어링에서 가장 자주 방치되는 자산이 프롬프트다. 많은 팀이 프롬프트를 소스 코드가 아니라 설정값 정도로 취급한다. 문자열 하나를 슬쩍 고치고 배포한 뒤, 며칠이 지나 품질이 떨어졌다는 제보를 받고서야 무엇이 바뀌었는지 뒤늦게 추적한다. 하지만 대규모 언어모델(LLM) 시스템에서 프롬프트는 사실상 프로그램의 논리 그 자체다. 한 줄의 지시문이 바뀌면 출력 분포 전체가 흔들린다. 그러므로 프롬프트는 코드와 동일한 규율로 관리해야 한다. 형상 관리 저장소에 넣고, 변경 이력을 남기고, 리뷰를 거쳐 병합한다.

실무에서는 프롬프트를 소스 파일과 뒤섞지 않고 별도의 템플릿 파일로 분리하는 방식이 잘 통한다. 예를 들어 지시문 본문과 변수 자리를 나눠 system.md, user.jinja 같은 파일로 두고, 코드에서는 렌더링만 담당하게 한다. 이렇게 나눠 두면 프롬프트 변경분만 따로 diff를 볼 수 있어 리뷰가 깔끔해진다. 더 중요한 점은 프롬프트마다 버전 번호를 붙여 로그에 함께 기록하는 것이다. 어떤 응답이 프롬프트 v3.2에서 나왔는지, v4.0에서 나왔는지를 사후에 구분할 수 있어야 회귀를 잡아낼 수 있다.

프롬프트 변경은 코드 변경보다 위험한 구석이 있다. 컴파일러가 잡아 주는 오류가 없기 때문이다. 세미콜론을 빠뜨리면 빌드가 깨지지만, 지시문에서 '반드시 JSON으로만 답하라'는 문장을 실수로 지워도 시스템은 아무 불평 없이 돌아간다. 다만 출력 품질만 조용히 무너질 뿐이다. 그래서 프롬프트 변경에는 반드시 자동 평가를 붙인다. 대표 입력 수십 건을 고정해 두고, 프롬프트를 고칠 때마다 그 묶음을 다시 돌려 결과 차이를 본다. 이 회귀 검사가 없으면 프롬프트 개선은 도박이 된다.

한 가지 더 짚을 함정은 프롬프트와 모델 버전의 결합이다. 같은 프롬프트라도 모델 공급자가 백엔드를 조용히 업데이트하면 출력이 달라진다. 특정 프롬프트는 특정 모델 스냅샷에 맞춰 튜닝된 상태라고 보는 편이 안전하다. 그래서 모델 식별자를 프롬프트 버전과 함께 고정하고, 공급자가 모델을 바꾼다는 공지가 뜨면 그것을 하나의 배포 이벤트로 다룬다. 모델과 프롬프트를 짝으로 묶어 관리하는 습관이 재현성을 지키는 핵심이다.

컨텍스트 윈도우라는 예산 — 토큰 회계의 기술

하네스를 설계하다 보면 결국 부딪히는 물리적 한계가 컨텍스트 윈도우다. 모델이 한 번에 받아들이는 토큰 수에는 상한이 있고, 이 상한은 그냥 용량 제한이 아니라 비용과 지연시간, 품질을 동시에 결정하는 예산이다. 검색 증강 생성(RAG) 파이프라인에서 검색된 문서를 무작정 다 밀어 넣으면 안 되는 이유가 여기 있다. 토큰이 늘수록 요금이 선형으로 오르고, 응답 지연도 길어지며, 무엇보다 정작 중요한 지시가 방대한 배경 텍스트에 묻혀 버린다.

토큰 회계를 제대로 하려면 먼저 입력을 구성 요소로 나눠 각 몫을 정해야 한다. 시스템 지시에 얼마, 대화 이력에 얼마, 검색 문맥에 얼마, 그리고 모델이 생성할 응답 여유분에 얼마를 배정할지 미리 정한다. 예컨대 12만 8천 토큰짜리 윈도우를 쓴다고 해도, 실제로는 시스템 지시 2천, 이력 1만, 검색 문맥 3만, 출력 여유 4천처럼 나눠 쓰고 나머지는 안전 완충으로 남긴다. 이 배분을 코드에 상수로 박아 두지 말고, 실제 토큰 수를 측정해 초과하면 오래된 이력부터 잘라내는 절삭 정책을 붙여야 운영 중 폭주를 막는다.

여기서 반드시 알아 둘 현상이 '중간 소실(lost in the middle)'이다. 긴 문맥을 넣으면 모델은 맨 앞과 맨 뒤에 놓인 정보는 잘 활용하지만 가운데에 묻힌 정보는 놓치는 경향을 보인다. 그래서 검색 문맥을 배치할 때 관련도가 가장 높은 문서를 앞이나 뒤 끝자락에 두는 재배치가 실효를 낸다. 단순히 유사도 순으로 나열하기보다, 상위 문서를 양 끝에 배치하고 덜 중요한 것을 가운데로 미는 편이 답변 정확도에 도움이 된다. 문맥 길이를 늘리는 것과 문맥을 잘 고르는 것은 전혀 다른 문제다.

긴 문맥을 다룰 때 나오는 또 다른 절약 기법이 프롬프트 캐싱이다. 여러 요청에서 반복되는 앞부분, 이를테면 고정된 시스템 지시나 대용량 참조 문서를 공급자 측에서 캐시해 두면 재계산을 건너뛴다. 여러 상용 API가 이 기능을 제공하며, 캐시된 토큰은 일반 입력 토큰보다 크게 저렴하게 과금된다. 하네스 설계 단계에서 프롬프트의 고정부와 가변부를 명확히 분리해 두면 이런 캐싱 이점을 그대로 챙길 수 있다. 토큰 회계는 인색해서가 아니라, 예산을 알아야 품질을 설계할 수 있기 때문에 하는 일이다.

도구 호출과 에이전트 루프 — 하네스가 손발을 갖출 때

구조화 출력이 모델의 말을 데이터로 바꾸는 장치라면, 도구 호출(tool calling, 함수 호출)은 모델에게 손발을 달아 주는 장치다. 모델 자체는 계산기를 두드리거나 데이터베이스를 조회하지 못한다. 그저 '지금 이 함수를 이런 인자로 부르고 싶다'는 의사를 구조화된 형태로 표현할 뿐이다. 실제 실행은 하네스가 맡는다. 모델이 함수 호출 JSON을 뱉으면, 하네스가 그것을 파싱해 진짜 함수를 실행하고, 결과를 다시 모델에게 돌려준다. 이 왕복이 에이전트 루프의 기본 골격이다.

루프의 흐름을 구체적으로 그려 보면 이렇다. 사용자 질문이 들어온다. 모델이 '날씨 조회 도구를 서울 인자로 부르겠다'고 응답한다. 하네스가 날씨 API를 호출해 결과를 얻는다. 그 결과를 대화 이력에 도구 응답으로 추가하고 모델을 다시 부른다. 모델은 이제 실제 데이터를 손에 쥐고 최종 답변을 생성한다. 도구가 여러 개 필요하면 이 순환이 여러 바퀴 돈다. 문제는 이 순환에 자연스러운 종료 조건이 없다는 점이다. 모델이 같은 도구를 무한히 부르며 맴도는 상황을 반드시 막아야 한다.

그래서 에이전트 루프에는 반드시 상한을 건다. 최대 반복 횟수를 정하고, 같은 도구를 같은 인자로 연속 호출하면 차단하며, 누적 토큰이나 누적 비용에도 천장을 둔다. 실무에서 흔히 겪는 사고가 바로 이 안전장치 부재로 인한 비용 폭주다. 테스트 중에는 몇 바퀴로 끝나던 루프가, 예상치 못한 입력에서 수십 바퀴를 돌며 요금을 태운다. 반복마다 지금 몇 번째인지, 어떤 도구를 왜 불렀는지 로그로 남겨 두면 나중에 루프가 어디서 꼬였는지 추적할 수 있다.

도구 호출의 신뢰성은 도구 설명을 얼마나 정확히 적었느냐에 크게 좌우된다. 각 도구의 이름, 목적, 인자의 의미를 모델이 읽을 스키마로 명료하게 기술해야 한다. 인자 설명이 모호하면 모델은 엉뚱한 값을 채워 넣는다. 여기서 자주 나오는 오해가 '도구를 많이 주면 똑똑해진다'는 생각이다. 오히려 반대인 경우가 많다. 선택지가 늘수록 모델은 도구를 잘못 고르고, 필요 없는 호출을 남발한다. 한 번의 판단에 노출하는 도구는 정말 필요한 것으로 추려 두는 편이 정확도와 비용 양쪽에 이롭다.

지연시간과 비용의 물리학 — 스트리밍, 배치, 모델 라우팅

사용자가 체감하는 품질에서 지연시간은 정확도만큼 중요하다. LLM 응답에는 두 개의 시계가 있다. 첫 토큰이 나오기까지 걸리는 시간(time to first token)과, 그 뒤로 토큰이 흘러나오는 속도(tokens per second)다. 챗봇처럼 사람이 실시간으로 읽는 인터페이스라면 첫 토큰까지의 지연이 특히 결정적이다. 3초 동안 빈 화면을 보여 주면 사용자는 시스템이 멈춘 줄 안다. 그래서 스트리밍이 사실상 필수다. 완성된 답을 기다렸다 한꺼번에 보여 주는 대신, 생성되는 대로 토큰을 흘려보내면 체감 지연이 극적으로 줄어든다.

다만 스트리밍은 구조화 출력이나 도구 호출과 부딪힌다. JSON을 스트리밍하면 중간 단계에서는 문법이 깨진 불완전한 조각이 나온다. 이 조각을 그대로 파싱하려 들면 오류가 난다. 그래서 사람이 읽을 자유 텍스트는 스트리밍하되, 기계가 소비할 구조화 데이터는 완결된 뒤에 검증하는 식으로 경로를 나눠 설계한다. 두 요구를 한 응답에 억지로 섞으면 어느 쪽도 만족스럽지 못하다.

비용 최적화의 큰 축은 모델 라우팅이다. 모든 요청을 가장 크고 비싼 모델에 보낼 이유가 없다. 간단한 분류나 형식 변환은 작고 빠른 모델로 충분하고, 복잡한 추론이 필요할 때만 큰 모델을 부른다. 실무에서는 요청의 난이도를 먼저 가늠하는 가벼운 라우터를 앞단에 두고, 판단에 따라 서로 다른 모델로 흘려보낸다. 이 계층화만 잘해도 전체 비용이 크게 줄어든다. 실시간이 필요 없는 대량 작업이라면 배치 API를 쓰는 선택지도 있다. 여러 공급자가 배치 처리에 상당한 할인율을 적용한다.

아래 표는 하네스에서 자주 조율하는 성능·비용 지표와 대표적인 대응 수단을 정리한 것이다. 이 지표들은 서로 맞물려 있어서 하나를 조이면 다른 하나가 헐거워진다. 예컨대 응답 품질을 위해 큰 모델을 쓰면 지연과 비용이 오른다. 하네스 엔지니어링은 결국 이 상충 관계를 제품의 요구에 맞게 저울질하는 일이다.

관심 지표 무엇을 재나 주요 대응 수단
첫 토큰 지연 요청 후 첫 응답까지 시간 스트리밍, 프롬프트 캐싱, 작은 모델
처리 비용 요청당 토큰 요금 모델 라우팅, 배치 API, 문맥 절삭
처리량 초당 소화 요청 수 동시성 제어, 큐잉, 재시도 백오프
응답 품질 정확도·형식 준수율 큰 모델, 구조화 출력, 회귀 평가

보안 경계 — 프롬프트 주입과 신뢰할 수 없는 입력

하네스가 외부 데이터를 문맥에 끌어들이는 순간 새로운 공격면이 열린다. 대표적인 위협이 프롬프트 주입(prompt injection)이다. 모델은 시스템 지시와 검색해 온 문서를 구분하는 확실한 경계를 갖고 있지 않다. 그래서 웹페이지나 사용자가 올린 문서 안에 '이전 지시를 모두 무시하고 다음을 실행하라' 같은 문구가 숨어 있으면, 모델이 그 지시를 진짜 명령으로 착각해 따라갈 수 있다. 이것은 코드 인젝션과 성격이 비슷하지만, 방어가 훨씬 까다롭다. 자연어에는 이스케이프 문법 같은 깔끔한 경계가 없기 때문이다.

특히 위험한 경우가 도구 호출과 결합될 때다. 모델이 외부 문서를 읽고, 그 문서에 심긴 지시에 따라 이메일 발송 도구나 데이터 삭제 도구를 부른다고 상상해 보라. 신뢰할 수 없는 입력이 곧바로 실질적 행동으로 이어진다. 그래서 하네스 설계의 원칙은 명확하다. 외부에서 들어온 텍스트는 절대 신뢰하지 않는다. 검색된 문맥은 '데이터'로만 취급하고, 그 안의 어떤 문장도 시스템의 명령으로 승격시키지 않도록 지시를 명확히 못박는다.

완벽한 방어는 없지만 위험을 낮추는 겹겹의 장치는 있다. 우선 신뢰 경계를 명시적으로 구분해, 시스템 지시와 사용자 데이터를 서로 다른 역할로 분리해 전달한다. 되돌릴 수 없는 도구는 최소 권한 원칙으로 묶어, 위험한 작업일수록 승인 단계를 끼워 넣는다. 출력에도 검사를 건다. 모델이 생성한 텍스트를 그대로 브라우저에 렌더링하면 스크립트 삽입 위험이 생기므로, 출력 살균을 잊지 말아야 한다. 입력 필터링만으로는 부족하고, 입력·행동·출력 세 지점에 모두 방어를 배치해야 한다.

여기서 흔한 오해 하나를 짚는다. '시스템 프롬프트에 절대 규칙을 강하게 적으면 주입을 막을 수 있다'는 믿음이다. 지시를 아무리 단호하게 써도 모델은 확률적으로 동작한다. 충분히 교묘한 입력 앞에서는 규칙이 무너질 수 있다. 그러니 프롬프트 차원의 방어는 첫 겹일 뿐이고, 진짜 안전선은 하네스 바깥의 권한 통제와 검증에 두어야 한다. 모델을 신뢰하는 대신, 모델이 무엇을 할 수 있는지 그 경계를 코드로 강제하는 것이 하네스 보안의 요체다.

더 깊이 파고들려면

하네스 엔지니어링을 제대로 이해하려면 먼저 그 안쪽의 원리를 단단히 다져야 한다. 다음 토큰을 확률로 예측하는 언어모델의 작동, 트랜스포머와 자기어텐션(self-attention), 토큰화와 임베딩, 그리고 temperature·top-k·top-p 같은 디코딩 파라미터의 감각이 바탕이 된다. 이 기초가 없으면 하네스의 각 장치가 왜 필요한지 설득력 있게 다가오지 않는다.

그다음은 하네스를 이루는 개별 기법들이다. 자기 교정 루프의 원리는 Self-Refine 연구에서, 생각과 행동을 엮는 에이전트 루프는 ReAct 논문에서 출발점을 찾을 수 있다. 추론 단계를 드러내는 사고 사슬(Chain-of-Thought) 프롬프팅, 모델이 스스로 규범을 따르게 하는 Constitutional AI, 언어모델을 다면적으로 재는 HELM 평가 프레임워크도 함께 보면 하네스의 각 기둥이 어떤 연구 배경에서 나왔는지 이어진다.

실무 감각을 잡는 데는 Anthropic이 공개한 실효적 에이전트 구축 지침이 좋은 출발점이다. 언제 단순한 워크플로로 충분하고 언제 자율 에이전트가 필요한지, 어디에 사람을 세워야 하는지를 현장의 언어로 정리해 두었다. 결국 하네스 엔지니어링의 핵심 태도는 하나로 모인다. 모델은 확률로 그럴듯한 것을 만드는 강력한 심장이고, 그 심장을 안전한 제품으로 잇는 골격을 짜는 것이 사람의 몫이다. 원리를 아는 사람이 그 골격을 잘 짓는다.

출처

  1. ReAct: Synergizing Reasoning and Acting
  2. Chain-of-Thought Prompting
  3. Self-Refine: Iterative Refinement with Self-Feedback
  4. Constitutional AI: Harmlessness from AI Feedback
  5. Holistic Evaluation of Language Models (HELM)
  6. Building effective agents
뉴스레터로 매주 받아보기 ← 기술블로그 목록