ouroboros · 고유기능: 진화 루프 · 드리프트 게이트 · 감사 원장(provenance)

한 줄 요약

ouroboros는 실행→검증→반성→재생성을 반복하며 스스로 사양을 개선하되, 목표 이탈을 막고(드리프트 게이트) 모든 결정의 출처를 영수증으로 남긴다(감사 원장). 왜 배우나: 단발 생성이 실패하는 건 사양에 숨은 가정 때문이다. 이 셋이 맞물려 가정을 드러내고·이탈을 막고·근거를 추적 가능하게 만든다.


그림

flowchart TD
    S[1세대 사양 Seed] --> EX[실행 Execute]
    EX --> EV["3단계 평가<br/>기계검증→시맨틱→다중모델합의"]
    EV --> DR{"드리프트 ≤ 0.3 인가?"}
    EV --> CV{"수렴했나?<br/>유사도≥0.95 / 정체 / 진동"}
    CV -- 아니오 --> W["Wonder: 온톨로지 공백을 질문으로"]
    W --> R["Reflect: 온톨로지 변이안 생성"]
    R --> SG["Seed 재생성 + OntologyDelta 계산"]
    SG --> EX
    CV -- 예/소진 --> END["수렴 / 소진 / 정체로 종료"]
    EX -. Write·Edit 훅 .-> DM[drift-monitor.py 자동 호출]
    EV -. 이벤트 기록 .-> LOG["(EventStore<br/>lineage.* / drift.measured)"]

뱀이 자기 꼬리를 무는(ouroboros) 것처럼, 한 세대의 출력이 다음 세대의 입력이 된다.


쉽게 풀기

세 부품이 맞물려 돈다.

1. 진화 루프 — “초안을 계속 고쳐 쓰는 작가” 글을 쓰고(실행)→편집자가 보고(검증)→점수를 매긴다(평가). 여기까지가 한 세대(generation). 이어서 “뭘 놓쳤지?”를 묻고(Wonder), 반성해서(Reflect), 원고를 다시 쓴다(Seed 재생성). 개념 구조(ontology)가 안정되거나(수렴) 최대 세대수에 닿을 때까지 반복한다.

2. 드리프트 게이트 — “나침반” 고치다 보면 처음 의도에서 슬금슬금 벗어난다. 매 세대 “목표에서 얼마나 멀어졌나”를 목표 0.5 + 제약 0.3 + 온톨로지 0.2 가중치로 합산하고, 0.3을 넘으면 이탈 신호다. 사람이 아니라 코드를 고칠 때마다(Write/Edit) 훅이 자동으로 점검한다.

3. 감사 원장(provenance) — “출처가 적힌 영수증” 사양의 각 항목이 (가) 사용자가 말한 사실인지, (나) 저장소에서 읽은 사실인지, (다) 모델 추측인지를 출처로 분류해 기록한다. 핵심 규율: 추측과 근거를 절대 섞지 않는다.

바닥 — 이벤트 소싱(Event Sourcing) 모든 상태는 EventStore 이벤트의 **재생(replay)**으로 복원된다(거래내역으로 잔고 재계산). 덕분에 lineage_id 하나만 있으면 세션이 끊겨도 이어서 진화한다.

flowchart LR
    subgraph 진화루프
      A[실행] --> B[평가] --> C[Wonder] --> D[Reflect] --> E[Seed 재생성] --> A
    end
    B -.측정.-> G["드리프트 게이트<br/>≤0.3"]
    E -.기록.-> L["감사 원장<br/>근거/추측 분리"]
    A & B & C & D & E -.append.-> ES["(이벤트 소싱<br/>lineage_id로 재개)"]

핵심 정리

루프를 제어하는 손잡이 (EvolutionaryLoopConfig · evolution/loop.py)

손잡이기본값무엇을 정하나
max_generations30강제 종료 상한(세대수)
convergence_threshold0.95이 이상 유사도면 수렴 후보
stagnation_window3변화 없음 연속 N세대면 정체
eval_min_score0.7수렴 허용 최소 평가점수

드리프트 점수 공식 (DriftMetrics · observability/drift.py)

항목범위어떻게 재나
goal_drift0–1Seed 목표 텍스트와 Jaccard 거리
constraint_drift0–1제약 위반 × 0.1, 최대 1.0
ontology_drift0–1온톨로지 필드명 집합 Jaccard 거리

combined = goal*0.5 + constraint*0.3 + ontology*0.2, is_acceptable = combined ≤ 0.3(NFR5 게이트).

영수증 한 줄의 구조 (LedgerEntry · auto/ledger.py)

필드필수의미
key/valueY항목 식별자 / 결정값 (예: goal.primary)
sourceY출처 분류(아래 콜아웃)
confidenceY신뢰도 0–1(clamp)
statusYmissing/weak/defaulted/inferred/confirmed/conflicting/blocked

출처는 “근거”와 “추측”으로 갈리며, 충돌 시 모델이 판단하지 않고 결정론적으로 해소한다.

flowchart TD
    K[같은 key 충돌] --> P{SOURCE_PRIORITY 비교}
    P -- 우열 있음 --> WIN[높은 우선순위 채택]
    P -- 동급 --> CF{confidence 비교}
    CF -- 우열 있음 --> WIN
    CF -- 동점 --> CONF["CONFLICTING<br/>사람 결정 대기"]

AI 파이프라인에 어떻게 끼어드나

한 세대는 고정 순서로 흐르고, 각 단계 전후로 이벤트가 쌓인다. evolve_step은 상태를 들지 않고 매번 이벤트 재생으로 복원하므로 세션이 바뀌어도 이어 돈다.

flowchart LR
    T["ooo evolve/ralph/auto"] --> RP["replay_lineage<br/>이벤트 재생"]
    RP --> PJ["LineageProjector.project<br/>OntologyLineage 복원"]
    PJ --> RR["find_resume_point<br/>재개"]
    RR --> GEN["wondering→reflecting→seeding→executing→evaluating"]
    GEN -. lineage.generation.* append .-> ES["(EventStore)"]
    GEN -. _check_shutdown .-> SD[협력적 중단 체크포인트]
  • 트리거: ooo evolve/ralph/autoevolve_step()(한 세대). Ralph는 RalphLoopRunner.run()이 이를 백그라운드 잡으로 반복.
  • 드리프트 감시 두 경로: (a) 평가 직후 DriftMeasurement.measure()observability.drift.measured 이벤트 기록, (b) hooks/hooks.json PostToolUse(Write|Edit)가 편집마다 scripts/drift-monitor.py 호출(timeout 3s).
  • 모델 입력: 이전 세대의 평가요약(EvaluationSummary)·실행출력. Wonder가 온톨로지 공백을 질문으로, Reflect가 질문을 변이안으로 바꾼다.
  • 3단계 평가(evaluation/pipeline.py): Stage1 기계검증(lint/build/test/static/coverage, $0)→실패 시 즉시 종료 / Stage2 시맨틱(LLM, AC 준수 + score≥0.8) / Stage3 다중모델 합의(drift·ontology_changed 트리거 시).

실제 예시

직접 만들 때 — 복붙 가능한 최소 골격

직접 만들 때 체크리스트

  • 모든 상태 전이를 EventStore에 append, 재개는 이벤트 재생으로만(직접 영속화 금지 — OntologyLineage는 read model).
  • 단계 순서 고정: wondering→reflecting→seeding→executing→evaluating. 단계 전후 중단 체크포인트.
  • 드리프트 goal0.5/constraint0.3/ontology0.2, 게이트 ≤0.3. 매 세대 측정 이벤트 기록.
  • 수렴은 단일 신호 금지: 유사도≥0.95 AND eval(approved+score≥0.7) AND AC/regression AND ontology 실제 진화(evolved_count>0) AND validation 미스킵.
  • 정체(window=3)·진동(period-2 양쪽 반주기)·반복질문(70% overlap×2회) 별도 종료 신호.
  • 무한루프 방지: max_generations + per-iteration 타임아웃 + grade-regression/oscillation 정지(Ralph).
  • 원장: source로 evidence/assumption 분리, key 충돌은 priority→confidence→CONFLICTING(모델 판단 금지). gateway provenance는 env var로만, redact 후 기록.

요약 & 셀프체크

3줄 요약

  1. 진화 루프는 실행→평가→Wonder→Reflect→Seed 재생성을 수렴 또는 최대 세대수까지 반복해 사양 자체를 진화시킨다.
  2. 드리프트 게이트(목표0.5/제약0.3/온톨로지0.2, ≤0.3)가 목표 이탈을, 다중 게이트(eval·AC·regression·evolution·validation)가 품질 후퇴를 막는다.
  3. 감사 원장은 모든 결정의 출처를 근거/추측으로 분리해 기록하고, 이벤트 소싱 덕에 lineage_id 하나로 언제든 재현·재개된다.

스스로 답해보기

  • 드리프트 점수가 0.3을 넘으면 어떤 신호이고, 어떤 세 항목을 어떤 가중치로 합산하나?
  • “수렴(converged)“으로 종료되려면 온톨로지 유사도 외에 어떤 조건들이 함께 충족돼야 하나?
  • 감사 원장에서 assumptionrepo_fact를 절대 섞지 않는 이유는 무엇이고, key 충돌은 어떻게 해소하나?

연결

OB_개요 · _분석축_루브릭 · OB_30_event-sourcing-and-projection-readmodel · OB_40_orchestrator-execution-loop · OB_20_spec-engine-seed-and-double-diamond · OB_70_extension-points-hooks-skills-commands