VB
Victor Buzin
Назад к блогу
Delibera: field note о форме продукта
2026-10-216 мин чтения

Delibera: field note о форме продукта

Первые посты про Delibera описывали ранние релизы: библиотека, пара стратегий дебатов, провайдеры LLM. С тех пор проект прошёл четырнадцать версий на NuGet, и форма продукта изменилась заметно. Этот текст — заметка изнутри работы: что за продукт получился, какие решения его определили и где он честно заканчивается.

Это не анонс. Это описание состояния на конкретный момент: версия 10.5.3, .NET 10, MIT-лицензия.

Что не так с решением, принятым в чате

Проблема, из которой вырос проект, не в том, что модель отвечает плохо. Проблема в том, что решение, принятое в чате, живёт в переписке. У него нет владельца, нет версии, нет состава участников, и через месяц никто не может восстановить, какие варианты рассматривались и почему выбрали этот.

Совет из нескольких независимых моделей решает часть этой задачи: появляется транскрипт, участники видят аргументы друг друга, а финальный вердикт пишет отдельная модель, которая читает весь разбор. Но сам по себе совет — не продукт. Продукт начинается там, где появляются версии, метрики, воспроизводимые замеры и понятная граница ответственности.

Три ограничения, из которых выросла форма

Интерфейс — это то, что переживёт смену модели

Модели провайдеров меняются каждый месяц, и логика принятия решений не должна меняться вместе с ними. Отсюда разделение: доступ к LLM живёт за ILLMProvider (и за Microsoft.Extensions.AI), а стратегии дебатов, Knowledge Keeper, компрессия и Operator работают поверх интерфейсов и не знают, кто именно отвечает.

Побочный эффект, который мы оценили выше ожиданий: когда провайдер меняется, меняется только адаптер. Стратегия дебатов, протокол раундов и формат вердикта остаются прежними — а это ровно тот слой, в котором живёт накопленная логика продукта.

Измерить раньше, чем заявить

Правило проекта простое: число появляется на сайте только после того, как измерено. Сайт проекта рендерит Markdown из самого репозитория, поэтому страница физически не может анонсировать версию, фичу или число, которых в коде нет.

Это оказалось важнее, чем мы ожидали. Тот самый «30–70% экономии от компрессии контекста» был написан в README до того, как кто-то это измерил. Когда мы добавили измерения, выяснилось, что компрессор не вызывался вообще: десять прогонов показывали ровно 0,00% при нулевых накладных расходах. Сейчас в документации стоит измеренное значение — 11,7–12,1%, — и страница не даст его подменить.

Для продукта, чья ценность в том, что он заменяет человеческое суждение, дисциплина измерения — это не отдел качества. Это часть определения.

Подробные цифры и методика лежат в разделе «Measured Behaviour» на сайте проекта, а архитектурные решения — в разделе «Measured Rounds» роадмапа: релиз 10.5.0 мы назвали именно так, потому что его содержанием был запуск на реальных дебатах и поиск того, что сломано.

Версия — это обещание, а не номер

Публичный API мы версионируем как NET_MAJOR.FEATURE.PATCH: в пределах десятой ветки ломающих изменений нет, ломка возможна только на границе крупных фич. Внутри ветки стабильным держится то, на что опираются интеграторы, и меняется то, что внутри.

Для аудитории .NET это ожидаемо и почти тривиально. Но для ИИ-фреймворка это не деталь: у библиотек этого класса привычка поставлять пре-релиз с «экспериментальным API» и переписывать его в следующем месяце. Мы решили, что стабильное ядро должно пережить смену модельного провайдера, — а значит, и смену второстепенных фич вокруг него.

Что в продукте есть сейчас

Ядро — библиотека Delibera.Core на .NET 10 (C# 15 preview):

  • совет из нескольких моделей, три стратегии раундов (StandardDebate, CritiqueDebate, ConsensusDebate) плюс своя стратегия через IDebateStrategy;
  • Chairman — отдельная модель для синтеза финального вердикта;
  • Knowledge Keeper — точечный поиск по векторному корпусу (Qdrant или pgvector) по ходу дебатов;
  • Operator — микро-агент с доступом к MCP-серверам;
  • компрессия контекста между раундами (семантика, дедупликация, суммаризация, гибрид);
  • AutoChunking — прогрессивная раздача больших документов по раундам;
  • кэш результатов (InMemory, File, Redis) с детерминированным ключом;
  • стриминг раундов по SSE, персистентность и возобновление дебатов, структурированный вывод, наблюдаемость через OpenTelemetry;
  • библиотека шаблонов дебатов: ArchitectureReview, RiskAssessment, CodeReview, ProductDecision, SecurityAudit, DataArchitecture.

Поверх ядра — Delibera.Server (ASP.NET Core Minimal API: REST + SSE) и Delibera.Redis (оркестрация и кэш в Redis). Оба опубликованы на NuGet.

Есть веб-интерфейс на Nuxt 4: список дебатов, создание, живые раунды по SSE, вердикт со статистикой токенов, экспорт в Markdown, отмена. Он обращается к API только через серверный прокси Nitro — потому что сервер не регистрирует CORS-политику.

Тесты: 620 проходящих (493 Core, 117 Server, 10 gRPC), ни одного упавшего и ни одного пропущенного, плюс 34 теста веб-интерфейса. Релизная сборка чистая под -warnaserror.

Как это выглядит в коде — минимальный совет:

using Delibera.Core.Council;
using Delibera.Core.Providers;

using var factory = new ProviderFactory();
var ollama = factory.CreateOllama("http://localhost:11434");

var result = await new CouncilBuilder()
    .AddMember("llama3.2:3b", ollama, "Analyst")
    .AddMember("qwen2.5:7b", ollama, "Strategist")
    .SetChairman(Chairman.CreateStandard("qwen2.5:7b", ollama))
    .WithStandardDebate()
    .WithUserPrompt("Microservices vs Monolith for a 5-person startup?")
    .WithMaxRounds(4)
    .Build()
    .ExecuteAsync();

Как начать

Пакеты живут на NuGet:

dotnet add package Delibera.Core
dotnet add package Delibera.Server
dotnet add package Delibera.Redis

Образы — на Docker Hub, linux/amd64 и linux/arm64:

docker pull techbuzzz/delibera-server:10.5.3
docker pull techbuzzz/delibera-webui:10.5.3

Полный стек поднимается одной командой из клона репозитория: docker compose up -d поднимает сервер, веб-интерфейс, Redis, Qdrant, PostgreSQL с pgvector и Ollama.

Про безопасность. API дебатов не аутентифицирован и не ограничен по частоте, а дебат тратит реальные кредиты у провайдера. Поэтому все публикуемые порты привязаны к 127.0.0.1, и это не украшение: привязка и есть механизм защиты. Прежде чем выставлять стек в сеть, которой вы не управляете, поставьте перед ним аутентифицирующий обратный прокси. Ниже мы приводили измерения против Ollama Cloud — то есть против провайдера, за который кто-то платит.

Где у продукта проходит граница

Часть вещей в продукте ещё не доведена, и честнее сказать об этом прямо, чем оставить в README.

Redis не даёт распределённого исполнения раундов. В сервере есть переключатель Delibera:Redis:Enabled, он регистрирует Redis-оркестратор и, опционально, кэш результатов. Но DebateWorkerService — это сознательная заглушка: раунды по воркерам не раздаются. Всё, что даёт Redis сегодня, — публикация событий раундов в Streams.

Веб-интерфейс не разворачивается на GitHub Pages. Сайт проекта статический, у него нет серверного рантайма, а UI зависит от same-origin прокси /api/delibera/**. Статический экспорт без такого прокси потребовал бы отдельного CORS-разрешённого API-хоста. Образ techbuzzz/delibera-webui существует именно для запуска рядом с Delibera.Server.

VerdictDto в веб-интерфейсе — в основном оболочка. Маппер заполняет recommendation и rawJson; confidence, riskLevel, risks и conditions не заполняются никогда. Структурированный вывод существует, но он привязан к шаблону вердикта и лежит внутри rawJson.

Operator недоступен через API. Замеры показали operatorInteractions: 0 на каждом дебате, измеренном через сервер: сервер подключает Knowledge Keeper, а не Operator. MCP-инструменты работают внутри библиотеки, но не через эту поверхность.

Удаление осиротевших векторов не реализовано. Если исходный документ уменьшился, старые точки остаются в индексе: для этого нужен filter-delete на IVectorStore, а это ломающее изменение интерфейса.

Каналы раундов не ограничены по ёмкости. Channel.CreateUnbounded всё ещё используется в локальном и Redis-оркестраторах: потребитель, который перестал читать, будет бесконечно наращивать память.

Семантика длительности попадания в кэш сбита. Для попадания в кэш TotalDuration сообщает, откуда взят результат, а не сколько ждал вызывающий. Это зафиксированное продуктовое решение, а не баг, но называть его поведением «0,0 с» стоит аккуратно.

gRPC-пакеты не входят в GA-матрицу публикации. Delibera.Grpc и Delibera.Grpc.Client существуют, но не публикуются как часть релизной линейки.

Что дальше

Роадмап короткий и в нём есть места, о которых стоит сказать вслух.

Релиз 10.6.0 — «Harden the Edges»: удаление осиротевших векторных точек, ограничение ёмкости каналов раундов, и решение по двум продуктовым вопросам — WithCache в ICouncilBuilder и семантика длительности кэша.

11.0.0 — миграция на .NET 11, когда он выйдет из preview. Это будет ломающий релиз, и переносить в него будем только то, что действительно изменилось.

Пока этого нет, мы продолжаем измерять: следующий круг замеров — по качеству вердикта и по тому, как часто совет из нескольких моделей реально даёт результат лучше одного сильного промпта. Протокол и методика — в разделе «Measured Behaviour» на сайте проекта.

В этой серии: Назад — Multi-LLM-дебаты в .NET: архитектура, компромиссы, измерения

Источники

Если вы выбираете, нужен ли вашей команде совет из нескольких моделей, начните с измерений на своих задачах, а не с чтения документации. Обсудить внедрение → techbuzzz.ru

Похожие статьи