ouroboros · 스펙 엔진: Seed 계약과 Double-Diamond(Interview→Ambiguity→Seed)
한 줄 요약
ouroboros의 스펙 엔진은 “코드를 짜기 전에 무엇을 만들지부터 수학적으로 못 박는” 장치로, 인터뷰로 요구사항을 캐묻다가 모호함 점수가 0.2 이하로 떨어지면 그 합의를 고칠 수 없는 설계서(Seed) 로 굳힌다.
왜 배우나: AI 코딩이 실패하는 진짜 원인은 출력이 아니라 입력(요구사항) 이 흐릿한 데 있다. “느낌상 됐을 때”가 아니라 “수치가 됐다 할 때” 작업을 시작하게 만드는 이 골격이, 에이전트 하네스가 안정적으로 일하는 출발점이기 때문이다.
그림
flowchart TD A["ooo interview / pm 시작"] --> B["인터뷰 엔진: 질문 생성 → 답변"] B --> C{"PM 모드인가?"} C -->|예| C1["질문 분류기<br/>(기획/개발 구분·재구성·나중에-결정)"] C1 --> D C -->|아니오| D["모호도 채점<br/>LLM이 명료도를 JSON으로 반환"] D --> E{"점수 ≤ 0.2 그리고<br/>모든 바닥값 통과?"} E -->|아직 모호함| B E -->|충분히 명확| F["Seed 생성기: 게이트 재확인"] F --> G["seed-architect 프롬프트로<br/>요구사항 추출"] G --> H["불변(frozen) Seed 모델 빌드<br/>→ YAML 저장"] H --> I["ooo run: Seed를 계약으로 해석"] I --> J["실행 프롬프트 섹션으로 렌더링"] J --> K["실행 AI가 계약을<br/>'렌즈'로 삼아 구현"] K -.다음 세대(Gen2+).-> L["회고: 개념(ontology)만 합의로 진화"] L --> H
쉽게 풀기
스펙 엔진을 “깐깐한 건축 사무소의 상담실” 이라고 생각하면 쉽다.
- 묻고 또 묻기 (인터뷰) — 보통 AI는 “앱 만들어줘” 한 마디에 바로 삽을 든다. ouroboros는 건축가처럼 “용도는? 예산은? 꼭 지켜야 할 조건은?”을 충분히 명확해질 때까지 캐묻는다. 사람과 AI가 소크라테스식 문답을 주고받는다.
- 명확함을 ‘점수’로 재기 (모호도 점수) — 여기서 핵심은, 명확함을 느낌이 아니라 숫자로 측정한다는 점이다. 매 라운드마다 LLM이 “목표가 얼마나 또렷한가, 제약이 또렷한가, 합격 기준이 또렷한가”를 0~1 점수로 매긴다. 이 점수들을 가중평균해 거꾸로 뒤집으면 “모호함 점수”가 나온다. 이 점수가 0.2 이하로 내려가야만 다음 칸으로 넘어갈 수 있다.
- 설계도 못 박기 (Seed) — 인터뷰가 끝나면 합의 내용을 고칠 수 없는 설계서(YAML) 로 굳힌다. 이것이 바로
Seed다. Seed는 워크플로의 “헌법”이라서, 목표(goal)·제약(constraints)·합격 기준(acceptance_criteria)은 한 번 박히면 절대 못 바꾼다. 유일하게 바꿀 수 있는 건 “개념 렌즈(ontology)“뿐인데, 이건 나중 세대에서 합의를 거쳐야만 조금씩 진화한다. - PM 모드라는 변형 — 같은 인터뷰를 제품 매니저 눈높이로 바꾼 것이 PM 모드다. 발산(아이디어 넓게 펼치기)과 수렴(좁혀 정리하기)을 오가는 더블 다이아몬드 방식이라, 질문을 “기획용/개발용”으로 분류하고 애매한 질문은 다시 다듬거나 “나중에 결정”으로 미뤄둔다.
- 설계도를 실제 지시문으로 변환 (렌더링) — 굳어진 Seed는 나중에 실행 AI에게 줄 프롬프트의
## Constraints,## Acceptance Criteria,## Auto Recursion Guard같은 섹션으로 자동 변환되어 주입된다. 즉 설계서가 곧 작업 지시서가 된다.
비유 한 줄
“느낌상 준비됐을 때 짓지 말고, 수학이 준비됐다 할 때 짓는다.” 시작점을 수치 게이트로 잠근 것이 스펙 엔진의 정체성이다.
핵심 정리
Seed = 불변 헌법. 목표·제약·기준은 못 고치고, 개념 렌즈만 진화한다.
| 항목 | 무엇 | 가변성 |
|---|---|---|
goal / constraints / acceptance_criteria | 목표·하드 제약·합격 기준 | 불변(frozen) |
ontology_schema | 개념 렌즈(결정이 모호할 때 보존할 경계) | 진화 가능 |
metadata | 출처 기록(점수·인터뷰ID·부모Seed) | 불변 |
모호도 게이트 한눈에
- 게이트 임계값:
AMBIGUITY_THRESHOLD = 0.2→ 점수가 0.2 이하면 Seed 만들 준비 완료- 공식:
모호도 = 1 − Σ(명료도 × 가중치)(즉 1 빼기 가중평균 명료도)- greenfield 가중치: Goal 0.40 / Constraint 0.30 / Success 0.30 (brownfield는 Context 0.15 추가, 나머지 하향)
- 마일스톤: INITIAL(≤1.0) → PROGRESS(≤0.4) → REFINED(≤0.3) → READY(≤0.2)
- LLM은 온도 0.1로 채점해 재현성 확보
자동 완료는 “점수 게이트”만으로는 부족하다. 아래를 동시에 만족해야 한다.
-
overall_score ≤ 0.2(가중평균 게이트 통과) - 각 차원의 바닥값(floor) 통과 — Goal 0.75 / Constraint 0.65 / Success 0.70 (brownfield Context 0.60)
- 둘 다 만족 시
qualifies_for_seed_completion참 → 인터뷰 종료
실제 예시
실제로 저장된 Seed YAML(발췌). ambiguity_score가 0.2 이하라는 게 “게이트를 통과한 증거”다.
# .ouroboros/seeds/seed_78c8e6e41813.yaml
goal: Build ouroboros_ac_tree_hud MCP tool that returns a render-ready, cursor-based
AC tree snapshot as compact markdown for live execution monitoring, ...
task_type: code
brownfield_context:
project_type: brownfield
context_references:
- path: src/ouroboros/events.py
role: primary
summary: EventStore with get_events_after pattern using last_row_id cursor, ...
constraints:
- Python 3.14+ with uv run
- Must integrate with existing EventStore and workflow.progress.updated events
- max_nodes default 50 as hard cap
acceptance_criteria:
- AC tree renders correctly for depth 1 trees
- Cursor returns delta one-liner when no new workflow.progress.updated events since cursor row ID
ontology_schema:
name: ACTreeHUD
description: Domain model for a cursor-based acceptance criteria tree HUD ...
fields:
- name: session_id
type: string
description: Session identifier for the execution being monitored
required: true
evaluation_principles:
- name: visual_correctness
description: Rendered markdown matches the specified format with correct icons ...
weight: 0.25
exit_conditions:
- name: all_ac_rendered
description: All acceptance criteria pass and tool renders correctly at all depth levels
criteria: AC tree renders for depth 1, 2, 3 with correct icons and tree structure
metadata:
seed_id: seed_78c8e6e41813
version: 1.0.0
created_at: '2026-04-04T18:09:30.786879Z'
ambiguity_score: 0.1105 # <= 0.2 게이트 통과한 증거
interview_id: interview_20260404_180516
parent_seed_id: null # Gen1(부모 없음)직접 Seed를 만들고 실행 프롬프트로 변환하는 최소 흐름(복붙용).
# my_spec_engine.py
from ouroboros.core.seed import (
Seed, SeedMetadata, OntologySchema, OntologyField,
EvaluationPrinciple, ExitCondition,
)
from ouroboros.core.seed_contract import SeedContract
from ouroboros.core.seed_contract_prompt import (
render_seed_contract_for_execution, render_auto_recursion_guard,
)
seed = Seed(
goal="Build a CLI task manager in Python",
constraints=("Python 3.14+", "No external database"),
acceptance_criteria=("Tasks can be created", "Tasks can be listed"),
ontology_schema=OntologySchema(
name="TaskManager",
description="Task domain lens",
fields=(OntologyField(name="tasks", field_type="array",
description="List of tasks"),),
),
evaluation_principles=(EvaluationPrinciple(name="completeness",
description="All ACs met", weight=1.0),),
exit_conditions=(ExitCondition(name="all_met",
description="All ACs pass", criteria="100% pass"),),
metadata=SeedMetadata(ambiguity_score=0.15), # <= 0.2 게이트 충족 표시
)
# 불변 확인: 아래는 ValidationError 발생 (헌법은 못 고친다)
# seed.goal = "new"
# 실행 프롬프트로 렌더링
prompt = render_seed_contract_for_execution(SeedContract.from_seed(seed))
prompt += "\n\n" + render_auto_recursion_guard()게이트를 라이브러리 없이 공식만으로 직접 계산해 보기.
# 모호도 = 1 − 가중평균(명료도)
clarity = {"goal": 0.9, "constraint": 0.85, "success": 0.8}
weight = {"goal": 0.40, "constraint": 0.30, "success": 0.30}
ambiguity = round(1.0 - sum(clarity[k]*weight[k] for k in clarity), 4)
ready = ambiguity <= 0.2 # → 0.135, True (게이트 통과)AI가 이걸 두 번 만지는 이유
스펙 엔진은 AI를 (1) 스펙을 만들 때(채점·추출)와 (2) 스펙을 소비시킬 때(실행 프롬프트로 렌더링) 두 군데서 호출한다. 채점 LLM은 “JSON만 출력”하게, 추출 LLM은
GOAL:/CONSTRAINTS: a | b같은 줄 기반 prefix 포맷으로 출력하게 해 파싱을 견고하게 만든다(JSON보다 파서 친화적). 렌더링 시 ontology는 “출력 형태가 아니라, 결정이 모호할 때 보존해야 할 개념 경계”로 주입된다.
요약 & 셀프체크
- ouroboros는 코드보다 먼저 요구사항을 다듬고, “충분히 명확함”을 모호도 점수 0.2 게이트로 수치화한다.
- 게이트를 통과한 합의는 고칠 수 없는 Seed(헌법) 가 되고, 오직 개념 렌즈(ontology)만 다음 세대에서 진화한다.
- Seed는 실행 단계에서 자동으로 프롬프트 섹션으로 변환되어, 실행 AI가 모든 결정을 이 계약을 통해 해석하도록 강제한다.
스스로 답해보기:
- “점수 게이트만 통과”하면 자동 완료되는가? (아니다 — 무엇을 추가로 만족해야 하는가?)
- Seed에서 절대 못 바꾸는 것과, 유일하게 바꿀 수 있는 것은 각각 무엇인가?
- 추출 LLM에게 JSON 대신 줄 기반 prefix 포맷을 시키는 이유는?
연결
근거 파일
/home/seunghyeong/harness-work/ouroboros/src/ouroboros/core/seed.py/home/seunghyeong/harness-work/ouroboros/src/ouroboros/core/seed_contract.py/home/seunghyeong/harness-work/ouroboros/src/ouroboros/core/seed_contract_prompt.py/home/seunghyeong/harness-work/ouroboros/src/ouroboros/bigbang/ambiguity.py/home/seunghyeong/harness-work/ouroboros/src/ouroboros/bigbang/pm_interview.py/home/seunghyeong/harness-work/ouroboros/src/ouroboros/bigbang/seed_generator.py/home/seunghyeong/harness-work/ouroboros/src/ouroboros/agents/seed-architect.md/home/seunghyeong/harness-work/ouroboros/.ouroboros/seeds/seed_78c8e6e41813.yaml/home/seunghyeong/harness-work/ouroboros/CLAUDE.md(ooo 명령 → skill 매핑)
Codex 교차검증 (원문 보존)
- Seed 불변성은 Pydantic
frozen=True+tuple타입 조합으로 해시 가능·불변을 강제한다. 손으로 쓴 seed를 위해evaluation_principles/exit_conditions는 문자열 리스트로 줘도 객체로 승격하는field_validator(mode="before")가 달려 있다.OntologyField:name/field_type(aliastype) /description/required:bool=True.- 채점 시 decide-later 항목은 “의도적 보류”로 명시해 점수 페널티를 주지 않는다.
- 추출 system 프롬프트는
seed-architect에이전트(src/ouroboros/agents/seed-architect.md)를 로드한다.- 렌더링은
render_seed_contract_for_execution()이## Seed Contract / ## Goal / ## Constraints / ## Ontology / ## Evaluation Principles / ## Exit Conditions섹션을 만들고, 별도로## Auto Recursion Guard(중첩 auto 호출 금지)를 붙인다.- ontology는 “It is not a mandatory output outline” — 강제 출력 개요가 아니라 모호 시 보존할 개념 경계로 주입된다.
- (degraded 경로) ledger 미완·deadline 회수 시
generation_mode/degraded/unresolved_slots로 구분한다.