Skip to main content

MemPatrol

CI PyPI Python License: MIT

English | 中文

MemPatrol is a dependency-free toolkit for long-lived AI agents: memory hygiene (patrol), prompt-ready recall, and a judge-first conversation gate. It runs on plain SQLite, ships a CLI and a read-only MCP server, and does not depend on an IM framework.

SQLite memories ──► patrol / diagnose ──► auditable repairs (--apply + backup)
        │
        ├──────────► recall scorer ─────► prompt-ready memory block
        │                                      │
chat events ──► gatekeep ◄────────────────────┘
        └────► LLM judge ──► send / handoff / tool_task / drop

MemPatrol terminal demo

Install in 10 seconds

pip install mem-patrol
memorix --version        # primary CLI; `mem-patrol` is the alias, import stays `memorix`

Why MemPatrol

  • Deterministic by default. Audits, decay/consolidation plans, intent expiry and finish gates are pure functions. The LLM only appears where a human would otherwise hand-edit memory.
  • Judge-first interjection. A small set of hard gates rejects the obvious "not now"; after that, the LLM judge decides whether there is a natural insertion point. Throttling limits rate, it does not silently veto.
  • Portable. One memories table is enough. Map any SQLite schema with --schema, or let --schema auto inspect it. jieba is optional.
  • Boring operations. Read-only by default; writes require --apply and take a snapshot first. Every release has offline eval and a real-corpus replay gate.
  • No host lock-in. No AstrBot/QQ/Telegram types in the kernels. Any adapter can build the small Context/rows structures.

Zero-LLM paths

No model is required for most of the toolkit: memorix diagnose/recall/eval, all MCP tools, the recall scorer and decide(ctx) (no judge) run offline. decide without a judge returns candidate after the deterministic gates; wire a host judge only for the final speak/no-speak decision. See docs/ZERO-LLM.md.

Architecture

flowchart LR
  A[(SQLite memory DB)] --> B[policy kernels<br/>orphan / decay / consolidate / intents / finish]
  B --> C[CLI diagnose + apply<br/>read-only by default]
  A --> D[recall scorer<br/>anchor + instruction separation]
  D --> E[prompt-ready block<br/>with provenance]
  F[chat events] --> G[gatekeep<br/>8 gates + directed path]
  H[LLM judge] --> G
  G --> I[send / handoff / tool_task / drop]
  D --> G
  C --> J[MCP stdio server]
  D --> J
  G --> J

Full component map: docs/ARCHITECTURE.md.

Install

pip install mem-patrol                 # from PyPI
# or: python -m pip install .
memorix --version                      # mem-patrol 0.3.6

The distribution is mem-patrol; the import package and original CLI remain memorix for compatibility. mem-patrol / mem-patrol-mcp are aliases.

Quickstart

# read-only diagnosis (works with any schema)
memorix diagnose --db messages.db --schema auto

# plan before writing; --apply creates a DB snapshot first
memorix decay --db messages.db
memorix decay --db messages.db --apply

# offline regression
python -m memorix.eval --suite all --baseline eval/baseline.json
python -m memorix.retrieval.bench      # 6/6
python -m memorix.gatekeep.bench       # 9/9

# prompt-ready recall (lexical, no LLM)
memorix recall --db messages.db --query "为什么说 8-23 比 8-19 更难" --user-id <id>

End-to-end: group-chat interjection

from memorix.gatekeep import Context, decide
from memorix.retrieval.context import recent_private_context
from memorix.retrieval.score import recall
from memorix.retrieval.store import load_rows

rows = load_rows("messages.db")
context = recent_private_context("messages.db", user_id)
memory = recall(user_message, rows=rows, context=context, user_id=user_id)

outcome = decide(
    Context(
        text=user_message,
        user_id=user_id,
        config={
            "enable_group_interjection": True,
            "group_interject_max_daily": 30,
            "group_interject_judge_min_consult_seconds": 10,
            "group_interject_min_interval_minutes": 1,
        },
    ),
    judge=llm_judge,  # returns {"should_reply": bool, "text": ..., "needs_tools": ...}
)
if outcome.action == "sent":
    send(outcome.text)

The judge is only called after the cheap gates pass. Directed messages (@/reply/name) take a fast path. Step-by-step walkthrough: docs/END-TO-END.md.

Recall backends (lexical default, vector optional)

from memorix.retrieval.provider import make_provider

provider = make_provider("lexical")                 # default, zero deps
# provider = make_provider("vector", embedder=embed_batch)  # bring your own embeddings
block = provider.recall(user_message, rows=rows, context=context, user_id=user_id)

VectorProvider is an optional path (cosine + embedding cache); no model is bundled. The ONNX adapter is in memorix.retrieval.vector_onnx (pip install 'mem-patrol[vector]'). Lexical remains the default until vector wins on the real corpus without regressions.

Lexical boundary (be honest about synonyms)

Lexical recall does not know synonyms. With the built-in example, these return (empty):

  • memory “喜欢喝冰美式” → query “咖啡”
  • memory “想学吉他” → query “音乐”

That is expected, not a bug: the default is lexical. A vector provider is available as an optional seam (make_provider("vector", embedder=...)); no model is bundled. See docs/VECTOR-PROVIDER.md.

MCP

{
  "mcpServers": {
    "memorix": {
      "command": "memorix-mcp",
      "args": ["--profile", "both", "--db", "/path/to/messages.db"]
    }
  }
}

Six read-only tools: memorix_diagnose, memorix_orphan, memorix_recall, memorix_gate_check, memorix_judge_replay, memorix_eval. See docs/MCP.md.

Two modes

profile what it is for MCP tools
chat recall + gatekeep, no patrol writes recall / gate_check / judge_replay
project patrol, diagnose, recall, eval diagnose / orphan / recall / eval
both both kernels, judge-first with light throttle all 6
python -m memorix.profiles --list
memorix profile --name chat

Any SQLite schema

memorix diagnose --db my.db --schema schema.json   # explicit mapping
memorix diagnose --db my.db --schema auto          # auto-detect
MEMORIX_SCHEMA=schema.json memorix diagnose --db my.db

Only memories is required; status/finish report a clear SchemaError if message tables are not mapped. A privacy-preserving real-corpus replay is in eval/real_corpus.json: docs/REAL-CORPUS.md.

Quality and benchmarks

  • 381 tests; CI on Python 3.10 / 3.11 / 3.12.
  • Synthetic §10.3 replay: 6/6; real production replay: 6/6 current, 5/6 historical (one data gap documented, not an algorithm failure).
  • On a 1,238-memory read-only snapshot: cold load ~0.18 s, warm recall median ~43 ms, injected block ~620 chars. Environment-specific; see docs/BENCHMARKS.md.

CI/CD

  • .github/workflows/ci.yml — pytest + both benches + eval on Python 3.10/3.11/3.12.
  • .github/workflows/release.yml — v* tag → test, build wheel/sdist, install smoke, GitHub Release assets.
  • .github/workflows/publish.yml — manual PyPI upload via trusted publishing (OIDC; no token in secrets).

Docs

Community

License

MIT. See LICENSE. MIT was chosen for minimal adoption friction; it has no explicit patent grant (Apache-2.0, used by 0.2.0/0.2.1, does). See FAQ.md.

Metadata

Release files for mem-patrol 0.3.6

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for mem-patrol 0.3.6
File Size Uploaded Provenance
mem_patrol-0.3.6.tar.gz 790.2 kB Without provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for mem-patrol 0.3.6
File Interpreter ABI Platform
mem_patrol-0.3.6-py3-none-any.whl Python 3 none any Details

Total release size: 891.3 kB

Release files / mem_patrol-0.3.6.tar.gz

Download URL mem_patrol-0.3.6.tar.gz
Size 790.2 kB
Tags Source
SHA-256 checksum
How to use checksums
4cdadcf3c94bbe40dead9babd975edd77c99209d6fc324465d60640c55861be7
BLAKE2b-256 checksum
How to use checksums
925ee3874afbdf06e273d1c348092ccbfe9305f8c852daf88b659747cd45f13a
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.12.3

Release files / mem_patrol-0.3.6-py3-none-any.whl

Download URL mem_patrol-0.3.6-py3-none-any.whl
Size 101.1 kB
Tags Python 3
SHA-256 checksum
How to use checksums
385bd1a04e60f7fd2e70881fa51333d7c3b4b665a9593c1e08c7a56531ebbaee
BLAKE2b-256 checksum
How to use checksums
1f60523cee7878d30936e1e9dbf446edd5251120b51ba906ea4961d01437c693
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.12.3

Security for mem-patrol 0.3.6

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page