Найти в Дзене
Один Rust не п...Rust

RAG с LLM

t.me/oneRustnoqRust Написать простой веб-сервер с использованием Axum, который реализует защищённый JWT эндпоинт /query. Эндпоинт принимает текстовый запрос, выполняет поиск в векторной базе Qdrant, извлекает релевантный контекст и генерирует ответ с помощью модели OpenAI. Научиться использовать: SECRET_KEY — секрет для проверки подписи JWT. В продакшене его нужно хранить безопасно (например, в переменной окружения). Входящий JSON-документ: Общее состояние приложения: Определяется собственный тип ошибок. Все ошибки преобразуются в HTTP-ответы: Источники ошибок: Функция проверяет подпись токена с помощью того же секрета. Клеймы не проверяются особо (используется default validation), payload декодируется в serde_json::Value. Middleware аутентификации: Важное замечание: код ожидает, что значение заголовка Authorization — это чистый токен (например, Authorization: eyJhbGciOi...). Обычно используется формат Bearer <token>. Чтобы поддерживать стандарт, нужно добавить разбор Bearer. Выполняет
Оглавление
GitHub - nicktretyakov/auth_RAG
ML на RUST без заморочек

t.me/oneRustnoqRust

Для чего нужна данная статья? :

Написать простой веб-сервер с использованием Axum, который реализует защищённый JWT эндпоинт /query. Эндпоинт принимает текстовый запрос, выполняет поиск в векторной базе Qdrant, извлекает релевантный контекст и генерирует ответ с помощью модели OpenAI.

  • Вектор эмбеддинга запроса фиксированный (всегда один и тот же).
  • Вызов OpenAI не реализован — возвращается простая строка.
  • API-ключ OpenAI захардкожен и не используется.

Зачем Вам это уметь? :

Научиться использовать:

  • Axum — асинхронный веб-фреймворк. Импортируются роутинг, middleware, типы HTTP и извлекаторы.
  • qdrant_client — клиент для векторной БД Qdrant.
  • serde — для (де)сериализации JSON.
  • Arc — для безопасного совместного использования состояния между задачами.
  • jsonwebtoken — для проверки JWT-токенов.
  • thiserror — удобное создание ошибок.
  • tracing — структурированное логирование.
  • reqwest — HTTP-клиент (для будущего вызова OpenAI API).

SECRET_KEY — секрет для проверки подписи JWT. В продакшене его нужно хранить безопасно (например, в переменной окружения).

Создание RAG

Входящий JSON-документ:

  • text — обязательный текст запроса пользователя.
  • collection — опциональное имя коллекции в Qdrant (по умолчанию "documents").
  • limit — опциональное количество возвращаемых результатов (по умолчанию 1).

Общее состояние приложения:

  • Клиент Qdrant, обёрнутый в Arc, чтобы безопасно делить между обработчиками.
  • reqwest::Client для будущих запросов к OpenAI.

Обработка ошибок

Определяется собственный тип ошибок. Все ошибки преобразуются в HTTP-ответы:

  • Unauthorized → 401
  • Все остальные → 500

Источники ошибок:

  • Ошибки Qdrant
  • Ошибки OpenAI/anyhow
  • Ошибки reqwest

Проверка JWT-токена

Функция проверяет подпись токена с помощью того же секрета. Клеймы не проверяются особо (используется default validation), payload декодируется в serde_json::Value.

Middleware аутентификации:

  • Берёт заголовок Authorization.
  • Пытается преобразовать его в строку.
  • Проверяет токен.
  • Если всё ок — пропускает запрос дальше (next.run(req)).
  • Иначе — возвращает 401.

Важное замечание: код ожидает, что значение заголовка Authorization — это чистый токен (например, Authorization: eyJhbGciOi...). Обычно используется формат Bearer <token>. Чтобы поддерживать стандарт, нужно добавить разбор Bearer.

Поиск в Qdrant

Выполняет поиск ближайших векторов в указанной коллекции. Возвращает структуру SearchResponse.

Генерация ответа (заглушка)

На данный момент — просто заглушка. В реальной реализации здесь должен быть POST-запрос к https://api.openai.com/v1/chat/completions с промптом, включающим query и context.

Основной обработчик запроса

Последовательность действий:

  1. Логирует входящий запрос.
  2. Определяет коллекцию и лимит.
  3. В реальности здесь должен быть вызов модели эмбеддингов (например, OpenAI text-embedding-3-small).
  4. Ищет в Qdrant.
  5. Берёт текст из payload первого результата (очень грубо — через format!("{:?}", ...), что выдаст Some("текст") или None).
  6. Генерирует ответ (заглушка).
  7. Возвращает JSON вида { "response": "..." }.

Функция main

  • Настраивает логирование (уровень берётся из RUST_LOG).
  • Создаёт клиент Qdrant.
  • Создаёт HTTP-клиент для OpenAI.
  • Формирует общее состояние.
  • Создаёт роутер: только один POST-эндпоинт /query.
  • Применяет middleware аутентификации ко всему роутеру.
  • Запускает сервер.

Что работает сейчас

  • Сервер стартует.
  • Принимает POST-запросы на /query с JSON-тело { "text": "..." }.
  • Проверяет JWT в заголовке Authorization.
  • Всегда ищет один и тот же вектор в Qdrant.
  • Возвращает фиктивный ответ.

Что нужно доработать для реального использования

  1. Эмбеддинги: добавить реальный вызов модели эмбеддингов (OpenAI, Cohere, локальная модель и т.д.).
  2. OpenAI чат: реализовать generate_response с правильным запросом к /v1/chat/completions.
  3. Извлечение контекста: правильно доставать текст из payload (обычно point.payload["text"].as_str()).
  4. Аутентификация: парсить Bearer <token>.
  5. Обработка ошибок: исправить маппинг ошибок Qdrant (сейчас они попадают в OpenAIError).
  6. Безопасность: вынести ключи в env-переменные.