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로 재개)"]
추측 클래스(assumption_only_sections로 분리): assumption, inference, conservative_default, auto_fill_inference (그 외 blocker 등)
충돌 해소: SOURCE_PRIORITY → confidence → 동점이면 CONFLICTING(결정론적).
선택 필드: reversible(기본 True), rationale, evidence(인용 목록).
수렴·진동 측정 단위 (OntologyDelta.similarity · core/lineage.py): 가중 유사도 = 0.5*name_score + 0.3*type_score + 0.2*exact_score(이름 존재 0.5 / 같은 타입 0.3 / 설명까지 동일 0.2). 이 값이 수렴·정체·진동 판정의 입력이다.
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[협력적 중단 체크포인트]
드리프트 감시 두 경로: (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 트리거 시).
펼쳐보기: Ralph 정지 조건 ( ralph_loop.py)
성공: qa passed · converged
실패: failed · interrupted · exhausted · stagnated
이상 패턴: oscillation_detected(최근 window findings_hash 동일) · grade_regressing(최근 window 등급 단조 하락)
시간 한계: iteration_timeout(기본 1800s) · wall_clock_exhausted
실제 예시
펼쳐보기: 진화 루프 한 세대 골격 (실제 발췌 evolution/loop.py)
# _run_generation_phases, Gen2+ 골격if generation_number > 1 and lineage.generations: prev_gen = next( (g for g in reversed(lineage.generations) if g.phase == GenerationPhase.COMPLETED), lineage.generations[-1], ) # Wonder → "무엇을 더 배워야 하나" wonder_result = await self.wonder_engine.wonder( current_ontology=current_seed.ontology_schema, evaluation_summary=prev_gen.evaluation_summary, execution_output=prev_gen.execution_output, lineage=lineage, seed=current_seed, ) # Reflect → ontology 변이안 생성 reflect_result = await self.reflect_engine.reflect( current_seed=current_seed, execution_output=prev_gen.execution_output or "", evaluation_summary=prev_gen.evaluation_summary, wonder_output=wonder_output, lineage=lineage, ) # Seed 재생성 + ontology delta 계산 new_seed = self.seed_generator.generate_from_reflect(current_seed, reflect_output).value ontology_delta = OntologyDelta.compute(current_seed.ontology_schema, new_seed.ontology_schema) current_seed = new_seed
# evolution/convergence.py — eval 게이트(수렴 차단 영수증)if self.eval_gate_enabled and latest_evaluation is not None: eval_blocks = not latest_evaluation.final_approved or ( latest_evaluation.score is not None and latest_evaluation.score < self.eval_min_score # 0.7 ) if eval_blocks: return ConvergenceSignal( converged=False, reason=f"Ontology stable (similarity {latest_sim:.3f}) but evaluation unsatisfactory", ontology_similarity=latest_sim, generation=current_gen, )
위 내용은 ouroboros 소스에서 직접 확인한 스키마·상수·단계순서를 근거로 한다. 드리프트 가중치 0.5/0.3/0.2와 ≤0.3 게이트, 수렴의 다중 게이트(eval≥0.7 + AC/regression + evolution + validation), 출처 5종 evidence-backed vs 추측 클래스 분리, 충돌의 결정론적 해소(source priority→confidence→CONFLICTING)는 코드 상수와 일치함.
/home/seunghyeong/harness-work/ouroboros/src/ouroboros/events/lineage.py (lineage_created/generation_*/ontology_evolved/converged/exhausted/stagnated 이벤트 팩토리)