Delibera — Быстрый старт

Это руководство за несколько минут проведёт вас через ваше первое обсуждение с помощью ИИ в Delibera. По его завершении у вас будут запущенные локально многомодельные дебаты и Markdown-стенограмма на диске.


1. Требования#

  • .NET 10 SDK (≥ 10.0.301) — проект нацелен на net10.0 и собирается с LangVersion=preview для включения возможностей C# 15. См. NET10-Upgrade-RU.md для полных заметок о миграции.
  • Запущенный экземпляр Ollama — либо:
    • Локально: установите Ollama и запустите ollama serve, либо
    • Cloud: зарегистрируйтесь в Ollama Cloud и получите API-ключ (без установки, без GPU).
  • Опционально — для роли Operator: Node.js + npx для запуска MCP-серверов (например, @playwright/mcp, @marp-team/marp-cli). См. раздел 7.

Модели для установки#

Выберите один из наборов моделей ниже в зависимости от вашего железа. Набор 🟡 «Стандартный» рекомендуется для большинства случаев.

🟢 Минимальный — слабое железо / smoke-тесты (≈ 2 ГБ всего)#

ollama pull llama3.2:1b
ollama pull qwen2.5:1.5b
ollama pull nomic-embed-text

🟡 Стандартный — рекомендуется (≈ 7 ГБ всего)#

ollama pull llama3.2:3b
ollama pull qwen2.5:7b
ollama pull nomic-embed-text

🔴 Высокопроизводительный — GPU с ≥ 24 ГБ VRAM или Ollama Cloud (≈ 30+ ГБ)#

ollama pull llama3.1:8b
ollama pull qwen2.5:14b
ollama pull mistral:7b
ollama pull nomic-embed-text

ℹ️ Ollama Cloud использует те же имена моделей — локальная загрузка не нужна, только API-ключ.


2. Установка пакета#

dotnet add package Delibera.Core

Или клонируйте репозиторий и сошлитесь на Delibera.Core напрямую:

git clone https://github.com/techbuzzz/Delibera.git
cd Delibera

Попробуйте из командной строки#

🆕 Самый быстрый способ попробовать Delibera — CLI delibera: без консольного проекта и без composition root:

dotnet run --project src/Delibera.Cli -- run "Микросервисы или монолит?" --rounds 3

CLI читает ту же секцию конфигурации Delibera, что и сервер, поэтому советует ровно те модели, которые вы уже настроили:

{
  "Delibera": {
    "Providers": {
      "ollama": {
        "Models": {
          "llama3": { "Endpoint": "http://localhost:11434" },
          "qwen":   { "Endpoint": "http://localhost:11434" }
        }
      }
    },
    "Ollama": { "ApiKey": "" }
  }
}

Записи моделей без Endpoint пропускаются. Поддерживаемые ключи провайдеров — ollama и yandex, ключ передаётся через Delibera:Ollama:ApiKey или Delibera:Yandex:ApiKey. Если ничего не настроено, команда объяснит, чего не хватает, а не вернёт молча пустой дебат.

Команда Что делает
delibera run "<вопрос>" --strategy s --rounds N --temperature T --output path --stream --json --verbose Запускает дебат и печатает результат
delibera resume <debate-id> --store <dir> --json Восстанавливает DebateResult из завершённых раундов чекпоинта
delibera compare <baseline.json> <comparison.json> --output path --html Сравнивает два сохранённых результата
delibera benchmark "<вопрос>" --iterations N --rounds R Показывает разброс по повторным прогонам
# Показывать раунды по мере готовности и сохранить результат для машины
delibera run "Выкладываем ли мы в пятницу?" --stream --json --output result.md

# Сравнить два прогона одного вопроса
delibera run "Выкладываем ли мы в пятницу?" --json > baseline.json
delibera run "Выкладываем ли мы в пятницу?" --json > today.json
delibera compare baseline.json today.json --output diff.md

# Повторить вопрос и посмотреть, насколько стабилен ответ
delibera benchmark "Выкладываем ли мы в пятницу?" --iterations 5 --rounds 3

📌 resume отчитывается о прогрессе, а не продолжает дебат. Чекпоинт хранит завершённые раунды и параметры, под которыми они выполнялись, а не готовый DebateResult, поэтому CLI восстанавливает результат и печатает его с IsCompleted = false. Продолжить запуск — значит воссоздать весь набор провайдеров из этого снимка параметров, а это задача хоста.

run возвращает 0 при успехе, 1 при обработанной ошибке и 2 при ошибке использования.


3. Ваше первое обсуждение#

Создайте консольный проект и вставьте следующее:

// Program.cs
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()
    .WithSystemPrompt("You are a software architecture expert.")
    .WithUserPrompt("Microservices vs Monolith for a 5-person startup?")
    .WithMaxRounds(4)
    .SaveResultTo("./deliberation.md")
    .Build()
    .ExecuteAsync();

Console.WriteLine(result.FinalVerdict);

Запустите:

dotnet run

Delibera проведёт структурированные многораундовые дебаты и запишет полную стенограмму и вердикт председателя в файл deliberation.md.

☁️ Используете Ollama Cloud? Замените endpoint на https://api.ollama.com и передайте API-ключ: factory.CreateOllama("https://api.ollama.com", "YOUR_API_KEY").


4. Использование Dependency Injection#

using Delibera.Core.DependencyInjection;
using Microsoft.Extensions.DependencyInjection;

var services = new ServiceCollection();

// Привязывает секцию "Delibera" из appsettings.json
services.AddDelibera(configuration, "Delibera");

var provider = services.BuildServiceProvider();
var builder = provider.GetRequiredService<ICouncilBuilder>();

appsettings.json:

{
  "Delibera": {
    "Strategy": "Standard",
    "MaxRounds": 4,
    "Temperature": 0.7,
    "SystemPrompt": "You are a knowledgeable AI expert participating in a council debate.",
    "Providers": {
      "DefaultType": "Ollama",
      "DefaultEndpoint": "http://localhost:11434",
      "ApiKey": "",
      "EmbeddingModel": "nomic-embed-text"
    },
    "Compression": {
      "Enabled": true,
      "Strategy": "Hybrid",
      "TargetRatio": 0.5,
      "EnableCache": true,
      "MaxCacheEntries": 256
    },
    "Rag": {
      "Enabled": false,
      "ProviderType": "Qdrant",
      "Host": "localhost",
      "Port": 6334,
      "CollectionName": "council_knowledge",
      "ConnectionString": null
    },
    "Output": {
      "Directory": "./debate_results",
      "SeparateFiles": true,
      "FilePrefix": null
    }
  }
}

Для Ollama Cloud задайте Providers:DefaultEndpoint равным https://api.ollama.com и Providers:ApiKey равным вашему ключу.


5. Добавление сжатия контекста#

using Delibera.Core.Compression;
using Delibera.Core.Providers.LLM;

var ollama = new OllamaProvider("http://localhost:11434");
var embeddings = new OllamaEmbeddingProvider(ollama, "nomic-embed-text");

var result = await new CouncilBuilder()
    .AddMember("llama3.2:3b", ollama, "Analyst")
    .AddMember("qwen2.5:7b", ollama, "Strategist")
    .SetChairman(Chairman.CreateStandard("qwen2.5:7b", ollama))
    .WithCompression(CompressionStrategy.Hybrid,
        llmProvider: ollama,
        modelName: "llama3.2:3b",
        embeddingProvider: embeddings)
    .WithCompressionCache()
    .WithUserPrompt("Analyze our architecture options...")
    .WithMaxRounds(4)
    .Build()
    .ExecuteAsync();

Console.WriteLine(result.TokenStats?.ToSummary());

Сжатие экономит замеренные 11.7–12.1% prompt-токенов без потери смысла контекста дебатов. См. performance-measurements.md.


6. Добавление RAG с Qdrant#

docker run -d -p 6333:6333 -p 6334:6334 qdrant/qdrant
using Delibera.Core.Council;
using Delibera.Core.Models;
using Delibera.Core.Providers.LLM;
using Delibera.Core.Providers.RAG;

var ollama = new OllamaProvider("http://localhost:11434");
var embeddings = new OllamaEmbeddingProvider(ollama, "nomic-embed-text");

var ragFactory = new VectorStoreFactory();
var rag = ragFactory.CreateQdrant(embeddings, "localhost", 6334);

var kkMember = new CouncilMember("llama3.2:3b", ollama, "Knowledge Keeper");
var keeper = new KnowledgeKeeper(rag, kkMember, "architecture_kb");
await keeper.IndexFileAsync("./docs/architecture.md");

var result = await new CouncilBuilder()
    .AddMember("llama3.2:3b", ollama, "Backend Expert")
    .AddMember("qwen2.5:7b", ollama, "DevOps Expert")
    .SetChairman(Chairman.CreateStandard("qwen2.5:7b", ollama))
    .WithKnowledgeKeeper(keeper)
    .WithStandardDebate()
    .WithUserPrompt("Our startup has 4 developers. Microservices or monolith?")
    .WithMaxRounds(4)
    .Build()
    .ExecuteAsync();

💡 Замените CreateQdrant(...) на CreatePgVector(embeddings, connectionString), чтобы использовать PostgreSQL/pgvector.


7. Использование Operator (MCP-инструменты)#

Operator — это микроагент, который соединяет совет с внешними инструментами через серверы MCP (Model Context Protocol) — веб-навигация, доступ к файловой системе, генерация Marp-презентаций и многое другое. Участники делегируют задачу в любой момент, написав маркер [[OPERATOR: ...]], и Operator запускает нужные инструменты и возвращает результат в следующий раунд.

7.1. Установка требований#

Серверы из примеров ниже запускаются через npx, поэтому вам нужны Node.js + npx:

# Проверьте, что Node.js / npx доступны
node --version
npx --version

# Для сервера браузера один раз установите движки Playwright:
npx playwright install

Также нужна запущенная Ollama с дешёвой моделью для Operator (например, llama3.2) — Operator использует свою собственную модель, отдельную от участников совета.

7.2. Настройка MCP-серверов#

Каждый MCP-сервер описывается через McpServerConfig. Используйте Stdio(...) для локальных процессов или Http(...) для удалённых серверов:

using Delibera.Core.Models;

var servers = new[]
{
    // 🌐 Браузер — навигация, чтение страниц, клики, скриншоты
    McpServerConfig.Stdio(
        name: "browser",
        command: "npx",
        arguments: new[] { "-y", "@playwright/mcp@latest", "--headless" }),

    // 🎯 Marp — генерация презентаций HTML/PDF/PPTX из Markdown
    McpServerConfig.Stdio(
        name: "marp",
        command: "npx",
        arguments: new[] { "-y", "@marp-team/marp-cli", "--server", "./out" }),

    // …или удалённый HTTP/SSE MCP-сервер с заголовками авторизации:
    // McpServerConfig.Http(
    //     name: "remote",
    //     endpoint: "https://my-mcp-host.example.com/mcp",
    //     additionalHeaders: new Dictionary<string, string> { ["Authorization"] = "Bearer <token>" }),
};
Сервер Транспорт Команда запуска Что даёт
🌐 browser stdio npx -y @playwright/mcp@latest --headless Навигация по сайтам, чтение страниц, клики, скриншоты
🎯 marp stdio npx -y @marp-team/marp-cli --server <dir> Генерация презентаций (HTML/PDF/PPTX) из Markdown

7.3. Прямое использование (без совета)#

using Delibera.Core.Council;
using Delibera.Core.Interfaces;
using Delibera.Core.Models;
using Delibera.Core.Providers.LLM;
using Delibera.Core.Providers.Mcp;

var ollama = new OllamaProvider("http://localhost:11434");

await using var @operator = new Operator(
    new CouncilMember("llama3.2", ollama, "Operator"),
    new IMcpClient[] { new McpClientAdapter(servers[0]), new McpClientAdapter(servers[1]) });

await @operator.InitializeAsync();   // подключается к серверам и обнаруживает инструменты

// 1) Открыть сайт и пересказать его
var browse = await @operator.ExecuteTaskAsync(
    "Open https://modelcontextprotocol.io and briefly summarize what MCP is.");
Console.WriteLine(browse.FinalAnswer);

// 2) Сгенерировать презентацию из 3 слайдов и сохранить как deck.html
var deck = await @operator.ExecuteTaskAsync(
    "Generate a 3-slide Marp presentation about .NET 10 and save it as deck.html.");
Console.WriteLine(deck.FinalAnswer);

await using освобождает MCP-клиентов через ValueTask DisposeAsync у Operator.

7.4. Operator внутри дебатов#

Добавьте Operator в совет с помощью удобной перегрузки WithOperator(...). Участники затем делегируют работу через маркер [[OPERATOR: ...]], и их результаты внедряются в последующие раунды:

var result = await new CouncilBuilder()
    .AddMember("llama3.2:3b", ollama, "Researcher")
    .AddMember("qwen2.5:7b",  ollama, "Reviewer")
    .SetChairman(Chairman.CreateStandard("qwen2.5:7b", ollama))
    // Operator использует свою более дешёвую модель; reuseCompression разделяет компрессор совета
    .WithOperator("llama3.2", ollama, servers, reuseCompression: true)
    .WithStandardDebate()
    .WithSystemPrompt(
        "You may delegate research or presentation tasks to the Operator by writing " +
        "[[OPERATOR: <task>]] in your message.")
    .WithUserPrompt("Research the latest .NET 10 features and prepare a short summary deck.")
    .WithMaxRounds(4)
    .SaveResultTo("./deliberation.md")
    .Build()
    .ExecuteAsync();

Console.WriteLine(result.FinalVerdict);

Сообщение участника может выглядеть так:

I think we should highlight the runtime improvements.
[[OPERATOR: open https://learn.microsoft.com/dotnet/core/whats-new/dotnet-10 and list the top 5 features]]

Все взаимодействия с Operator записываются по раундам и отображаются в финальном Markdown-отчёте в блоке 🛠️ Operator Interactions.

7.5. Настройка через appsettings.json#

{
  "Delibera": {
    "Operator": {
      "Enabled": true,
      "ModelName": "llama3.2",
      "ReuseCompression": true,
      "McpServers": [
        {
          "Name": "browser",
          "Transport": "Stdio",
          "Command": "npx",
          "Arguments": [ "-y", "@playwright/mcp@latest", "--headless" ]
        },
        {
          "Name": "marp",
          "Transport": "Stdio",
          "Command": "npx",
          "Arguments": [ "-y", "@marp-team/marp-cli", "--server", "./out" ]
        }
      ]
    }
  }
}

▶️ Запустите полное демо: dotnet run --project src/Delibera.ConsoleApp -- --operator-mcp. См. OperatorMcpToolsExample.cs и NET10-Upgrade-RU.md для деталей.


8. Сохранение раздельных выходных файлов#

var (resultPath, statsPath, logsPath) = await result.SaveAllAsync("./output");
// → debate_<timestamp>_result.md
// → debate_<timestamp>_statistics.md
// → debate_<timestamp>_logs.md

Каждый файл — это обычный Markdown, идеальный для коммита в git, публикации в чат-инструменты или передачи обратно в другую LLM.


9. Изучите ConsoleApp#

В репозитории есть запускаемое демо в src/Delibera.ConsoleApp/, которое задействует каждую возможность:

Пример Команда Что демонстрирует
Быстрый старт dotnet run Дебаты по умолчанию из appsettings.json
Dependency injection dotnet run -- --di AddDelibera() + резолвинг ICouncilBuilder
Раздельный вывод dotnet run -- --separate-files SaveAllAsync() — три Markdown-файла
Сжатие контекста dotnet run -- --compression Все 4 стратегии + кэш + статистика токенов
Несколько провайдеров dotnet run -- --multiprovider Cloud + локальные модели в одном совете
RAG (Qdrant) dotnet run -- --rag Индексирование документов + Knowledge Keeper
RAG (pgvector) dotnet run -- --pgvector Векторный поиск на базе PostgreSQL
Operator (основы) dotnet run -- --operator Роль Operator с MCP-инструментами
Operator + MCP dotnet run -- --operator-mcp 🆕 MCP-серверы browser + Marp в совете
Microsoft.Extensions.AI dotnet run -- --msai 🆕 IChatClient/IEmbeddingGenerator + middleware

Вызов инструментов (участники вызывают функции)#

🆕 Участник может вызывать инструменты в свой ход — прочитать файл, обратиться по URL или достучаться до MCP-сервера.

using Delibera.Core.Interfaces;
using Delibera.Core.Tools;

// Привязан к каталогу, отклоняет выход за его пределы, ограничивает чтение 1 МБ по умолчанию
var files = new FileSystemToolProvider("./repo");

// Доступны только эти хосты; обычный HTTP отклоняется, если вы его явно не разрешили
var http = new HttpToolProvider(httpClient, ["docs.example.com", "api.github.com"]);

// Или подключите инструменты MCP-сервера
var mcp = new McpToolProvider(mcpClient);

var result = await new CouncilBuilder()
    .AddMember("qwen2.5:7b", ollama, "Analyst")
    .WithUserPrompt("Что валидирует CouncilBuilder.Build()?")
    .WithTools(files, http)
    .WithMaxToolIterations(5)
    .Build()
    .ExecuteAsync();

IToolProvider — интерфейс из двух членов: Name и GetToolsAsync(CancellationToken), возвращающий AIFunction. Реализуйте его, чтобы открыть свои инструменты.

Транспорт вызова зависит от провайдера участника:

Провайдер Транспорт Почему
Оборачивает настоящий IChatClient ToolCallTransport.Native На проводе идут настоящие function-call сообщения
Только строковый адаптер ToolCallTransport.Marker Строковый адаптер сводит каждое сообщение к message.Text, структурированные вызовы через него не проходят — вызов выражается маркером [[TOOL: name {json}]] в тексте ответа

Сам цикл вызовов принадлежит FunctionInvokingChatClient из M.E.AI, а не собственной реализации с ручным повтором. Оба транспорта пишут в один и тот же лог, поэтому читать произошедшее не приходится через ветвление по транспорту:

foreach (var call in result.ToolCalls)          // и по раундам: round.ToolCalls
{
    Console.WriteLine($"{call.MemberName} → {call.ToolName} ({call.Transport}, {call.Duration})");
}

В ToolCallLog также есть Arguments, Result, Succeeded, ErrorMessage и RoundNumber.


Лимиты расходов и ограничения частоты#

🆕 Ограничьте, сколько может потратить дебат. Превышение лимита не приводит к исключению — дебат возвращает деградированный результат вместе с уже потраченным: лимит, который бросает исключение, оставляет вас без отчёта.

using Delibera.Core.Cost;

// A money ceiling needs prices: spend only exists once tokens are priced
var pricing = new ModelPricingRegistry("""
[
  { "model": "qwen2.5:7b",   "inputPerMillion": 0.30, "outputPerMillion": 0.40 },
  { "model": "llama3.2:3b",  "inputPerMillion": 0.10, "outputPerMillion": 0.10 }
]
""");

var result = await new CouncilBuilder()
    .WithPricingRegistry(pricing)
    .WithCostLimit(0.50m)                                  // Abort at the ceiling (default)
    .WithRateLimit(20, TimeSpan.FromMinutes(1))             // 20 calls per minute, per model
    // ...
    .Build()
    .ExecuteAsync();

if (result.IsDegraded)
{
    Console.WriteLine($"Stopped early: {result.CostEstimate?.WasTruncated}");
    foreach (var failure in result.FailedMembers) { /* why */ }
}

WithCostLimit(decimal, CostLimitBehavior) по умолчанию использует Abort; также доступны WarnAndContinue и Ignore. WithTokenBudget(long, …) ограничивает токены и не требует прайс-листа.

⚠️ WithCostLimit(...) без WithPricingRegistry(...) приводит к InvalidOperationException на Build(). Без цен каждый вызов стоит ноль, поэтому лимит настроен, залогирован и не сработает никогда.

DebateResult.CostEstimate — record CostEstimate: TotalCost, TotalPromptTokens, TotalCompletionTokens, Members, IsEstimate, WasTruncated. IsEstimate выставляется, когда у модели не нашлось цены, поэтому оценка никогда не примет вид инвойса.

Помимо сокращений на builder можно подставить свой ICostGate (WithCostGate) или IRateLimiter (WithRateLimiter); встроенные реализации — BudgetCostGate, TokenBucketRateLimiter, ModelPricingRegistry и CostLedger. RateLimitBehavior — это Queue (по умолчанию), Throw или Drop; RateLimitScope — Global, PerProvider, PerModel (по умолчанию) или PerMember.


Сравнение двух дебатов#

🆕 Diff показывает, насколько расходятся два прогона одного вопроса:

using Delibera.Core.Debate;

DebateDiff diff = baseline.Diff(today);

Console.WriteLine($"Verdict similarity: {diff.VerdictSimilarity:P0}");
Console.WriteLine($"Verdict changed:   {diff.VerdictChanged}");
Console.WriteLine($"Missing rounds:    {string.Join(", ", diff.MissingRoundNumbers)}");

foreach (var round in diff.Rounds)
{
    foreach (var member in round.Members)
    {
        // word-level, rendered as **added** / ~~removed~~
        Console.WriteLine(member.InlineDiff);
    }
}

await diff.SaveToMarkdownAsync("diff.md");
await diff.SaveToHtmlAsync("diff.html");       // self-contained

Раунды сопоставляются по номеру раунда, а участники — по отображаемому имени, никогда по позиции в списке. Поэтому сравнение прогона с четырьмя раундами и прогона с тремя сообщает о недостающем раунде, а не сдвигает все последующие сравнения на неверного участника. Diff также сообщает AddedRoundNumbers, MembersOnlyInOld, MembersOnlyInNew и IsEmpty, а каждый уровень можно вывести или сохранить:

DebateDiff → ToMarkdown(), ToHtml(), SaveToMarkdownAsync(path, ct), SaveToHtmlAsync(path, ct) RoundDiff → RoundNumber, RoundName, Members, MemberSimilarity, VerdictChanged MemberDiff → Member, OldText, NewText, Similarity, InlineDiff


gRPC-транспорт#

🆕 Проекты Delibera.Grpc и Delibera.Grpc.Client отдают тот же совет по HTTP/2 и protobuf — для клиентов, которым нужен типизированный клиент вместо SSE:

Сервис RPC
DebateService CreateDebate, GetDebate, ListDebates, CancelDebate, StreamDebate (server streaming)
ScenarioService RunScenario, ValidateScenario
CorpusService ListCorpora, ListDocuments, IndexDocument
builder.Services.AddGrpc();
builder.Services.AddSingleton(sp => new DebateServiceImpl(
    sp.GetRequiredService<IDebateOrchestrator>(),
    () => BuildCouncil(),
    sp.GetRequiredService<ILogger<DebateServiceImpl>>()));

app.MapGrpcService<DebateServiceImpl>();

Сервис лежит поверх IDebateOrchestrator, а не рядом с ним, поэтому дебат, выполненный через gRPC, идёт тем же путём распределённого исполнения и кэширования результатов, что и дебат через REST. См. src/Delibera.Grpc/README.md для разбора со стороны клиента.


Дальнейшие шаги#

  • Прочитайте README для полного обзора возможностей, архитектуры и паттернов проектирования.
  • См. docs/Server.md — ASP.NET Core хостинг с REST API и SSE-стримингом.
  • См. docs/caching.md для кэширования результатов и docs/distributed-debates.md для распределённой оркестрации дебатов.
  • См. CONTRIBUTING.md, если хотите внести вклад.

⚖️ Delibera — Продуманные решения с помощью ИИ

Source: docs/QuickStart-RU.md on GitHub — edit it there and this page updates on the next deploy.