Перейти к основному содержимому

ТЗ: Модуль AI-аналитики (aiAnalytics) для backend

Метаданные

ПараметрЗначение
Дата создания2026-07-02
Дата последнего изменения2026-07-02
Статус апрува⏳ На рассмотрении
Дата апрува

1. Назначение

Добавить в apps/backend (NestJS) новый модуль aiAnalytics с двумя защищёнными JWT эндпоинтами:

  1. Настроение рынка — индекс страха и жадности (Fear & Greed) + AI «Market Outlook» (сценарный прогноз). По образцу продукта zenfi (/Users/vovilonn/Documents/work/defilab/zenfi/apps/backend, модули markets + assistant).
  2. AI-аналитика пула Uniswap — при клике на пул в продукте «index» пользователь получает связный AI-разбор пула (метрики + краткая аналитика двух токенов) в формате Markdown для красивого рендера на фронте. Данные пула берутся из The Graph (тот же источник, что питает продукт index), аналитика токенов — из источников семейства CoinGecko.

Авторизация к OpenAI выполняется не по pay-per-use API-ключу, а через подписку ChatGPT Plus посредством локального OpenAI-совместимого прокси (CLIProxyAPIPlus). Backend работает со стандартным openai SDK, но с baseURL, направленным на прокси.

Реализуется только backend для этих двух эндпоинтов. Фронт — отдельной задачей.


2. Функциональные требования

2.1. Пользовательские сценарии

  1. Настроение рынка. Авторизованный пользователь открывает раздел рынка → фронт зовёт GET /ai-analytics/market/outlook → получает значение Fear & Greed + структурный сценарный прогноз (bias, scenarios[], summary, disclaimer) и отображает виджет.
  2. AI-разбор пула. Авторизованный пользователь в продукте index кликает «аналитика» на конкретном пуле → фронт зовёт POST /ai-analytics/pool/analytics с адресом пула (poolId = on-chain адрес пула, 0x-форма — не числовой id из whitelist) и network → получает готовый Markdown-документ (заголовок, таблица метрик пула, таблица по двум токенам, блок «на что обратить внимание», «итог») и рендерит его.

2.2. Бизнес-логика

Эндпоинт 1 — GET /ai-analytics/market/outlook

  • Fear & Greed: тянется с https://api.alternative.me/fng/?limit=1 (без ключа). Кэш Redis, TTL AI_ANALYTICS_FNG_TTL_SEC (по умолчанию 300 c).
  • Market Outlook: один вызов LLM. Входной блок собирается из:
    • CoinGecko GET /global (total market cap, 24h volume, BTC/ETH dominance, 24h change),
    • CoinGecko GET /coins/markets?vs_currency=usd&order=market_cap_desc&per_page=100&page=1&sparkline=true&price_change_percentage=1h,24h,7d (топ-гейнеры/лузеры + altseason-индикатор),
    • значение Fear & Greed.
  • Altseason-индикатор (считаем сами, без ссылки на внешний код): доля топ-N (например топ-50, исключая стейблкоины) альткоинов, обогнавших BTC по доходности за 7 дней. altseason = round(100 * countAltsBeatingBtc7d / totalAltsConsidered); при недостатке данных поле опускается.
  • Ответ (структурный JSON, как zenfi):
{
"fearGreed": { "value": 72, "valueClassification": "Greed", "timestamp": 1782986400 },
"outlook": {
"bias": "bullish" | "bearish" | "neutral",
"scenarios": [
{ "trigger": "если BTC закрепится выше 70k", "direction": "up" | "down" | "sideways", "range": "3–6%", "note": "одно короткое предложение обоснования" }
],
"summary": "2–3 предложения простым языком",
"disclaimer": "короткая оговорка о рисках, оканчивается фиксированной фразой"
},
"generatedAt": "2026-07-02T10:00:00.000Z",
"cached": true
}
  • Язык текста прогноза — русский (продукт RU). Это решение зафиксировано (см. также §5).
  • Фиксированная фраза дисклеймера: «Не является финансовой рекомендацией.». Валидатор ответа LLM: если outlook.disclaimer не оканчивается этой фразой — детерминированно дописываем её (не отбраковываем ответ). Аналогично при несоответствии структуры JSON — сначала одна повторная попытка, при повторной неудаче — детерминированный фолбэк (см. ниже).
  • Кэш AI-результата: Redis, TTL AI_ANALYTICS_OUTLOOK_TTL_SEC (по умолчанию 3600 c). Конкурентные первые генерации дедуплицируются in-process (inflight-map).
  • Поле cached: относится к тому, была ли AI-часть (outlook) отдана из кэша. Значение Fear & Greed кэшируется отдельно (внутренняя деталь) и на флаг не влияет.
  • Устойчивость: каждый апстрим оборачивается try/catch с деградацией; при полном отказе LLM возвращается детерминированный фолбэк-объект той же формы (bias='neutral', пустые/дефолтные сценарии, дисклеймер с фиксированной фразой), который НЕ кэшируется (чтобы повторить попытку позже). Эндпоинт не отдаёт 5xx из-за одного упавшего апстрима.

Эндпоинт 2 — POST /ai-analytics/pool/analytics

  • Request: { poolId: string /* 0x + 40 hex, on-chain адрес пула */, network?: string }.
    • network необязателен; дефолт — AI_ANALYTICS_DEFAULT_NETWORK (совпадает с сетью текущего сабграфа и пулов index).
    • MVP-ограничение сети: поддерживается ровно одна сеть — та, на которую настроен сабграф. Если network передан и не равен AI_ANALYTICS_DEFAULT_NETWORK400 Bad Request (иначе метрики пришли бы из чужого сабграфа, а токен-данные из другой сети — рассинхрон). Enum значений (bsc|base|ethereum|polygon) задан на будущее, но в MVP валидируется на равенство дефолту.
    • Невалидный формат poolId (не 0x+40 hex) → 400 (валидация DTO).
  • Сбор метрик (детерминированно, в коде):
    Поле шаблонаИсточник
    Заголовок $SYM0 / $SYM1 — Uniswap v3 (Network)символы токенов из сабграфа + версия/сеть из конфигурации (префикс $ — у обоих символов)
    Комиссия (%)feeTier / 10000 (напр. 3000 → 0.30%); feeTier берём из getPoolsOverview().items[0].feeTier или из расширенного getPoolAnalytics
    TVLgetPoolAnalytics().tvlUSD
    Объём 24чgetPoolAnalytics().volume24hUSD
    Заработок пула 24чgetPoolAnalytics().fees24hUSD
    Доходность (APR, годовых)getPoolsOverview().items[0].apr7dPercent (fees7d/avgTVL·365/7·100)
    Проскальзывание (~$5k)эвристика (см. ниже), помечается «~»
    Цена токенаtoken-data (CoinGecko/GeckoTerminal), сверяется с ценой сабграфа ethPriceUSD·derivedETH
    Изменение 24ч, Капитализацияtoken-data
    «Что это» / проза / IL / «Итог»LLM на основе переданных чисел
  • Оценка проскальзывания (MVP-эвристика): приближение через constant-product на TVL как для 50/50-пула: slippage% ≈ tradeSize / (tradeSize + TVL/2) · 100, где tradeSize = 5000 USD. Проверка на примере шаблона: TVL $224.5k → 5000 / (5000 + 112250) ≈ 4.26% → «~4–5%». Значение помечается как приблизительное. Точный расчёт по тиковой ликвидности v3 — техдолг (см. 4.2).
  • Генерация Markdown: собранный структурный блок метрик передаётся LLM. Системный промпт: писать строго по шаблону на русском языке, использовать переданные числа дословно, ничего не выдумывать, для отсутствующих значений (mcap/±24ч мелких токенов) — писать «—» или опускать, не придумывать; вернуть только Markdown (без преамбулы и без ```-ограждений). Пост-обработка: срезаем возможные code-fences и текст до первого заголовка #. Если в ответе нет ни одного заголовка # — считаем это отказом LLM и отдаём детерминированный фолбэк.
  • Ответ:
{
"markdown": "# $JESSE / $WETH — Uniswap v3 (BSC)\n...",
"metrics": {
"pool": { "poolId": "0x…", "network": "bsc", "dex": "uniswap-v3", "feeTierPercent": 0.3,
"tvlUsd": 224500, "volume24hUsd": 680000, "fees24hUsd": 2040,
"aprPercent": 332, "slippage5kPercent": 4.3 },
"token0": { "address": "0x…", "symbol": "JESSE", "name": "…"|null, "priceUsd": 0.001176,
"change24hPercent": 54.7|null, "marketCapUsd": 1180000|null },
"token1": { "address": "0x…", "symbol": "WETH", "name": "…"|null, "priceUsd": 2400,
"change24hPercent": 1.2|null, "marketCapUsd": null }
},
"generatedAt": "2026-07-02T10:00:00.000Z",
"cached": false
}
  • Главное поле — markdown; metrics отдаём дополнительно (для нативного рендера на фронте).
  • Обязательные поля токена: address, symbol, priceUsd. Поля name, change24hPercent, marketCapUsdnullable (мелкие токены могут не иметь данных; см. 4.1.2). В примере token1.marketCapUsd: null показывает именно этот случай.
  • Поле cached: относится к тому, была ли markdown (AI-часть) отдана из кэша.
  • Кэш: Redis по ключу ai:pool:{network}:{poolId}, TTL AI_ANALYTICS_POOL_TTL_SEC (по умолчанию 900 c). Числовые данные графа уже кэшируются UniswapV3GraphService. Конкурентные первые генерации дедуплицируются in-process по ключу (network, poolId).
  • Устойчивость / ошибки:
    • getPoolAnalytics(poolId) вернул пусто/нет ключевых полей (пул не найден в сабграфе, нет токенов/TVL) → 404 Not Found (pool_not_found).
    • LLM недоступен → деградируем до детерминированного минимального Markdown (заголовок + таблица метрик, без AI-прозы). Такой фолбэк НЕ кэшируется (как и в эндпоинте 1), чтобы после восстановления LLM выдать полноценный ответ.
    • Эндпоинт не отдаёт 5xx из-за отказа LLM/токен-апстрима.

2.3. UI/UX требования

Не применимо (backend-only). Ответ эндпоинта 2 — валидный Markdown, рассчитанный на прямой рендер на фронте.


3. Техническая реализация

3.1. Архитектура

Вариант A1 (утверждён): переиспользуем существующий UniswapV3GraphService; OpenAI-клиент — новый провайдер за DI-токеном внутри aiAnalytics.

apps/backend/src/aiAnalytics/
ai-analytics.module.ts # регистрация; imports: [LiquidityModule] (для UniswapV3GraphService)
ai-analytics.controller.ts # 2 роута, @UseGuards(JwtAuthGuard) + @ApiBearerAuth + @ApiTags
providers/
openai.provider.ts # OPENAI_CLIENT = new OpenAI({ baseURL, apiKey }) → CLIProxyAPIPlus
services/
market-outlook.service.ts # fear&greed + Market Outlook (LLM → JSON) + inflight-dedup + кэш
pool-analytics.service.ts # сбор метрик пула + LLM → Markdown + inflight-dedup + кэш
market-data.service.ts # CoinGecko /global,/coins/markets + alternative.me/fng (axios + кэш)
token-data.service.ts # CoinGecko contract + GeckoTerminal fallback (по network+address)
utils/
slippage.util.ts # оценка price impact ~$5k (чистая функция)
markdown.util.ts # снятие fences/преамбулы, валидация наличия заголовка
llm.util.ts # надёжный парс JSON из ответа LLM (json_object → текст-фолбэк)
prompts/
market-outlook.prompt.ts # системный промпт + builder входа (чистые, тестируемые)
pool-analytics.prompt.ts # системный промпт + builder входа под шаблон (RU)
dto/
index.ts # request/response DTO (class-validator + @ApiProperty)

Поток эндпоинта 2 (controller → pool-analytics.service):

  1. UniswapV3GraphService.getPoolAnalytics(poolId) → TVL, объём24ч, комиссии24ч, цены токенов, метаданные токенов (address/symbol/decimals) и feeTier (после расширения маппинга, см. 3.2);
  2. UniswapV3GraphService.getPoolsOverview([poolId])result.items[0].apr7dPercentfeeTier, если не берём из шага 1);
  3. token-data.service по адресам токенов → mcap, ±24ч, описания;
  4. slippage.util → оценка проскальзывания;
  5. сборка структурного блока метрик → openai.provider (LLM) → Markdown → markdown.util пост-обработка;
  6. кэш в Redis, ответ.

3.2. Описание технической реализации

Изменения в существующем коде

  1. src/liquidity/liquidity.module.ts: добавить UniswapV3GraphService в exports (сейчас он только в providers), чтобы AiAnalyticsModule мог его инъектить через imports: [LiquidityModule].
  2. src/liquidity/uniswap-v3-graph.service.ts: расширить возвращаемый тип getPoolAnalytics() — добавить метаданные токенов (token0/token1: { address, symbol, decimals }) и feeTier. Данные уже приходят в POOL_ANALYTICS_QUERY (feeTier, token0/token1 { id symbol decimals derivedETH }) — правится только маппинг (строки ~478–487), тип в uniswap-v3-graph.types.ts (~56–65). Инцидентная уборка (по ходу правки этого файла): удалить отладочные console.log на строках 512/526/570/585 в getMarketAnalytics.
  3. src/app.module.ts: зарегистрировать AiAnalyticsModule в imports; добавить новые env в Joi-схему.
  4. .env.example: добавить новые переменные.

OpenAI-провайдер (providers/openai.provider.ts)

// псевдо-конфиг
new OpenAI({
baseURL: process.env.OPENAI_BASE_URL ?? 'http://localhost:8317/v1', // CLIProxyAPIPlus
apiKey: process.env.OPENAI_API_KEY, // клиентский токен прокси (см. ниже)
});
  • Вызовы: chat.completions.create({ model: AI_MODEL, messages: [system, user] }).
  • temperature НЕ передаём. Backend ChatGPT-Plus/Codex, к которому проксирует CLIProxyAPIPlus, использует ограниченную Responses-поверхность и отклоняет temperature (HTTP 400); reasoning-модели GPT-5 также требуют temperature=1. Параметр отправляем только если задан AI_TEMPERATURE и модель заведомо его поддерживает (по умолчанию — не отправляем).
  • response_format: json_object — необязательная оптимизация, не обязательный контракт. Тот же Codex-backend может не поддерживать response_format. Поэтому JSON-эндпоинт (outlook) работает по двухпутевой схеме, где парсинг обязателен: (1) пробуем с response_format: { type: 'json_object' }; (2) при ошибке/неподдержке — повтор без него; в обоих случаях извлекаем JSON из текста (llm.util) и валидируем структуру по схеме. Никогда не полагаемся на то, что ответ уже чистый JSON.
  • Эндпоинт 2: обычный текст (Markdown); пост-обработка markdown.util.
  • Модель AI_MODEL конфигурируема и выбирается из фактически доступных на подписке моделей — сверяется через GET {OPENAI_BASE_URL}/models на запущенном прокси (не все codex-варианты доступны на ChatGPT-аккаунте). Не хардкодим.
  • Клиентский OPENAI_API_KEY: CLIProxyAPIPlus требует от клиента ключ только если в его config.yaml непустой список api-keys. Для боевого окружения делаем OPENAI_API_KEY обязательным и документируем, что его значение должно совпадать с записью в api-keys прокси. (Это отдельный ключ от Management API secret-key.)

Источники данных по токенам (token-data.service.ts) — вариант B1

  • Основной: CoinGecko GET https://api.coingecko.com/api/v3/coins/{asset_platform}/contract/{address}name, description.en, market_data.current_price.usd, market_data.price_change_percentage_24h, market_data.market_cap.usd. Keyless или с COINGECKO_API_KEY (demo/pro header).
  • Фолбэк (чисто on-chain токены): GeckoTerminal:
    • GET https://api.geckoterminal.com/api/v2/networks/{network}/tokens/{address}attributes.price_usd, fdv_usd, market_cap_usd, name, symbol.
    • ±24ч цены НЕТ в атрибутах токена — берём из GET .../tokens/{address}/poolsdata[0].attributes.price_change_percentage.h24 (топовый пул токена). Keyless.
  • Числовая цена дополнительно сверяется с ценой из сабграфа.
  • Кэш token-data: Redis, TTL AI_ANALYTICS_TOKEN_TTL_SEC (по умолчанию 300 c).
  • Маппинг сети → идентификаторы:
    networkCoinGecko asset_platformGeckoTerminal slug
    bscbinance-smart-chainbsc
    basebasebase
    ethereumethereumeth
    polygonpolygon-pospolygon_pos

Кэш и хранение

  • Только Redis (CACHE_MANAGER), без новых таблиц БД. TTL передаём в форме { ttl: <секунды> } (стиль cache-manager v4, как в проекте).
  • TTL (все конфигурируемы через env): fear&greed 300 c (AI_ANALYTICS_FNG_TTL_SEC), outlook 3600 c (AI_ANALYTICS_OUTLOOK_TTL_SEC), token-data 300 c (AI_ANALYTICS_TOKEN_TTL_SEC), pool markdown 900 c (AI_ANALYTICS_POOL_TTL_SEC).

Новые переменные окружения

ПеременнаяНазначениеДефолт
OPENAI_BASE_URLURL CLIProxyAPIPlushttp://localhost:8317/v1
OPENAI_API_KEYклиентский токен прокси (обязателен для боевого; совпадает с api-keys прокси)
AI_MODELимя модели у прокси/подписки (из GET /v1/models)— (обязателен для боевого)
AI_TEMPERATUREопц.; передаётся только если модель поддерживаетне задан (не отправляется)
AI_ANALYTICS_DEFAULT_NETWORKсеть пулов index / сабграфаbsc
AI_ANALYTICS_FNG_TTL_SECTTL кэша fear&greed300
AI_ANALYTICS_OUTLOOK_TTL_SECTTL кэша outlook3600
AI_ANALYTICS_TOKEN_TTL_SECTTL кэша token-data300
AI_ANALYTICS_POOL_TTL_SECTTL кэша pool markdown900
COINGECKO_API_KEYопц. ключ CoinGecko (лимиты)

AI_MODEL в Joi — .optional() в dev, но обязателен для боевого запуска (проверяется на старте сервиса; без него LLM-эндпоинты отдают деградированный ответ). Уже используемые графом переменные: UNISWAP_V3_SUBGRAPH_URL / THE_GRAPH_API_KEY + UNISWAP_V3_SUBGRAPH_ID.

Авторизация эндпоинтов

Оба роута — @UseGuards(JwtAuthGuard) + @ApiBearerAuth(), без пейволла. Пользователь читается через @Request() req: { user: JwtPayloadDto }. Роуты защищены явно (по умолчанию в проекте роут публичный).

Тестирование

  1. Юнит (jest, замоканы axios/openai): builder'ы промптов; slippage.util (проверка на числах шаблона → 4.26%); altseason-расчёт; форматтеры/маппинг метрик; markdown.util (снятие fences, случай без заголовка); llm.util (парс JSON из «грязного» ответа); валидатор дисклеймера (дописывание фразы); маппинг token-data и сети.
  2. Сервисные: market-outlook.service → корректный DTO, обе ветки response_format (с/без), фолбэк без кэша; pool-analytics.service → markdown содержит ожидаемые числа, 404 при пустом графе, деградация без кэша (моки графа + CoinGecko/GeckoTerminal + OpenAI).
  3. e2e (supertest, замоканы апстримы): оба роута → 200 и корректная форма; 401 без JWT; 400 при network != default и невалидном poolId; 404 при неизвестном пуле.
  4. Живой smoke-тест (реальные запросы) — скрипт apps/backend/scripts/ai-analytics-smoke.sh + runbook (см. ниже).

Runbook живого smoke-теста

  1. Поднять CLIProxyAPIPlus (docker), задать config.yaml (в т.ч. api-keys: [<OPENAI_API_KEY>]).
  2. Залогиниться в ChatGPT Plus одноразовым OAuth (--codex-login); токен сохраняется в ~/.cli-proxy-api/. Требуется активная подписка ChatGPT Plus/Pro.
  3. Проверить доступные модели: curl {OPENAI_BASE_URL}/models → выбрать AI_MODEL.
  4. Выставить env (OPENAI_BASE_URL, OPENAI_API_KEY, AI_MODEL, сабграф, дефолтная сеть), запустить pnpm dev-back, получить dev-JWT.
  5. curl GET /ai-analytics/market/outlook → валидный JSON аутлука; curl POST /ai-analytics/pool/analytics с реальным poolId (0x-адрес) из index → непустой Markdown. Скрипт печатает результаты и коды ответов.

Контрольная точка. Как только будет готов OpenAI-коннектор (openai.provider.ts + env), у пользователя запрашивается логин в OpenAI (ChatGPT Plus через CLIProxyAPIPlus). Гейтится только реальное/живое тестирование LLM — дальнейшая разработка сервисов с замоканным OpenAI (этапы 3–5) может продолжаться параллельно.


4. Проблемы и компромиссы

4.1. Известные ограничения

  1. Один сабграф-эндпоинт (одна сеть).
    • UniswapV3GraphService работает с единственным сконфигурированным сабграфом. Пулы index сейчас на BSC.
    • Влияние: MVP анализирует пулы только сети AI_ANALYTICS_DEFAULT_NETWORK; иные значения network400. Мультисеть — техдолг (см. 4.2).
  2. Покрытие мелких токенов данными.
    • Очень мелкие токены могут отсутствовать в CoinGecko-by-contract; фолбэк — GeckoTerminal; если и там нет — name/marketCapUsd/change24hPercent могут быть null.
    • Влияние: поля nullable; LLM инструктируется писать «—»/опускать, не выдумывать.
  3. Зависимость от локального прокси и подписки.
    • Реальные LLM-запросы требуют запущенного CLIProxyAPIPlus, залогиненного в ChatGPT Plus. Это операционная зависимость вне кода.
    • Влияние: автотесты мокают OpenAI; «реальные» проверки — через живой smoke-тест.
  4. Ограниченная Responses-поверхность подписки.
    • Backend ChatGPT-Plus/Codex не поддерживает temperature и, возможно, response_format → не отправляем temperature, JSON-парс делаем всегда сами. Набор моделей зависит от подписки → AI_MODEL конфигурируем.

4.2. Технический долг

  • Точный расчёт проскальзывания по тиковой ликвидности Uniswap v3 (сейчас — эвристика на TVL). План: отдельная итерация после MVP.
  • Мультисеть для сабграфа (network → endpoint map). План: когда появятся пулы index на других сетях.
  • Персистентное хранение истории аналитик (сейчас только Redis-кэш). План: при необходимости истории/аудита.
  • Опциональный per-user rate-limit (cooldown) на LLM-эндпоинты (Redis, 429). Вынесен из MVP; основная защита — JWT + кэш.

4.3. Риски

  • Рейт-лимиты CoinGecko/GeckoTerminal (keyless). Митигация: кэш + опц. COINGECKO_API_KEY.
  • Квоты/лимиты подписки ChatGPT Plus (могут вернуть «usage limit reached»). Митигация: кэш результатов, деградация до фолбэка, мониторинг.
  • Нестабильность/недоступность прокси. Митигация: детерминированные фолбэки в обоих эндпоинтах, эндпоинты не 5xx.
  • Галлюцинация чисел LLM. Митигация: все числа считаются в коде и передаются готовыми; жёсткое правило «не выдумывать»; пост-валидация.
  • Расхождение сети пула и сабграфа. Митигация: валидация network == default (400), документированное ограничение.

5. Вопросы на дополнительное обсуждение

  • Точная модель AI_MODEL, доступная через CLIProxyAPIPlus на подписке (для дефолта в .env.example). Определяется по GET /v1/models на запущенном прокси.
    • Кому адресовано: заказчик / DevOps

Язык вывода обоих эндпоинтов зафиксирован — русский (решение принято, не требует доп. согласования).


6. План реализации

6.1. Этапы разработки

  • Этап 1: Каркас модуля aiAnalytics (module/controller/dto), регистрация в app.module.ts, env + Joi.
  • Этап 2: OpenAI-коннектор (openai.provider.ts + llm.util) + env → контрольная точка: запросить у пользователя логин в OpenAI (гейтит только живые тесты).
  • Этап 3: Эндпоинт 1 (market-data.service + market-outlook.service + промпт + altseason) с юнит- и сервис-тестами.
  • Этап 4: Экспорт + расширение UniswapV3GraphService (метаданные токенов/feeTier, уборка console.log); token-data.service; slippage.util.
  • Этап 5: Эндпоинт 2 (pool-analytics.service + промпт + markdown.util) с тестами (включая 400/404/деградацию).
  • Этап 6: e2e-тесты (supertest) + живой smoke-скрипт и runbook; реальная проверка обоих эндпоинтов.
  • Этап 7: Документация (Swagger, engineering docs), финальная сверка.

6.2. Критические зависимости

  • Запущенный и залогиненный CLIProxyAPIPlus (ChatGPT Plus) — для реальных LLM-запросов.
  • Корректно сконфигурированный сабграф Uniswap v3 под сеть пулов index (UNISWAP_V3_SUBGRAPH_URL).
  • Доступность CoinGecko / GeckoTerminal.

7. Тестирование

Описано в §3.2 «Тестирование» и «Runbook живого smoke-теста»: юнит + сервисные + e2e (замоканные апстримы) и живой smoke-тест на реальных запросах после логина в ChatGPT Plus.


8. Документация

  • API-документация (Swagger) для обоих эндпоинтов.
  • Engineering docs: описание модуля aiAnalytics и интеграции с CLIProxyAPIPlus.
  • Runbook по запуску живого smoke-теста (поднятие прокси + логин + выбор модели).

9. Ссылки

  • Референс: zenfi/apps/backend — модули markets, assistant (система-промпт: src/markets/analysis-prompt.ts).
  • Существующий граф-клиент: apps/backend/src/liquidity/uniswap-v3-graph.service.ts.
  • Продукт index: apps/backend/src/liquidity/liquidity.service.ts (whitelist пулов, партнёрская подпись).
  • CLIProxyAPIPlus: https://github.com/kaitranntt/CLIProxyAPIPlus, https://help.router-for.me/