ZakupkiAI - это локально запускаемый ИИ-ассистент, специализирующийся на российском законодательстве о госзакупках (44-ФЗ и 223-ФЗ). Он использует современные Большие Языковые Модели (LLM), запускаемые локально (через llama-cpp-python) или через API (Google Gemini), и обладает способностью искать актуальную информацию в реальном времени с помощью поискового API (Tavily) для предоставления точных и обоснованных ответов.
Основные цели:
- Создать ассистента, способного отвечать на вопросы по 44-ФЗ и 223-ФЗ.
- Реализовать механизм RAG (Retrieval-Augmented Generation), который позволяет ассистенту:
- Принимать решение о необходимости поиска внешней информации.
- Генерировать релевантные поисковые запросы (с уточнением 44-ФЗ/223-ФЗ).
- Использовать поисковый API (Tavily) для нахождения релевантных веб-страниц.
- Загружать, очищать и обрабатывать контент найденных страниц.
- Находить наиболее релевантные фрагменты текста (чанки) на этих страницах с использованием векторного поиска (FAISS + Sentence Transformers).
- Синтезировать финальный ответ, основываясь на найденной информации и указывая источники (URL).
- Обеспечить возможность запуска как с локальными LLM (требующими ресурсов CPU/GPU), так и с облачными моделями (Gemini).
- Предоставить простой и удобный веб-интерфейс на базе Streamlit для взаимодействия с ассистентом.
- Использовать современные инструменты управления зависимостями (
Pixi) и контейнеризацию (Docker) для легкой настройки и воспроизводимости окружения.
.
├── .devcontainer/ # Конфигурация для VS Code Dev Containers (Docker)
│ ├── Dockerfile # Инструкции по сборке Docker-образа
│ ├── docker-compose.yml # Определение сервиса Docker
│ └── devcontainer.json # Конфигурация VS Code
├── data/
│ └── raw/
│ └── verified_sources.txt # Список доменов для инфо-проверки RAG
├── models/ # Директория для хранения локальных LLM моделей (GGUF)
│ └── gguf_models/ # (создается, если используется локальная LLM)
├── src/ # Исходный код приложения
│ ├── agent/ # Логика RAG-пайплайна и LLM
│ │ ├── __init__.py
│ │ ├── executor.py # Основной пайплайн запроса (решение, поиск, RAG, синтез)
│ │ ├── models.py # Загрузка LLM (локальной или Gemini) и эмбеддингов
│ │ ├── prompts.py # Шаблоны промптов для LLM
│ │ └── tools.py # Инструменты: Поиск (Tavily), RAG над страницами
│ ├── utils/ # Вспомогательные функции
│ │ ├── __init__.py
│ │ └── helpers.py # Утилиты (загрузка доменов, извлечение домена из URL)
│ ├── __init__.py
│ └── config.py # Конфигурация приложения (Pydantic + .env)
├── .env.example # Пример файла переменных окружения
├── .gitignore
├── pixi.lock # Файл блокировки зависимостей Pixi
├── pixi.toml # Определение зависимостей и задач для Pixi
├── README.md # Этот файл
└── streamlit_app.py # Скрипт Streamlit UI (точка входа)
Ассистент использует следующий пайплайн для ответа на вопросы, требующие актуальной информации:
- Решение о Поиске: LLM анализирует запрос пользователя и решает, нужен ли поиск в интернете (Промпт:
DECIDE_SEARCH_PROMPT). - Генерация Поискового Запроса: Если поиск нужен, LLM генерирует оптимизированный поисковый запрос, стараясь добавить "44-ФЗ" или "223-ФЗ" (Промпт:
GENERATE_SEARCH_QUERY_PROMPT). - Поиск URL (Tavily): Сгенерированный запрос передается в API Tavily Search (
run_tavily_search). Запрашивается несколько (max_search_results) наиболее релевантных результатов. Tavily возвращает список URL и краткое описание контента. - Проверка Доверенных (Логирование): Из результатов Tavily извлекаются URL. Проверяется, сколько из этих URL принадлежат доменам из списка
verified_sources.txt. Эта информация логируется и добавляется в начало финального ответа (check_urls_against_verified_list). Фильтрация на данном этапе отключена. - RAG над Найденными Страницами (
process_multiple_urls->rag_on_single_page):- Для каждого URL, найденного Tavily:
- Загрузка: Страница загружается с помощью
WebBaseLoaderс таймаутом и обработкой ошибок (load_web_page_robust). - Очистка: HTML очищается от лишних тегов (
BeautifulSoupTransformerбезtags_to_extract). Если очистка не удалась, используется сырой HTML. - Чанкинг: Полученный текст разбивается на перекрывающиеся фрагменты (чанки) с помощью
RecursiveCharacterTextSplitter. - Векторизация и Поиск: Чанки индексируются с помощью FAISS и модели эмбеддингов (
Sentence Transformers). Выполняется поиск наиболее релевантных чанков по исходному запросу пользователя. - Сбор Контекста: Тексты найденных релевантных чанков собираются вместе, к каждому добавляется префикс с указанием URL-источника и номера чанка.
- Загрузка: Страница загружается с помощью
- Для каждого URL, найденного Tavily:
- Синтез Финального Ответа: Собранный контекст (включая информацию об ошибках обработки некоторых URL и статистику проверки доверенных источников) и исходный запрос пользователя передаются в LLM (Промпт:
SYNTHESIZE_ANSWER_PROMPT). LLM генерирует финальный структурированный ответ, обязательно ссылаясь на источники (URL), если использовалась информация из них.
- Язык: Python 3.9+
- Веб-интерфейс: Streamlit
- Оркестрация RAG/LLM: LangChain (LCEL)
- Поиск: Tavily Search API (
tavily-python) - Загрузка/Парсинг Веб:
langchain-community(WebBaseLoader,BeautifulSoupTransformer) - Локальные LLM:
llama-cpp-python(через LangChain) - Облачные LLM: Google Gemini API (
langchain-google-genai) - Эмбеддинги: Sentence Transformers (
sentence-transformers, через LangChain) - Векторный Поиск: FAISS (
faiss-cpu) - Управление Зависимостями: Pixi (
pixi) - Контейнеризация: Docker, Docker Compose
- Конфигурация: Pydantic, python-dotenv
Этот метод рекомендуется, так как он обеспечивает идентичное окружение для разработки и запуска.
-
Пререквизиты:
- Установленный Docker и Docker Compose.
- Установленный VS Code с расширением Dev Containers.
-
Клонируйте репозиторий:
git clone https://github.com/pyramidheadshark/ZakupkiAI.git # Замените URL, если нужно cd ZakupkiAI
-
Настройте Переменные Окружения:
- Скопируйте
.env.exampleв.env:cp .env.example .env - Откройте
.envи обязательно укажите вашTAVILY_API_KEY. - Если вы планируете использовать Google Gemini:
- Укажите ваш
GOOGLE_API_KEY. - Измените
LLM_PROVIDER="local"наLLM_PROVIDER="gemini". - (Опционально) Измените
GEMINI_LLM__GEMINI_MODEL_NAME, если хотите использовать другую модель Gemini.
- Укажите ваш
- Если вы используете локальную LLM:
- Убедитесь, что
LLM_PROVIDER="local". - Скачайте нужную модель в формате GGUF (например, с Hugging Face).
- Создайте директорию:
mkdir -p models/gguf_models - Поместите скачанный
.ggufфайл вmodels/gguf_models/. - Очень важно: Укажите правильный путь к вашему файлу модели в
.envв переменнойLOCAL_LLM__MODEL_GGUF_PATH(например,LOCAL_LLM__MODEL_GGUF_PATH=models/gguf_models/ваша_модель.gguf). - (Опционально) Настройте
LOCAL_LLM__N_GPU_LAYERSдля использования GPU (укажите количество слоев для выгрузки на GPU,-1для всех,0для CPU). Убедитесь, что ваша система и Docker настроены для работы с GPU.
- Убедитесь, что
- Скопируйте
-
Откройте Проект в Dev Container:
- Откройте VS Code.
- Нажмите
F1илиCtrl+Shift+P(Cmd+Shift+Pна macOS). - Введите и выберите:
Dev Containers: Open Folder in Container... - Выберите папку с проектом (
ZakupkiAI). - VS Code автоматически соберет Docker-образ (это может занять некоторое время при первом запуске) и запустит контейнер. Pixi установит все зависимости внутри контейнера.
-
Запустите Streamlit Приложение:
- После того как контейнер запустится и VS Code подключится к нему, откройте терминал внутри VS Code (
Terminal->New Terminal). - Выполните команду запуска, определенную в
pixi.toml:pixi run start
- Альтернативно, можно запустить напрямую:
streamlit run streamlit_app.py
- После того как контейнер запустится и VS Code подключится к нему, откройте терминал внутри VS Code (
-
Откройте Приложение:
- VS Code обычно автоматически пробрасывает порт Streamlit (
8501). Вы увидите уведомление с кнопкой "Open in Browser". - Или вручную откройте в браузере:
http://localhost:8501
- VS Code обычно автоматически пробрасывает порт Streamlit (
- Текущее состояние:
- Реализован основной RAG-пайплайн с использованием Tavily и локальных/Gemini LLM.
- Очистка HTML упрощена для лучшего извлечения текста.
- Фильтрация по доверенным источникам отключена (используются все результаты Tavily), но ведется подсчет и логирование совпадений.
- Добавлено подробное логирование для диагностики RAG.
- Интерфейс Streamlit позволяет задавать вопросы и получать ответы.
- Возможные улучшения:
- Более Устойчивый Парсинг: Исследовать и внедрить более надежные методы извлечения основного контента со сложных веб-страниц (например, библиотеки
readability-lxml,trafilatura, илиlangchain_community.document_transformers.Html2TextTransformer). - Асинхронная Обработка: Перевести загрузку и обработку страниц в асинхронный режим для ускорения ответа при обработке нескольких URL.
- Поддержка Истории Диалога: Добавить память в LangChain пайплайн для поддержания контекста разговора.
- Улучшение Промптов: Дальнейшая оптимизация промптов для более точного управления поведением LLM (решение о поиске, генерация запроса, синтез ответа).
- Оценка Качества: Разработать метрики и тестовые наборы для оценки точности и релевантности ответов.
- Fine-tuning (Опционально): Дообучение (fine-tuning или continued pre-training) локальной LLM на корпусе текстов по 44-ФЗ/223-ФЗ для улучшения ее базовых знаний.
- Переключение на Google CSE: Если Tavily + обработка всех URL не даст стабильных результатов, рассмотреть переход на Google Custom Search Engine с поиском только по доверенным сайтам.
- Визуализация Источников: Более наглядно отображать источники информации в интерфейсе Streamlit.
- Более Устойчивый Парсинг: Исследовать и внедрить более надежные методы извлечения основного контента со сложных веб-страниц (например, библиотеки
Проект распространяется под лицензией MIT.