Туманомір — CLI для вимірювання неоднозначності специфікацій для AI
Огляд
Незалежний R&D-проєкт: CLI на Go, що перетворює “специфікація незрозуміла” з розмитої скарги на три вимірювані метрики, за якими можна гейтити контекст AI-агента ще до написання коду.
Спершу опублікована методологія — «Джерело Невідомості» запропонувала $K_{drift}$ (нетрасовані вимоги), $D_{const}$ (щільність обмежень) та $D_{pair}$ (розкид генерацій на N семплах) як спосіб призначити ціну неоднозначності — як інженерний допуск, а не відчуття. Туманомір — референсна реалізація: 25 днів, 66 комітів, жодного відкату, гейтить власну специфікацію в CI з першого дня.
Результат: робочий пайплайн check/measure/gate/calibrate/label, де кожне твердження в статті-продовженні підкріплене реальним, відтворюваним прогоном CLI — не переказом.
Що побудовано
$ 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)
D_pair: — (stochastic layer: run `tumanomir measure` with an instrument)
П’ять команд, кожна продемонстрована на живій специфікації самого інструмента:
check— детермінований шар, без мережі, ~17мс на 1МБmeasure— стохастичний шар проти реального Ollama-приладу (N генерацій, структурний AST-диф)gate— обидва шари разом для CI, відмовляється тихо деградувати, якщо передано--tempбез резолвленого приладуcalibrate— кореляція Спірмена значень метрик проти міток результату з корпусу, без переміруlabel— єдина команда, якій дозволено записувати outcome рядка корпусу
Архітектура
spec.md ──► check (K_drift, D_const) ─┐
└─► measure (D_pair, instrument) ─┴─► gate ──► exit 0/1 (CI)
│
corpus.jsonl ──► calibrate (Spearman) ◄────────┘
Детерміновані пакети (internal/metrics, internal/spec, internal/config, internal/calibrate) не мають права торкатися мережі — це перевіряється тестом, який парсить їхні транзитивні залежності й падає, якщо десь з’явиться net/*, а не конвенцією в коментарі, яку легко забути під тиском дедлайну.
Ключові інженерні рішення
| Рішення | Чому |
|---|---|
Hand-written byte scanner замість regexp для $K_{drift}$ |
3260 → 14 алокацій на операцію, незалежно від кількості вимог — повний check на 1МБ укладається в 16.7мс |
$D_{const}$ архітектурно не може блокувати гейт (VerdictBlock) |
Це лексичний проксі, а не істина в останній інстанції — advisory-only за задумом, зафіксовано як тестований інваріант, а не застереження |
PromptV1 як іменована, версіонована Go-константа |
Відтворюваність — промт генерації ніколи не є inline-літералом, що може непомітно дрейфувати |
Spearman, а не Pearson, у calibrate |
outcome — довільна, задана користувачем шкала; має сенс лише монотонний зв’язок |
| Жодного embedding-based synonym matching для шуму найменувань | Явно відхилено — це повернуло б недетермінізм моделі в метрику, чия цінність саме у фіксованому, відтворюваному приладі |
Найважливіша знахідка: тест, що ізолює шум найменування від структурної розбіжності, показав — сам вибір ідентифікаторів здатен дати $D_{pair} = 0.6667$ на фіксованій структурі: дві третини сигналу схожості біля порогу блокування може бути тим, як модель назвала змінну, а не тим, як вона зрозуміла специфікацію. Зафіксовано як виміряне, відкрите Фаза-2 питання, а не згладжено.
Результати
- 66 комітів, 64 злитих PR, жодного відкату за 25 днів
- v0.1.0-dev, feature-complete — 5 команд працюють наскрізно, без зовнішніх залежностей крім
gopkg.in/yaml.v3 make dogfoodгейтить власну специфікацію інструмента в CI при кожній зміні- 233x менше алокацій у сканері $K_{drift}$ після цільового переписування (PR #68)
- Кожен вивід CLI, процитований в опублікованій статті-продовженні, перезапущено проти живого бінарника перед публікацією — не переказано з пам’яті
Код: github.com/valpere/tumanomir · Матеріали: «Джерело Невідомості» → «Туманомір»
Стек
Go · Ollama (qwen3-coder:30b, прилад) · AST-структурний diff · Кореляція Спірмена · YAML config · GitHub Actions CI