Postgres memory для AI Agent в n8n: как хранить историю чата в production ¶
Обновлено: 2026-05-29
Короткий ответ ¶
Postgres memory в n8n — это способ хранить историю сообщений AI-диалога в устойчивой базе, а не в памяти одного процесса. Это важно для production: пользователь может продолжить разговор, worker может смениться, workflow может выполняться в queue mode, а история должна оставаться доступной и проверяемой. Правильная настройка начинается не с подключения Postgres node, а с дизайна session_id, retention policy, privacy rules, ограничения размера памяти и понимания, какие факты можно отдавать модели. Memory помогает поддерживать контекст, но не должна превращаться в бесконечный склад всех сообщений.
Чем memory отличается от RAG ¶
Memory хранит историю конкретного диалога или пользователя: “что уже спросили”, “что агент ответил”, “какие уточнения были”, “какой заказ обсуждали”. RAG хранит внешние знания: инструкции, документы, policies, статьи, runbook-и. Их нельзя смешивать.
Если пользователь спрашивает: “а что ты мне только что предложил?”, нужна memory. Если он спрашивает: “как настроить webhook за reverse proxy?”, нужен RAG. Если он говорит: “примени это к моему случаю”, нужны оба слоя: memory для “мой случай” и RAG для инструкции.
Плохой паттерн — индексировать историю чатов в общий vector store без правил. Так можно случайно показать данные одного клиента другому или начать использовать приватный диалог как источник фактов.
Когда нужна именно Postgres memory ¶
Postgres memory нужна, когда:
- чат работает в production;
- пользователь возвращается к разговору;
- workflow работает в queue mode или на нескольких worker;
- нужно хранить audit trail;
- нужна политика retention и удаления;
- важно понимать, какие сообщения повлияли на ответ;
- нельзя полагаться на локальную память процесса;
- есть требования к backup и контролю доступа.
Для демо можно использовать простую память. Для публичного чата, поддержки, AI sales assistant, внутреннего помощника или multi-user сценария лучше сразу проектировать устойчивое хранилище.
Главный ключ — session_id ¶
Если session_id плохой, memory будет бесполезной или опасной. Слишком общий ID смешает диалоги разных пользователей. Слишком случайный ID не позволит продолжить разговор. Хороший session_id должен отражать границу контекста.
Примеры:
| Сценарий | Хороший session_id | Плохой session_id |
|---|---|---|
| публичный чат на сайте | visitor_id + conversation_id | только IP |
| Telegram bot | telegram_user_id + chat_id | username |
| support ticket | ticket_id | email клиента |
| CRM lead assistant | lead_id + workflow_name | имя компании |
| внутренний ассистент | user_id + workspace_id | общий default |
Не используйте PII как session_id без необходимости. Email лучше хешировать или хранить отдельно от технического ключа.
const crypto = require('crypto');
const raw = `${$json.channel}:${$json.user_id}:${$json.conversation_id}`;
const sessionId = crypto.createHash('sha256').update(raw).digest('hex');
return [{ json: { ...$json, session_id: sessionId } }];
Архитектура workflow ¶
Production-схема:
- Chat Trigger/Webhook/Telegram Trigger принимает сообщение.
- Identify session создаёт стабильный
session_id. - Load memory получает последние сообщения или summary.
- Classify task определяет, нужна ли история.
- Retrieve knowledge при необходимости получает RAG chunks.
- Build context объединяет текущий запрос, memory summary и источники.
- AI Agent отвечает или вызывает tools.
- Validate проверяет формат, источники и safety.
- Save memory записывает пользовательское сообщение и финальный ответ.
- Retention job удаляет или сворачивает старые сообщения.
Важно: сохраняйте в memory не все внутренние debug-данные, а только то, что должно участвовать в будущих ответах. Tool outputs, токены, headers и внутренние payload лучше хранить в execution log, а не в chat memory.
Что именно сохранять ¶
Минимально:
{
"session_id": "...",
"role": "user",
"content": "Как подключить webhook к CRM?",
"created_at": "2026-05-29T10:00:00Z",
"message_id": "msg_001",
"channel": "site_chat",
"language": "ru"
}
Для assistant message:
{
"session_id": "...",
"role": "assistant",
"content": "Для production используйте Production URL...",
"source_ids": ["playbook_webhook_production_checklist"],
"confidence": 0.86,
"needs_human": false,
"created_at": "2026-05-29T10:00:07Z"
}
Не сохраняйте в memory raw credentials, access tokens, полные API headers, персональные документы или внутренние stack traces, если они не нужны для следующего ответа.
Ограничение размера памяти ¶
Бесконечная память ухудшает ответы и повышает стоимость. Нужно решать, сколько истории передавать модели. Типовой вариант:
- последние 4–8 сообщений — как есть;
- более старые сообщения — summary;
- факты о пользователе — отдельным structured state;
- устаревшие факты — удалять или помечать датой;
- длинные вложения — не хранить в memory, а ссылаться на документ.
Пример memory summary:
{
"session_id": "...",
"summary_version": "v2",
"summary": "Пользователь настраивает n8n self-hosted за Nginx. Проблема: webhook production URL ведёт на localhost. Уже рекомендовано проверить WEBHOOK_URL и forwarded headers.",
"facts": {
"stack": "Docker Compose + Nginx",
"problem": "wrong production webhook URL"
},
"updated_at": "2026-05-29T10:15:00Z"
}
Summary не должно становиться источником истины. Если пользователь позже уточнил, что у него Cloudflare, текущий ввод важнее старого summary.
Privacy и retention ¶
Memory — это пользовательские данные. Даже если это “просто чат”, там могут быть телефоны, email, имена клиентов, номера заказов, коммерческие условия, жалобы и внутренние детали. Поэтому нужна политика:
- сколько дней хранить историю;
- какие поля маскировать;
- кто имеет доступ к raw messages;
- как удалить историю по запросу;
- какие каналы считаются публичными;
- можно ли использовать диалоги для eval dataset;
- как обезличивать данные перед тестами.
Для публичного бота хорошая настройка: сохранять последние сообщения ограниченное время, хранить summary без PII, удалять raw через retention job, не использовать чужую историю как RAG-источник.
Queue mode и устойчивость ¶
В production n8n может выполнять workflow на разных workers. Если память живёт только в памяти процесса, следующий запрос может попасть на другой worker и потерять контекст. Поэтому для активных production workflow лучше использовать внешнее хранилище: Postgres или Redis Chat Memory. Postgres удобен, если нужен долговременный audit, SQL-запросы, backup и retention.
Не привязывайте логику к “этому worker помнит прошлый шаг”. Вся критичная история должна быть в общем хранилище, доступном всем execution.
Отладка memory ¶
Когда агент отвечает “не помню” или путает пользователей, проверяйте:
- одинаковый ли
session_idмежду сообщениями; - не меняется ли channel/conversation_id;
- сколько сообщений реально загружается;
- не обрезается ли история до пустого массива;
- не смешиваются ли пользователи;
- не сохраняется ли assistant draft вместо финального ответа;
- не конфликтует ли summary с последним сообщением.
Логируйте:
{
"session_id_hash": "abc123",
"memory_messages_loaded": 6,
"memory_summary_used": true,
"memory_saved": true,
"retention_days": 30,
"pii_redaction_applied": true
}
Что не стоит делать ¶
- один
session_id=defaultдля всех; - хранить токены и credentials в истории;
- передавать всю историю без ограничения;
- использовать memory как базу знаний;
- доверять memory больше, чем текущему сообщению;
- не иметь удаления/retention;
- сохранять raw PII в eval dataset;
- не тестировать queue mode.
FAQ ¶
Postgres memory заменяет RAG?
Нет. Memory хранит историю диалога, RAG хранит внешние документы и знания.
Что лучше: Postgres или Redis memory?
Redis часто удобен для быстрой временной памяти, Postgres — для долговременной истории, audit, SQL и retention. Выбор зависит от сценария.
Можно ли хранить всю историю навсегда?
Технически можно, но для privacy и качества ответов лучше ограничивать retention и передавать модели только нужную часть.
Как не смешать пользователей?
Проектируйте стабильный session_id по user/conversation boundary и не используйте общий default.
Что делать, если summary устарел?
Отдавайте приоритет текущему сообщению, храните timestamp и пересобирайте summary после важных изменений.