Проект: Чат бот в телеграмм с актуальной информацией, собранной с новостного паблика coinTelegraph https://ru.cointelegraph.com/ , на тему криптовалют.
Стек проекта:
- java 21
- Spring boot, Spring Ai
- Lombok
- Qdrant
- Default model for chat: qwen2.5:3b-instruct(local)
- Default model for embeddign: bge-m3
- Parsing: jsoup,Selenium
- Chunking strategy: sentence splitting
Бот реализован с использованием RAG (Retreaval argument generation), то есть текст(в данном случае) из статей загружается в виде векторов в qdrant(векторная база данных) и в последующем ищется нужная информация для передачи LLM в качестве контекста.
Какую проблему решает? - Решает проблему актуальности данных, а также позволяет LLM использовать внутренний арсенал документов компаний.
Как происходило сохранение и chunking данных из статей в qdrant:
-
Данные извлекаются с сайта, текст статьи сохраняется в Article(entity), также сохраняется link, title.
-
Далее идет chunking, текст, приводится к нижнему регистру, чистится от лишних символов, остаются только буквы и разделители предложений.
-
На следующем этапе весь текст делится по разделителям на предложения и очищается от стоп-слов, следом группируется на чанки по кол-ву предложений, в моей реализации по 4 и сохраняется в поле List с Chunk в Article. Сам Chunk имеет поля text, index.
-
В QdrantRepository создаются документы на основе представленных статей и чанков в них, в этот процесс входит embedding. После создания всех документов они сохраняются в Qdrant. Используется вектор на 1024 элемента (model bge-m3) и косинусное сравнение(по углу между векторами).
- Пользователь вводит запрос.
- Запрос обрабатывается как текст для чанков и отправляется в Qdrant.
- Qdrant возвращает топ-K наиболее схожих чанков (обычно 20).
- На основе этих чанков строится контекст.
- Common — простой поиск по топ-K чанкам, контекст формируется напрямую из найденных фрагментов.
- Advanced — расширенный поиск:
- Генерируются 5 переформулировок запроса.
- Для каждой переформулировки и самому запросу ищутся топ-K фрагментов.
- Из них составлятся контектс по релевантночти каждого, контекст вмещает 30 лучших.
Промт:
You are an assistant that MUST refuse any request for instructions to commit harm, build weapons, engage in illegal activities, or provide step-by-step instructions for wrongdoing.
If a user asks for such instructions, respond with the refusal:
"Извините, я не могу помогать с инструкциями по причинению вреда или изготовлению оружия. Могу помочь с безопасной информацией про криптовалюту."
USER prompt (передаётся вместе с контекстом):
Задача: Тщательно проанализируй предоставленный контекст и дай развёрнутый ответ на вопрос.
Общие правила:
- Отвечай ТОЛЬКО на основании информации, явно присутствующей в блоке "Контекст" ниже.
- Если запрос — инструкция по вреду / незаконной деятельности, СРАЗУ ОТКАЖИСЬ согласно правилу (не продолжай).
- Если в контексте нет данных, подтверждающих ответ — напиши ровно: "Недостаточно информации".
- Не форматируй текст жирным или иным маркером.
- Каждый фактический пункт обязан иметь источники в формате.
- Не добавляй внешней информации и не упоминай, что используешь контекст.
Контекст:
{context}
Вопрос:
{question}
Ответ:
- Настройки LLM для обработки промта с контекстом, направленные на максимизацию точности фактов:
- temperature: 0.1 - низкая креативность
- top-p: 0.9 - суммарная вероятность токенов на каждом шаге
- top-k: 40 - выборка 40 токенов на каждом шаге -repeat-penalty: 1.1 - штраф за повтор