Skip to main content
Glama

AWHM Lite

CI

Внешняя долговременная память для LLM-агентов. Без облака, без API-ключей, работает полностью локально.

AWHM Lite даёт любому LLM постоянную память между разговорами: журналирование в режиме «только добавление», сопоставление по регулярным выражениям, граф памяти с учётом противоречий, консолидация без вызовов LLM, а также поиск через слияние лексических и семантических признаков.

Статус: исследовательский прототип. Создан в феврале 2026 года, опубликован в августе 2026 года; v0.2.0 укрепила его, v0.3.0 добавила хуки, Stage 2, разрешение сущностей, путешествия во времени, хранение в SQLite и оценку на реальном корпусе. 145 тестов, CI на Python 3.11–3.13.

Документация проекта

  • docs/awhm-whitepaper.md: полная статья об архитектуре AWHM, подмножеством которой является этот проект

  • docs/awhm-whitepaper-vs-lite.md: что Lite сохраняет, а что оставляет за бортом

  • docs/Future Plans.md: запланированный следующий шаг (незаметное промежуточное ПО для каждого хода)

Related MCP server: claude-memory-mcp

Как это было создано

Архитектура и стоящие за ней идеи — мои. Код был целиком написан ИИ-агентами кодирования (в основном Claude Code) под моим руководством: я задавал дизайн, определял объём задач, просматривал результаты и направлял работу. Уайтпейпер был создан тем же способом.

INTERACTION TIME                        OFFLINE (SESSION END)
────────────────────                    ─────────────────────
┌──────────────────┐   real-time log    ┌──────────────────────┐
│  PRIMARY AGENT   │──────────────────► │  STAGE 1 CONSOLIDATION│
│  (user-facing)   │   (middleware,     │  (symbolic only,      │
└──────┬───────────┘    no LLM)         │   zero LLM calls)     │
       │                                └──────────┬───────────┘
       │ queries                                   │ writes
       ▼                                           ▼
┌──────────────┐    ┌──────────┐    ┌──────────────────────┐
│  RETRIEVAL   │◄───│ SESSION  │    │    FLAT MEMORY GRAPH  │
│  ENGINE      │    │ BUFFER   │    │                      │
│              │◄───┤(checked  │    │  nodes: episodic,    │
│ BM25 +       │    │ first)   │    │  semantic, procedural│
│ embedding    │    └──────────┘    │                      │
│ similarity   │◄───────────────────│  edges: typed        │
│              │                    │  strength: rec + freq│
└──────────────┘                    └──────────────────────┘
       ▲
       │ fallback (first ~10 sessions)
┌──────┴───────┐
│   RAW LOGS   │
│ (append-only)│
└──────────────┘

Установка

# Core (numpy, spaCy, dateparser) plus the sentence-transformers embedding model
pip install -e ".[embeddings]"

# spaCy NER model (used in consolidation; without it, entity extraction is skipped)
python -m spacy download en_core_web_sm

# Claude Code MCP integration
pip install -e ".[mcp]"

# Optional: Anthropic SDK client for Stage 2 (the default Stage 2 client is
# the Claude Code CLI and needs nothing extra)
pip install -e ".[anthropic]"

sentence-transformers опционален, поскольку тянет за собой PyTorch. Без него запускайте сессии с use_mock_embeddings=True (детерминированные векторы на основе хеша — достаточно для тестов и для знакомства с CLI). Настоящая модель (all-MiniLM-L6-v2, 22 МБ) загружается при первом использовании.

Быстрый старт

API на Python

from awhm import AWHMSession
from awhm.types import Role

# Start a session (also usable as a context manager: `with AWHMSession.start_session() as session:`)
session = AWHMSession.start_session()

# Log messages
session.log_message(Role.USER, "My name is Alice")
session.log_message(Role.ASSISTANT, "Hello Alice!")
session.log_message(Role.USER, "I prefer Python over JavaScript")
session.log_message(Role.USER, "The API endpoint is https://api.example.com/v2")

# Query memory (works immediately via session buffer)
results = session.query("What language does the user prefer?")
for r in results:
    print(f"[{r.source}] {r.content}")

# Consolidate into long-term memory graph
session.consolidate_current()

# End session (flushes WAL, saves graph)
session.end_session()

Интеграция с LLM

AWHM работает как промежуточный слой. Он не вызывает никакой LLM — вы подключаете его к чему угодно:

from awhm import AWHMSession
from awhm.types import Role

session = AWHMSession.start_session()

def handle_message(user_text):
    session.log_message(Role.USER, user_text)

    # Retrieve relevant memories
    memories = session.query(user_text, k=5)
    memory_context = "\n".join(f"- {m.content}" for m in memories)

    # Inject into system prompt
    system = f"Memories from past conversations:\n{memory_context}"
    response = your_llm_call(system_prompt=system, user_message=user_text)

    session.log_message(Role.ASSISTANT, response)
    return response

# At end of conversation:
session.consolidate_current()
session.end_session()

CLI

awhm status                        # Show system stats
awhm query "Python preferences"    # Search memory
awhm query "API endpoint" --include-history --trace
awhm consolidate                   # Run Stage 1 on pending sessions
awhm snapshot create               # Backup current graph
awhm snapshot list                 # List snapshots
awhm snapshot restore --path FILE  # Restore from snapshot
awhm delete NODE_ID                # Hard-delete a node (privacy)
awhm eval --json                   # Run built-in benchmark report

Интеграция с Claude Code (хуки, рекомендуемый способ)

Благодаря хукам память работает на каждом ходе, и модели не нужно вызывать инструмент. Каждый хук — это отдельный короткоживущий процесс; между ними буфер сессии восстанавливается из своего журнала упреждающей полезной записи.

Событие

Команда

Что делает

UserPromptSubmit

awhm hook prompt

Журналирует запрос, извлекает лучшие воспоминания (BM25 + буфер; добавьте --semantic, чтобы также использовать эмбеддинги) и возвращает их как скрытый контекст

Stop

awhm hook stop

Журналирует ответ ассистента

SessionEnd

awhm hook session-end

Консолидирует сессию в граф (добавьте --stage2 или задайте AWHM_STAGE2=1, чтобы также запускать Stage 2 через claude -p)

awhm hook settings          # prints the block to merge into ~/.claude/settings.json

Хуки никогда не блокируют сессию: любая ошибка пишется в stderr, и процесс завершается с кодом 0. Задайте AWHM_DATA_DIR, чтобы изменить расположение памяти.

Интеграция с Claude Code (MCP)

AWHM Lite поставляется как MCP-сервер, чтобы Claude Code мог использовать его как инструмент.

Настройка

# Install with MCP support
cd awhm-lite
pip install -e ".[mcp]"

# Register with Claude Code
claude mcp add --transport stdio awhm-lite -- awhm-mcp

Или добавьте вручную в .claude/settings.json:

{
  "mcpServers": {
    "awhm-lite": {
      "type": "stdio",
      "command": "awhm-mcp",
      "env": {
        "AWHM_DATA_DIR": "~/.awhm"
      }
    }
  }
}

Доступные MCP-инструменты

Инструмент

Описание

memory_query

Поиск в памяти по теме запроса на линейном языке (необязательные include_history, with_trace)

memory_log

Записать сообщение в закрытый журнал разговора

memory_consolidate

Извлечь воспоминания из ожидающих обработки сессий в граф

memory_status

Показать количество узлов, рёбер и сессий

memory_snapshot_create

Создать резервный снимок

memory_delete_node

Полностью удалить узел, заодно очистить попадающие в сё данные снимков

После подключения Claude Code автоматически получает доступ к этим инструментам и может искать и сохранять воспоминания между кластерными разговорами.

Как это работает

Исходные журналы

Каждое сообщение добавляется в файл JSONL (по одному на сессию). Режим только добавить, не изменяется никогда, кроме жёстких удалений для конфиденциальности. Это источник правды.

Буфер сессии

По каждому сообщению пользователя в реальном времени работает сопоставитель на основе регулярных выражений, ловящий:

  • Исправления: «на самом деле, X — это Y», «нет, это X»

  • Предпочтения: «я предпочитаю X», «всегда X», «никогда не делай X»

  • Факты: «endpoint — это X», «меня зовут X»

  • Результаты: «это сработало», «это не сработало»

Он захватывает примерно 60–70% явных сигналов с нулём обращений к LLM. При поиске буфер проверяется первым — это даёт мгновенную целостность внутри сессии. При поиске по умолчанию записи буфера, которые более позднее утверждение заменяет (тот же слот или явное исправление через пару сообщений), скрываются, так что исправления побеждают. Сохраняется через журналы упреждающей записи на сессию (сброс каждые 30 см, если нечего изменений нет — не сбрасывается).

Граф памяти

Плоский ориентированный граф с тремя типами узлов (эпизодический, семантический, процедурный) и тремя типами рёбер (временное, абстракция, ассоциация).

Теперь каждый узел несёт метаданные жизственного цикла противоречий:

  • canonical_key (идентификатор типа слота, например факт: мой любимый язык,)

  • status (active, superseded, outdated)

  • supersedes (идентификаторы старых узлов, заменённых этим)

  • valid_from / valid_to

  • confidence

Хранится как JSON, загружается в память.

Обратная совместимость: старые файлы графа (без полей жизненного цикла) автоматически мигрируются в памяти при загрузке.

Оценка силы

У каждого узла есть составная сила-оценка:

S(v) = 0.4 * recency + 0.6 * frequency

Свежесть — по степенному распаду: s_rec = (1 + 0.1 * hours)^(-0.3) — примерно 0.79 через 24 часа, 0.40 через 7 дней, 0.27 через 30 дней. Частота — количество обращений, нормированное на 90-й перцентиль.

Консолидация (Stage 1)

Запускается в конце сессии, ноль запросов LLM:

  1. Named Entity через spaCy: люди, организации, места, продукты. Числовые и временные метки лексем (CARDINAL, MONEY, DATE, …) отфильтровываются: они создавали шумнее вершины. Настраивается через ner_labels.

  2. Разбор времени через dateparser: распознавание «вчера», «5 марта» в ISO-метки.

  3. Извлечение по правилам: те же регулярные выражения, что и в буфере сессии, для новых сообщений.

  4. Связывание сущностей: сопоставление сущностей уже существующим вердам (одно умножение косинусных матриц, затем согласование типа сущности и проверка строковой близости).

  5. Дедупликация: одинаковые утверждения в пределах пакета сжимаются; почти дубли существующих узлов (косинус > 0.92) усиливают имеющийся узел, а не создают новый.

  6. Коммит: назначение канонических ключей, замена противоречивых воспоминаний, добавление вершин и рёбер, обновление сил.

Противоречия: канонические ключи

Канонический ключ задаёт слот, который заполняет утверждение. Два активных воспоминания с одинаковым ключом занимают одно место, поэтому новое заменяет старое (status=superseded, valid_to выставляется, на новом узле появляется связь supersedes).

Выражение

Ключ

«Любимый язык — Python»`

fact:my preferred language

«Я живу в Кейптауне»

fact:i live in

«Я предпочитаю тёмную тему»

preference:dark

«Никогда не используй табы»

policy:use:tabs

«Я использую Python для скриптовэтинг»

нет (аддитивное)

Правила сознательно консервативны, потому нет LLM-модели — нет судьи намерений:

  • Одинаковый ключ: всегда заменать (слот переформулся новым значением).

  • Семейства «предпочитаю» и «политика»: явное указание («на самом деле я предпочитаю Rust») переопределяет предыдущее утверждение того же семействуют, если оно появилось в том же кластере в течение correction_window_messages (по умолчанию 3) от него. Без маркера исправления предпочтения аддитивны: «prefer tabs» и «prefer dark mode» остаются активны оба.

  • Семейство фактов: замена происходит только при точном совпадении ключа, так что исправление по API-эндпоинту не заценит ваше имя.

  • Всё нераспознанное не получает ключа и никогда ничего не заменяет.

Сущности

Named entities резолвятся в один узел, как бы они ни были записаны. Поверхностные формы нормализуются (регистр, притяжательность, кортеж суффиксов, домены: „Acme Holdings Ltd“ и „acme.com“ оба упрощаются до „acme“), затем к исходному выполняются идеальный элас, и по безусловному токен- вхождению („Acme“ внутри „Acme Holdings“), и вообще по эмбеддингам, той же тип wala. Каждый найденный упомен записывается как алиас узла, а заявления получают ассоц-ребя к называемым сущностям, так что retrieval может перейти от „Acme“ ко всему, что о ней известно.

St. 2 (необязательный LLM-рефаймент, без наружного API-ключе)

У Stage 1 жёсткий предел: он ловит фразу «я предпочитаю Rust», но пропускает обходную «давай то оneal Rust». Stage 2 запускают после полуания офлайн, он просит model на предложи memories, которые правила не нашли. LLM только предлагает: код wh-проверяет каждая предложение (схема, наличие цитируемыхе сообщенийные номера обязательны, минимальный капитал) и сбрасывает уже captured, и проходят через те же слот-идеи и правила вытеснения. Ретривер стаётся zero-LLM.

Клиент по умолчанию запускает out-of-process Claude Code CLI (claude -p со strict output structure) — использует вашу существующую логин-процедуру, никакого ключа API не хранится. Он помечается вызов, чтобы memory hooks не сработали внутри этого вызова.

awhm consolidate --stage2                    # Claude Code CLI, default model
awhm consolidate --stage2 --stage2-model sonnet
from awhm import AWHMSession, AWHMConfig

config = AWHMConfig(stage2_enabled=True, stage2_model="sonnet")
with AWHMSession.start_session(config) as session:   # builds ClaudeCodeClient
    ...
    session.consolidate_current()

Любой объект против complete_json(system, user, schema) -> str может действовать как client (llm_client=...). Для применяющих оплату по API, приложеный в комплект кли Spec (Anthropic SDK) (stage2_client="anthropic", extra intro [anthropic]).

Поиск

Zero LLM-вызовов. Признаковая фюзия:

  1. Проверка буфера: сначала том в буфера сессии (мгновенные попадания, всегда выше результатов из графа)

  2. Поиск якорей: BM25 терм обозначений + косинусительная близость эмбеддингов (union). Индекс BM25 построится в процессе (Lucene-style IDF, даже мелкие корпусы адекватно составляются) и кеш до изменений узлов.

  3. История filter: по умолчанию доступны только nodes с status=active

  4. Фпризнак исчисляет: семическая близость + лексикальный цичет + сила + уверенность, минус штраф за противоречие* от number вы precision. Сила пересчитывается только для кандидатов на уровне рейтингования

  5. Вернуть топ-к (default 10)

  6. Распространение соседей: к ним из первый-hopов (связанной entities, последовательных episode) объединяются в кандидаты с распащей веса, оцениваемым уже по feature association. Смещение может только из-за, которые сами current

  7. Cold-start fallback: у ф- (для первых ~10 сессий) также запускается BM25 по raw logs. Эти покрыты разряда в [0,0, raw_log_score_scale], никогда не могут стоять над реальными graph-матчами.

Путешествия во времени

Тот факт времени несёт окно действительности. Даты introduced через „from“/„since" ставится понимают valid_from, „until" означает valid_to, а supersesjor закрывает окно старого факта. query(..., as_of="2026-03-01") отвечает, что было true в тот момент, включая superseded memories:

awhm query "API endpoint"                       # what is true now
awhm query "API endpoint" --as-of 2026-02-01    # what was true then

Используйте include_history=True, чтобы выдать на свет superseded/retracted воспоминания.

Используйте with_trace=True, чтобы получить на каждым результаты ranking-feature trace.

Evaluation

Встроенный benchmark — это синтетический smoke test (трое умеющие correction-heavy запросы плюс удаление) продіння подозрительный места. Реальные цифранов при повторении его playback:

awhm eval                                            # built-in synthetic benchmark
awhm eval --corpus my_sessions.json                  # native format, see below
awhm eval --corpus longmemeval_s.json --longmemeval --limit 50

Оба возвращают Recall@k, nDCG@k, уровень ошибок противоречий, задержку p50/p95 и полноту по категориям. Родной формат корпуса — {"sessions": [{"id", "messages": [{"role", "content"}]}], "questions": [{"id", "question", "expected": [...], "forbidden": [...], "as_of", "category"}]}. Экземпляры LongMemEval консолидируются и опрашиваются изолированно, в соответствии с протоколом бенчмарка. Совпадение определяется по подстроке ответа — это намеренно заниженная нижняя граница: перефразированные совпадения не учитываются.

Измерено (только этап 1, oracle-сплит, 500 вопросов): Recall@5 0.196 — от 0.40 на односеансовых пользовательских фактах до 0.00 на предпочтениях, при 4 ms на запрос. Это наглядный предел regex; этап 2 существует, чтобы его поднять. Полная таблица, оговорки и воспроизведение результатов в docs/benchmarks.md.

Конфигурация

Все параметры настраиваются через AWHMConfig:

Параметр

По умолчанию

Описание

alpha

0.3

Скорость затухания (показатель степенного закона)

beta

0.1

Константа масштабирования затухания

w_rec

0.4

Вес недавности в оценке силы

w_freq

0.6

Вес частоты в оценке силы

retrieval_profile

"balanced"

Профиль взвешивания при поиске

w_semantic

0.55

Вес семантической близости

w_lexical

0.20

Вес лексической составляющей BM25

w_strength

0.15

Вес силы узла

w_confidence_interval

0.10

Вес достоверности консолидации

contradiction_penalty

0.35

Штраф за неактивные воспоминания

include_history_by_default

False

Включать заменённые/отозванные воспоминания по умолчанию

trace_retrieval

False

Выводить трассировку ранжирования по умолчанию

k

10

Число элементов в top-k поиске

related_threshold

0.85

Косинусный порог для связывания сущностей

deduplication_threshold

0.92

Косинусный порог для дедупликации

bm25_anchor_ratio

0.5

Лексический якорь, если оценки >= доля × лучшая оценка BM25

embedding_threshold

0.3

Минимальное косинусное сходство для множества якорей

raw_log_score_scale

0.5

Верхняя граница для cold-start raw-log оценок попаданий

neighbor_expansion / neighbor_decay

True / 0.6

Добавлять соседей графа якоря на один переход, с этим множителем веса рёбер

association_weight

0.10

Вес свидетельств соседей в смеси

storage_backend

"json"

"json" (один файл) или "sqlite" (инкрементальное сохранение)

stage2_enabled

False

Офлайн-доработка LLM после первого этапа

stage2_client / stage2_model

"claude-code" / None

claude-code (CLI, без ключа) или anthropic; псевдоним модели, None — по умолчанию клиента

stage2_max_messages / stage2_min_confidence

60 / 0.5

Сообщений на один вызов LLM; варианты с уверенностью ниже этого порога отбрасываются

correction_window_messages

3

Насколько близко должно быть явное исправление, чтобы заменить действие/политику

ner_labels

PERSON, ORG, GPE, ...

Метки сущностей spaCy, которые становятся узлами

buffer_flush_interval

30s

Интервал синхронизации WAL

ann_index_type

"none"

Зарезервированный режим ANN-индекса

delete_snapshots_on_hard_delete

True

При жёстком удалении затирать соответствующие снапшотные воспоминания

from awhm.config import AWHMConfig

config = AWHMConfig(
    data_dir="~/.my-project-memory",
    k=20,
    w_rec=0.5,
    w_freq=0.5,
)

Каталог данных

~/.awhm/
├── logs/                          # Raw JSONL logs (one per session)
│   ├── {session_id}.jsonl
│   └── ...
├── graph/
│   ├── memory_graph.json          # The memory graph (storage_backend="json")
│   └── memory_graph.sqlite        # ... or one row per node (storage_backend="sqlite")
├── snapshots/
│   └── snapshot_{timestamp}.json  # Manual backups
├── wal/
│   └── {session_id}.wal           # Per-session write-ahead logs
└── meta/
    ├── consolidated_sessions.json # Tracks which sessions have been processed
    ├── deletion_tombstones.jsonl  # Deletion tombstones
    └── deletion_ledger.jsonl      # Deletion audit ledger

Тестирование

pip install -e ".[dev]"
pytest tests/ -v

Все тесты используют MockEmbeddingService (детерминировано между процессами, без загрузки моделей). ruff check . запускает линтер; CI запускает его и тесты на Python 3.11, 3.12 и 3.13.

Зависимости

Пакет

Размер

Назначение

name

~29 MB

Векторные вычисления

numpy

~29 MB

Векторные вычисления

spacy + en_core_web_sm

~35 MB

NER-разметка

dateparser

~2 MB

Разбор дат

tensor (not always?)

~3 MB (+PyTorch ~350 MB)

Модель эмбеддеров

sentence-transformers (optional, [embeddings])

~3 MB (+PyTorch ~350 MB)

Модель эмбеддингов

mcp (optional, [mcp])

~1 MB

Интеграция с Claude Code

BM25 реализован внутри пакета (около 60 строк), поэтому зависимостей для ранжирования нет.

Эмбеддиинг-модель (all-MiniLM-L6-v2, 22 MB) загружается при первом использовании в ~/.cache/huggingface.

Структура проекта

src/awhm/
├── __init__.py            # AWHMSession facade (top-level API)
├── config.py              # All parameters + path helpers
├── types.py               # Enums: Role, NodeType, NodeStatus, EdgeType, BufferEntryType
├── mcp_server.py          # MCP server for Claude Code
├── hooks.py               # Claude Code hook commands (prompt / stop / session-end)
├── timeutil.py            # Timestamp parsing, validity windows
├── eval/                  # Built-in benchmark + real-corpus replay (LongMemEval loader)
├── raw_log/               # Append-only JSONL logging
├── session_buffer/        # Regex pattern matching + WAL
├── graph/                 # Memory graph, strength scoring, JSON/SQLite stores
├── consolidation/         # NER, temporal, extraction, entities, dedup, Stage 2, pipeline
├── retrieval/             # Embedding, BM25, ranking, retrieval engine
├── snapshots/             # Snapshot create/restore/list
├── deletion/              # Hard-delete cascade
└── cli/                   # argparse CLI
A
license - permissive license
Not graded
quality - not tested
B
maintenance

Maintenance

Maintainers
Response time
Release cycle
Releases (12mo)
Commit activity

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Related MCP Servers

  • A
    license
    Not graded
    quality
    D
    maintenance
    A persistent memory MCP server for Claude Code that enables long-term recall across sessions via hybrid search, code intelligence, and tools for reading/writing memory.
    23
    1
    MIT
  • A
    license
    Not graded
    quality
    B
    maintenance
    A MCP server that gives Claude Code and other AI assistants long-term memory by automatically extracting technical knowledge from conversations and retrieving relevant experiences in future sessions.
    14
    MIT

View all related MCP servers

Related MCP Connectors

  • Cloud-hosted MCP server for durable AI memory

  • Hosted MCP server connecting claude.ai, ChatGPT and other AI apps to your own computer

  • One memory, every AI: Claude, ChatGPT, Perplexity, Gemini, Cursor, OpenClaw, Hermes, any MCP client.

View all MCP Connectors

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/juderosendev/awhm-lite'

If you have feedback or need assistance with the MCP directory API, please join our Discord server