English translation is not available yet. Showing Russian content.
Агент с памятью через векторную БД
ТЕХНИЧЕСКОЕ ЗАДАНИЕ: Агент с памятью через векторную БД
1. Цель задачи
Разработать агента на базе LangGraph, который сохраняет долгосрочную память в векторной базе данных Qdrant. Агент должен уметь извлекать релевантные факты из прошлых диалогов и использовать их для ответов, даже после десяти и более смен тем. Главная задача — не просто хранить историю чата, а реализовать семантический поиск для релевантного контекста, чтобы агент «помнил» важные детали сквозь диалоги.
Ключевой результат Рабочий агент, который корректно отвечает на вопрос, требующий знания факта из раннего диалога (из перечня 10+ диалогов), с точностью recall@1 ≥ 0.7.
2. Исходные данные
| Что нужно | Откуда взять |
|---|---|
| LangGraph агент (заготовка) | Создать самому (простой ReAct-агент) или взять из Pet 220 |
| Векторная БД Qdrant | Docker-образ qdrant/qdrant или SaaS на cloud.qdrant.io |
| Модель эмбеддингов | sentence-transformers/all-MiniLM-L6-v2 (локально) или OpenAI text-embedding-3-small |
| LLM (для агента) | OpenAI GPT-4o / Ollama (llama3.2:3b) |
| Набор тестовых диалогов | Сгенерировать самостоятельно (10+ уникальных сценариев) |
Если нет реального инструмента — симулируем:
- Qdrant — запустить локально через Docker:
docker run -d --name qdrant -p 6333:6333 qdrant/qdrant - LLM — использовать Ollama + модель llama3.2:3b (бесплатно, локально).
ollama pull llama3.2:3b - Тестовые диалоги — написать Python-скрипт, который генерирует 12 коротких диалогов (по 3-4 реплики) на разные темы, с перекрестными ссылками. Например:
- Диалог 1: пользователь говорит, что любит готовить пасту.
- Диалог 9: вопрос «Какой рецепт пасты я упоминал?».
3. Технологический стек
| Компонент | Инструменты | Назначение |
|---|---|---|
| Фреймворк агента | LangGraph (Python) | Построение графа агента с состояниями |
| Векторная БД | Qdrant (v1.12+) | Хранение и поиск семантических фактов |
| Эмбеддинги | sentence-transformers (all-MiniLM-L6-v2) | Преобразование текста в векторы |
| LLM | OpenAI API / Ollama | Генерация ответов и синтез памяти |
| Клиент Qdrant | qdrant-client (Python) | Взаимодействие с коллекциями |
| Тестирование | Pytest + скрипты | Проверка recall и воспроизводимости |
4. Этапы выполнения
Этап 1: Создание базового LangGraph агента (1 час)
Действия
- Установить зависимости: langgraph,
langchain-openai(илиlangchain-ollama), qdrant-client, sentence-transformers. - Реализовать класс
MemoryAgentStateс полями:- messages: list — история текущего диалога (включая системные сообщения)
memory_summaries: list— извлечённые факты из прошлых диалогов (short-term память)
- Построить граф с узлами:
retrieve_memory— получение релевантных фактов из Qdrant по последнему запросу пользователя- `generate_response`` — вызов LLM с промптом, включающим память и историю
- `update_memory`` — после ответа агента извлечение ключевых фактов и сохранение в Qdrant
- Определить рёбра: после
generate_response→ всегдаupdate_memory→ цикл.
Ожидаемый результат этапа Агент отвечает на простые вопросы без памяти, граф работает.
Этап 2: Интеграция Qdrant и эмбеддингов (1 час)
Действия
- Создать класс
VectorMemory:__init__: подключение к Qdrant (QdrantClient(host="localhost", port=6333))create_collection(name="agent_memory", size=384): размер эмбеддинга для all-MiniLM-L6-v2- add_fact(session_id, text, metadata=None): эмбеддинг текста → upsert в коллекцию
- search(query_text, top_k=5): эмбеддинг запроса → search → возвращает тексты и метаданные
- В узле
retrieve_memory: - В узле
update_memory:- Извлечь ответ агента из state.messages[-1]
- С помощью LLM выделить ключевые факты (например, через промпт «Извлеки факты из последнего ответа»)
- Сохранить каждый факт в Qdrant через memory.add_fact(session_id, fact_text)
Ожидаемый результат этапа Агент может сохранять однократный факт и потом его извлекать.
Этап 3: Генерация тестовых диалогов и сквозной сценарий (45 мин)
Действия
- Написать скрипт
generate_dialogs.py, который создаёт 12 диалогов вида:
(для разных тем: хобби, работа, личные данные и т.д.)user: Моё любимое блюдо — карбонара. assistant: Запомнил! Карбонара — твоё любимое блюдо. - В диалогах №6, №9, №12 задать перекрёстные вопросы, например:
- Диалог 6: «Помнишь, что я говорил про карбонару?»
- Диалог 9: «Какое моё любимое блюдо?»
- Диалог 12: «Что я люблю готовить?»
- Запустить цикл: для каждого диалога очистить
memory_summaries(но не Qdrant), передать сообщения агенту, собрать ответы. - Сохранить все факты в Qdrant с единым session_id (например,
"test_user_1").
Ожидаемый результат этапа Подготовлены 12 диалогов и скрипт для автоматического прогона.
Этап 4: Тестирование долгосрочной памяти (1 час)
Действия
- Написать тест в pytest:
- Если recall@1 < 0.7:
- Увеличить top_k при поиске до 10
- Улучшить промпт в
update_memory— добавить дедупликацию (проверить по cosine similarity > 0.9 с существующими фактами) - Перетестировать.
- Дополнительно: проверить, что агент не «забывает» при смене темы между диалогами.
Ожидаемый результат этапа recall@1 ≥ 0.7, ответ на сквозной вопрос корректен.
Этап 5: Оптимизация и документирование (30 мин)
Действия
- Добавить логирование (
logging.debug) для каждого шага — чтобы дебажить. - Написать краткую документацию в README:
- Архитектура агента (граф).
- Команды запуска.
- Как воспроизвести тест.
- Сложить код в структуру:
agent_memory/ ├── agent.py # граф ├── vector_memory.py # класс для Qdrant ├── prompts.py # шаблоны ├── tests/ │ └── test_memory.py ├── requirements.txt └── README.md
Ожидаемый результат этапа Проект готов к демонстрации и повторному использованию.
5. Критерии приемки (Definition of Done)
- Агент отвечает на вопросы без памяти (базовый функционал).
- Факты из первых 5 диалогов доступны в 10-м диалоге (проверено тестом).
- Qdrant хранит не менее 50 фактов после всех диалогов.
-
recall@1при поиске факта по смыслу не ниже 0.7. - Код размещён в структурированном репозитории, есть
requirements.txt. - Написана документация (README) с инструкцией по запуску.
- Агент корректно обрабатывает пустой результат поиска (нет релевантных фактов).
- Граф агента не зацикливается (максимум 5 итераций за один диалог).
6. Ожидаемый результат
Основной артефакт папка agent_memory/ с рабочим кодом, файлы:
agent.py— граф LangGraphvector_memory.py— обёртка Qdranttests/test_memory.py— pytest с тестом на recall
Дополнительно
Содержание agent.py: граф из трёх узлов, функция компиляции, точка входа для интерактивного чата.
7. Возможные сложности и их решение
| Сложность | Решение |
|---|---|
| LLM извлекает шум вместо фактов | Уточнить промпт: «Извлеки только конкретные факты, не обобщения». Использовать few-shot примеры. |
| Дублирование фактов в Qdrant | Перед вставкой проверить cosine similarity с существующими фактами в коллекции. Не добавлять, если >0.95. |
| Размер эмбеддингов не совпадает | all-MiniLM-L6-v2 возвращает 384, проверить collection.get_info().config.params.vectors.size. |
| Агент отвечает слишком длинно | Ограничить max_tokens и добавить в системный промпт «Отвечай кратко». |
| Qdrant не отвечает локально | Проверить Docker-контейнер, порт 6333, использовать python -m qdrant_client для теста. |
| Тест не проходит из-за случайности LLM | Заморозить seed для LLM (если есть поддержка) или перезапустить несколько раз. |
8. Бюджет времени (оценка)
| Этап | Время |
|---|---|
| 1. Базовый LangGraph агент | 1 ч |
| 2. Интеграция Qdrant | 1 ч |
| 3. Генерация тестовых диалогов | 45 мин |
| 4. Тестирование памяти | 1 ч |
| 5. Оптимизация и документация | 30 мин |
| Итого | 4 ч 15 мин |
Примечание Если выполняете задачу первый раз, заложите +1-2 часа на отладку Qdrant и эмбеддингов.
9. Связанные вопросы из базы знаний
| Вопрос | Тема |
|---|---|
| 42 | LangGraph: StateGraph vs MessageGraph |
| 57 | Интеграция Qdrant с LangChain |
| 128 | Семантический поиск с cosine similarity |
| 301 | ReAct агент с памятью |
| 512 | Извлечение фактов с помощью LLM |
| 789 | Pytest для RAG-систем |
| 850 | Деплой Qdrant через Docker Compose |
10. Чек-лист самопроверки
- Я установил все зависимости из
requirements.txtи запустил Qdrant. - Я проверил, что агент отвечает на обычный вопрос без памяти (до интеграции).
- Я написал тест
test_memory.pyи запустил его — он проходит. - Я убедился, что в коде нет хардкода паролей/токенов (только переменные окружения).
- Я закоммитил код в Git с осмысленным сообщением.