3줄 요약
- 꼼비(ggombee)가 AI 에이전트를 실무에 적용하며 만든 code-forge 프로젝트의 진화 과정을 기록한 글이다.
- 프롬프트 엔지니어링의 본질적 한계(자연어는 강제력이 없다)를 인식하고, 시스템 레벨에서 에이전트를 통제하는 하네스 엔지니어링으로 전환했다.
- hooks 3층 구조, 에이전트 빌드타임 컴파일, AGENTS.md 멀티모델 컨벤션까지 실전에서 검증된 구체적인 구현 사례를 제시한다.
프롬프트의 한계: 부탁에서 강제로
git add -A 쓰지 말라고 굵은 글씨로 적어놔도 에이전트는 가끔 그냥 쓴다. 프롬프트는 결국 자연어이고, 자연어로 된 지시에는 강제력이 없다. 저자는 이 경험에서 핵심 전제를 도출한다.
프롬프트가 쓸모없다는 게 아니다. 프롬프트만으로는 부족하다는 것이다.
그래서 “부탁하는 걸 그만두고” 에이전트가 실행되는 환경 자체를 설계하기 시작했다. 업계에서는 이를 하네스 엔지니어링이라 부른다. OpenAI가 2025년 2월에 소개하고, 마틴 파울러도 다룬 개념이다.
사고 모델 단순화: 6단계에서 4단계로
인지적 도제 이론 기반의 6단계 사고 모델(읽기\→반응\→분석\→재구조화\→구조화\→성찰)을 만들었으나 세 가지 문제가 터졌다.
- 토큰 낭비: 버튼 색상 바꾸는데 “현재 구조를 분석합니다…”
- 경계 모호: READ와 REACT의 차이를 만든 본인도 설명하기 애매
- 실패 루프 부재: 검증에서 lint가 터지면 어디로 돌아가야 하는지 정의 안 됨
4단계로 줄인 결과:
GROUND → APPLY → VERIFY
↑ ↓
└─── ADAPT ←─────┘
(실패 시만)
- GROUND: 코드를 먼저 읽는다. 기억에서 추론하지 않는다.
- APPLY: 관찰한 패턴대로 구현. 5가지 불변 제약(읽기 우선, 패턴 준수, 정책 보존, 최소 변경, 스코프 준수).
- VERIFY: lint/tsc/테스트를 도구로 확인.
- ADAPT: 실패 원인 분석 후 GROUND로 복귀. 같은 원인 2회 실패 시 접근법 변경, 3회 시 사람에게 위임.
4개 파일 수백 줄이 1개 파일 99줄로 줄었고, 에이전트는 오히려 더 잘 따랐다. 저자의 결론: 줄였는데도 잘 되면 원래 그만큼만 필요했던 것이다.
모듈화와 에이전트 컴파일
컨벤션 모듈화
Emotion, Jotai 등 스택 컨벤션이 규칙 파일에 하드코딩되어 팀 공유가 불가능했다. modules/ 디렉터리로 추출하여 프리셋으로 조합하는 구조로 전환. 현재 13개 모듈(프레임워크 3, 디자인 시스템 2, 상태 관리 3, 스타일링 3, 테스팅 2). 에이전트와 사고 모델은 동일하게 두고 스택만 교체한다.
STATE/ACT 분리와 빌드타임 컴파일
에이전트를 STATE(아는 것)와 ACT(하는 것)로 분리하면 조합이 가능해진다.
code-reviewer = lang.typescript + framework.react + quality.review
implementor = lang.typescript + framework.react + dev.implement
처음에는 런타임에 규칙 파일을 주입했으나 28개 파일 이중 관리 문제가 발생. “TypeScript를 매번 런타임에 해석하는 사람은 없다"는 발상으로 빌드타임 컴파일로 전환했다. /smith-build로 STATE+ACT 체인을 미리 계산하여 정적 .md 파일로 출력한다. 복잡성을 제거한 게 아니라 빌드타임으로 옮긴 것이다.
배포: Git 레포에서 플러그인으로
설정을 별도 Git 레포로 관리했으나 동기화 문제 발생. Claude Code 플러그인 공식 지원 이후 claude plugin install로 전면 교체. session-init.sh SessionStart 훅이 세션마다 리모트 버전을 확인하고 자동 업데이트한다.
하네스 엔지니어링: 핵심 구조
3층 아키텍처
| Layer | 비유 | 역할 | 강제력 |
|---|---|---|---|
| Layer 1 (hooks) | 소방 스프링클러 | 시스템 레벨 가드레일 | 무시 불가 |
| Layer 2 (프롬프트) | 금연 표지판 | 지시·컨벤션 안내 | 대부분 따르지만 강제 아님 |
| Layer 3 (에이전트) | 출입 카드 | 권한 기반 접근 제어 | 권한 없으면 불가 |
command 훅 + prompt 훅 = 2층 가드레일
- command 훅(regex): 빠르고 확실.
git add .,rm -rf등 알려진 패턴 즉시 차단. - prompt 훅(haiku 기반 LLM): 느리지만 의미를 이해. “강제 푸시해줘” 같은 자연어도 판별.
두 개를 레이어로 쌓으면 regex가 놓치는 부분을 LLM이 잡는다.
exit 0 스텁에서 실동작 11개로
솔직히 처음에 hooks는 exit 0 4줄짜리 스텁 5개에 불과했다. 리팩터링하면서 기존 5개를 채우고 6개를 추가하여 11개 실동작 441줄이 되었다. 방향은 멀티모델 토론에서 나온 한마디가 잡았다.
“새로 만들 것은 최소한으로. 있는 것을 채우고 다듬는다.”
주목할 만한 훅 배치
- SubagentStop: 서브 에이전트 완료 시 tsc 실행. 세션 종료가 아닌 각 에이전트 완료 시점에 타입 체크를 걸어 오류를 즉시 포착.
- PreCompact: 컨텍스트 압축 직전에 브랜치·미커밋 파일·스테이지 상태를 스냅샷으로 주입. 압축 후에도 복귀 지점 보존.
- Stop (quality-gate.sh): 응답 완료마다 eslint –quiet + tsc –noEmit 실행. 에이전트가 “잘 됐다"고 해도 린터가 최종 확인.
멀티모델 협업
같은 Claude 모델끼리는 비슷한 편향을 공유한다. Claude가 짠 구현 계획을 Codex(GPT)에게 보여주면 다른 시각에서 허점을 찾아낸다. 경험적으로 확인된 사실: 비판적 의견이 들어갔을 때 더 좋은 결과가 나온다.
AGENTS.md: AAIF 표준
CLAUDE.md는 Claude만 읽지만, AGENTS.md(AAIF 표준)는 Codex CLI·Cursor가 네이티브로 읽는다. /setup이 두 파일을 동시에 생성하고, CLAUDE.md가 AGENTS.md를 임포트하는 구조. 어떤 모델이든 같은 하네스를 따르면 같은 아웃풋이 나오는 것이 목표다.
실제 토론 사례
- 구조 검증: Codex가 “참조 파일 중복, @참조 매트릭스 과도” CONDITIONAL REJECT. Critic(Claude Opus)이 “무조건 줄이는 게 답이 아니다” 반론. 결과적으로 중복은 정리하되 역할별 참조는 유지.
- v3.0 방향성: Builder(실용) vs Visionary(비전) vs Critic(비판) 3라운드. Critic의 “있는 것을 채우고 다듬는다"가 hooks 실구현의 방향을 결정.
가장 흥미로운 지점
이 글에서 가장 인상 깊은 부분은 “exit 0 스텁 5개에서 실동작 11개로” 가는 과정이다. 하네스를 설계했다고 말하면서 실제로는 껍데기에 의존하고 있었다는 솔직한 고백이 설득력을 높인다. 새 기능을 추가하는 것보다 있는 것을 실제로 채우는 것이 더 큰 효과를 낸다는 결론은, 소프트웨어 개발 전반에 통하는 이야기다.
command 훅(regex)과 prompt 훅(LLM)을 레이어로 쌓는 아이디어도 실용적이다. 알려진 패턴은 빠르게 잡고, 의미적 판단이 필요한 부분은 LLM에 맡기는 구조는 비용과 안전의 균형을 잘 잡는다.
출처
꼼비(ggombee), 2026년 4월 30일, 요즘IT 원문: https://yozm.wishket.com/magazine/detail/3733/ GitHub: https://github.com/ggombee/code-forge