Ralph Loop, OpenClaw - 새로운건 없었다

ALF 팀에서 Agentic Workflow 를 구성한 이야기

Mong • Engineer

  • 엔지니어링

안녕하세요, 채널톡 엔지니어 몽 입니다.

요새는 많이 좋아지고 있지만, 얼마 전까지만 하더라도 Claude Code로 작업을 하다가 짜증이 나는 경우가 많았습니다. "구현을 완료했습니다"라고 말해놓고 막상 보면 절반만 되어 있고, 다시 시키면 또 "완료했습니다". 3번째 재시도 끝에 결국 직접 마무리했는데, 그날 밤 Geoffrey Huntley라는 호주 개발자의 블로그를 발견했습니다.

Shell

이 사람은 그냥 무한 루프를 돌렸습니다. 에이전트가 끝났다고 말해도 다시 같은 프롬프트를 넣는 거죠. "진짜로 끝날 때까지." 이름은 심슨 캐릭터 Ralph Wiggum에서 따왔다고 합니다. 똑똑하진 않지만 끈질기게 포기하지 않는 캐릭터.

단순하다고 무시할 게 아닌 게, Y Combinator 해커톤에서 한 팀이 이걸 GCP 인스턴스에 올려놓고 잠들었더니 아침에 6개 레포지토리에 1,100개 커밋이 찍혀있었다고 합니다. Browser Use를 Python에서 TypeScript로 거의 다 포팅했고, 비용은 800달러. 시간당 10.50달러짜리 개발자를 고용한 셈이었습니다.

이 패턴이 왜 되는지 궁금해서 좀 더 살펴봤습니다

Ralph Loop가 먹히는 이유

보통 AI랑 대화하면 모든 정보가 컨텍스트 창에 쌓입니다. 대화가 길어질수록 초반 정보가 흐려지고 노이즈가 늘면서 성능이 떨어지죠. Ralph Loop는 이걸 우회합니다. 진행 상황을 컨텍스트가 아니라 파일과 git에 저장하는 겁니다. 컨텍스트가 차면? 새 에이전트를 띄우면 됩니다. fresh한 에이전트가 파일시스템 상태를 읽고 이어서 작업하니까요.

이 패턴은 [snarktank/ralph](https://github.com/snarktank/ralph) (9.2K stars) 같은 파생 프로젝트를 만들어냈고, oh-my-opencode 같은 도구들도 `/ralph-loop` 명령어로 이걸 내장하고 있습니다.

그럼 프로덕션에서도 이렇게 돌리면 되나?

그대로는 어렵겠다 싶었습니다. Ralph Loop를 뜯어보고 나서 든 첫 번째 생각이 "이걸 고객한테 쓸 수는 없겠는데"였거든요.

이유를 가장 잘 보여주는 프로젝트가 Peter SteinbergerOpenClaw (152K+ stars)입니다. Steinberger는 PDF SDK인 PSPDFKit을 13년간 운영한 후 매각한 iOS 개발자인데, 이 사람이 만든 건 코딩 도구가 아니라 진짜 비서입니다. WhatsApp, Slack, Discord, iMessage, Telegram 등 12개 이상 채널을 연결하고, Mac에서 로컬 실행되면서 자율적으로 행동합니다. 비행기 체크인도 해주고, 이메일 요약도 해주고, 캘린더 관리도 해줍니다.

그런데 이게 커밋이 8,700개가 넘어요.

왜 이렇게 커졌나 뜯어보니까, 결국 프로덕션에서 자율 루프를 돌리려면 bash 한 줄로는 해결 안 되는 문제들이 쏟아집니다. WhatsApp으로 온 사람이랑 Slack으로 온 사람이 같은 사람인지 어떻게 알지? 에이전트가 멋대로 결제를 해버리면? 루프가 버그로 무한 반복하면 비용은? 같은 작업을 두 번 실행해도 안전한가? 그리고 오늘 짚어볼 질문 — "끝났다"를 어떻게 정의하지?

OpenClaw는 soul document(에이전트의 성격/행동 원칙을 정의한 문서), Gateway 기반 세션 라우팅, 사용량 모니터링 같은 것들로 이 문제들을 하나하나 해결했습니다. 다만 코드가 커서 핵심 패턴을 뽑아내기가 어려웠는데, HKUDS의 Nanobot (~1.4K stars)이라는 프로젝트가 같은 구조를 4,600줄로 구현하고 있어서 이걸로 뜯어봤습니다.

핵심은 `AgentLoop` 클래스 330줄에 다 들어있었어요:

Python

LLM을 호출하고, 도구 호출이 있으면 실행하고, 없으면 종료. max_iterations가 무한 루프를 막는 가드레일이고, has_tool_calls 분기가 "계속할지 멈출지"를 결정합니다. while문, 상태 누적, 종료 조건 — 어디서 많이 본 구조죠. 에이전트 루프라는 이름을 빼면 전통적인 재시도 패턴이랑 크게 다르지 않습니다.

그런 눈으로 우리 FrontALF 엔진 코드를 다시 열어봤는데, "아 이것도 결국 같은 구조였구나" 싶은 부분이 꽤 있었습니다.

채널톡 FrontALF는 어떻게 다른가

여기서부터가 좀 다른 이야기입니다.

Ralph Loop나 OpenClaw가 추구하는 건 "끝까지 시도해서 답을 찾아내는 자율성"입니다. 개인 프로젝트나 개발 도구라면 그게 맞아요. 루프가 좀 오래 돌아도, 비용이 좀 나와도, 결과만 좋으면 되니까.

그런데 채널톡의 FrontALF 엔진도 비슷한 루프를 돌리고 있는데, 놓인 상황이 달랐습니다. 고객이 "배송비 환불 받을 수 있어?"라고 물었는데 ALF가 10분째 RAG 검색을 반복하고 있으면요? 아무리 정확한 답이 나와도 이미 고객은 떠났습니다. 환불 처리를 자동화했는데 API 호출이 실패해서 무한 재시도를 하고 있으면요? 같은 환불이 두 번 처리될 수도 있습니다.

그래서 우리한테는 끈질김만큼이나 "언제, 어떤 이유로 종료되는지"가 중요했습니다.

Agent Loop vs Task — 루프를 두 개로 나눈 이유

처음부터 두 개였던 건 아닙니다. 고객 질문에 답변하는 것(Agent Loop)과, 환불 같은 실제 액션을 처리하는 것(Task)은 요구사항이 너무 달랐습니다.

Agent Loop는 고객 질문에 정확히 답변하는 패턴입니다. RAG로 고객사의 지식(FAQ, 제품 정보, 정책)을 검색해서 답변을 만들어요. 상태는 currentHistory에만 쌓이고, 세션이 끝나면 사라집니다. 단순하고 빠르죠.

Go

Task는 사람이 반드시 해야 했던 액션까지 AI에 위임하는 패턴입니다. 여러 노드(Start → Code → Function → Message → End)를 거치면서 TaskMemory에 상태를 저장해요. 중간에 실패해도 해당 노드부터 재실행할 수 있고, 민감한 작업은 사람 승인을 기다릴 수도 있습니다.

Go

구분

Agent Loop

Task

상태 관리

Stateless (currentHistory만)

Stateful (TaskSession, TaskMemory)

용도

질문-답변, 검색

다단계 워크플로우, 외부 대기

종료 조건

checkShouldContinue() 또는 maxTurns

워크플로우 완료 또는 실패

재실행

처음부터 다시

특정 노드부터 가능

한마디로, Agent Loop는 "한 번에 끝내기", Task는 "나눠서 진행하기"입니다.

둘 다 공유하는 안전장치 — maxTurns

두 패턴 모두 공통으로 가지고 있는 게 하나 있습니다. "언제 멈출 것인가"에 대한 하드 리밋이에요.

Go

아무리 복잡한 작업이라도 10턴 안에 끝내든지, 아니면 MaxReachedAgent가 지금까지의 작업을 정리합니다. 이 에이전트의 역할은 하나예요: 지금까지 뭘 했는지 요약하고, 사용자가 다음에 뭘 할 수 있는지 알려주는 것.

Ralph Loop는 무한 루프라서 멈추는 조건이 없어요. 개인 프로젝트라면 그래도 되는데, 고객 응대에서 10턴째 돌고 있으면 비용도 비용이지만 고객이 기다리고 있거든요. 그냥 묵묵히 끊기는 것과, "여기까지 했고, 이렇게 이어가시면 됩니다"라고 정리해서 넘기는 것은 사용자 경험에서 완전히 다릅니다.

솔직히 이 10이라는 숫자가 최적인지는 아직 모릅니다. 단순 질문은 3턴이면 충분한데, 복잡한 워크플로우는 10턴도 빠듯할 때가 있어요. 상황에 따라 동적으로 조절하는 게 다음 과제 중 하나입니다.

이 안전장치를 깔아두고, 실제로 각 패턴이 어떻게 도는지 보겠습니다.

실제로 Agent Loop가 도는 모습

"배송비 환불 받을 수 있어?" — 단순해 보이는 질문인데, 정확한 답을 주려면 3턴이 필요합니다.

Plaintext
Turn 1: rag_search("배송비 환불") → RagSearchStartEvent
  └─ RAG Handler 내부:
     - questionAnalysis: "배송비", "환불", "정책" 키워드 추출
     - search attempt 1: 결과 2건, 충분성 판단 → 부족
     - 쿼리 재생성: "반품 배송비 부담", "무료 반품 조건"
     - search attempt 2: 결과 5건, 충분성 판단 → 충분
     └─ RagSearchResultEvent 발행
Turn 2: RagResultAgent 활성화
  └─ 검색 결과 5건을 컨텍스트로 답변 생성
  └─ "무료 반품 조건에 해당하면 배송비 환불이 가능합니다..."
Turn 3: message tool → 최종 응답 전송 (break)

여기서 중요한 건 Turn 1입니다. RAG Handler가 단순 검색이 아니에요. 질문 분석 → 검색 → 충분성 판단 → 재검색 이 미니 루프를 내부에서 돌립니다. 첫 검색으로 2건밖에 못 찾으면 쿼리를 재생성해서 다시 검색하는 거죠. 이 과정이 currentHistory에 쌓여서 Turn 2에서 5건의 검색 결과를 한꺼번에 보고 답변을 만들 수 있습니다.

Ralph Loop가 "끝날 때까지 반복"이라면, 여기서는 같은 원리가 "충분한 검색 결과를 찾을 때까지 반복"이라는 형태로 나타나는 겁니다.

Task가 필요한 순간 — 환불 처리

이번에는 질문이 아니라 액션입니다. 고객이 "주문 취소하고 환불받고 싶어요"라고 했을 때, 여기엔 Agent Loop로는 부족한 이유가 있어요. 고객사마다 환불 정책이 다르고, 주문 상태(배송 전/후)에 따라 처리 방식이 달라지고, 부분 환불/전체 환불 계산도 필요합니다. 무엇보다 돈이 오가는 작업이라 실수가 허용되지 않아요. 이전에는 반드시 사람이 처리해야 했습니다.

Task는 이런 파이프라인을 노드로 쪼개서 처리합니다:

Plaintext
[Start Node] 주문 정보 조회
     ↓ TaskMemory["order"] = {id, status, items, paid_at, ...}
[Code Node] 환불 정책 확인
     ↓ TaskMemory["policy"] = {refundable: true, reason: "배송 전 취소"}
[Code Node] 환불 금액 계산
     ↓ TaskMemory["refund"] = {amount: 35000, method: "원결제수단"}
[Message Node] 담당자 승인 요청 (선택적)
     ↓ 고액이거나 예외 케이스면 사람 승인 대기
[Function Node] 실제 환불 API 호출
     ↓ TaskMemory["result"] = {success: true, refund_id: "RF-12345"}
[Message Node] 고객에게 완료 안내
     ↓
[End Node] 완료

Agent Loop였다면 Function Node에서 API 호출이 실패했을 때 전체를 처음부터 다시 돌려야 합니다. 주문 정보 다시 조회하고, 정책 다시 확인하고. Task는 해당 노드만 재실행하면 됩니다. TaskMemory에 이전 단계 결과가 다 남아있으니까요.

그리고 담당자 승인 대기. Agent Loop는 기본적으로 동기적이라서 "누군가의 응답을 기다리며 멈춰있기"가 어렵습니다. Task는 Message Node에서 멈춰놓고 승인이 오면 다음 노드로 넘어가는 게 자연스럽습니다.

새로운 건 없었다

여러 프로젝트를 뜯어보고 느낀 건, 에이전트 루프에 새로운 Computer Science가 없다는 겁니다. while문, 상태 머신, 재시도 패턴, graceful shutdown — 전부 예전부터 있던 것들이에요. Ralph Loop의 bash 한 줄이든, Nanobot의 330줄이든, ALF 던, 벗겨보면 상태를 누적하면서 종료 조건까지 실행되는 반복문입니다.

"에이전트"라는 이름이 붙으니까 뭔가 새로운 패러다임처럼 느껴지는데, 실제로는 전통적인 패턴들을 LLM이라는 새 재료에 맞게 잘 말아 넣은 거에 가깝습니다. 차이는 반복문 안에서 LLM이 "다음에 뭘 할지"를 결정한다는 것, 그리고 "끝"을 어떻게 정의하느냐에 있어요. Ralph Loop는 정의하지 않고(무한 루프), OpenClaw는 soul document와 타임아웃으로 정의하고, 우리는 maxTurns + MaxReachedAgent로 정의합니다.

더 똑똑한 한 번의 호출보다, 실패와 정보 부족을 반복과 종료 설계로 흡수하는 것. 그게 핵심이었고, 그건 오래된 엔지니어링 원칙이기도 합니다.

저희도 아직 실험 중입니다. maxTurns를 동적으로 조절하는 것, Task 노드 간 의존성을 더 유연하게 만드는 것, RAG 충분성 판단을 개선하는 것 — 풀어야 할 문제가 많습니다. 이런 문제를 같이 풀어갈 분을 찾고 있어요. 채용 공고 보기

참고

Ralph Loop (Geoffrey Huntley)

snarktank/ralph (9.2K stars)

OpenClaw (152K+ stars)

Nanobot (~1.4K stars)

We Make a Future Classic Product