Skip to main content

Agent memory as normative source code: a lintable, queryable memory DSL for LLM agents

Project description

memdsl

English | 中文

English

Agent memory as normative source code.

Most agent memory systems store what you said. Almost none of them distinguish what an agent must obey from what it may consider. memdsl treats long-term memory as a small, typed, lintable source language — .mem files you can read, review, diff, version, and test — where a boundary is a rule to enforce, not a fact to recall.

preference schedule.deep_work_mornings {
  subject: User
  claim: "Prefers deep work in the morning; meetings after 2pm."
  force: strong
  scope: scheduling
  evidence { source: chat quote: "Stop booking me into morning meetings." }
}

boundary privacy.no_family_in_public {
  subject: User
  rule: "Never include family member names or details in public-facing content."
  force: hard
  scope: global
  exceptions: [user_explicit_override]
  evidence { source: chat quote: "Anything about my family stays out of blog posts. Always." }
}

The difference matters: a preference shapes suggestions; a boundary binds. Flat embeddings of "user said X" erase exactly this distinction, which is why agents violate rules their memory technically "contains".

30 seconds: same question, one declaration apart

Ask an agent to "draft a public blog post about building Aurora as a working parent." A similarity-ranked memory gives it the old chats where the kids' names appear — and nothing that ranks a rule above a reminiscence. The retrieved snippets are all just CONTEXT, so the names end up in the post:

retrieved: "...shipped the sync fix after bedtime, Theo finally sleeping..."
retrieved: "aurora beta feedback thread..."

With the boundary declared, the evidence pack puts the rule where it cannot be missed — in MUST, above every piece of context, whether or not the query lexically matched it:

$ memdsl query examples/alex/ -q "draft a public blog post about aurora"
MUST
- [boundary:privacy.no_family_in_public] Never include family member names
  or details in public-facing content. (exceptions: ['user_explicit_override'])

CONTEXT
- [state:aurora.beta_progress] Private beta with 40 testers; ...

Feed the pack to the LLM as context and the compliant behavior is to write the post without family details — and to be able to cite boundary:privacy.no_family_in_public as the reason. The pack is the prompt; the MUST layer is what changes the answer.

Lint it, query it

Memory that can be wrong deserves a linter:

$ memdsl lint examples/lint-demo/
broken.mem:7:  error[unresolved_symbol]        subject 'User.Barista' is not a declared entity
broken.mem:20: error[missing_evidence]         active fact 'home.timezone' has no evidence block
broken.mem:28: warning[type_force_mismatch]    preference uses force: hard; promote it to a boundary
broken.mem:41: warning[boundary_without_exception]  confirm it is truly unconditional
broken.mem:53: warning[stale_state]            as_of 2025-11-02 is older than 180 days

6 declarations, 2 error(s), 3 warning(s)

Queries return a layered evidence pack, not a hit list. Hard boundaries surface even when the query doesn't lexically match them; declared conflicts are shown, not averaged away; open issues surface as gaps instead of being hallucinated over:

$ memdsl query examples/alex/ -q "should aurora keep the free tier"
# resolved subjects: Project.Aurora
MUST
- [boundary:privacy.no_family_in_public] Never include family member names ...

SHOULD
- (none)

CONTEXT
- [decision:aurora.pricing_free_tier] Keep a permanent free tier.
- [state:aurora.beta_progress] Private beta with 40 testers; sync is the top complaint. (as_of 2026-06-20)
- [goal:aurora.revenue_target_2026] Reach $2k MRR from Aurora by end of 2026.

CONFLICT
- [decision:aurora.pricing_free_tier] conflicts_with [aurora.revenue_target_2026]

MISSING
- open issue [open_issue:aurora.pricing_undecided]: Paid tier pricing is undecided: $5 flat vs usage-based.

Feed that pack — MUST/SHOULD/CONTEXT/CONFLICT/MISSING — to your LLM as context. Every line carries a declaration id, so answers are citable and auditable back to source and evidence.

Install

pip install memdsl        # or: pip install -e . from a checkout
memdsl lint examples/alex/
memdsl query examples/alex/ -q "plan tomorrow morning"
memdsl explain examples/alex/ decision:aurora.db_postgres_migration

Zero runtime dependencies. Python 3.9+.

Note: memdsl lint examples/alex/ reports one intentional warning — Alex's schedule.no_meetings_before_10 boundary declares no exceptions, and the linter asks you to confirm it is truly unconditional. That nudge is the feature; a clean run is examples/mira/.

What's in the box

  • A tiny declarative language (.mem): typed declarations (entity, fact, preference, boundary, principle, decision, state, open_issue) with force, scope, evidence, relations (supersedes, conflicts_with, refines, ...), and lifecycle status. Full grammar and semantics in docs/SPEC.md.
  • A linter with ten diagnostics: dangling symbols, missing evidence, ambiguous aliases, stale states, boundaries without exceptions, preferences masquerading as laws, unmarked supersede chains.
  • A query executor implementing the EvidencePack contract, plus explain for tracing one declaration's relations and provenance.
  • Synthetic example personas (examples/) — fictional users "Alex" and "Mira" — and a deliberately broken file for the linter demo.

What memdsl is not

  • Not a replacement for Mem0, Zep, or LangMem. Those are retrieval/extraction platforms. memdsl is a source format and contract for the layer above: what memory means, how strongly it binds, and how it is maintained. You could compile .mem files into any of them.
  • Not a retrieval engine. The reference executor is deliberately naive lexical matching — enough to demonstrate the contract. Production use should plug BM25/embeddings behind the same EvidencePack interface. Do not benchmark toy retrieval and conclude the format failed.
  • Not an auto-writer. v0.1 has no write pipeline. The spec (§10) describes a gated, human-reviewed write path; automation should be earned with audited metrics, not assumed.

Does the approach work?

Early evidence from the private system this was extracted from (DigitalSelf, single-user): on a 100-question eval over the author's real long-term memory, DSL-structured retrieval nearly doubled top-1 precision against the same system's tuned RAG baseline (0.57 vs 0.30; hit rate 0.67 vs 0.53). On public conversational benchmarks (LongMemEval, LoCoMo) it performs at parity with baselines under a retrieval-only harness whose target mapping is still being audited — we explicitly do not claim public-benchmark wins. Current honest costs: seconds-level query latency at scale in the private implementation (being moved to write-time compilation), and n=1 personalization. A reproducible benchmark report will be published separately.

The interesting unmeasured dimension — and the reason this exists — is compliance, not recall: existing memory benchmarks test whether an agent can find a fact, none test whether it respects a boundary. A boundary-compliance benchmark is the roadmap's centerpiece.

Related work

Typed/structured agent memory is converging fast: MemIR (typed memory IR, provenance-role separation), Zep/Graphiti (temporal knowledge graphs with fact validity windows), A-Mem (Zettelkasten-style linked notes), MemOS (memory scheduling), and the CLAUDE.md/AGENTS.md culture of local, reviewable context files. memdsl's position in that landscape: local-first plain-text source, an explicit normative layer (force + boundaries + exceptions + MUST/SHOULD rendering), and code-style diagnostics as a first-class surface.

Roadmap

  • Target-mapping audit + reproducible benchmark report
  • Boundary-compliance benchmark (does the agent respect MUST items?)
  • Pluggable retrieval backends (BM25, embeddings) behind the EvidencePack contract
  • Module directory compilation for query planning
  • Gated write pipeline with review queue

Today, memdsl defines memory as typed, auditable source code. Future runtimes can navigate these declarations the way developers navigate code — following relations, inspecting evidence, tracing history, and asking for missing information instead of guessing.

License

Code: [MIT](LICEN

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

memdsl-0.1.0.tar.gz (24.3 kB view details)

Uploaded Source

Built Distribution

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

memdsl-0.1.0-py3-none-any.whl (18.2 kB view details)

Uploaded Python 3

File details

Details for the file memdsl-0.1.0.tar.gz.

File metadata

  • Download URL: memdsl-0.1.0.tar.gz
  • Upload date:
  • Size: 24.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.10

File hashes

Hashes for memdsl-0.1.0.tar.gz
Algorithm Hash digest
SHA256 72e07634ae1d8dd3f153711dcc2004c42bd7ac0a07218a79e676c208fa879c99
MD5 ca4d4acdafc01c822120ac0e03e3d4b7
BLAKE2b-256 6f2f32f95c88592a70c86997ace14c697579539a619296c621a9aa2fab8bf601

See more details on using hashes here.

File details

Details for the file memdsl-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: memdsl-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 18.2 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.10

File hashes

Hashes for memdsl-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 d0e27257a37aad0fb5ebb983f8a4b00a91492400fba46dde0bb373a7edd71896
MD5 0be800c672dd4cd452845df6010516eb
BLAKE2b-256 641e65ff1821063b9885fd45f24087a784367bf81089d33d7c8ce844f624128c

See more details on using hashes here.

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