2026 Hermes Agent persistent memory на Mac Mini M4: SQLite FTS5, gateway 7×24, Metal/UMA и архитектура аренды MACCOME

около 19 мин чтения · MACCOME

Hermes Agent (Nous Research) переводит агента из «одноразового чата» в stateful runtime: эпизодическая память в ~/.hermes/state.db (SQLite WAL + FTS5), семантическая — в MEMORY.md / USER.md, gateway держит Telegram/Discord/CLI на одном SessionDB. Кто строит 7×24 agent gateway, упирается не в prompt engineering, а в конкуренцию записи в SQLite, рост WAL на NVMe и отсутствие стабильного хоста без сна ноутбука. Ниже — шесть аппаратно-операционных гейтов, таблица слоёв памяти, механика FTS5/trigram, UMA Mac Mini M4 (120 / 273 GB/s), семь шагов деплоя и матрица «локальный Mac vs аренда MACCOME».

Шесть гейтов: почему «stateless LLM wrapper» не переживает production gateway

Hermes заменил per-session JSONL на единый SQLite — правильный шаг для cross-channel recall. Но production gateway добавляет жёсткие ограничения, которые README не закрывает:

  1. Write contention на одном state.db. Gateway + CLI + worktree-агенты делят один файл. Без BEGIN IMMEDIATE, timeout 1 s и jitter-retry (20–150 ms, до 15 попыток) — convoy effect и «database is locked» под пиковой нагрузкой Telegram.
  2. WAL и TBW NVMe. Каждая запись message порождает WAL-фрагмент; checkpoint каждые 50 writes (PASSIVE). За месяцы 7×24 state.db-wal + state.db-shm на домашнем SSD — измеримый износ; на арендованном инстансе TBW — OpEx платформы.
  3. FTS5 без понимания dual-tokenizer. Schema v10+ добавила messages_fts_trigram для CJK/substring; латиница идёт в messages_fts (unicode61). Неверный маршрут — «пустой recall» при живой истории.
  4. Session lineage после compression split. parent_session_id образует цепочки при переполнении контекста. Поиск по title без понимания lineage возвращает устаревший fork, не актуальную ветку.
  5. Локальный Metal-инференс + SQLite I/O на одной UMA. M4 base — 120 GB/s bandwidth; M4 Pro — 273 GB/s. Модель 7–8B Q4 (~5 GiB) + gateway + FTS backfill v11 конкурируют за тот же unified pool без PCIe offload.
  6. Ноутбук ≠ 7×24. Sleep, смена Wi‑Fi, закрытие крышки — gateway offline, Telegram webhook теряет continuity. Mac Mini M4 на стойке или удалённый инстанс — другой класс SLA.

Архитектура памяти Hermes решает софтверный барьер; железный и операционный — на стороне хоста.

Три слоя persistent memory Hermes (механика)

Hermes не сводит память к vector DB. Три явных tier с разной latency и consistency:

  • Эпизодическая (state.db): таблицы sessions, messages, billing/reasoning columns, schema_version 11. Полная история tool_calls (JSON), finish_reason, codex replay items — для API replay через get_messages_as_conversation().
  • Индексируемая (FTS5): external-content → inline re-index в v11; триггеры INSERT/UPDATE/DELETE синхронизируют messages_fts и trigram-таблицу. search_messages() с source_filter, role_filter, snippet с >>>match<<<.
  • Семантическая (файлы): ~/.hermes/memories/MEMORY.md (факты мира агента), USER.md (модель пользователя), skills/ с SKILL.md — bootstrap в system prompt, не в SQLite.

Batch runner и RL trajectories намеренно вынесены из state.db — не путать с gateway persistence.

Reasoning replay columns: messages хранят reasoning, reasoning_content, reasoning_details (JSON), codex_reasoning_items, codex_message_items — schema v6–v9. Gateway при replay в OpenAI-compatible API восстанавливает полный chain-of-thought без re-derivation из assistant text. Это увеличивает row size в SQLite и FTS index footprint после v11 re-index — планируйте disk headroom: 100k messages × avg 2 KB content ≈ 200 MB main file + FTS overhead ~1,5–2×.

state_meta key/value: отдельная таблица для runtime flags вне session scope — migration version hints, feature toggles. Не дублирует MEMORY.md; используется кодом, не LLM recall path.

СлойХранилищеLatencyКонкурентностьТипичный сбой
Эпизодическаяstate.db WALms (local NVMe)1 writer + N readersdatabase locked без retry
FTS5 recallmessages_fts + trigramsub-ms на <1M rowsread-only при searchCJK query в unicode61 table
СемантическаяMEMORY.md, USER.mddisk read per turnfile lock OSраздувание bootstrap tokens
Skillsskills/*/SKILL.mdon-demand loadgit worktree splitдублирующие skill без index
Gateway channelssource tag в sessionsnetwork-boundmulti-platform fan-inoffline host = broken webhook

Сравнение с OpenClaw: там memory_search и AGENTS.md — см. чеклист bootstrap и memory_search. Hermes жёстче типизирует billing и session lineage в SQL; OpenClaw гибче в channel ecosystem. На одном Mac Mini оба стека возможны параллельно при разнесении HERMES_HOME и OPENCLAW_STATE_DIR.

SQLite FTS5 и write path: что происходит под gateway

Каждый append_message() — транзакция с немедленным FTS sync. Критические константы из hermes_state.py:

# Write contention (SessionDB)
_WRITE_MAX_RETRIES = 15
_WRITE_RETRY_MIN_S = 0.020   # 20ms jitter floor
_WRITE_RETRY_MAX_S = 0.150   # 150ms jitter ceiling
_CHECKPOINT_EVERY_N_WRITES = 50   # WAL PASSIVE checkpoint

# FTS5 external-content sync (simplified)
CREATE TRIGGER messages_fts_insert AFTER INSERT ON messages BEGIN
    INSERT INTO messages_fts(rowid, content) VALUES (new.id, new.content);
END;

WAL mode даёт concurrent readers (CLI history browse во время gateway write) при одном writer. Checkpoint PASSIVE не блокирует readers агрессивно, но при бурсте Telegram-группы 50-write порог срабатывает чаще — NVMe write amp растёт линейно с message rate, не с user count.

Schema v11 переиндексировала FTS на inline mode с полями tool_name + tool_calls — поиск «terminal deploy error» находит tool rows, не только assistant text. Миграции idempotent через _reconcile_columns(); version-gated chain — только для FTS/trigram backfill.

Session compression split: gateway при переполнении контекста создаёт child session с parent_session_id. Title lineage (Fix Docker Build #2) — механизм continuity без re-ingest всей истории в prompt. SQL recursive CTE из документации — единственный корректный способ audit long-running agent.

FTS5 query surface: search_messages() принимает синтаксис FTS5 — implicit AND для docker deployment, quoted phrase "exact phrase", boolean OR/NOT, prefix deploy*. Метод _sanitize_fts5_query() оборачивает hyphenated terms в кавычки и срезает dangling operators (hello ANDhello). Результат включает snippet с маркерами >>>match<<< и context — одно сообщение до/после match (truncate 200 chars). Фильтры source_filter=["cli"] и exclude_sources=["telegram"] позволяют разделить recall по каналам без второго индекса.

Billing columns в sessions: schema v5+ хранит input_tokens, output_tokens, cache_read_tokens, cache_write_tokens, reasoning_tokens, estimated_cost_usd, pricing_version. Gateway агрегирует spend per session — SQL GROUP BY model ORDER BY total_cost DESC из документации даёт FinOps slice без внешнего ledger. Для 7×24 production это критично: persistent memory без cost attribution превращается в некontrollируемый API burn.

Gateway persistence: multi-channel fan-in на одном Mac

Hermes gateway — long-lived process: принимает Telegram, Discord, CLI, тегирует source в sessions, пишет в общий state.db. Архитектурные следствия для Mac Mini M4:

  • Process model: один gateway + N ephemeral CLI; все конкурируют за SQLite writer lock. Не масштабируется horizontal sharding без split HERMES_HOME per tenant.
  • Logs: gateway.log, agent.log, errors.log — triage отдельно от SQL; disk fill на 256 GB аренде решается prune sessions (prune_sessions(older_than_days=90)), не rm -rf state.db.
  • Remote access: CLI к удалённому gateway — SSH LocalForward по runbook SSH; Hermes не требует иного tunnel, но тот же паттер bind 127.0.0.1.
  • Local LLM sidecar: Ollama/llama.cpp на том же Mac — второй consumer UMA. Для coding agent с recall лучше API route + локальный state.db, чем 70B на 16 GB M4 base.
  • Cron и skills hub: ~/.hermes/cron/ и skills/.hub/ живут вне state.db — gateway читает SKILL.md on-demand; не смешивайте batch runner trajectories с episodic SQL без явного export.
  • HERMES_HOME isolation: env override позволяет tenant-per-directory на одном физическом Mac; каждый tenant — свой WAL file set. На shared rental инстансе — один HERMES_HOME per customer, не shared state.db.

Gateway persistence означает: process death ≠ memory loss, но disk corruption или uncheckpointed WAL после hard reboot — риск. Graceful launchctl stop + PASSIVE checkpoint перед maintenance window обязателен; на MACCOME snapshot делается на уровне платформы до reboot.

Mac Mini M4, Metal и UMA: bandwidth как скрытый лимит памяти

Persistent memory Hermes — в основном SQLite на NVMe (5–6 GB/s sequential на внутреннем SSD Mac Mini). Но если на том же хосте крутится локальный Metal-инференс, UMA bandwidth становится co-tenant:

ЧипUnified memory (max)BandwidthHermes gateway onlyGateway + local 7B Q4Gateway + ds4-class model
M432 ГБ120 GB/sдостаточно32 ГБ tightне влезает
M4 Pro64 ГБ273 GB/sкомфортно48–64 ГБ OKтолько API offload
Studio Ultra512 ГБ~800 GB/soverkillзапас под MoEсм. ds4 tier

UMA без PCIe round-trip — CPU page cache для SQLite и GPU weights читают один физический pool. На M4 Pro 273 GB/s — официальная спецификация Apple; для gateway-only workload это headroom, для concurrent MoE prefill — уже bottleneck рядом с memory capacity.

Metal не ускоряет SQLite, но определяет, можно ли colocate «память агента» и «мозг агента» на одном Mini без swap. Swap на macOS убивает gateway latency: WAL fsync + LLM page fault = multi-second Telegram reply.

7×24 профиль Mac Mini M4: 15 W idle class, 10Gb Ethernet option, нет battery thermal throttle ноутбука. launchd KeepAlive для gateway — штатный паттерн; на арендованном MACCOME инстансе тот же plist без борьбы с локальным sleep policy.

NVMe vs UMA — разделение ролей: SQLite page cache и WAL segments живут в DRAM через mmap; sequential read state.db на internal SSD Mac Mini — до ~5–6 GB/s. UMA bandwidth (120 / 273 GB/s) ограничивает только GPU/ANE path локальной модели. Типичный Hermes gateway без local LLM: CPU-bound на JSON serialize tool_calls, I/O-bound на WAL append — UMA headroom избыточен, зато 16 GB RAM floor быстро исчерпывается при одновременном macOS GUI + gateway + Ollama.

Metal inference colocation: weights 7B Q4 (~4,5 GiB) + KV cache + SQLite buffer pool + macOS — на 24 GB M4 остаётся <8 GB margin. Любой context spike в gateway (compression split + FTS backfill v11 на большой messages table) может trigger memory pressure → swap → WAL fsync latency spike. M4 Pro 48 GB — практический минимум для «gateway + small local model» без daily swap events.

Семь шагов: Hermes gateway на выделенном Mac Mini M4

  1. Tier RAM: gateway + API-only → M4 24 ГБ; gateway + Ollama 7B → M4 Pro 48 ГБ; local frontier → аренда 128 ГБ+ (тарифы).
  2. Install: pip install hermes-agent или clone NousResearch/hermes-agent; export HERMES_HOME=~/.hermes (override для multi-tenant).
  3. Init state.db: первый запуск SessionDB() создаёт schema v11, WAL, FTS triggers. Проверка: sqlite3 ~/.hermes/state.db "SELECT * FROM schema_version;" → 11.
  4. Gateway config: привязать Telegram/Discord tokens, model route (API key или local endpoint). Source tags (cli, telegram) пишутся автоматически в sessions.source.
  5. launchd 7×24: plist с KeepAlive, StandardOutPath~/.hermes/logs/gateway.log, ThrottleInterval 10 s на crash loop.
  6. Verify FTS: hermes search "deploy error" или Python db.search_messages("docker", source_filter=["telegram"]) — snippet с match markers.
  7. Prune policy: db.prune_sessions(older_than_days=90, source="telegram") — только ended sessions; не удаляет active lineage. Настройте cron на арендованном Mac через ~/.hermes/cron/ или system crontab.
  8. Remote ops: ssh -L 8787:127.0.0.1:8787 user@mac-rental для CLI; backup state.db* через export_all() перед schema bump. Центр помощи MACCOME — типовые plist и firewall.

После деплоя мониторьте три сигнала: (1) размер state.db-wal относительно main file — WAL >2× main без checkpoint = contention или missing PASSIVE checkpoint; (2) errors.log на «database is locked» — индикатор retry exhaustion; (3) gateway.log latency p95 reply time — коррелирует с swap на undersized RAM tier.

export HERMES_HOME=$HOME/.hermes
pip install hermes-agent
hermes gateway start --bind 127.0.0.1

# launchd snippet (~/Library/LaunchAgents/com.hermes.gateway.plist)
# KeepAlive + RunAtLoad + StandardErrorPath → ~/.hermes/logs/errors.log

sqlite3 $HERMES_HOME/state.db "PRAGMA journal_mode;"   # → wal
sqlite3 $HERMES_HOME/state.db "SELECT COUNT(*) FROM messages_fts;"

Три жёсткие цифры

  • Schema version: 11 — последний breaking change: re-index FTS inline с tool_name + tool_calls; trigram table с v10 для CJK. Документация: session-storage.
  • Write retry budget: до 15 попыток × 20–150 ms jitter = worst case ~2,25 s на contested write; timeout SQLite 1 s vs default 30 s — сознательный fail-fast под multi-process gateway.
  • UMA bandwidth Mac Mini: M4 120 GB/s, M4 Pro 273 GB/s (Apple specs, 2024/2025 refresh) — co-tenant metric для colocated Metal inference + SQLite page cache.

Локальный Mac vs аренда MACCOME: TCO gateway 7×24

Сравнение «купить Mac Mini M4 Pro 64 ГБ для Hermes gateway» vs «аренда MACCOME 24/7». Электричество и админка в Capex не входят.

ВариантCapex3 года netstate.db / WAL7×24 SLATier elasticity
M4 Pro 64 ГБ покупка~450–550 тыс. ₽~250–300 тыс. ₽ netсвой NVMe TBWдомашний uplinkRAM soldered
M4 24 ГБ покупка~200–250 тыс. ₽~120–150 тыс. ₽ netтот же рискноутбук ≠ Miniнет апгрейда RAM
MACCOME M4/M4 Pro / мес036 × месяцplatform NVMeDC uplink24→48→64 ГБ
MACCOME 128 ГБ+ / мес0для local LLM colocate+ model weightsSSH/VNC 24/7128→256 tier

Покупка Mini для Hermes таит три ловушки: (a) soldered RAM — gateway сегодня на API, завтра с Ollama 13B = новая машина; (b) TBW домашнего SSD на WAL 7×24 без prune policy; (c) residential uplink и sleep policy ноутбука ломают Telegram webhook continuity. Локальный POC на M4 24 ГБ — разумный первый шаг; production multi-channel gateway с FTS recall и optional Metal sidecar — класс задач для выделенного Mac Mini M4 на MACCOME: OpEx, persistent ~/.hermes на платформенном NVMe, launchd без компромиссов с battery, tier 48→128 ГБ без перепайки.

Альтернатива «Docker на VPS» для Hermes gateway ломается на трёх точках: Linux container не даёт Metal для local inference sidecar; bind mount SQLite WAL через network FS (NFS/EBS) — documented path к corruption; egress webhook latency из US-East в EU Telegram user base добавляет 80–150 ms без выигрыша в стоимости относительно Mac Mini rental в том же регионе. VPS имеет смысл только для API-only gateway без local weights — но тогда Mac Mini M4 16 GB на MACCOME закрывает тот же profile с native SQLite fsync semantics.

Матрица buy vs rent: TCO Mac Mini M4. Multi-model routing рядом с Hermes: OpenRouter matrix. POC без Capex: оформить заказ на почасовую аренду.

Итог: SQLite FTS5 + UMA + 7×24 хост = полный стек Hermes

Hermes Agent доказал, что persistent agent memory не требует Postgres ни Pinecone на day one — WAL SQLite, FTS5 dual-tokenizer, session lineage и gateway fan-in закрывают episodic recall. Оставшийся барьер — хост, который не засыпает, не сжигает свой SSD и имеет headroom UMA под colocated inference.

Для команды, которая уже настроила gateway и упёрлась в lock contention logs или swap на 16 ГБ — апгрейд не в prompt, а в Mac Mini M4 Pro 48–64 ГБ или аренду MACCOME с тем же launchd plist. Metal/UMA на Apple Silicon — не маркетинг Hermes, а физический потолок colocation «memory DB + LLM weights» на одном silicon pool.

FAQ

Hermes Agent vs OpenClaw для persistent memory?

Hermes — SQL-native episodic store + FTS5; OpenClaw — markdown memory_search и AGENTS.md bootstrap. Разные модели; на одном Mac разнесите HERMES_HOME и OPENCLAW_STATE_DIR. См. чеклист OpenClaw memory.

Сколько RAM нужно для Hermes gateway 7×24?

API-only gateway: 16–24 ГБ на M4. С локальным 7B Q4 через Metal: M4 Pro 48 ГБ минимум. Frontier local — 128 ГБ+ аренда; см. тарифы MACCOME.

Как бэкапить state.db без corruption?

Используйте export_all() или SQLite backup API при живом WAL — не копируйте state.db-wal mid-write. Перед upgrade schema v11 — полный export; центр помощи MACCOME — snapshot перед maintenance window.

Можно ли Hermes gateway на ноутбуке?

POC — да. Production Telegram 7×24 — нет: sleep и thermal throttle ломают webhook SLA. Выделенный Mini или аренда MACCOME с launchd KeepAlive.