Tumanomir: "Source of the Unknown" 이후 실제로 구축된 것
요약
AI가 소비하는 명세의 불확실성을 측정하기 위한 Go 기반 CLI 도구인 Tumanomir의 구현 상세를 다룹니다. 결정론적 레이어와 확률적 레이어를 통해 명세의 품질을 검증하고 CI 환경에서 게이팅할 수 있는 기능을 제공합니다.
핵심 포인트
- Go 언어로 구현된 경량 CLI 도구로 의존성이 매우 낮음
- 결정론적(deterministic) 및 확률적(stochastic) 측정 레이어 제공
- CI 환경에서 명세의 품질을 기준으로 빌드를 제어하는 게이팅 기능
- 네트워크 접근을 차단하여 보안과 신뢰성을 확보한 아키텍처
"Source of the Unknown"은 AI가 소비하는 명세(spec)에 안개가 얼마나 끼어 있는지를 측정하기 위한 세 가지 지표를 제안하며 다음과 같이 유보하며 마무리했습니다: "구현 세부 사항은 별도의 순수 엔지니어링 글의 주제입니다."
이 글이 바로 그 글입니다. 25일(2026년 7월 3일~27일)에 걸쳐, 이 방법론은 Tumanomir가 되었습니다 — Go CLI, v0.1.0-dev, 66개의 커밋, 0개의 되돌리기(revert), 그리고 gopkg.in/yaml.v3 외에는 의존성이 없습니다. 이는 CI(지속적 통합)에서 자체 명세를 결정론적(deterministically)으로 게이팅(gates)합니다.
다섯 가지 명령어, 실제 터미널 출력
check는 네트워크 없이 결정론적 레이어(deterministic layer)를 즉시 실행합니다:
$ tumanomir check docs/requirements.md
K_drift: 0.00 [ok] (threshold 0.20, 0/33 requirements untraced)
D_const: 0.03 [warn] (threshold 0.35, 101 markers / 3905 prose tokens)
...
이것이 Tumanomir의 도그푸딩(dogfooding) — 즉, 자신의 명세를 스스로 측정하는 것입니다. $D ext{ extunderscore}const$가 [warn]에 걸리는 것은 버그가 아닙니다. 이 지표는 구조적으로 게이트를 차단할 수 없도록 설계되었습니다 (REQ-CHK-06). 이것은 어휘적 대리 지표(lexical proxy)이지, 절대적인 진실(ground truth)이 아닙니다.
measure는 라이브 모델을 대상으로 확률적 레이어(stochastic layer)를 실행합니다:
$ tumanomir measure --instrument ollama:qwen3-coder:30b \
-n 3 --temp 1.0 --sim-threshold 0.95 \
docs/investigation/_sanity/specs/sharp.md
...
우리는 동일한 도구(instrument)와 동일한 파라미터를 사용하여 동일한 명세를 대상으로 세 번 연속 실행했습니다 — 특정 결과를 찾으려 한 것이 아니라, 단지 기사를 위한 자료를 수집한 것입니다. 결과는 하나의 숫자가 아니었습니다:
| 실행(Run) | 명령어(Command) | D_pair | 판정(verdict) |
|---|---|---|---|
| 1 | measure | 0.21 | ok |
| ... |
temp=1.0에서, 동일한 명세와 동일한 도구를 사용하더라도 정확히 언제 Enter를 누르느냐에 따라
gate: --temp 인자가 전달되었으나 해결된 instrument가 없음 (no --instrument 및 .tumanomir.yaml의 instrument: section 없음) — 결정론적(deterministic-only) 모드로 조용히 다운그레이드하는 것을 거부함 (REQ-GATE-02)
단순한 정확성을 넘어 신뢰를 얻는 아키텍처 (Architecture that earns trust, not just correctness)
네 개의 결정론적 패키지(internal/metrics, internal/spec, internal/config, internal/calibrate)는 네트워크에 접근할 권한이 없습니다. 이는 주석이 아닌 테스트를 통해 강제됩니다:
$ go test ./internal/ -run TestNoNetworkImports -v
--- PASS: TestNoNetworkImports (0.05s)
첫 번째 $K_{drift}$ 구현체는 regexp를 사용했으며, 요구사항 수에 따라 할당량이 1:1로 증가했습니다 (1MB 코퍼스 기준 3260 allocs/op). 이를 수동으로 작성한 바이트 스캐너(byte scanner)로 재작성(PR #68)한 결과, 요구사항 수와 무관하게 14 allocs/op로 감소했으며, 1MB에 대해 16.7ms 만에 전체 check를 완료했습니다.
예상치 못했던 발견 사항
구조적 발산(structural divergence)으로부터 명명 노이즈(naming noise)를 분리하여 테스트한 결과, 하나의 고정된 구조 내에서 식별자 텍스트만 변경한 세 가지 피스처(fixtures)에서 $D_{pair} = 0.6667$이 나타났습니다. 유사도 공간의 3분의 2가 이름 선택만으로 소비된 것입니다. Tumanomir의 프롬프트가 생성하는 빈 본문의 Go 타입 스켈레톤(skeleton)의 경우, 이는 0.30 차단 임계값 근처의 $D_{pair}$ 신호 중 상당 부분이 구조적 모호함이 아닌 명명 스타일 노이즈일 수 있음을 의미합니다. 이는 확정된 결론이 아닌, 해결해야 할 Phase-2 과제입니다. 두 가지 수정 방안이 명시적으로 거부되었으며 테스트에 기록되었습니다: 임베딩 기반의 유의어 매칭(embedding-based synonym matching, 모델의 비결정성(non-determinism)을 다시 도입함)과 레벤슈타인 거리(Levenshtein distance, 유의어가 아닌 오타를 잡아냄).
아직 해결되지 않은 사항
실제 결과값(outcome-labeled)이 라벨링된 교정 코퍼스(calibration corpus)는 아직 존재하지 않습니다. 즉, 임계값(threshold values) (0.20 / 0.35 / 0.30)은 첫 번째 기사에서 제시된 가설일 뿐, 교정된 수치(calibrated numbers)가 아닙니다. $D_{pair}$는 문서 상대적(document-relative)이 아닌 기기 상대적(instrument-relative)입니다. v0.1은 작동 온도(working temperature)에서의 교정된 기준선(calibrated baseline)에 대한 차이(delta)가 아니라, 스트레스 테스트 온도(stress-test temperature, 1.0)에서의 절대값에 따라 게이팅(gates)됩니다. 이는 숨겨진 것이 아니라 선언된 차이(divergence)입니다 (REQ-CFG-01). $D_{const}$는 여전히 어휘적 대리 지표(lexical proxy)이며, 첫 번째 기사에서 언급된 RFLP 그래프는 존재하지 않습니다. $K_{drift}$는 엄격 모드(strict-mode)에서만 작동합니다. 현재 단 하나의 기기(Ollama)와 단 하나의 투영(projection, Go 타입 정의)만이 테스트되었습니다.
전체 기술 문서, 5개의 모든 명령 출력값, 그리고 "구현됨(implemented)"과 "가설(hypothesis)"을 구분하는 전체 주장 단계는 다음을 참조하십시오: tumanomir on my blog. 코드: github.com/valpere/tumanomir.
Valentyn Solomko — 우크라이나 소프트웨어 엔지니어
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기