# Octolib 0.39.0: к стеку присоединилась модель, которая не может написать ни предложения

> Octolib 0.39.0 добавляет четвёртый тип вызова моделей: оценку. Jev от TypeSafe отвечает на типизированные вопросы «да/нет», choice и score с калиброванными вероятностями меньше чем за полсекунды — напрямую, через Cloudflare или через ваш собственный OctoHub. Плюс медиа через Cloudflare Workers AI, ONNX-эмбеддинги, Inception Mercury и Tinker, и структурированный вывод, который валидируется ровно один раз.

# Octolib 0.39.0: к стеку присоединилась модель, которая не может написать ни предложения

С того дня, как мы выделили его из Octocode, каждый вызов модели в нашем стеке был одним из трёх. Сгенерировать текст. Превратить текст в вектор. Отранжировать векторы относительно запроса. [Octolib](https://github.com/muvon/octolib) — Rust-библиотека под [Octomind](https://octomind.run), [OctoHub](https://github.com/muvon/octohub), [Octocode](https://github.com/muvon/octocode) и [Octobrain](https://github.com/muvon/octobrain) — содержала по модулю на каждый: `llm`, `embeddings`, `reranker`. Потом у неё появился `media`.

Сегодня появляется четвёртый, и этот не похож на остальные. `octolib::evaluation` обращается к модели, которая **не может сгенерировать ни одного токена**. Вы передаёте ей состояние и набор типизированных вопросов, а она возвращает вероятности. Да или нет, одно из N, позиция на шкале. За 70–500 миллисекунд. По $0.042 за миллион входных токенов, причём вывод бесплатный — потому что выводить нечего, биллинга нет.

Модель — [Jev](https://octomind.run/blog/jev-system-one-model-ai-agents) от TypeSafe AI, которая вышла из стелс-режима 15 сентября. TypeSafe называет эту категорию **моделями System One**. Мы считаем её самым интересным новым примитивом, появившимся в AI-стеке в этом году, и Octolib 0.39.0 — наша ставка на неё: одна строка `provider:model`, три способа до неё добраться и тот же учёт стоимости, что мы применяем ко всему остальному.

Это главная новость. Релиз также превращает Cloudflare Workers AI в полноценного медиапровайдера, добавляет локальный ONNX-бэкенд эмбеддингов, подключает двух новых LLM-провайдеров и наконец заставляет структурированный вывод валидироваться ровно один раз. Всё, что случилось после 0.36.1, — ниже.

## Зачем агенту модель, которая только решает

Разберите агента для кодинга до основания — и останется одна большая модель, делающая работу. Вокруг неё сидит управляющий слой, который выносит маленькие, ограниченные суждения о том, что эта модель делает. Запускать ли эту shell-команду? Какой из сорока навыков подходит этому запросу, если вообще какой-то? Действительно ли релевантна эта извлечённая память? Нужен ли агенту весь этот результат инструмента на 40 КБ, или ни байта из него?

У каждого серьёзного агента для кодинга есть этот второй слой. У нас это [супервизор Octomind](https://octomind.run/docs/usage/14-supervisor). У Anthropic — классификатор авторежима в Claude Code. И в обоих случаях модель общего назначения выносит эти суждения, когда её определяющая способность — писать развёрнутое объяснение — выключена именно для этих вызовов. Запрос туда-обратно к frontier-модели, повторяемый на каждое решение, чтобы получить одно слово.

Jev создан для таких решений и больше ни для чего. Три примитива:

- **Noul** — вопрос «да/нет», на который модель отвечает вероятностью того, что ответ «да».
- **Choice** — один вариант из заданного вами набора, с вероятностью для каждого варианта и уверенностью, показывающей, насколько «острое» распределение.
- **Score** — позиция на упорядоченной шкале уровней, описанных словами; может попасть и между уровнями.

Он принимает состояние один раз и оценивает все вопросы по нему за один параллельный проход, так что десятый вопрос почти бесплатен. Его обучали на калибровку, а не на предпочтения, поэтому ответ «90%» должен оказываться верным примерно в 90% случаев на большой выборке предсказаний. И пространство ответов замкнуто по построению: модель может выбрать неверный вариант, но не может выдумать новый — значит, нечего чинить в JSON и нечего нечётко сопоставлять с enum.

Длинную версию — что это, где ломается, что люди построили с ним за первые 72 часа и каждое решение управляющего слоя внутри агента, которое он мог бы взять на себя, с живыми цифрами — мы написали в блоге Octomind: **[Jev Explained: модель System One от TypeSafe и решения внутри каждого AI-агента](https://octomind.run/blog/jev-system-one-model-ai-agents)**. Этот пост — про библиотеку.

## API

Модуль называется `evaluation`, он включён по умолчанию и компилируется сам по себе, если вам нужно только это:

```toml
octolib = { version = "0.39", default-features = false, features = ["evaluation"] }
```

Форма следует остальному Octolib. Один тип запроса, один высокоуровневый хелпер, одна строка `provider:model`:

```rust
use octolib::{evaluate, Answer, EvaluationRequest, Question};

async fn triage() -> octolib::EvaluationResult<()> {
    // Требует TYPESAFE_API_KEY; используйте "cloudflare:typesafe/jev", чтобы списывать кредиты AI Gateway.
    let request = EvaluationRequest::new("Help! My payouts have been failing for 3 days.")
        .with_question("is_urgent", Question::noul("Does this convey urgency?"))
        .with_question(
            "department",
            Question::choice(
                "Which team should handle this?",
                [("billing", "Payments, refunds"), ("technical", "Bugs, outages")],
            ),
        )
        .with_question(
            "frustration",
            Question::score("How frustrated is the customer?", ["Calm", "Frustrated", "Very angry"]),
        );
    let response = evaluate("typesafe:jev-latest", request).await?;
    if let Answer::Noul { noul } = response.answers["is_urgent"] {
        println!("urgent with p={noul:.2}, cost {:?}", response.usage.cost);
    }
    Ok(())
}
```

Состоянием может быть строка, JSON-объект или массив. Ответы возвращаются типизированными под теми же id, что и вопросы — `Answer::Noul`, `Answer::Choice` с картой вероятностей и уверенностью, `Answer::Score` с легендой, — так что в Rust вы ветвитесь по числам, а не по распарсенному тексту.

Три маршрута к одной модели:

| Строка модели | Через что доходит до Jev | Ключи |
| ------------------------- | ---------------------------------------------- | --------------------------------------------- |
| `typesafe:jev-latest`     | API TypeSafe напрямую (ранний доступ)          | `TYPESAFE_API_KEY`                            |
| `cloudflare:typesafe/jev` | Cloudflare AI, биллинг через кредиты AI Gateway | `CLOUDFLARE_API_KEY`, `CLOUDFLARE_ACCOUNT_ID` |
| `octohub:jev`             | Ваш собственный прокси OctoHub, стоимость сообщает хаб | `OCTOHUB_API_KEY`, `OCTOHUB_API_URL`          |

Несколько деталей, которые важны только под серьёзной нагрузкой, — а это как раз та часть, которая нам важна:

- **Здесь ретраи безопасны.** У оценки нет побочных эффектов, поэтому повтор не может задвоить работу. Клиент повторяет запрос на 429, 529 и 5xx с бэкоффом и уважает `Retry-After` с потолком в 30 секунд. Генерационные POST-запросы в других местах Octolib намеренно _не_ повторяются; этот — повторяется.
- **Стоимость считается по опубликованному тарифу**, $0.042 за миллион входных токенов, и попадает в `usage.cost`, как и любой другой вызов. Через OctoHub побеждает стоимость, сообщённая хабом.
- **Поле `model` в ответе сообщает версионированную модель, которая ответила.** `jev-latest` сдвигается, когда TypeSafe выпускает новую версию; если вы настраиваете пороги — логируйте её и фиксируйте.
- **Id моделей валидируются до отправки запроса.** `openai:jev-latest` — неподдерживаемый провайдер; `cloudflare:@cf/zai-org/glm-5.3` — чат-модель, а не модель оценки. Оба падают при конструировании, а не при отправке запроса.

У Jev контекст 32k, и собственная документация TypeSafe говорит, что точность падает по мере того, как состояние заполняется материалом, который вопросу не нужен. Урезайте состояние. Задавайте много вопросов; отправляйте мало.

## Куда это встраивается: Octomind и OctoHub

Мы не добавляем модули в Octolib ради забавы. Каждый модуль существует потому, что он понадобился продукту, и этот понадобился дважды.

**Супервизор Octomind** уже работает на инварианте: бесплатные сигналы отсекают лишнее до модели, а вызовы модели редки. Сегодня в `main` `[supervisor.evaluate]` добавляет калиброванный третий уровень между «бесплатным» и «frontier» на трёх стыках: фильтр релевантности после извлечения памяти, Choice по реестру, когда все правила активации навыков воздержались, и предварительная проверка, решающая, нужно ли пакету инструментов вообще будить модель-авторизатор. Каждый стык — один переключатель, по умолчанию выключен. Одна попытка на вызов, пятисекундный таймаут, без ретраев; любой сбой сохраняет прежнее поведение для этого хода. И одно правило, которое мы поставили бы во главу любой интеграции с Jev: **состояние никогда не несёт результатов инструментов или сообщений ассистента**, потому что ревьюер никогда не должен читать то, что, возможно, его уговаривает.

**OctoHub** получает `POST /v1/evaluations`. Настройте алиас один раз, в своём прокси, со своими ключами:

```toml
[evaluation_models]
"jev" = ["typesafe:jev-latest", "cloudflare:typesafe/jev"]
```

и каждый клиент на каждом языке — Python-хук, TypeScript-воркер, shell-скрипт — вызывает `octohub:jev` и получает те же типизированные ответы, ту же строку стоимости на запрос и ту же атрибуцию, что и его чат-комплишены. Поэтому `octohub` — третий провайдер оценки в Octolib: библиотека вызывает прокси, прокси вызывает Octolib.

Оба попадут в следующие релизы Octomind и OctoHub; библиотека вышла первой, потому что библиотека всегда выходит первой.

## Cloudflare Workers AI теперь медиапровайдер

В прошлый раз стек `media` в Octolib покрывал изображения, видео, речь и транскрипцию через OpenRouter, Replicate, fal, ElevenLabs и Runway. 0.39.0 добавляет **Cloudflare Workers AI** для изображений, речи и транскрипции через тот же синхронный эндпоинт `/ai/run`, который использует чат-провайдер.

Зарегистрированные модели с возможностями, опциями и ориентировочными ценами:

- **Изображения:** FLUX.1 schnell, FLUX.2 dev, Leonardo Lucid Origin и Phoenix 1.0. Цена за выходной тайл и шаг.
- **Речь:** Deepgram Aura 1 и Aura 2 (английский и испанский), MeloTTS. Цена за символ.
- **Транскрипция:** Whisper, Whisper large-v3-turbo, Deepgram Nova 3. Цена за минуту аудио; сегменты, слова, языки и метаданные парсятся в типизированную транскрипцию.

Каталог загружается лениво и кеширует страницы, с фолбэком на метаданные для моделей, которые Cloudflare ещё не внесла в список. Там, где апстрим сообщает стоимость в нейронах, она сохраняется как сообщённая, а не заменяется оценкой длительности. Сиды изображений уходят только тем моделям, которые их принимают; типы речи сохраняются, а не приводятся насильно.

Чат-сторона провайдера Cloudflare получила ту же доработку: выбирает и нормализует уровни рассуждения по моделям, применяет схемы только к моделям в формате OpenAI, которые их действительно соблюдают, разрешает перекрывающиеся id моделей по самому длинному совпадению и для неизвестных моделей откатывается к ориентировочным возможностям вместо того, чтобы падать.

## Всё остальное после 0.36.1

Десять релизов за двенадцать дней. Сжатая версия.

**Два новых LLM-провайдера.** [Inception Labs](https://www.inceptionlabs.ai/) предоставляет семейство диффузионных LLM Mercury: `inception:mercury-2.5`, рядом Mercury 2, вывод по JSON-схеме и вызов инструментов. [Tinker](https://thinkingmachines.ai/tinker/) от Thinking Machines предоставляет своё семейство Inkling плюс модели с открытыми весами (Nemotron, GLM, Kimi, Qwen, GPT-OSS, DeepSeek) и чекпоинты сэмплеров; id моделей с двоеточиями обрабатываются, а короткие имена вроде `tinker:inkling` разрешаются в id serverless-инференса. Оба идут с примерами, которые берут спецификацию модели из командной строки.

**ONNX-эмбеддинги, локально.** Новый провайдер эмбеддингов `onnx` загружает графы и токенизаторы HuggingFace в ONNX Runtime — `onnx:<org>/<repo>`, с необязательным `#path-to.onnx` для конкретного графа, — предпочитает квантованные графы, учитывает метаданные пулинга и статические размерности, группирует запросы в батчи с отслеживанием использования токенов. Он встаёт рядом с FastEmbed и HuggingFace-бэкендом на Candle как третий способ делать эмбеддинги без ключа API. Это единственное нарушение совместимости в раунде: у `EmbeddingProviderType` появился вариант `Onnx`, так что исчерпывающим match нужна новая ветка.

**Структурированный вывод валидируется один раз.** Прежний сценарий мог повторять провалившуюся валидацию схемы и агрегировать usage между попытками. Это было благонамеренно и неправильно: прятало поведение провайдера за циклом. Теперь ровно один вызов схемы к апстриму, один централизованный шаг извлечения и валидации, а ошибка парсинга или валидации возвращается с логированием проблемного ответа и без повтора. Клиентские вызовы инструментов сохраняются после валидации финального вывода. Два провайдера в том же изменении перестали обещать лишнее: Ollama и Cloudflare больше не заявляют гарантированное соблюдение схем, которое не могут обеспечить, а OpenAI **падает с ошибкой** (fails closed), когда указания схемы игнорируются, вместо того чтобы вернуть что-то, что лишь выглядит правильным.

**Уровни усилия рассуждения маппятся по провайдерам.** Модели GLM у Alibaba и Z.ai сопоставлены с поддерживаемыми ими уровнями усилия; Fireworks сохраняет максимальный уровень для GLM 5.2. Z.ai теперь принимает сообщения с сохранённым мышлением и оставляет ходы ассистента «только мышление» вместо того, чтобы их выбрасывать.

**Список моделей изменился.** Снятые модели Cerebras заменены на `qwen-3.8-27b` с поддержкой видео и лимитом входа 128K. Снятые маршруты V4 у DeepSeek заменены на `deepseek-flash`, трактуемый как мультимодальный; цены V4.1 Flash исправлены и добавлены на Alibaba. Отозванные модели Hetzner заменены на Qwen. Кеширование BytePlus и сторонние цены обновлены. OctoHub теперь определяет размерности эмбеддингов при конструировании и кеширует их по эндпоинту и модели, из-за чего конструирование провайдера OctoHub стало асинхронным — второе небольшое изменение API, о котором стоит знать. Маппер провайдеров валидирует алиасы моделей, сохраняет квантованные и отклоняет несопоставленные модели при конструировании, а не при первом запросе.

**Мелочи, но важные.** Аудиоформаты fal маппятся на значения, которые fal принимает (MP3 и PCM; неподдерживаемые форматы используют значение по умолчанию для модели). Пустое содержимое сообщений снова разрешено. `rustls` на 0.23.45, `dirs` на 7.0.

## Обновление

- `cargo add octolib@0.39` или поднимите версию. Все фичи включены по умолчанию; новая фича `evaluation` не тянет ничего тяжёлого.
- Если вы делаете `match` по `EmbeddingProviderType`, добавьте ветку `Onnx`.
- Если вы конструируете провайдер эмбеддингов OctoHub напрямую, он теперь `async`.
- Если вы полагались на то, что структурированный вывод повторяет провалившуюся валидацию, — больше не повторяет. Ошибка несёт то, что вернул провайдер; решайте в своём коде.
- Установите `TYPESAFE_API_KEY` или `CLOUDFLARE_API_KEY` плюс `CLOUDFLARE_ACCOUNT_ID` и попробуйте пример выше. Он съедает пару сотен входных токенов: меньше двух тысячных цента.

## FAQ

**Что такое модель оценки?**
Модель, которая отвечает на типизированные вопросы о состоянии калиброванными вероятностями вместо генерации текста. Jev от TypeSafe — первая такая модель — возвращает вероятность «да/нет» (Noul), один вариант из набора (Choice) или позицию на шкале (Score) за 70–500 мс. Она не умеет писать код или прозу. Её работа — решения вокруг вашего LLM: маршрутизация, допуск, фильтрация релевантности, выбор навыка.

**Нужен ли аккаунт TypeSafe, чтобы ей пользоваться?**
Нет. Прямой доступ — по листу ожидания, но `cloudflare:typesafe/jev` работает уже сегодня с аккаунтом Cloudflare и загруженными кредитами AI Gateway, а `octohub:jev` работает через ваш собственный OctoHub с любым из ключей за ним.

**Octolib 0.39.0 что-нибудь ломает?**
Один вариант enum (`EmbeddingProviderType::Onnx`), один конструктор стал async (эмбеддинги OctoHub), и структурированный вывод больше не ретраит провалы валидации. Чат, медиа, реранжирование и каждая строка модели, которой вы уже пользуетесь, не изменились.

**Jev уже внутри Octomind?**
Гейты оценки уже в `main` Octomind, за `[supervisor.evaluate]`, по умолчанию выключены и выйдут в следующем релизе. Этот релиз Octolib — то, на чём они построены.

**Какие продукты работают на Octolib?**
Octomind (каждый чат-вызов, эмбеддинг и гейт оценки), OctoHub (чат, эмбеддинги, медиа и новый маршрут `/v1/evaluations`), Octocode (чат, эмбеддинги, реранжирование) и Octobrain (эмбеддинги, реранжирование). Когда провайдер меняет API или цены, правка живёт в одном месте и достаётся каждому продукту.

## Суть

Работа Octolib всегда состояла в том, чтобы следующая модель становилась изменением в одну строку для всего, что мы выпускаем. Обычно это значит новый провайдер или исправленная цена. Иногда это значит новый _тип_ модели, и библиотеке приходится решать, как он вписывается.

Модели оценки вписываются как четвёртый модуль, потому что это другая функция: не «напиши мне ответ», а «вот состояние, вот мои вопросы — скажи, что ты думаешь и насколько сильно». Агентам этот примитив был нужен с первого pre-tool-хука. Мы имитировали его одним токеном от рассуждающей модели. Теперь это типизированный вызов, с честной ценой, в одной строке от вас.

**[github.com/muvon/octolib](https://github.com/muvon/octolib)** · Apache-2.0 · [crates.io](https://crates.io/crates/octolib) · [подробный разбор Jev](https://octomind.run/blog/jev-system-one-model-ai-agents) в блоге Octomind.
