
30개의 파일에서 2개로: 에이전트 시스템을 90% 슬림화한 방법
요약
30개 이상의 파일로 구성되어 복잡도가 높아진 에이전트 시스템을 2개의 파일과 2개의 스크립트로 슬림화한 리팩터링 과정을 다룹니다. 코드 수준의 강제 대신 프롬프트 주입 규칙을 활용하여 시스템의 유지보수성과 경량성을 확보하는 방법을 제시합니다.
핵심 포인트
- 파일 의존성 증가로 인한 회귀 테스트 부담 및 시스템 비대화 문제 해결
- 코드 수준의 강제보다 프롬프트 주입 규칙이 에이전트 제어에 더 효과적임
- SOUL.md와 .hermes.md를 활용한 엔진 구조의 단순화
- 세션 전환 및 모델 변경에도 견고한 경량 아키텍처 구축
고통 (The Pain): 서로를 참조하는 30개 이상의 파일들. 스크립트 하나를 바꾸면 5개의 게이트(gate)가 망가집니다. 그리고 에이전트가 엔진을 로드하는 것을 기억해야만 전체 시스템이 작동합니다. 한 번이라도 잊어버리면 모든 게이트는 종이 호랑이가 되어버립니다.
배울 내용: 왜 프롬프트 주입(prompt-injected) 규칙이 코드 수준의 강제(code-level enforcement)보다 나은지, 어떻게 SOUL.md + .hermes.md가 30개의 파일로 구성된 엔진을 대체하는지, 그리고 세션 전환과 모델 변경에도 견딜 수 있는 '2개 파일 + 2개 스크립트' 시스템에 대해 알아봅니다.
"만약 내가 엔진을 실행하는 것을 기억해야 한다면, 그 엔진은 존재해서는 안 된다."
이전 기사에서 우리는 콘텐츠 생성부터 이상 징후 모니터링(anomaly monitoring)까지 전체 체인을 다루는 4가지 AI 파이프라인을 구축했습니다. 아키텍처는 작동하고 있었지만, 코드베이스는 계속 커지고 있었습니다.
지속성 상태(persistent state), 품질 시스템(quality system), 그리고 운영 모니터링(ops monitoring)을 추가한 후, 나의 에이전트 엔지니어링 스택은 자연스럽게 비대해졌습니다. 코드가 잘못 작성된 것이 아니라, 기능이 많아질수록 파일도 많아지는 것이었습니다. 3개월이 지나자 스택은 엔진 스크립트, 레지스트리(registries), 검증 스크립트(validation scripts), 설정 매핑 파일(config mapping files) 등 30개 이상의 파일로 부풀어 올랐습니다. 이 파일들은 서로를 참조하고 의존했습니다. 하나를 변경하면 다섯 개를 회귀 테스트(regression-test)해야 했습니다.
그것은 더 이상 "경량(lightweight)"하지 않았습니다. 더 이상 "물리적(physical)"이지 않았습니다.
그래서 지난달 저는 결정을 내렸습니다. 전면 재작성(full rewrite)하기로 말이죠. 5개 게이트 시스템의 30개 이상의 파일 중, 28개를 삭제하고 2개의 파일과 2개의 스크립트만 남겼습니다.
이 글은 그 슬림화 과정에 대한 완전한 사후 분석(postmortem)입니다. 단순한 삭제가 아닌, _리팩터링(refactoring)_입니다.

시스템 슬림화: 30개 파일 → 2개 파일 + 2개 스크립트
1. 문제점: 30개 파일의 강제 시스템은 그 자체로 짐이 된다
지속성 상태, 품질 시스템, 운영 모니터링을 구축한 후, 저는 자연스럽게 더 깊은 질문을 던지기 시작했습니다. 어떻게 하면 에이전트가 매번 규칙을 따르도록 보장할 수 있을까?
그 질문에 대한 저의 첫 번째 답변은 진입 게이트(entry gate), 세션 격리(session isolation), 그리고 사전 점검(pre-checks)이라는 "세 가지 잠금(three locks)"이었습니다.
| 잠금 (Lock) | 가로채기 지점 (Interception point) | 역할 (What it does) |
|---|---|---|
| EntryGate | entry | 분해되지 않은 복잡한 작업은 거부됨 |
| ... |
나중에 저는 4번 게이트(종료 상태 점검, terminal-state check)와 5번 게이트(이상 징후 알림 + 지속성, anomaly alerting + persistence)를 추가했고, 이는 5개 게이트 시스템이 되었습니다.
5개 게이트는 잘 작동했습니다. 위반율은 0으로 떨어졌고, 이상 징후는 자동으로 알림이 왔으며, 세션 간 지속성(cross-session persistence)도 작동했습니다. 하지만 치명적인 결함이 하나 있었습니다. 바로 에이전트가 이를 실행해야 한다는 사실을 스스로 기억해야 한다는 점이었습니다.
5개 게이트는 독립적인 Python 엔진인 loop_engine.py로 구현되었습니다. 모든 실행 전에 에이전트는 엔진을 로드하고, 규칙을 등록하고, 상태를 초기화하고, 단계별 게이트(step gates)를 실행하고, 종료 점검(terminal check)을 수행해야 했습니다. 만약 에이전트가 엔진을 로드하는 것을 잊어버린다면 — 엔진은 시작조차 되지 않으며 — 5개 게이트는 그저 종이 위의 글자에 불과했습니다.
설상가상으로, 엔진 자체만 30개 이상의 파일로 구성되어 있었습니다. 구조는 대략 다음과 같았습니다:
hermes-harness/
├── loop_engine.py # 메인 엔진 (200줄)
├── skill_registry.json # 스킬 레지스트리 (skill registry)
...
30개 이상의 파일이 서로를 참조하고 있었습니다. 스크립트 하나를 바꾸면 5개 게이트 전체에 영향을 미쳤습니다. 리팩터링(refactor)을 할 때마다 디렉토리 전체를 뒤져야 했습니다.
5개 게이트는 한 가지 문제를 해결했지만, 또 다른 문제를 만들어냈습니다.
2. 핵심 통찰: 물리적 강제 집행은 코드가 아닌 프롬프트에 존재한다
재작성하기 전, 저는 스스로에게 한 가지 핵심적인 질문을 던졌습니다. 5개 게이트의 _본질_은 무엇인가?
그것들은 코드 계층(code layer)에서 에이전트의 불법적인 행동을 가로채는 if문과 raise문의 집합입니다. 하지만 코드 가로채기에는 전제 조건이 있습니다. 에이전트가 반드시 코드의 실행 환경(execution environment) 내부에서 실행되고 있어야 한다는 점입니다.
만약 에이전트가 세션을 전환하거나, 모델을 바꾸거나, 혹은 단순히 loop_engine.py를 호출하지 않는다면, 모든 물리적 강제 집행(physical enforcement)은 증발해 버립니다.
진정한 물리적 강제 집행(physical enforcement)은 코드 안에 존재하지 않습니다. 그것은 프롬프트(prompt) 안에 존재합니다. 프롬프트는 모든 대화에서 자동으로 주입되기 때문에, 에이전트(agent)가 이를 별도로 로드할 필요가 없습니다. 에이전트는 엔진을 실행하는 것을 잊을 수는 있지만, 프롬프트를 보는 것을 잊을 수는 없습니다. 프롬프트 내의 지침은 첫 번째 턴(turn)부터 즉시 효력을 발휘하기 때문입니다.
이러한 통찰이 새로운 시스템 전체의 설계를 이끌었습니다:
기존 시스템:
프롬프트(prompt) → 규칙(rules) → 에이전트가 엔진 로드를 기억함 → 코드가 강제 집행
↓
...
따라서 새로운 시스템은 단 2개의 파일과 2개의 스크립트로 구성됩니다. 엔진은 필요하지 않습니다.
3. 새로운 시스템: 2개의 파일 + 2개의 스크립트

선언적 규칙(Declarative Rules)에서 물리적 강제 집행(Physical Enforcement)으로
파일 1: SOUL.md (시스템 레벨의 철칙, 243행)
SOUL.md는 Hermes Agent가 모든 세션에 자동으로 주입하는 시스템 레벨의 프롬프트(prompt)입니다. 여기에는 "데이터를 절대 조작하지 말 것"부터 "첫 번째 턴에서 반드시 작업을 수행할 것"에 이르기까지 17개의 철칙(iron rules)이 담겨 있습니다.
핵심은 규칙의 개수가 아니라, _어떻게 로드되는가_에 있습니다. 에이전트가 능동적으로 읽는 것이 아니라, Hermes의 세션 초기화(session-initialization) 메커니즘에 의해 자동으로 주입됩니다. 에이전트가 단 한 마디를 내뱉기도 전에, SOUL.md는 이미 컨텍스트(context) 안에 자리 잡고 있습니다.
SOUL.md가 로드되는 방식 — 에이전트는 아무것도 기억할 필요가 없습니다:
$ cat ~/.hermes/SOUL.md | head -10
# SOUL.md — 시스템 운영 보편 법칙 (System Operating Universal Law)
# 이 파일은 Hermes Agent를 위한 시스템 레벨의 철칙을 담고 있습니다.
...
강제 집행을 위해 30개의 파일이 필요했던 규칙들이 이제는 프롬프트(prompt)에 작성되어 있습니다. 더 가볍고, 더 물리적이며, 에이전트가 우회하는 것이 불가능합니다.
파일 2: .hermes.md (프로젝트 레벨의 SOP, 127행)
SOUL.md가 전체 시스템을 관장한다면, .hermes.md는 단일 프로젝트의 표준 운영 절차(SOP)를 관장합니다.
.hermes.md 예시 (프로젝트 레벨의 제약 사항):
rules:
- name: maker-checker-separation
rule: "동일한 에이전트가 Maker(작성자)와 Checker(검토자) 역할을 동시에 수행할 수 없음"
...
SOUL.md + .hermes.md가 기존 시스템의 파일 5개를 대체합니다:
skill_registry.json(10개의 스킬 매핑)allowed_transitions.json+forbidden_transitions.json(단계별 화이트리스트/블랙리스트)role_configurations.json(역할 설정)- 3개의
check_*.py사전 점검(pre-check) 스크립트
5개의 파일이 2개로 대체되었습니다.
스크립트 1: check_completion.py (종료 상태 점검)
이것은 유일하게 유지된 게이트(gate) #4 — 종료 상태 점검기(terminal-state checker)입니다. 하지만 저는 이를 엔진 인스턴스화(instantiation)가 필요했던 Python 클래스에서 3개의 CLI 명령어로 변환했습니다:
$ check_completion.py create task-42
✅ Task task-42 created
...
핵심 로직은 단 세 가지 점검으로 이루어집니다:
def cmd_done(task_id):
# 세 가지 조건을 점검합니다:
# 1. 작업이 실제로 성공적으로 실행되었는지
...
기존 시스템의 TerminalStateChecker는 인스턴스화가 필요하고 엔진에 의해 호출되는 클래스였습니다. 새로운 시스템은 3개의 순수 CLI 명령어로 구성되어 있어, import engine이나 환경 로딩 없이도 어떤 터미널에서든 실행 가능합니다.
스크립트 2: send_alert.py (이상 징후 알림)
기존의 게이트 #5는 5개의 규칙을 가진 이상 탐지 엔진(anomaly engine)이었으며, 백그라운드 프로세스와 주기적인 스캐닝(timed scanning)이 필요했습니다. 새로운 시스템은 2개의 CLI 명령어입니다:
$ send_alert.py check task-42
🚨 [WARNING] Task task-42 has been running for 25 minutes, over the timeout threshold
🚨 [ERROR] Task task-42 executed successfully but no checker was dispatched
...
예약된 백그라운드 프로세스에서 → 온디맨드(on-demand) CLI 명령어로 변경되었습니다. 이는 기능이 약해진 것이 아니라 복잡성을 제거한 것입니다. 작업 종료 시 에이전트가 send_alert.py check를 호출하는 것은 백그라운드 폴링(polling)보다 나쁠 것이 없으며, 상주 프로세스(resident process)를 제거해 줍니다.
4. 전후 비교 (Before & After)
| 차원 (Dimension) | 기존 5개 게이트 | 새로운 시스템 | 변화량 |
|---|---|---|---|
| 파일 수 | 30개 이상 | 2개 파일 + 2개 스크립트 | −90% |
| ... |
핵심 수치: 28개 파일 삭제, 기능 손실 0.
이것은 기능 삭제가 아니라 _변환 (translation)_입니다. 코드 내에 존재하던 규칙들은 프롬프트 (prompt)로 다시 작성되었고, 엔진 인스턴스화 (instantiation)가 필요했던 클래스 (classes)들은 순수한 CLI 명령어로 바뀌었습니다.
전체 워크플로 (workflow)가 다음 3단계로 축소됩니다:
- 1단계: 시작 (Startup) — 시간 확인 +
STATE.md읽기
$ date && cat STATE.md
- 2단계: 작업 실행 (Execute the task) — 규칙은
SOUL.md와.hermes.md에 있음
# (에이전트가 철칙에 따라 실행함; 로드할 엔진이 없음)
- 3단계: 종료 (Finish) — 터미널 확인
$ check_completion.py done task-42
5. 내가 겪은 함정들 (Pitfalls)
함정 1: "자동화 (Automation)"를 낭만적으로 바라보지 마라
기존의 이상 탐지 엔진 (anomaly engine)은 5분마다 스캔을 수행했습니다. 꽤 똑똑해 보였죠. 하지만 3개월 동안 실행해 본 결과, 폴링 (polling)을 통해 포착된 이상 징후는 23%에 불과했습니다. 나머지 77%는 에이전트가 작업을 마친 후 스스로 send_alert.py check를 호출하여 이미 포착된 상태였습니다.
백그라운드 폴링 (background polling)의 복잡성이 그것이 해결하려는 문제보다 더 컸습니다. 폴링을 삭제하고, 수동 트리거 (manual trigger)를 유지하세요.
함정 2: 클래스 (Classes)보다 CLI 명령어가 낫다
기존의 TerminalStateChecker는 클래스로 래핑 (wrapped)되어 있었습니다:
checker = TerminalStateChecker()
result = checker.verify(task_file)
우아해 보이지만, 에이전트가 이를 인스턴스화하려면 TerminalStateChecker가 존재한다는 사실을 알아야 합니다. 만약 에이전트가 임포트 (import)를 잊어버리면, 체크는 절대 실행되지 않습니다.
반면 CLI 명령어를 사용하면:
check_completion.py done task-42
에이전트는 Python 클래스를 이해할 필요 없이 명령어 이름만 기억하면 됩니다. 명령어가 실패하면 에러를 직접 출력하므로, "로드하는 것을 잊었다"와 같은 실패 모드가 발생하지 않습니다.
함정 3: 에이전트의 "규칙을 숙지했습니다"라는 말을 믿지 마라
저는 전형적인 실수를 저질렀습니다. SOUL.md 상단에 규칙을 적어두고 에이전트가 매 턴마다 이를 따를 것이라고 가정했습니다. 하지만 대화가 5번째 턴에 접어들자, 에이전트는 스스로 규칙을 만들어내기 시작했습니다: "이전 논의에 따라...", "이전 단계에서 확인된 바와 같이..." — 프롬프트에 바로 자리 잡고 있는 철칙들을 우회하면서 말이죠.
해결책: 물리적 검증 (physical verification)이 반드시 존재해야 합니다. SOUL.md는 규칙을 정의하고, check_completion.py done은 물리적 검증을 수행합니다. 두 개의 레일(rails)이 있는 셈입니다: 프롬프트에는 규칙을, 코드에는 검증을 두는 것입니다. 프롬프트는 "잊힐" 수 있지만, CLI 명령의 출력은 에이전트가 조작할 수 없습니다.
6. 결론: 빛은 물리적이다
제가 에이전트를 위해 구축했던 모든 "시스템"을 되돌아보면 — 초기 3계층 물리적 강제화 방식부터 30개의 파일, 그리고 오늘날의 2개 파일 + 2개 스크립트 구성에 이르기까지 — 가장 강력했던 것은 가장 단순한 것이었습니다.
제가 더 게을러졌기 때문이 아닙니다. 마침내 무언가를 깨달았기 때문입니다: 물리적 강제화 (physical enforcement)의 핵심은 코드의 복잡성이 아니라, 에이전트가 건너뛸 수 없는 최소한의 경로라는 점입니다.
30개의 파일로 구성된 엔진? 에이전트는 그것을 실행하는 것을 잊을 수 있습니다. 2개의 파일과 2개의 스크립트? 에이전트는 원하더라도 잊을 수 없습니다. SOUL.md는 프롬프트에 자동으로 주입되며, check_completion.py는 셸(shell)에 상주하며 언제든 호출될 수 있기 때문입니다.
두 시스템 중 어느 것이 진정한 "물리적 강제화"인지 맞춰보시겠습니까?
지금까지의 시리즈: 이전 글들은 영구 상태 (persistent state)부터 품질 시스템 (quality systems), 운영 모니터링 (ops monitoring), 파이프라인 아키텍처 (pipeline architecture)에 이르는 전체 경로를 다루었습니다. 이번 글은 시스템을 30개 파일에서 2개 파일 + 2개 스크립트 설정으로 슬림화했습니다.
다음 글: 코드를 작성하는 것만으로는 부족하다 — 셀프 어택 테스트 (Self-Attack Testing)가 가장 어려운 테스트다. 새로운 시스템을 배포한 후, 저는 한 차례의 셀프 어택 테스트(5가지 공격 방법을 모두 시뮬레이션함)를 실시했고, 예상치 못한 돌파구를 발견했습니다...
저자 소개: Wu Ji (无记) — 에이전트 엔지니어링 (Agent engineering), 루프 엔지니어링 (Loop Engineering), 디지털 전환 (digital transformation)에 집중하는 AI 및 디지털화 실무자. 실용적이고 직접적인 튜토리얼 — 따라 하기만 하면 바로 작동합니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기