Більшість LLM-чатботів страждають від «амнезії» — кожен новий сеанс розпочинається з чистого аркуша, і користувач змушений щоразу пояснювати контекст заново. Цей туторіал покаже, як побудувати агента, який пам’ятає факти між сесіями, використовуючи векторну базу даних і LangChain. Реалізація займе близько 2-3 годин, якщо ти вже маєш базовий досвід із Python. Для старту потрібні акаунти OpenAI та Pinecone, а також Python 3.11+.
🛠️ Що знадобиться
- Python 3.11+ — основна мова реалізації; переконайся, що встановлено через
python --version - OpenAI API (GPT-4o) — мозок агента; потрібен платний акаунт, приблизно $5 вистачить для розробки
- Pinecone — безкоштовний план дозволяє зберігати до 100K векторів, цього достатньо для старту
- LangChain 0.3+ — фреймворк для оркестрації агента та пам’яті; безкоштовний, відкритий код
- LangGraph — бібліотека для побудови stateful-агентів поверх LangChain; безкоштовна
- Redis (локально або Upstash) — зберігає короткострокову пам’ять сесії; Upstash має безкоштовний tier
📋 Покрокова інструкція
Крок 1: Налаштування середовища та встановлення залежностей
Відкрий термінал і створи нову папку проєкту: mkdir memory-agent && cd memory-agent. Потім створи віртуальне середовище командою python -m venv venv і активуй його: на macOS/Linux — source venv/bin/activate, на Windows — venv\Scripts\activate. Встанови всі необхідні пакети однією командою: pip install langchain==0.3.* langgraph openai pinecone-client redis python-dotenv tiktoken. Створи файл .env у корені проєкту і додай туди три рядки: OPENAI_API_KEY=sk-..., PINECONE_API_KEY=pcsk-..., REDIS_URL=redis://localhost:6379 — ці ключі отримаєш на наступному кроці.

Крок 2: Отримання API-ключів та створення Pinecone індексу
Зайди на platform.openai.com → натисни «API Keys» у лівому меню → клікни «Create new secret key» → скопіюй ключ і встав у .env. Далі зайди на app.pinecone.io → після реєстрації натисни «Create Index» → введи назву agent-memory → обери «Dimensions: 1536» (це відповідає моделі text-embedding-3-small) → «Metric: cosine» → натисни «Create Index». Після створення перейди в «API Keys» у лівому сайдбарі → скопіюй ключ у .env. Підводний камінь: безкоштовний план Pinecone дозволяє лише один індекс — не створюй зайвих під час тестів.
Крок 3: Реалізація модуля пам’яті
Створи файл memory.py і встав наступний код. Цей модуль відповідає за запис і читання довгострокової пам’яті через Pinecone:
from pinecone import Pinecone
from openai import OpenAI
from dotenv import load_dotenv
import os, uuid, json
from datetime import datetime
load_dotenv()
pc = Pinecone(api_key=os.getenv("PINECONE_API_KEY"))
index = pc.Index("agent-memory")
client = OpenAI(api_key=os.getenv("OPENAI_API_KEY"))
def embed_text(text: str) -> list:
response = client.embeddings.create(
model="text-embedding-3-small",
input=text
)
return response.data[0].embedding
def save_memory(user_id: str, content: str, memory_type: str = "fact"):
vector = embed_text(content)
metadata = {
"user_id": user_id,
"content": content,
"type": memory_type,
"timestamp": datetime.utcnow().isoformat()
}
index.upsert(vectors=[{
"id": str(uuid.uuid4()),
"values": vector,
"metadata": metadata
}], namespace=user_id)
def recall_memory(user_id: str, query: str, top_k: int = 5) -> list:
vector = embed_text(query)
results = index.query(
vector=vector,
top_k=top_k,
namespace=user_id,
include_metadata=True
)
return [match.metadata["content"] for match in results.matches]
Функція save_memory перетворює текст на вектор і зберігає його в Pinecone з прив’язкою до конкретного user_id через namespace — це дозволяє ізолювати пам’ять різних користувачів.
Крок 4: Побудова агента з LangGraph
Створи файл agent.py. Тут ми зберемо агента, який перед кожною відповіддю підтягує релевантні спогади і після кожного обміну зберігає нові факти:
from langgraph.graph import StateGraph, END
from langchain_openai import ChatOpenAI
from langchain_core.messages import HumanMessage, SystemMessage
from memory import save_memory, recall_memory
from typing import TypedDict, List
import os
from dotenv import load_dotenv
load_dotenv()
llm = ChatOpenAI(model="gpt-4o", temperature=0.7,
api_key=os.getenv("OPENAI_API_KEY"))
class AgentState(TypedDict):
user_id: str
user_input: str
memories: List[str]
response: str
def retrieve_memories(state: AgentState) -> AgentState:
memories = recall_memory(state["user_id"], state["user_input"])
state["memories"] = memories
return state
def generate_response(state: AgentState) -> AgentState:
memory_context = "\n".join(f"- {m}" for m in state["memories"])
system_prompt = f"""Ти корисний асистент з довгостроковою пам'яттю.
Ось що ти пам'ятаєш про цього користувача:
{memory_context if memory_context else 'Поки що нічого.'}
Використовуй ці знання у відповідях природно."""
messages = [
SystemMessage(content=system_prompt),
HumanMessage(content=state["user_input"])
]
result = llm.invoke(messages)
state["response"] = result.content
return state
def store_memory(state: AgentState) -> AgentState:
combined = f"Користувач сказав: {state['user_input']}. Відповідь: {state['response']}"
save_memory(state["user_id"], combined)
return state
graph = StateGraph(AgentState)
graph.add_node("retrieve", retrieve_memories)
graph.add_node("generate", generate_response)
graph.add_node("store", store_memory)
graph.set_entry_point("retrieve")
graph.add_edge("retrieve", "generate")
graph.add_edge("generate", "store")
graph.add_edge("store", END)
agent = graph.compile()
def chat(user_id: str, message: str) -> str:
result = agent.invoke({
"user_id": user_id,
"user_input": message,
"memories": [],
"response": ""
})
return result["response"]
Крок 5: Тестування та запуск агента
Створи файл main.py для інтерактивного чату та запусти його командою python main.py:
from agent import chat
USER_ID = "user_ukraine_001"
print("Агент з пам'яттю запущено. Введи 'вихід' для завершення.\n")
while True:
user_input = input("Ти: ").strip()
if user_input.lower() in ["вихід", "exit", "quit"]:
break
response = chat(USER_ID, user_input)
print(f"Агент: {response}\n")
Протестуй стійкість пам’яті: у першій сесії скажи агенту «Мене звати Олексій, я розробник із Києва». Зупини програму (Ctrl+C) і запусти знову. Напиши просто «Що ти знаєш про мене?» — агент має відповісти, використовуючи збережені факти. Якщо відповідь містить твоє ім’я та місто — система пам’яті працює коректно.
⚠️ Типові помилки та як їх уникнути
- Неправильна розмірність векторів — якщо обрав модель
text-embedding-ada-002(1536 dims) але в Pinecone вказав 768, отримаєш помилку. Завжди перевіряй:text-embedding-3-small= 1536,text-embedding-3-large= 3072 - Накопичення нерелевантної пам’яті — без фільтрації агент зберігає все підряд, включно з дрібницями. Додай логіку: зберігай лише повідомлення довжиною понад 20 символів і лише якщо вони містять факти (можна попередньо класифікувати через LLM)
- Витік пам’яті між користувачами — якщо забудеш передавати
namespace=user_idу Pinecone-запитах, всі користувачі бачитимуть чужі дані. Перевір кожен викликindex.query()іindex.upsert() - Перевищення контекстного вікна — якщо
top_k=20і кожен спогад довгий, system prompt може перевищити ліміт токенів. Тримайtop_kу межах 5-7 і обрізай текст спогадів до 200 символів
💡 Поради для кращого результату
По-перше, додай окремий тип пам’яті для «ключових фактів» — зберігай їх з вищим пріоритетом через додаткове поле importance: "high" у metadata і фільтруй за ним у першу чергу. По-друге, раз на тиждень запускай «консолідацію» — передавай всі спогади користувача в GPT-4o з проханням стиснути їх у короткий профіль (200 слів), і зберігай цей профіль як окремий вектор з типом summary. По-третє, для продакшну додай TTL-логіку: зберігай у metadata поле expires_at і видаляй застарілі спогади через Pinecone Metadata Filtering. По-четверте, логуй усі операції з пам’яттю у файл — це допоможе дебажити випадки, коли агент «забув» важливе або запам’ятав зайве.

❓ Часті запитання (FAQ)
1. Чи можна замінити Pinecone на безкоштовну альтернативу?
Так, Chroma або Qdrant чудово підходять для локальної розробки. Встанови Qdrant через Docker: docker run -p 6333:6333 qdrant/qdrant, а потім заміни pinecone-client на qdrant-client у коді. Для продакшну Pinecone все одно надійніший через керований хостинг.
2. Як обмежити доступ до чужої пам’яті?
Використовуй namespace у Pinecone — кожен user_id отримує ізольований простір. Додатково хеш user_id перед використанням як namespace: import hashlib; ns = hashlib.sha256(user_id.encode()).hexdigest()[:16] — це приховає реальні ідентифікатори.
3. Скільки коштуватиме в продакшні на 1000 користувачів?
Pinecone Starter безкоштовний до 100K векторів — це приблизно 20 спогадів на користувача. OpenAI embeddings коштують $0.02 за 1M токенів — практично безкоштовно. Основні витрати — GPT-4o: ~$0.01-0.05 за розмову залежно від довжини.
4. Що робити, якщо агент «галюцинує» спогади?
Додай score-threshold у Pinecone query: якщо match.score < 0.75 — не передавай цей спогад у контекст. Низький score означає, що знайдений вектор мало схожий на запит, і краще нічого не підказати, ніж підказати невірне.
5. Як додати підтримку кількох мов у пам’яті?
Модель text-embedding-3-small вже підтримує мультимовність — вона коректно знаходить семантично схожі тексти навіть якщо запит і спогад написані різними мовами. Нічого додатково налаштовувати не потрібно.
🏁 Підсумок
Ти побудував повноцінного LLM-агента з персистентною пам’яттю: агент зберігає факти між сесіями у векторній базі Pinecone, семантично шукає релевантні спогади перед кожною відповіддю, і керується LangGraph-графом для чіткого розподілу відповідальності між вузлами retrieve → generate → store.
Починай прямо зараз із кроку 2 — створи акаунти на OpenAI і Pinecone, отримай ключі і запусти базову версію. Як тільки побачиш, що агент «пам’ятає» тебе між запусками програми — додавай власну логіку фільтрації та консолідації спогадів.
РОЗСИЛКА
📬 Щотижневий AI-дайджест
Найкращі статті про ШІ та автоматизацію — без спаму, лише суть
Без спаму · Відписатись будь-коли

