Инициализировать репозиторий «Дейл»

Первый коммит: модульный монолит ядра (.NET 10) и gRPC-сервисы
ai/ml/telegram, фронтенд Vue 3/Vite/Tailwind, документация (ТЗ,
инструкция пользователя, техдокументация, код-стайл), бэклог,
скрипты развёртывания и архив прототипа LeadRadar.
This commit is contained in:
Rustam Khalimov
2026-09-11 02:50:17 +03:00
commit 9e07568ddd
1402 changed files with 177470 additions and 0 deletions
@@ -0,0 +1,47 @@
using Deal.Contracts.Integrations.Models;
namespace Deal.Contracts.Integrations;
/// <summary>
/// Порт ИИ-инструментов Discovery и генерации ключевых слов (Ruling 9, план Task 15/18/19).
/// </summary>
/// <remarks>
/// Порт объявлен в Contracts, потому что контракт потребляет модуль Discovery (воркер/эндпоинты) на этапе 6;
/// реализация — gRPC-адаптер ai-service <c>GrpcAiTools</c>, регистрируемый при <c>Services:Ai:UseLocal=false</c>
/// (Ruling 6). Локальная реализация <c>LocalAiTools</c> (UseLocal=true) методы не поддерживает: Discovery-воркер
/// при aiEnabled=false/сбое сам выбирает эвристику (python discovery_eval L186194), generate-keywords-эндпоинт
/// (Task 19) отдаёт мягкую ошибку {keywords: [], error}. Методы повторяют прототип 1:1:
/// <c>backend/app/routers/discovery_routes.py</c> L189211 (generate-keywords) и
/// <c>backend/app/services/discovery_eval.py</c> L5054/L153194 (оценка fit). Выключатели aiEnabled/aiFilterEnabled
/// порт не читает — ветки выключателей отрабатывает вызывающий (воркер Discovery, Ruling 10).
/// </remarks>
public interface IAiTools
{
/// <summary>
/// Генерация поисковых ключевых слов discovery-задачи по описанию ниши (discovery_routes L3647).
/// </summary>
/// <remarks>
/// Очистку (<c>_clean_keywords</c>: ≤30, ≤60 символов, дедуп) и мягкую ошибку {keywords: [], error} для UI
/// делает вызывающий (эндпоинт Task 19, Ruling 11); адаптер возвращает сырые ключи модели и ok=false при
/// недоступности сервиса/провайдера (без исключений наружу — мягкая форма Ruling 11).
/// </remarks>
/// <param name="description">Описание ниши/задачи (вызывающий режет до 4000, как discovery_routes L29).</param>
/// <param name="ct">Токен отмены.</param>
/// <returns>Результат: ok + ключевые слова (пусто — модель не выделила ключей или сервис недоступен) + текст ошибки.</returns>
public Task<AiGenerateKeywordsResultDto> GenerateKeywordsAsync(string description, CancellationToken ct);
/// <summary>
/// Оценка соответствия сообщения задаче поиска (промпт discovery_eval L5054; Ruling 5/10).
/// </summary>
/// <remarks>
/// Вызывающий (воркер Discovery, Task 18) зовёт только при aiEnabled и при сбое/исключении сам падает в
/// эвристику по ключам (python evaluate_message L186194) — порт ошибки пробрасывает наружу.
/// </remarks>
/// <param name="text">Текст сообщения кандидата (вызывающий режет до 4000).</param>
/// <param name="description">Описание задачи поиска (discovery_eval L51).</param>
/// <param name="keywords">Ключи задачи (discovery_eval L52).</param>
/// <param name="ct">Токен отмены.</param>
/// <returns>Решение fit + краткая причина (потолок причины 200, как _AI_REASON_LIMIT L43).</returns>
public Task<AiEvaluateFitResultDto> EvaluateFitAsync(
string text, string description, IReadOnlyCollection<string> keywords, CancellationToken ct);
}