Перейти к содержанию

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-схема:

  1. Chat Trigger/Webhook/Telegram Trigger принимает сообщение.
  2. Identify session создаёт стабильный session_id.
  3. Load memory получает последние сообщения или summary.
  4. Classify task определяет, нужна ли история.
  5. Retrieve knowledge при необходимости получает RAG chunks.
  6. Build context объединяет текущий запрос, memory summary и источники.
  7. AI Agent отвечает или вызывает tools.
  8. Validate проверяет формат, источники и safety.
  9. Save memory записывает пользовательское сообщение и финальный ответ.
  10. 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

Когда агент отвечает “не помню” или путает пользователей, проверяйте:

  1. одинаковый ли session_id между сообщениями;
  2. не меняется ли channel/conversation_id;
  3. сколько сообщений реально загружается;
  4. не обрезается ли история до пустого массива;
  5. не смешиваются ли пользователи;
  6. не сохраняется ли assistant draft вместо финального ответа;
  7. не конфликтует ли 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 после важных изменений.