using Deal.Contracts.Integrations.Models; namespace Deal.Contracts.Integrations; /// /// Порт ИИ-инструментов Discovery и генерации ключевых слов (Ruling 9, план Task 15/18/19). /// /// /// Порт объявлен в Contracts, потому что контракт потребляет модуль Discovery (воркер/эндпоинты) на этапе 6; /// реализация — gRPC-адаптер ai-service GrpcAiTools, регистрируемый при Services:Ai:UseLocal=false /// (Ruling 6). Локальная реализация LocalAiTools (UseLocal=true) методы не поддерживает: Discovery-воркер /// при aiEnabled=false/сбое сам выбирает эвристику (python discovery_eval L186–194), generate-keywords-эндпоинт /// (Task 19) отдаёт мягкую ошибку {keywords: [], error}. Методы повторяют прототип 1:1: /// backend/app/routers/discovery_routes.py L189–211 (generate-keywords) и /// backend/app/services/discovery_eval.py L50–54/L153–194 (оценка fit). Выключатели aiEnabled/aiFilterEnabled /// порт не читает — ветки выключателей отрабатывает вызывающий (воркер Discovery, Ruling 10). /// public interface IAiTools { /// /// Генерация поисковых ключевых слов discovery-задачи по описанию ниши (discovery_routes L36–47). /// /// /// Очистку (_clean_keywords: ≤30, ≤60 символов, дедуп) и мягкую ошибку {keywords: [], error} для UI /// делает вызывающий (эндпоинт Task 19, Ruling 11); адаптер возвращает сырые ключи модели и ok=false при /// недоступности сервиса/провайдера (без исключений наружу — мягкая форма Ruling 11). /// /// Описание ниши/задачи (вызывающий режет до 4000, как discovery_routes L29). /// Токен отмены. /// Результат: ok + ключевые слова (пусто — модель не выделила ключей или сервис недоступен) + текст ошибки. public Task GenerateKeywordsAsync(string description, CancellationToken ct); /// /// Оценка соответствия сообщения задаче поиска (промпт discovery_eval L50–54; Ruling 5/10). /// /// /// Вызывающий (воркер Discovery, Task 18) зовёт только при aiEnabled и при сбое/исключении сам падает в /// эвристику по ключам (python evaluate_message L186–194) — порт ошибки пробрасывает наружу. /// /// Текст сообщения кандидата (вызывающий режет до 4000). /// Описание задачи поиска (discovery_eval L51). /// Ключи задачи (discovery_eval L52). /// Токен отмены. /// Решение fit + краткая причина (потолок причины 200, как _AI_REASON_LIMIT L43). public Task EvaluateFitAsync( string text, string description, IReadOnlyCollection keywords, CancellationToken ct); }