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).
- Локально: установите Ollama и запустите
- Опционально — для роли 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 3CLI читает ту же секцию конфигурации 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 runDelibera проведёт структурированные многораундовые дебаты и запишет полную стенограмму и вердикт
председателя в файл 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/qdrantusing 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.
