“소프트웨어 엔지니어” 랄프 위검
게시일: 2025년 12월 17일 | 원문 작성일: 2025년 7월 14일 | 저자: Geoffrey Huntley | 원문 보기
핵심 요약
랄프(Ralph)는 AI 코딩 에이전트를 while 루프 안에서 돌리는 기법이에요. 심슨스의 랄프 위검1처럼 단순하지만, 제대로 튜닝하면 그린필드 프로젝트2에서 놀라운 결과를 만들어낼 수 있어요.
- 루프 당 하나의 작업만 — 컨텍스트 윈도우를 아끼려면 한 번에 한 가지만 시켜야 해요
- 서브에이전트로 컨텍스트 확장 — 메인 컨텍스트에 할당하지 말고 서브에이전트를 스폰해서 무거운 작업 처리
- 백프레셔3는 필수 — 테스트, 타입 체커, 정적 분석기로 잘못된 코드 생성을 걸러내야 해요
- 예측 가능하게 실패해요 — 랄프의 아름다움은 비결정론적 세상에서 예측 가능한 방식으로 실패한다는 거예요. 실패 패턴을 알면 튜닝할 수 있죠
- 시니어 전문성 필수 — 랄프를 가이드할 시니어 엔지니어 없이는 불가능해요
랄프란 무엇인가
최근 제 소셜 미디어를 보셨다면 랄프에 대해 이야기하는 걸 보셨을 거예요. 랄프가 뭐냐고요? 랄프는 기법이에요. 가장 순수한 형태로는 bash 루프예요.
while :; do cat PROMPT.md | npx —yes @sourcegraph/amp ; done랄프는 대부분의 회사에서 그린필드 프로젝트 아웃소싱의 대부분을 대체할 수 있어요. 결함이 있지만, 다양한 스타일의 프롬프트로 식별하고 해결할 수 있는 결함들이에요.
랄프의 아름다움은 바로 이거예요 — 비결정론적 세상에서 예측 가능한 방식으로 실패한다는 거죠.
랄프는 도구 호출과 사용량 제한이 없는 어떤 도구로든 할 수 있어요.
랄프는 현재 완전히 새로운 프로그래밍 언어를 만들고 있어요. 완전히 새로운 프로덕션급 난해한 프로그래밍 언어가 출시되기 직전이에요. 진짜 신기한 건 랄프가 이 언어를 만들 수 있었을 뿐만 아니라, LLM의 학습 데이터셋에 없는 이 언어로 프로그래밍까지 할 수 있다는 거예요.
랄프와 함께 소프트웨어 만들기
랄프로 소프트웨어를 만들려면 상당한 믿음과 최종 일관성4에 대한 신뢰가 필요해요. 랄프는 여러분을 시험할 거예요. 매번 랄프가 CURSED를 만들면서 잘못된 방향으로 갔을 때, 저는 도구를 탓하지 않고 내면을 들여다봤어요. 랄프가 뭔가 나쁜 걸 할 때마다, 랄프는 기타처럼 튜닝됩니다.
시작할 때는 놀이터가 없고, 랄프에게 하나 만들라고 지시해요.
랄프는 놀이터 만들기를 아주 잘해요. 하지만 미끄럼틀에서 떨어져서 멍이 들어 집에 와요. 그러면 미끄럼틀 옆에 “내려가세요, 뛰지 마세요, 주변을 살피세요”라는 표지판을 세워서 랄프를 튜닝해요. 그러면 랄프가 표지판을 볼 가능성이 높아져요.

결국 랄프는 표지판만 생각하게 되고, 그게 바로 결함 없이 느껴지는 새 랄프를 얻는 순간이에요.
PROMPT.md에 뭐가 있나요?
프로그래밍 커뮤니티에서 완벽한 프롬프트에 대한 집착이 있는 것 같아요. 완벽한 프롬프트 같은 건 없어요.
CURSED의 프롬프트를 가져가고 싶겠지만, 어떻게 다루는지 모르면 의미가 없을 거예요. 프롬프트를 그대로 가져가도 아마 같은 결과를 얻지 못할 거예요. 왜냐하면 LLM 행동을 관찰하면서 지속적으로 튜닝되어 온 거거든요. CURSED가 만들어질 때, 저는 스트림을 보면서 나쁜 행동 패턴—랄프를 튜닝할 기회—을 찾고 있어요.
먼저 기본기 좀
제가 SFO5에 있을 때, 모두가 멀티-에이전트, 에이전트 간 통신, 멀티플렉싱을 해결하려고 하고 있었어요. 이 단계에서는 필요 없어요. 마이크로서비스와 그에 따르는 모든 복잡성을 생각해보세요. 이제 마이크로서비스(에이전트)가 그 자체로 비결정론적이라면 어떨지 생각해보세요—완전 엉망이 되겠죠.
마이크로서비스의 반대는 뭘까요? 모놀리식 애플리케이션이에요. 수직으로 확장하는 단일 운영체제 프로세스요. 랄프는 모놀리식이에요. 랄프는 단일 저장소에서 단일 프로세스로 자율적으로 작동하며 루프당 하나의 작업을 수행해요.
랄프로 좋은 결과를 얻으려면, 랄프에게 루프당 한 가지만 하라고 시켜야 해요. 오직 한 가지만요. 좀 미친 소리처럼 들릴 수 있지만, 무엇이 가장 중요한지 랄프가 결정하도록 신뢰해야 해요. 이건 여러분이 “책임감 있는 엔지니어링”이라고 생각하는 것의 경계를 시험할 완전 핸즈오프 바이브 코딩6이에요.
LLM은 무엇이 중요하고 다음 단계가 뭔지 추론하는 데 놀라울 정도로 잘해요.
당신의 작업은 누락된 stdlib(specs/stdlib/* 참조)와 컴파일러 기능을 구현하고 병렬 서브에이전트를 사용해서 LLVM을 통해 cursed 언어로 컴파일된 애플리케이션을 만드는 거예요. fix_plan.md를 따르고 가장 중요한 것을 선택하세요.
위 프롬프트에서 곧 확장할 몇 가지가 있지만, 또 다른 핵심은 매 루프마다 결정론적으로 같은 방식으로 스택을 할당하는 거예요.
매 루프마다 스택에 할당하고 싶은 항목은 계획(“@fix_plan.md”)과 스펙이에요.
루프당 하나의 항목
루프당 하나의 항목이요. 여기서 반복해야겠어요—루프당 하나의 항목. 프로젝트가 진행되면서 이 제한을 완화할 수 있지만, 통제를 벗어나기 시작하면 딱 하나의 항목으로 좁혀야 해요.
핵심은 컨텍스트 윈도우가 약 170k밖에 안 된다는 거예요. 그래서 최대한 아껴 써야 해요. 컨텍스트를 많이 쓸수록 결과가 나빠져요. 네, 이건 낭비처럼 보여요. 매 루프마다 스펙을 새로 로드하고 재사용하지 않으니까요.
컨텍스트 윈도우 확장하기
에이전트 루프가 작동하는 방식은 도구를 실행하고 그 도구의 결과를 평가하는 거예요. 평가 결과가 컨텍스트 윈도우에 할당으로 추가돼요.
랄프는 기본 컨텍스트 윈도우에 할당하지 않는 사고방식이 필요해요. 대신, 서브에이전트를 스폰해야 해요. 기본 컨텍스트 윈도우는 스케줄러로 작동해야 하고, 테스트 스위트가 작동했는지 요약하는 것처럼 할당량이 많은 작업은 다른 서브에이전트에 맡겨야 해요.
당신의 작업은 누락된 stdlib(specs/stdlib/* 참조)와 컴파일러 기능을 구현하고 병렬 서브에이전트를 사용해서 LLVM을 통해 cursed 언어로 컴파일된 애플리케이션을 만드는 거예요. fix_plan.md를 따르고 가장 중요한 것을 선택하세요. 변경하기 전에 서브에이전트를 사용해서 코드베이스를 검색하세요(구현되지 않았다고 가정하지 마세요). 모든 작업에는 병렬 서브에이전트를 사용할 수 있지만 rust의 빌드/테스트에는 1개의 서브에이전트만 사용하세요.
서브에이전트의 병렬성 양을 제어할 수 있다는 것도 깨달아야 해요.
수백 개의 서브에이전트로 팬아웃한 다음 그 서브에이전트들에게 애플리케이션의 빌드와 테스트를 실행하라고 하면, 나쁜 형태의 백프레셔가 생겨요. 따라서 위 지시는 검증에는 단일 서브에이전트만 사용해야 하지만, 파일 시스템 검색과 파일 작성에는 원하는 만큼 서브에이전트를 사용할 수 있다는 거예요.
구현되지 않았다고 가정하지 마세요
이 모든 코딩 에이전트가 작동하는 방식은 ripgrep을 통해서예요. 코드 기반 검색은 비결정론적일 수 있다는 걸 이해하는 게 필수예요.
랄프의 일반적인 실패 시나리오는 LLM이 ripgrep을 실행하고 코드가 구현되지 않았다는 잘못된 결론에 도달하는 거예요. 이 실패 시나리오는 랄프에게 가정하지 말라고 지시하는 표지판을 세워서 쉽게 해결할 수 있어요.
변경하기 전에 코드베이스를 검색하세요(항목이 구현되지 않았다고 가정하지 마세요). 병렬 서브에이전트를 사용하세요. 깊이 생각하세요.
일어나보니 랄프가 여러 번 구현하고 있다면, 이 단계를 튜닝해야 해요. 이 비결정론이 랄프의 아킬레스건이에요.
1단계: 생성
코드 생성은 이제 저렴해요. 랄프가 생성하는 코드는 기술 표준 라이브러리와 스펙을 통해 완전히 제어할 수 있어요.
랄프가 잘못된 코드를 생성하거나 잘못된 기술 패턴을 사용하고 있다면, 올바른 패턴으로 유도하기 위해 표준 라이브러리를 업데이트해야 해요.
랄프가 완전히 잘못된 것을 만들고 있다면, 스펙이 잘못되었을 수 있어요. CURSED를 만들면서 배운 크고 어려운 교훈은 한 달이 지나서야 렉서 스펙이 두 가지 상반되는 시나리오에 대해 키워드를 두 번 정의했다는 걸 알아챘다는 거예요. 이로 인해 많은 시간이 낭비됐어요. 랄프가 바보 같은 짓을 하고 있었고, 도구 대신 운영자를 탓하기 쉬웠던 것 같아요.
2단계: 백프레셔
여기서 엔지니어링 모자를 써야 해요. 코드 생성은 이제 쉬우니까, 어려운 건 랄프가 올바른 것을 생성했는지 확인하는 거예요. 특정 프로그래밍 언어는 타입 시스템을 통해 내장된 백프레셔가 있어요.
”Rust! 최고의 타입 시스템이 있잖아”라고 생각할 수 있어요. 하지만 Rust의 한 가지 문제는 컴파일 속도가 느리다는 거예요. 정확성과 속도 사이에서 균형을 맞추는 게 중요해요.
어떤 언어를 사용할지는 실험이 필요해요. 컴파일러를 만들고 있어서 극도의 정확성을 원했고, 그래서 Rust를 사용했어요. 하지만 이 접근 방식은 더 천천히 빌드된다는 걸 의미해요. 이 LLM들은 한 번에 완벽한 Rust 코드를 생성하는 데 그리 잘하지 못해서, 더 많은 시도가 필요해요.
위 다이어그램에서는 그냥 “테스트와 빌드”라는 단어만 보여주지만, 여기서 엔지니어링 모자를 써야 해요. 무효한 코드 생성을 거부하는 백프레셔로 무엇이든 연결할 수 있어요. 보안 스캐너, 정적 분석기, 무엇이든 될 수 있어요. 하지만 핵심은 루프가 빨리 돌아야 한다는 거예요.
CURSED를 만들 때 기본이었던 프롬프트예요. 변경한 후, 구현되거나 개선된 코드 단위에 대한 테스트만 실행하세요.
기능을 구현하거나 문제를 해결한 후, 개선된 코드 단위에 대한 테스트를 실행하세요.
동적 타입 언어를 사용한다면, 랄프할 때 정적 분석기/타입 체커를 연결하는 게 중요하다는 걸 강조해야 해요:
그렇지 않으면 결과가 엉망이 될 거예요.
순간에 테스트의 중요성 포착하기
랄프에게 백프레셔 형태로 테스트를 작성하라고 지시할 때, 우리가 랄프에게 매 루프마다 한 가지만 하게 하고, 매 루프마다 새로운 컨텍스트 윈도우를 갖기 때문에, 그 순간에 랄프에게 테스트가 무엇을 하려고 하는지 의미와 중요성을 적어달라고 요청하는 게 중요해요.
중요: 문서를 작성할 때(예: rust doc이나 cursed stdlib 문서) 테스트와 그 구현이 왜 중요한지 포착하세요.
구현에서는 이렇게 보여요. 저한테는 LLM의 미래 반복을 위한 작은 메모를 남기는 것처럼 보여요. 테스트가 왜 존재하고 중요한지 설명하는 거죠. 미래 루프는 컨텍스트 윈도우에 그 추론을 갖지 못할 테니까요.
LLM이 테스트가 더 이상 관련 있는지 아니면 중요한지 결정하는 데 도움이 된다는 걸 알았어요. 테스트를 삭제할지, 수정할지, [실패를] 해결할지 결정에 영향을 줘요.
치팅 금지
Claude는 최소한의 플레이스홀더 구현을 하려는 내재적 편향이 있어요. 그래서 CURSED 개발의 다양한 단계에서 이 프롬프트의 변형을 사용해왔어요.
기능을 구현하거나 문제를 해결한 후, 개선된 코드 단위에 대한 테스트를 실행하세요. 기능이 누락되었다면 애플리케이션 스펙에 따라 추가하는 게 당신의 일이에요. 깊이 생각하세요.
당신의 작업과 관련 없는 테스트가 실패하면 변경의 증분으로서 이 테스트를 해결하는 게 당신의 일이에요.
9999999999999999999999999999. 플레이스홀더나 단순 구현을 하지 마세요. 완전한 구현을 원해요. 아니면 소리 지를 거예요
초기에 랄프가 이 표지판을 무시하고 플레이스홀더 구현을 해도 낙담하지 마세요. 모델은 보상 함수를 쫓도록 훈련되었고, 보상 함수는 컴파일되는 코드예요. 언제든 더 많은 랄프를 돌려서 플레이스홀더와 최소 구현을 식별하고 미래 랄프 루프를 위한 할 일 목록으로 변환할 수 있어요.
할 일 목록
말 나온 김에, 지난 몇 주간 TODO 목록을 만드는 데 사용해온 프롬프트 스택이에요. 랄프가 여러분을 시험할 거라고 말하는 부분이에요. 최종 일관성을 믿어야 하고, 대부분의 문제가 랄프가 실수하는 영역에 집중해서 더 많은 루프로 해결할 수 있다는 걸 알아야 해요.
specs/*를 공부해서 컴파일러 스펙을 배우고 fix_plan.md를 공부해서 지금까지의 계획을 이해하세요.
컴파일러 소스 코드는 src/*에 있어요
예제 소스 코드는 examples/*에, tree-sitter 소스 코드는 tree-sitter/*에 있어요. 공부하세요.
stdlib 소스 코드는 src/stdlib/*에 있어요. 공부하세요.
첫 번째 작업은 fix_plan.md를 공부하고(틀릴 수 있어요) 최대 500개의 서브에이전트를 사용해서 src/의 기존 소스 코드를 공부하고 컴파일러 스펙과 비교하는 거예요. 거기서 아직 구현되지 않은 항목의 우선순위로 정렬된 불릿 포인트 목록인 fix_plan.md를 생성/업데이트하세요. 깊이 생각하고 오라클을 사용해서 계획하세요. TODO, 최소 구현, 플레이스홀더 검색을 고려하세요. fix_plan.md를 공부해서 연구 시작점을 결정하고 서브에이전트를 사용해서 완료/미완료 항목을 최신 상태로 유지하세요.
결국 랄프는 TODO 목록에서 할 일이 없어지거나, 완전히 방향을 잃어요. 결국 랄프 위검이니까요. 이 단계에서는 취향의 문제예요. CURSED를 만들면서 TODO 목록을 여러 번 삭제했어요. TODO 목록은 제가 매처럼 지켜보는 거예요. 그리고 자주 버려요.
TODO 목록을 버리면, “그럼 다음 단계를 어떻게 알아?”라고 물을 수 있어요. 간단해요. 위처럼 새 TODO 목록을 생성하라는 명시적 지시로 랄프 루프를 돌리면 돼요.
TODO 목록이 있으면 랄프를 다시 시작해요… 계획 모드에서 빌드 모드로 전환하라는 지시와 함께…
루프백이 전부예요
랄프가 평가를 위해 LLM으로 자기 자신을 루프백할 수 있는 방식으로 프로그래밍하고 싶어요. 이건 엄청나게 중요해요. 항상 랄프를 자기 자신에게 루프백할 기회를 찾으세요. 추가 로깅을 지시하는 것처럼 간단할 수도 있고, 컴파일러의 경우 랄프에게 애플리케이션을 컴파일하고 LLVM IR 표현을 보라고 요청하는 것일 수도 있어요.
문제를 디버그할 수 있도록 필요한 경우 추가 로깅을 추가할 수 있어요.
랄프는 스스로 대학에 갈 수 있어요
@AGENT.md가 루프의 심장이에요. 랄프가 프로젝트를 컴파일하고 실행하는 방법을 지시해요. 랄프가 배움을 발견하면, 자기 개선을 허용하세요:
컴파일러나 예제를 실행하는 방법에 대해 새로운 걸 배우면 서브에이전트를 사용해서 @AGENT.md를 업데이트하되 간결하게 유지하세요. 예를 들어 올바른 명령을 배우기 전에 여러 번 명령을 실행했다면 그 파일을 업데이트해야 해요.
루프 중에 랄프가 뭔가 고쳐야 한다고 판단할 수 있어요. 그 추론을 포착하는 게 중요해요.
발견한 버그에 대해, 현재 작업과 관련 없더라도 서브에이전트를 사용해서 fix_plan.md에 문서화한 후 해결하거나 문서화하는 게 중요해요.
아침에 일어나니 코드베이스가 망가져 있을 거예요
네, 사실이에요. 때때로 아침에 일어나보면 컴파일되지 않는 망가진 코드베이스를 마주하게 될 거고, 랄프가 스스로 고칠 수 없는 상황이 생길 거예요. 이때 뇌를 켜야 해요. 판단을 내려야 해요. git reset —hard를 하고 랄프를 다시 시작하는 게 더 쉬운가요? 아니면 랄프를 구출할 수 있는 다른 일련의 프롬프트를 만들어야 하나요?
테스트가 통과하면 fix_plan.md를 업데이트하고, 변경된 코드와 fix_plan.md를 bash에서 “git add -A”로 추가한 다음 변경 사항을 설명하는 메시지와 함께 “git commit”을 하세요. 커밋 후 “git push”로 원격 저장소에 변경 사항을 푸시하세요.
빌드나 테스트 오류가 없으면 git 태그를 만드세요. git 태그가 없으면 0.0.0에서 시작하고 패치를 1씩 증가시키세요. 예를 들어 0.0.0이 없으면 0.0.1.
컴파일러를 처음 시작했을 때, 컴파일 오류가 너무 많아서 Claude의 컨텍스트 윈도우를 채웠던 걸 기억해요. 그래서 그때 컴파일 오류 파일을 Gemini에 던지고 Gemini에게 랄프를 위한 계획을 만들어달라고 했어요.
하지만 유지보수성은요?
그 주장을 들으면, “누구에 의한” 건지 묻게 돼요. 인간에 의한? 왜 유지보수가 인간 기준이어야 하나요? 우리는 필요할 때 해결/적응하기 위해 루프를 돌릴 수 있는 포스트-AI 단계에 있지 않나요?
AI가 만든 모든 문제는 다른 일련의 프롬프트로 해결할 수 있어요
좀 뻔뻔하다면 아마 GitHub에서 CURSED 코드베이스를 찾을 수 있을 거예요. 소셜 미디어에 공유하지 말아달라고 부탁드려요. 아직 출시 준비가 안 됐거든요. AI가 완전히 새로운 프로그래밍 언어를 만들 수 있고, 학습 셋에 학습 데이터가 없는 프로그래밍 언어로 프로그래밍하는 게 가능하다는 반박할 수 없는 증거가 있도록 이걸 튜닝하고 싶어요.
랄프는 세 가지 상태가 있어요. 덜 익은 상태, 완성된 상태, 또는 예상치 못한 행동이 튀어나오는 완성 상태 (가끔 꽤 좋은 결과도!)
CURSED가 출시되면, 랄프가 만들었다는 걸 이해하세요. 다음에 올 기법은 랄프가 아닐 거예요. 모델과 도구가 지금 상태로 유지된다면, 우리는 포스트-AGI 영역에 있다고 굳게 믿어요. 필요한 건 토큰이에요. 이 모델들은 토큰을 갈망하니까, 올바른 접근법으로 토큰을 던져주면 소프트웨어 개발을 자동화할 기본 요소가 있어요.
그렇게 말하긴 했지만, 엔지니어는 여전히 필요해요. 랄프를 가이드하는 시니어 전문성 없이는 이게 불가능해요. 엔지니어가 더 이상 필요 없고 도구가 엔지니어 없이 100% 작업을 할 수 있다고 주장하는 사람은 헛소리를 팔고 있는 거예요.
하지만 랄프 기법은 그린필드 프로젝트에서 현재 상태의 SWE 대다수를 대체할 수 있을 만큼 놀라울 정도로 효과적이에요.
마지막으로 이렇게 말할게요:
“기존 코드베이스에서 랄프를 사용할 방법은 절대 없어요”
하지만 시도해보신다면, 결과가 어땠는지 듣고 싶어요. 이건 그린필드를 부트스트랩하는 기법으로 가장 잘 작동하고, 90%까지 도달할 수 있다는 기대와 함께요.
CURSED를 만드는 데 사용된 현재 프롬프트
랄프가 CURSED를 만드는 데 사용하는 현재 프롬프트예요.
역자 주
- 랄프 위검(Ralph Wiggum): 미국 애니메이션 <심슨 가족>의 캐릭터로, 경찰서장의 아들이에요. “Me fail English? That’s unpossible!” 같은 대사로 유명한, 사랑스럽지만 단순한 캐릭터죠. 저자가 이 기법에 ‘랄프’라는 이름을 붙인 건 AI 에이전트가 랄프처럼 단순하고 예측 가능한 방식으로 실수하지만, 그래서 오히려 가이드하기 쉽다는 의미예요. ↩
- 그린필드 프로젝트(Greenfield project): 기존 코드나 레거시 시스템 없이 완전히 새로 시작하는 프로젝트를 말해요. 마치 빈 초록 들판(green field)에 건물을 짓는 것처럼요. 반대 개념은 ‘브라운필드(Brownfield)‘로, 기존 시스템을 수정하거나 확장하는 프로젝트예요. ↩
- 백프레셔(Backpressure): 원래 유체역학에서 온 용어로, 시스템이 처리할 수 없는 입력을 거부하거나 늦추는 피드백 메커니즘이에요. 여기서는 테스트 실패, 타입 에러, 린트 경고 등이 잘못된 코드 생성을 걸러내는 역할을 해요. ↩
- 최종 일관성(Eventual consistency): 분산 시스템에서 즉각적인 일관성 대신, 시간이 지나면 결국 모든 노드가 같은 상태에 도달한다는 개념이에요. 여기서는 “랄프가 당장은 실수하더라도 계속 루프를 돌리면 결국 원하는 결과에 도달한다”는 믿음을 표현해요. ↩
- SFO: 샌프란시스코 국제공항의 코드이자, 실리콘밸리/샌프란시스코 베이 에어리어를 가리키는 표현이에요. AI/테크 스타트업의 중심지죠. ↩
- 바이브 코딩(Vibe coding): 2025년 초 Andrej Karpathy가 만든 용어로, 코드의 모든 세부사항을 이해하지 않고 AI에게 원하는 것을 설명하고 결과를 받아들이는 프로그래밍 스타일이에요. “분위기(vibe)로 코딩한다”는 의미죠. ↩
저자 소개: Geoffrey Huntley는 오픈소스와 DevOps에 깊은 경험을 가진 소프트웨어 엔지니어로, 현재 AI 코딩 도구와 자동화에 집중하고 있어요.
참고: 이 글은 AI 코딩 에이전트를 자동화하는 “랄프 위검” 기법에 대한 실무 가이드예요. 저자는 이 기법으로 새로운 프로그래밍 언어 CURSED를 개발 중이에요.
원문: Ralph Wiggum as a “software engineer” - Geoffrey Huntley (2025년 7월 14일)
생성: Claude (Anthropic)