Skip to main content

A universal memory middleware for LLM agents — file-based, scope-aware, BYO LLM.

Project description

bobo-memory

CI License: MIT Python GitHub

English | 简体中文

Universal memory middleware for LLM agents — file-based, scope-aware, BYO LLM.

bobo-memory gives any Python LLM project a persistent, visible, governable memory system without requiring a database, a vector store, or an embedded LLM. The entire memory lives as Markdown files you can open in Obsidian.

Getting started: follow the tutorial (Chinese) from init through agent integration.

For coding agents / integrators: integration guide · one-page brief · multi-user Web / SaaS


Core philosophy

Principle Implementation
Files are memory All memories are *.md files in .bobo/memory/
Index-first prompting MEMORY.md acts as the prompt entry-point (hard-capped at 200 lines / 25 KB)
BYO LLM Zero LLM calls — the module exposes rules + tools, the agent decides what to write
Four engineering rails Every write: policy → guard → atomic_write → audit
Scope isolation user / project / local scopes for agent memory
Source citation Memories trace back to raw/ source files
Distributable snapshots Agent memory packaged as a versioned asset

Installation

pip install bobo-memory

Optional extras:

pip install "bobo-memory[mcp]"        # MCP stdio server for Claude Code / IDE agents
pip install "bobo-memory[watch]"      # directory watching: watchdog
pip install "bobo-memory[viewer]"     # web UI + read-only REST API: fastapi + uvicorn
pip install "bobo-memory[embedding]"  # vector recall: sentence-transformers

Quick start

1. Initialise

cd your-project/
bobo-memory init --agent-type my-agent --scope project

This creates .bobo/config.yaml and the memory directory structure.

2. Integrate with your agent

from bobo_memory import MemoryClient

mem = MemoryClient(
    project_root=".",
    agent_type="my-agent",
    scope="project",
)

# Inject memory context into system prompt
system_prompt = mem.build_system_prompt("You are a helpful assistant.")

# Pass tools to your LLM call
tools = mem.to_openai_tools()   # or to_anthropic_tools() / to_langchain_tools()

# Dispatch tool calls coming back from the LLM
result = mem.dispatch_tool_call(tool_name, arguments)

3. Receive external information streams

# Ingest a file (lands in raw/ + staging/ — no LLM called)
mem.ingest(adapter="markdown", path="article.md")

# Agent picks it up next turn (task is leased, not deleted)
result = mem.dispatch_tool_call("ingest_next", {})
# → returns raw content + instruction to integrate into wiki/memory

# Confirm when done — unconfirmed tasks are retried after the lease expires
mem.dispatch_tool_call("ingest_done", {"source_id": result["task"]["source_id"]})

4. Automatic memory capture (zero extra LLM calls)

memory_nudge() scans recent messages with pure rules (no LLM) for corrections, preferences, decisions and explicit "remember this" phrases. When something fires, it returns a one-line reminder to append to your NEXT request's system prompt — the main model then saves the memory inside its normal turn, so no additional API call is ever made:

nudge = mem.memory_nudge(messages)          # "" most of the time
if nudge:
    system_prompt += "\n\n" + nudge

# Optional dedup check before saving (BM25, no LLM):
similar = mem.find_similar("integration tests must pass before deploy")

Memory layers

Layer What it stores Scope
agent Agent-type specific long-term memory user / project / local
auto Project-wide persistent facts project
wiki LLM-maintained knowledge base (entities, concepts, sources) project
session Current-session summary for context compaction ephemeral
team Git-tracked shared team knowledge repo

Available tools (exposed to the LLM)

Tool Description
memory_save Save a new memory file + update index
memory_update Update an existing memory file
memory_list Read the MEMORY.md index
memory_read Read a specific memory file
memory_recall BM25 search across memory layers → ContextPack
memory_forget Soft-delete (move to .trash/)
memory_restore Restore from trash
memory_purge Permanent delete (audit log kept)
wiki_link Bidirectional cross-reference between wiki pages
wiki_log Append to wiki/log.md timeline
ingest_next Lease the next task from staging/pending.json (retried if not confirmed)
ingest_done Confirm an ingest task is integrated — removes it from staging

Policy configuration (.bobo/config.yaml)

policy:
  write_mode: direct          # "direct" or "proposal"
  layers:
    wiki:
      require_citation: true  # wiki pages must cite raw sources
      write_mode: proposal    # wiki writes go to proposals/ for review
    team:
      require_secret_scan: true
  forbidden_patterns:         # applied globally on every write
    - "api[_-]?key\\s*=\\s*['\"]?[A-Za-z0-9]{20,}"
    - "-----BEGIN (RSA |EC |DSA )?PRIVATE KEY-----"
  max_file_size_kb: 100
  trash:
    retention_days: 30
    allow_purge: true

Session memory & context compaction

# Check if it's time to extract a session summary
if mem.session.should_extract(messages):
    extract_prompt = mem.session.build_extract_prompt(messages)
    # Fork a sandboxed subagent with this prompt

# Context compaction (no LLM call if session memory exists)
if mem.compact.should_compact(messages, token_budget=180_000):
    summary = mem.compact.compact_with_session_memory(messages)
    keep = mem.compact.calculate_keep_index(messages)
    keep = mem.compact.adjust_index_to_preserve_invariants(messages, keep)
    new_messages = [
        messages[0],                                    # keep system
        mem.compact.create_compact_boundary(summary),   # boundary msg
        *messages[keep:],                               # tail
        *mem.compact.build_post_compact_attachments(tool_specs=mem.tool_specs()),
    ]

Snapshots — distributable agent memory

# Export current agent memory as a snapshot (ship with your project)
bobo-memory snapshot save --scope project

# Apply a snapshot to a fresh environment
bobo-memory snapshot apply --scope project

# Check snapshot sync status
bobo-memory snapshot status

Proposal queue

When write_mode: proposal, writes go to .bobo/proposals/ for review:

bobo-memory proposal list
bobo-memory proposal accept --id <id>
bobo-memory proposal reject --id <id>

CLI reference

bobo-memory init      [--agent-type NAME] [--scope SCOPE]
bobo-memory status
bobo-memory audit     [--date YYYY-MM-DD] [--limit N]
bobo-memory lint
bobo-memory ingest    --file PATH [--adapter markdown]
bobo-memory proposal  list / accept --id X / reject --id X
bobo-memory snapshot  save / apply / status
bobo-memory serve     [--port 8765]
bobo-memory mcp       [--agent-type NAME] [--scope SCOPE]

Disk layout

.bobo/
├── config.yaml
├── memory/
│   ├── auto/           MEMORY.md + *.md       (Auto Memory)
│   ├── agent/<type>/   project/ local/ user/  (Agent Memory)
│   ├── session/        <session_id>.md        (Session summaries)
│   ├── team/           MEMORY.md + *.md       (Team Memory)
│   └── wiki/           index.md  log.md  entities/  concepts/  sources/
├── raw/                <YYYY-MM-DD>/<source_id>.md   (immutable originals)
├── staging/            pending.json           (ingest queue)
├── proposals/          <layer>/<topic>.<uuid>.md
├── audit/              audit-<YYYY-MM-DD>.jsonl
└── snapshots/          <agent_type>/snapshot.json + *.md

~/.bobo/               (user-scoped agent memory, cross-project)

MCP server

Expose all 12 memory tools to any MCP host (Claude Code, IDE agents, …):

pip install "bobo-memory[mcp]"
bobo-memory mcp --agent-type my-agent        # stdio transport

Claude Code .mcp.json example:

{
  "mcpServers": {
    "bobo-memory": { "command": "bobo-memory", "args": ["mcp"] }
  }
}

Every MCP tool call runs through the same policy → guard → atomic → audit pipeline.


Read-only REST API (viewer)

bobo-memory serve (requires [viewer]) binds to 127.0.0.1 and exposes:

Endpoint Returns
GET /status / GET /storage system status / disk usage
GET /layers enabled layers + directories
GET /memories/{layer} the layer's MEMORY.md index
GET /memory?file=<rel-path> one memory file (guard-checked)
GET /recall?query=&k= BM25 recall as a ContextPack
GET /audit / /lint / /proposals audit log / wiki health / proposal queue

Framework integrations

Works with any LLM framework that supports function calling:

# OpenAI
tools = mem.to_openai_tools()

# Anthropic
tools = mem.to_anthropic_tools()

# LangChain
tools = mem.to_langchain_tools()

# Any custom framework
for spec in mem.tool_specs():
    print(spec.name, spec.parameters)

Requirements

  • Python 3.10+
  • pydantic >= 2.0
  • pyyaml >= 6.0
  • rank-bm25 >= 0.2.2
  • filelock >= 3.12
  • watchdog >= 4.0 (optional, pip install "bobo-memory[watch]" — only needed for watch_directory())

Development

git clone https://github.com/WinWin405/bobo-memory.git
cd bobo-memory
python -m venv .venv
# Windows: .venv\Scripts\activate
# macOS/Linux: source .venv/bin/activate
pip install -e ".[viewer]"
pytest bobo_memory/tests/

Contributing

See CONTRIBUTING.md. Issues and pull requests are welcome on GitHub.

License

MIT — see LICENSE for the full text.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

bobo_memory-0.2.0.tar.gz (85.9 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

bobo_memory-0.2.0-py3-none-any.whl (104.7 kB view details)

Uploaded Python 3

File details

Details for the file bobo_memory-0.2.0.tar.gz.

File metadata

  • Download URL: bobo_memory-0.2.0.tar.gz
  • Upload date:
  • Size: 85.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for bobo_memory-0.2.0.tar.gz
Algorithm Hash digest
SHA256 6c4ccabcd90c335f550d700500454f81d031e0fa6b7918f45c502c2f290307a8
MD5 6750b7ebfbd66f5419feb74cd3de4dc7
BLAKE2b-256 cd16809b0234a1d24f04c0e290a1a00589ab71a570b8b1ebaccbb2311398a2f4

See more details on using hashes here.

Provenance

The following attestation bundles were made for bobo_memory-0.2.0.tar.gz:

Publisher: release.yml on WinWin405/bobo-memory

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file bobo_memory-0.2.0-py3-none-any.whl.

File metadata

  • Download URL: bobo_memory-0.2.0-py3-none-any.whl
  • Upload date:
  • Size: 104.7 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for bobo_memory-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 573465ed95b0a9e9f10d995030083a4d04599d7c57c01491b3a54ee17f8fc0f9
MD5 78def4ba1d09b30ed900c651e25743da
BLAKE2b-256 15d7810af83e73c2dc6cf3ce02b5eb657c66957607726fc06103bc5052b96665

See more details on using hashes here.

Provenance

The following attestation bundles were made for bobo_memory-0.2.0-py3-none-any.whl:

Publisher: release.yml on WinWin405/bobo-memory

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page