자기개선·반복 루프 — 횡단분석
한 줄 요약
“스스로 고쳐가며 반복한다”는 대부분 `Stop을 막고 프롬프트를 다시 던지는’ 한 점으로 수렴하지만, 산출물 자체를 진화시키는 진짜 자기개선은 ouroboros와 OMC self-improve 둘뿐이다. 왜 배우나: 내 스택(Claude+Codex+OMC)에 무엇이 이미 충분하고 무엇이 빠졌는지를 알아야, 베낄 조각을 정확히 떼어다 끼울 수 있기 때문.
그림
아래 mermaid는 위 개념도를 흐름으로 한 번 더 풀어 본 것이다. 모든 반복 루프는 “한 단위 실행 → 멈출지 판정 → 못 멈추면 반성을 입혀 재주입”의 사이클을 돈다. 다른 점은 반성을 얼마나 깊게 입히느냐다.
flowchart TD A["한 단위 실행<br/>turn · story · generation · gate-retry"] --> B{"멈춰도 되나?<br/>수렴/완료 신호 검사"} B -->|"완료"| Z["종료 · 영수증 기록"] B -->|"미완"| C{"무한루프 가드<br/>N회 상한 초과?"} C -->|"초과"| Y["강제 종료 · 격리/에스컬레이션"] C -->|"여유 있음"| D[반성 주입] D --> D1["얕음: 도구결과를 히스토리에 재주입<br/>(CC · Codex · gajae)"] D --> D2["중간: 같은/보강 프롬프트 재주입<br/>(ralph-loop · OMC ralph)"] D --> D3["깊음: 사양/온톨로지 자체를 다시 씀<br/>(ouroboros · self-improve 토너먼트)"] D1 --> A D2 --> A D3 --> A
쉽게 풀기
이 주제는 “AI가 한 번 답하고 끝나지 않고, 스스로 더 돌게 만드는 장치”에 대한 이야기다. 단계별로 풀어 보자.
- 반복의 최소 단위를 정한다. “한 번 더 호출”이 아니라 상태가 쌓이는 한 묶음을 한 바퀴로 본다. 누구는 대화 한 턴(turn), 누구는 사용자 스토리 하나(story), 누구는 한 세대(generation), 누구는 게이트 재시도(gate-retry)를 한 바퀴로 친다.
- 멈출 때를 정한다. 다 했다는 신호가 와야 멈춘다. 약한 버전은 “약속 문구를 출력하면 완료”이고, 강한 버전은 “여러 조건이 동시에 충족돼야 완료”다.
- 반성을 다음 입력에 끼워 넣는다. 비유하면, 시험을 보고 채점 결과(틀린 이유)를 받아 다음 회차 답안에 반영하는 것이다. 얕게 반영하면 “방금 결과만 보고 한 번 더”, 깊게 반영하면 “문제 자체를 다시 출제”한다.
핵심 트릭 하나만 기억하면 된다. Claude Code가 외부에 열어 준 “루프를 더 돌리는” 유일한 문은 Stop 훅이다. 모델이 끝내려 할 때 훅이 block을 외치고 이유(reason)를 돌려주면, Claude Code는 그 이유를 다음 턴의 사용자 입력처럼 모델에게 보여준다. 모델은 “어, 새 지시가 왔네?” 하고 다시 일한다. Claude 위에 사는 거의 모든 하네스가 이 한 문으로 몰린다.
무한 핑퐁 주의
자기참조 루프의 1번 실패는 “끝없이 서로 부르기”다. 그래서 모두 가드를 둔다. Claude Code는 8연속 block이면 훅을 무시하고 강제 종료, ralph-loop는
MAX_ITERATIONS, ouroboros는max_generations, 내 akh2 패턴은 2번 실패하면 격리(quarantine)한다.
핵심 정리
반복 단위 · 종료 신호 · 반성 주입의 세 질문으로 본 요약 (자세한 행별 메커니즘은 아래 콜아웃으로 분산):
| 진영 | 대표 | 반복 단위 → 종료 신호 |
|---|---|---|
| Stop-hook persistence | CC native · ralph-loop | 턴 → 약속문구/상한 |
| evaluator-backed persistence | OMC ralph · autoresearch | 스토리 → PRD pass+리뷰 / max-runtime |
| robust runtime (자기개선 없음) | Codex · gajae-code | 턴 → end_turn |
| 진짜 진화 | self-improve · ouroboros | 세대 → 다중 AND 게이트 |
| artifact admission gate | 내 akh2 패턴 | gate-retry → 결정적 pass |
persistence 진영 — "완료 보장"이 목표 (산출물은 안 진화)
- CC native:
Stop훅decision:"block"+reason재주입이 유일한 루프 확장점. 자기개선 메커는 없음. 8연속 block 가드.- CC /goal: 세션 스코프 내장 persistence loop. 작은 evaluator가 매 턴 목표 충족을 판정해 다음 턴 유도.
- ralph-loop plugin:
stop-hook.sh가jq -n '{decision:"block", reason:$prompt}'로 같은 프롬프트(SAME PROMPT) 통째 재주입. 완료=프롬프트 속 약속문구, 종료=MAX_ITERATIONS.- OMC ralph:
prd.json(story별passes:bool+AC). 키워드→CCUserPromptSubmit훅 주입으로 발동(자체 루프 없음). 완료=모든 story pass AND 별도 reviewer(architect/critic/codex) 검증 AND deslop+회귀 재실행. writer≠reviewer.- fable-ish: ledger(
changed_files_seen/verification_results/stop_blocks)를 보고 Stop 차단.MAX_STOP_BLOCKS=2, “말만 하고 tool_use 없이 끝낸 턴” 정규식 탐지, fail-open.- 내 akh2 패턴:
admit.mjs결정적 게이트. FAIL 사유 JSON을 워커가 읽고 재시도, 2-fail→quarantine/격리→Claude 에스컬레이션. 경계는 fail-closed.
robust runtime 진영 — 의도적으로 자기개선을 안 넣음
- Codex:
run_turnloop, 도구결과(FunctionCallOutput)를 히스토리에 실어 재샘플링.end_turn==Some(false)면 follow-up 강제. mid-turn auto-compact, depth 가드. (단 Codex에도Stop훅의decision:"block"reason 재주입은 있다 — 코어 run_turn만 보면 없다고 좁혀야 정확.)- gajae-code: 두 겹 루프(바깥 while + 안쪽 도구). Harmony-leak truncate/온도+0.05 재시도(2회 후 escalate), abort race, 영수증. 자기개선은 상위 워크플로(
ultragoal/ralplan/deep-interview) 계층에 분리.
evolution 진영 — 사양/코드 자체를 진화
- OMC self-improve: Research→Plan→리뷰→N executor 병렬→Tournament(승자 1개 머지). no-regression(승자도 best_score 이상), 회귀 시
git reset --hard HEAD~1롤백. 종료 4종(target/plateau/max-iter/circuit-breaker),sealed_files로 평가기 자기수정 차단. 고정된 goal/benchmark 아래 “구현 개선” 탐색.- ouroboros:
wonder→reflect→seed→execute→evaluate. 이전 세대 평가요약을 Wonder(공백 질문)→Reflect(온톨로지 변이)에 입력해 사양 자체를 다시 씀(뱀이 꼬리 뭄). 드리프트 게이트(goal0.5/constraint0.3/ontology0.2 ≤0.3), 정상 수렴 경로에서만 다중 AND 게이트, stagnation/oscillation은 별도 종료. spec/ontology/lineage까지 진화.
모두가 공유하는 설계 원칙 (체크리스트):
- Stop 차단 + 프롬프트 재주입이 CC 위 하네스의 사실상 표준 (단, 재주입 내용은 같지 않음 — 같은 프롬프트/구체 사유/상태 지시로 갈림)
- 무한루프 가드 필수 (N회 상한 또는 “두 번 알리고 양보”)
- 반복 단위 = 상태가 쌓이는 묶음, 외부 파일/이벤트에 영속화해 세션 넘어 재개 (CC native·Codex 코어는 예외 — 상태 강제 안 함)
- 자율 모드는 “사람에게 안 묻는다”를 명시 정책으로 (self-improve “NEVER stop to ask”)
- fail-open (안전장치가 작업을 죽이면 안 됨 — 단 artifact 경계는 fail-closed)
실제 예시
가장 단순한 ralph 원형. Stop 훅이 같은 프롬프트를 통째로 다시 던진다.
# ralph-loop/hooks/stop-hook.sh (원형)
# 완료조건 미달이면 SAME PROMPT를 reason으로 재주입 → CC가 다음 턴 입력처럼 노출
jq -n --arg prompt "$PROMPT" '{decision:"block", reason:$prompt}'
# 종료: MAX_ITERATIONS 도달 시 RALPH_STATE_FILE 삭제 후 exit 0OMC self-improve의 진화 설정 — 점수만 보는 게 아니라 회귀·정체·차단기를 함께 본다.
// oh-my-claudecode/skills/self-improve/settings.json (핵심 키)
{
"max_iterations": 20,
"plateau_threshold": 0.01, // 개선폭이 이보다 작으면 정체로 카운트
"plateau_window": 3,
"target_value": 0.95, // 도달 시 종료
"regression_threshold": 0.0, // 승자도 best_score 이상이어야 머지
"circuit_breaker_threshold": 3, // 연속 악화 N회 시 차단
"sealed_files": ["benchmark/**", "eval/**"] // 평가기 자기수정 차단
}ouroboros 드리프트 게이트 — 점수가 올라도 원래 목표에서 이탈하면 잡는다.
# ouroboros/src/ouroboros/observability/drift.py (구조)
combined_drift = 0.5 * goal_drift + 0.3 * constraint_drift + 0.2 * ontology_drift
DRIFT_THRESHOLD = 0.3 # 초과 시 한 라운드 강제 중단·재정렬요약 & 셀프체크
3줄 요약:
- 모든 반복 루프는 “한 단위 실행 → 종료 판정 → 반성 주입 후 재주입”을 돌고, CC 위에서는 Stop 훅이 유일한 확장점이라 다들 거기로 수렴한다.
- 최대 분기선은 persistence(완료 보장, 산출물 불변) vs **evolution(사양/코드 진화)**이고, 진화 진영만 드리프트·no-regression 같은 안전장치를 추가로 짊어진다.
- 내 스택에 빠진 건 (a) 결정적 비용계단 게이트 (b) score 외 drift 측정 (c) 실패 격리·학습 — 셋 다 ouroboros+akh2에 완성형이 있다.
스스로 답해 보기:
- Stop 훅이 돌려주는
reason이 모델 입장에서 무엇으로 보이며, 왜 그게 “루프 연장” 트릭인가? - “변화 없음 = 수렴”이 왜 자기개선 루프에서 위험한 오판인가? ouroboros는 이걸 어떻게 막나?
- 검증을 항상 LLM reviewer로 돌리지 않으려면 어떤 “비용 계단”을 두는가? (Stage1/2/3)
연결
기능노트 근거:
- CC_10_agent-loop — Stop 훅
decision:block+reason재주입, 턴/도구 cadence, 8연속 block 가드, ralph-loop stop-hook.sh - CX_10_agent-loop-and-turn —
run_turnloop,end_turnfallback, mid-turn auto-compact, depth 가드 - OMC_10_entrypoint-hooks-loop — OMC는 자체 루프 없음, CC Stop/UserPromptSubmit 훅 위에 MAGIC KEYWORD 주입
- GJ_10_agent-loop — 두 겹 루프, followUp 재진입, Harmony-leak truncate/retry, abort race, 영수증
- OB_90_evolutionary-loop-drift-and-audit-ledger — 진화루프(Wonder→Reflect→Seed), 드리프트 게이트, 다중 수렴신호, 감사원장
- OB_40_orchestrator-execution-loop — AC-tree 레벨 병렬 실행, 코디네이터 리뷰 게이트, stall/heartbeat
- FB_70_stop-completion-gate — ledger 기반 Stop 차단, MAX_STOP_BLOCKS=2, stated_but_unstarted 정규식, fail-open
- MINE_90_pipeline-gates-receipts — admit/gate/claim-card/git-trailer, FAIL→재시도→2-fail→quarantine, 결정적 게이트
소스/문서:
/home/seunghyeong/harness-work/oh-my-claudecode/skills/ralph/SKILL.md— PRD-driven persistence, reviewer 선택, mandatory deslop+회귀/home/seunghyeong/harness-work/oh-my-claudecode/skills/self-improve/SKILL.md— tournament selection, no-regression+git reset --hard, 종료 4종, sealed_files/home/seunghyeong/harness-work/oh-my-claudecode/skills/autoresearch/SKILL.md— evaluator JSON(pass+score), max-runtime 정지, decision-log.md/home/seunghyeong/harness-work/ouroboros/src/ouroboros/evolution/loop.py·convergence.py·observability/drift.py·ralph_loop.py·evaluation/pipeline.py·auto/ledger.py/home/seunghyeong/harness-work/codex/codex-rs/core/src/session/turn.rs—run_turn, auto-compact, depth (+hooks/src/events/stop.rs)/home/seunghyeong/harness-work/gajae-code/packages/agent/src/agent-loop.ts— 두 겹 루프, Harmony 처리/home/seunghyeong/harness-work/fable-ish/hooks/stop_gate.py·scripts/verify_state.py— should_block_stop/mnt/d/akh2/pipeline/admit.mjs·gate.mjs·golden-queries.json— 결정적 게이트·물리 거부·회귀검증/home/seunghyeong/.claude/plugins/marketplaces/claude-plugins-official/plugins/ralph-loop/hooks/stop-hook.sh— SAME PROMPT 재주입 원형- 공식문서:
/mnt/d/6study/10_프레임워크분석/_원문아카이브/claude-code/hooks.md(Stop 결정 제어·8연속 block),cli-reference.md(--max-turns/--max-budget-usd)
내 스택에 차용할 구체안
내 스택은 CC(native Stop)+OMC(ralph/self-improve/autoresearch)를 이미 갖췄다. 빈 곳은 “진짜 진화”와 “결정적 게이트 비용계단”. ouroboros·akh2에서 떼어 온다.
- OMC ralph 완료조건에 다중 AND 게이트 이식. 단
changed_count>0을 항상 요구하면 분석·설정확인 같은 합법 no-op 완료가 막힌다 → “AC별 증거 ledger + 검증 명령 + 변경 파일 매핑 + regression 결과”를 reviewer 직전에 강제하는 형태가 낫다. fable-ish ledger를 PostToolUse 훅으로 채운다. - self-improve 위에 드리프트 측정 추가. score만 보면 점수는 오르는데 goal에서 이탈하는 사례를 못 잡는다. ouroboros
combined_drift ≤0.3을 측정하되, hard stop보다 critic 에스컬레이션/quarantine 트리거가 낫다(단어 Jaccard만으로 판정하면 좋은 개선도 오탐 —goal.md/sealed files/changed-file scope/benchmark category를 같이 본다). - 비용 계단 평가. 항상 LLM reviewer로 검증하지 말고 Stage1 결정적 코드 게이트(lint/test, $0, 실패 즉시 종료) → Stage2 단일 reviewer → Stage3 고위험 diff만 multi-model 합의. 단
admit.mjs는 wiki artifact 전용이라 그대로 못 옮기고 일반화해야 한다. - 실패 후보 bundle 격리. self-improve가 실패 원문을 안 남기는 건 아니다(
failure_analysis/iteration history/merge report 존재). 부족한 건 “사람이 바로 읽을 수 있는 실패 후보 bundle” — akh2 quarantine + autoresearch decision-log.md 결합. - stated-but-unstarted 정규식 가드를 공통 Stop 훅으로. fable-ish의 “말만 하고 끝낸 턴” 탐지를 OMC
context-guard-stop.mjs에 합치되, user input/권한/context-limit/auth 대기와 충돌 금지 — 기존 가드 앞단에 1~2회 제한 counter로 넣는다.
핵심 한 줄: persistence는 OMC ralph로 충분, 부족한 건 (a) 결정적 비용계단 게이트 (b) score 외 drift 측정 (c) 실패 격리·학습.
Codex 교차검증 (gpt-5.5, xhigh)
본 문서를
/home/seunghyeong/harness-work/소스와 대조한 결과, 방향은 맞지만 “반복 지속·검증 게이트·자기개선·사양 진화”를 한 표에 섞으면서 몇 군데 과장됨. 핵심 수정점만:
- 하네스 개수: 본문이 “7개”라 했으나 표엔 9개. fable-ish는 독립 행으로 넣어야 함.
- Codex/CC: Codex에도 Stop 훅 reason 재주입이 있다(
hooks/src/events/stop.rs) → “코어 run_turn만 보면 없음”으로 좁혀야. CC/goal도 별도 persistence loop로 누락.- “표준 ABI”·“모든 상태 영속”·“fail-open 공통”은 과한 일반화: 재주입 내용이 제각각이고, CC native/Codex 코어는 상태 강제 안 하며, akh2 admit.mjs는 fail-closed.
- 진화 진영 구분: self-improve=고정 goal 아래 구현 개선, ouroboros=spec/ontology/lineage 진화 — 같은 것으로 묶으면 안 됨.
- ouroboros 수렴: 다중 AND 게이트는 “유사도 기반 정상 수렴 경로”에서만. stagnation은
CONVERGED가 아니라STAGNATED로 별도 처리.- 종합: 비교 축을
Stop-hook persistence · evaluator-backed persistence · workflow/receipt gate · benchmark tournament · spec/ontology evolution · artifact admission gate로 다시 나누면 강해진다.