양파와 필터
요약
코딩 에이전트의 행동 범위를 제어하기 위한 두 가지 설계 관점인 '양파(Onion)' 모델과 '필터(Filter)' 모델을 비교 분석합니다. 양파 모델은 에이전트에게 역량을 추가하는 방식이며, 필터 모델은 부적절한 행동을 제거하는 제약 시스템 방식입니다.
핵심 포인트
- 양파 모델: 에이전트에게 도구와 컨텍스트를 추가하여 역량을 확장하는 가산적 접근
- 필터 모델: 에이전트의 행동 공간에서 부적절한 요소를 제거하는 제약 중심 접근
- 하네스 엔지니어링: 에이전트의 환각과 오류를 방지하기 위한 다층적 제어 구조 구축
코딩 에이전트(coding agent)를 위한 첫 번째 하네스(harness)를 구축하기 시작했을 때, 나는 양파를 떠올리지 않았습니다. 나는 제약 시스템(constraint system)을 떠올렸습니다.
LLM(Large Language Model)은 그 자체로 거의 무엇이든 할 수 있습니다. 코드를 작성할 수도 있고, 존재하지 않는 API를 환각(hallucinate)할 수도 있으며, 잘못된 파일을 수정하거나, 있어서는 안 될 디렉토리에서 셸 명령(shell command)을 실행할 수도 있고, 테스트 실패가 수용 가능하다고 판단하고 그냥 넘어갈 수도 있습니다. 주어진 턴(turn)에서 모델이 할 수 있는 일의 범위는 엄청납니다. 내가 생각한 하네스의 역할은 그 범위를 줄이는 것이었습니다.
이것은 내가 수학에서 배운 프레임워크(framing)입니다. 집합에서 시작하여 조건을 추가하면, 남은 것이 실제로 원하는 것이 될 때까지 집합은 점점 작아집니다. 각 필터(filter)는 술어(predicate)입니다. 하네스는 필터들의 시퀀스(sequence)입니다.
Birgitta Böeckeler는 _하네스 엔지니어링(harness engineering)_이라는 용어를 대중화하고 실무적인 어휘를 제공하기 위해 내가 본 것 중 가장 많은 노력을 기울여 왔습니다. 그녀의 멘탈 모델(mental model)은 양파입니다. 중심에 에이전트가 있고, 그 주변을 동심원 형태의 하네스 층이 둘러싸고 있으며, 각 층은 모델의 추론 루프(reasoning loop)에서 더 가깝거나 더 멉니다. 도구(tools), 컨텍스트(context), 훅(hooks), 샌드박스(sandboxes), 관측성(observability) 등이 그것입니다. 모델은 중간에 앉아 층들을 통해 뻗어 나가며, 층들은 모델과 세상 사이에 존재합니다.
나는 양파 모델을 좋아합니다. 가리킬 곳이 있다는 점에서 좋은 교육적 형태이기 때문입니다. "그것은 저 층이 아니라 이 층에 속해야 해." "이 훅(hook)은 여기서 실행돼."라고 말할 수 있죠. 하지만 무엇을 추가할지 결정할 때 내가 찾는 모델은 양파가 아닙니다.
동일한 기계에 대한 두 가지 그림
양파와 필터 시스템은 동일한 산물(artifact)을 반대 방향에서 설명합니다.
양파는 모델로부터 바깥쪽을 바라봅니다. 각 층은 에이전트가 자신의 일을 수행할 수 있도록 에이전트 주변에 감싸는 무언가입니다. 도구 인터페이스(tool surface), 시스템 프롬프트(system prompt), 샌드박스(sandbox), 리뷰 단계(review step) 등이 해당됩니다. 이 어휘는 가산적(additive)입니다. 에이전트에게 도구를 줍니다(give). 컨텍스트를 제공합니다(provide). 에이전트에게 갖춰줍니다(equip).
필터(filter)는 출력을 향해 안쪽을 들여다봅니다. 각 필터는 에이전트가 할 수 있지만 해서는 안 되는 행동에 대한 술어(predicate)입니다. 에이전트 행동의 전체 공간을 가져온 뒤, 술어를 통과하지 못하는 모든 것을 깎아냅니다. 샌드박스(sandbox)는 필터입니다. 이 디렉토리 내부의 파일 시스템 작업만이 살아남습니다. 타입 체크(type check)는 필터입니다. 컴파일 가능한 차이점(diffs)만이 살아남습니다. 필수 검토(required review)는 필터입니다. 검토자가 동의하는 변경 사항만이 살아남습니다.
동일한 기계이지만, 프레이밍(framing)이 반대입니다. 양파(onion)는 무엇을 '추가하느냐'에 관한 것입니다. 필터는 무엇을 '제거하느냐'에 관한 것입니다.
프레이밍이 중요한 이유
두 프레이밍은 설명할 수 있는 대상은 동일하지만, 당신을 서로 다른 결정으로 이끕니다.
양파 층(onion layers)으로 생각할 때, 저는 _역량(capabilities)_을 생각합니다. "에이전트가 필요한 도구를 가지고 있는가? 그것을 잘 사용할 컨텍스트(context)를 가지고 있는가?" 본능적으로 무언가를 추가하게 됩니다. 또 다른 도구, 또 다른 훅(hook), 프롬프트에 로드되는 또 다른 컨텍스트 조각 말입니다.
필터로 생각할 때, 저는 _제약(constraints)_을 생각합니다. "에이전트가 현재 허용되어 있지만 해서는 안 되는 행동은 무엇인가? 무엇이 빠져나가는가?" 본능적으로 무언가를 제거하게 됩니다. 더 좁은 샌드박스, 더 엄격한 프리 커밋(pre-commit), 더 작은 허용 목록(allowlist), 잘못된 곳에서 계속 실행되는 규칙에 대한 더 좁은 파일 범위 설정 등이 있습니다.
두 본능 모두 서로 다른 순간에 옳습니다. 역량이 부족한 에이전트는 더 많은 도구가 필요합니다. 역량이 과한 에이전트는 더 많은 필터가 필요합니다. 제가 본 대부분의 하네스(harness)는 첫 번째가 아니라 두 번째 방향에서 실패합니다. 즉, 에이전트는 충분한 역량을 가졌으나 제약이 부족하며, 그 증상은 에이전트가 확신을 가지고 잘못된 일을 수행한다는 것입니다.
수학적 습관
제가 필터 프레이밍을 기본값으로 사용하는 이유는, 하네스를 작성하기 전부터 이런 방식으로 생각하는 법을 배웠기 때문입니다.
수학 문제에서, 당신은 정답 집합의 요소들을 일일이 나열하지 않습니다. 집합을 먼저 씁니다. 그다음 조건을 추가합니다. 정수, 그다음 "양수", 그다음 "100 미만", 그다음 "소수". 정답은 모든 조건을 통과하여 살아남는 모든 것입니다.
하네스 (Harness)도 같은 모양입니다. 집합은 "이 에이전트가 출력할 수 있는 것들"입니다. 조건들은 당신이 쌓아 올린 필터 (Filters)입니다. 정답은, 어떤 주어진 차례에서든 그 모든 조건을 통과하여 살아남는 무엇이든 됩니다. 당신은 좋은 행동을 열거하는 것이 아니라, 나쁜 행동을 제약 (Constrain)하여 제거하는 것입니다.
이것이 바로 필터로 구성된 하네스가 레이어 (Layers)로 구성된 하네스보다 추론하기 더 쉬운 이유이기도 합니다. 필터는 논리곱 (Conjunction)을 통해 결합됩니다. 즉, 각 필터는 주어진 출력에 대해 독립적으로 참(True) 또는 거짓(False)입니다. 만약 나쁜 것이 통과했다면, 어떤 필터가 실패했는지 또는 어떤 필터가 누락되었는지 묻게 됩니다. 만약 좋은 것이 차단되었다면, 어떤 필터가 너무 엄격한지 묻게 됩니다. 디버깅 (Debugging) 작업이 국소적 (Local)입니다.
반면, 레이어는 위치를 가집니다. 당신은 새로운 행동 조각이 어떤 레이어에 속하는지에 대해 논쟁해야 합니다. 검증 (Validation)이 도구 (Tool)의 문제인지, 샌드박스 (Sandbox)의 문제인지, 아니면 리뷰 (Review)의 문제인지 말입니다. 양파 (Onion) 모델은 지리적 위치를 제공하며, 지리적 위치는 영역 다툼 (Turf wars)을 불러일으킵니다.
양파 모델이 여전히 승리하는 지점
양파 모델은 하네스를 구축해 본 적이 없는 사람에게 하네스를 설명할 때 더 나은 그림입니다.
사람들은 레이어를 이해합니다. 사람들은 "에이전트가 중간에 있고 세상은 외부에 있다"는 것을 이해합니다. 양파 모델은 에이전트가 세상을 직접 보지 않는다는 것, 그리고 에이전트가 하는 모든 일이 당신이 제어하는 무언가를 통과한다는 것을 명확하게 보여줍니다. 그 직관은 이 개념을 처음 접하는 사람에게 매우 중요한 지지대 역할을 하며, 필터 모델은 그 직관을 전달하기에는 너무 추상적입니다.
또한 양파 모델은 아키텍처 (Architecture)를 그릴 때 더 유용합니다. 이 훅 (Hook)이 어디서 실행되는지, 무엇을 보는지, 누가 그 출력을 읽는지 말입니다. 그곳에서는 위치가 중요합니다. 양파 모델은 이를 화이트보드에 그려낼 수 있는 방법을 제공합니다.
하지만 일단 아키텍처가 배치되고 매일 하네스를 튜닝 (Tuning)하게 되면, 질문은 거의 항상 필터에 관한 질문이 됩니다. 에이전트가 내가 원하지 않는 무엇을 하고 있는가? 추가되거나 강화된 어떤 제약 조건이 그것을 막는가? 그림이 가산적 (Additive)일지라도, 작업 자체는 감산적 (Subtractive)입니다.
규칙은 추가하는 것이 아니라 제약하는 것이다
저에게 있어 깨달음의 순간은, 제가 내리는 거의 모든 하네스 (Harness) 결정이 설령 그렇게 보이지 않더라도 결국 제약 (Constraints)에 관한 결정이라는 점을 깨달은 것이었습니다.
새로운 도구는 추가적 (Additive)인 것처럼 보이지만, 흥미로운 설계 질문은 그 도구가 할 수 없는 것이 무엇인지, 어떤 인자 (Arguments)를 거부하는지, 어떤 상태 (State)를 건드리지 않을 것인지에 관한 것입니다. 새로운 컨텍스트 (Context) 조각은 추가적인 것처럼 보이지만, 질문의 핵심은 그것이 존재함으로써 어떤 동작을 걸러내는지 (Filters out)입니다. 리뷰 파이프라인 (Review pipeline)에 추가된 새로운 에이전트 (Agent)는 저에게 도달하는 디프 (Diffs)에 대한 필터 (Filter)입니다.
양파 (Onion)는 그 조각이 어디에 들어가는지를 알려줍니다. 필터 (Filter)는 그 조각이 무엇을 위한 것인지를 알려줍니다. 저는 두 가지 관점 모두를 활용하고 싶지만, 규칙을 작성하거나 훅 (Hook)을 추가할 때 제 머릿속에 담고 있는 것은 필터입니다.
하네스 (Harness)는 에이전트 (Agent)에게 권한을 부여하지 않습니다. 에이전트는 이미 권한을 가지고 있습니다. 하네스는 에이전트가 그 권한을 가지고 무엇을 할 수 있도록 허용할지를 결정합니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기