Skip to content

feat(examples): add persistent_memory_agent — cross-session memory via Dakera MCP (closes #12) - #714

Open
ferhimedamine wants to merge 7 commits into
lastmile-ai:mainfrom
ferhimedamine:feat/dakera-persistent-memory
Open

feat(examples): add persistent_memory_agent — cross-session memory via Dakera MCP (closes #12)#714
ferhimedamine wants to merge 7 commits into
lastmile-ai:mainfrom
ferhimedamine:feat/dakera-persistent-memory

Conversation

@ferhimedamine

@ferhimedamine ferhimedamine commented Jul 1, 2026

Copy link
Copy Markdown

Summary

Adds examples/usecases/persistent_memory_agent/ — a working example of the long-term memory feature requested in #12. It uses Dakera, a self-hosted, decay-weighted MCP memory server, to give mcp-agent applications true cross-session persistence.

Crucially, it models memory as a project-continuity layer, not a "store everything the agent said" transcript dump (per the design discussion in #713).

Closes #12

The memory contract

The agent only promotes durable facts to long-term memory, and each record is tagged so it can be retrieved deliberately by scope later:

type:<decision | convention | failed_path | bug_fix | open_question>
project:<scope>
status:<accepted | candidate | superseded>

accepted facts are stored at importance 0.8–0.9 so they survive decay.

The three memory tiers (from #12)

Tier What it holds Storage Survives restart?
Working context Current in-process state mcp-agent Context No
Session trace What happened this run (auditable) Dakera session Yes
Project memory Durable, promoted, typed facts Dakera long-term index Yes

Files

examples/usecases/persistent_memory_agent/
├── mcp_agent.config.yaml   # MCP config; Dakera stdio server via npx @dakera-ai/dakera-mcp
├── main.py                 # Three-phase demo (see below)
└── README.md               # Contract, tiers, setup, per-phase tool usage

How it works

Phase 1 — Research & promote. The agent loads prior accepted facts, researches the gap, and stores only promotable, typed records under the contract.

Phase 2 — Scope-filtered recall. A fresh agent (simulating a restart) uses dakera_batch_recall filtered by project:… + status:accepted, so it pulls back only durable facts for this project — the scope-based retrieval from #12.

Phase 3 — Entity / knowledge-graph memory. dakera_extract_entities + dakera_knowledge_graph turn the stored prose into a queryable entity graph — the graph/entity tier from #12.

Re-run the script to see continuity: Phase 1 recalls what a prior run stored before adding anything new.

Dakera MCP config

mcp:
  servers:
    dakera:
      transport: stdio
      command: "npx"
      args: ["-y", "@dakera-ai/dakera-mcp"]
      env:
        DAKERA_API_URL: "${DAKERA_API_URL}"
        DAKERA_API_KEY: "${DAKERA_API_KEY}"

Self-host: docker run -d -p 3300:3300 -e DAKERA_API_KEY=demo ghcr.io/dakera-ai/dakera:latest

Testing notes

  • python -m py_compile main.py — clean.
  • ruff check and ruff format --check — pass.
  • Docstring coverage: 100% (module + all four functions).
  • All referenced tools (dakera_store, dakera_batch_recall, dakera_extract_entities, dakera_knowledge_graph, session tools) are exposed by the @dakera-ai/dakera-mcp server.

Checklist

  • New example is self-contained under examples/usecases/
  • README with prerequisites, run steps, and tool usage
  • Config uses the supported launcher (npx @dakera-ai/dakera-mcp) and correct image/port
  • Lint + format clean; 100% docstring coverage
  • Addresses the graph/entity + scope-based retrieval capabilities from Long-term memory support #12

Summary by CodeRabbit

  • New Features
    • Added a “Persistent Memory Agent” example demonstrating cross-session memory across separate runs.
    • Introduced a three-phase workflow (research, scope-filtered recall after restart, and knowledge graph exploration).
    • Added an MCP agent configuration to enable memory storage and recall, including timestamped logging.
  • Documentation
    • Expanded the example README with prerequisites, environment variables, first/second run instructions, and a clear summary of the agent’s MCP tools and memory contract.

@coderabbitai

coderabbitai Bot commented Jul 1, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

This PR adds a Persistent Memory Agent example for cross-session recall, scope-filtered retrieval, and entity graph lookup through Dakera via MCP. It includes runnable phases, MCP configuration, and usage documentation.

Changes

Persistent Memory Agent Example

Layer / File(s) Summary
MCP agent config and server wiring
examples/usecases/persistent_memory_agent/mcp_agent.config.yaml
Adds YAML config for the asyncio execution engine, console/file logging, MCP server definitions for dakera and fetch, and the default Anthropic model.
Module contract and scope wiring
examples/usecases/persistent_memory_agent/main.py
Adds the module docstring, MCP app initialization, project scope constant, and the memory contract string that defines tagging and promotability rules.
Research phase and promotion
examples/usecases/persistent_memory_agent/main.py
Expands research_phase() to use a Dakera and fetch agent for research, then stores contract-compliant typed records and prints what was persisted.
Recall phase and entrypoint
examples/usecases/persistent_memory_agent/main.py
Updates recall_phase() to use scope-filtered dakera_batch_recall on accepted facts and keeps main() running the phases under the __main__ guard.
Knowledge graph phase
examples/usecases/persistent_memory_agent/main.py
Adds knowledge_graph_phase() to recall accepted scoped facts, extract entities from recalled records, and query dakera_knowledge_graph for relationships.
README documentation
examples/usecases/persistent_memory_agent/README.md
Documents the memory tiers, prerequisites for running Dakera locally, run instructions, available MCP tools, and config notes for the example.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Possibly related issues

Suggested reviewers: saqadri

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 66.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed The PR covers short-term, long-term, and graph/entity memory, plus scope-filtered retrieval, which aligns with issue #12.
Out of Scope Changes check ✅ Passed The changes stay focused on the persistent memory example, its config, and documentation with no unrelated additions.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly matches the main change: adding the persistent_memory_agent example for cross-session memory via Dakera MCP.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@examples/usecases/persistent_memory_agent/main.py`:
- Around line 8-11: The Dakera prereq command in the persistent_memory_agent
example uses the wrong container image and port, so update the setup under the
Prereq section to match the self-hosted deployment convention. Change the docker
run invocation to use the correct `ghcr.io/dakera-ai/dakera:latest` image and
the expected `3300` port mapping so the example starts the intended service.

In `@examples/usecases/persistent_memory_agent/mcp_agent.config.yaml`:
- Around line 15-18: The Dakera MCP example is using an unsupported launcher, so
update the dakera config block to use a valid installation path instead of the
current uvx-based command. In the mcp_agent.config.yaml snippet under the dakera
service, replace the launcher command with an official option such as the npx
`@dakera-ai/dakera-mcp` package or the cargo binary, keeping the rest of the
transport config unchanged so the server can start correctly.

In `@examples/usecases/persistent_memory_agent/README.md`:
- Around line 44-56: The tool count in the persistent_memory_agent README is
inconsistent with the list shown. Update the sentence before the table or expand
the table so the count matches the actual entries; use the surrounding Dakera
tool section to keep the wording aligned with the tool names like dakera_store,
dakera_recall, and dakera_consolidate.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: a6e531db-c2d0-4aa7-bb68-a3b9379b2aa8

📥 Commits

Reviewing files that changed from the base of the PR and between f62d849 and 2b445ed.

📒 Files selected for processing (3)
  • examples/usecases/persistent_memory_agent/README.md
  • examples/usecases/persistent_memory_agent/main.py
  • examples/usecases/persistent_memory_agent/mcp_agent.config.yaml

Comment thread examples/usecases/persistent_memory_agent/main.py Outdated
Comment thread examples/usecases/persistent_memory_agent/mcp_agent.config.yaml Outdated
Comment thread examples/usecases/persistent_memory_agent/README.md Outdated
@coderabbitai

coderabbitai Bot commented Jul 1, 2026

Copy link
Copy Markdown

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🤖 web_search executed:

Dakera AI docker image dakera/dakera Docker Hub official image port 3000

Length of output: 12349


🤖 web_search executed:

dakera-mcp PyPI package uvx dakera-mcp

Length of output: 13182


@ferhimedamine, thanks for the detailed context! I dug into Dakera's public documentation to double-check before withdrawing those two points, and I'm seeing some inconsistencies worth flagging:

Docker image and port — Dakera's official Quick Start and Deployment guides (dakera.ai/docs/quickstart, dakera.ai/docs/deployment) both consistently show:

docker run -d --name dakera -p 3300:3300 \
  -e DAKERA_ROOT_API_KEY=your-secret \
  ghcr.io/dakera-ai/dakera:latest

using ghcr.io/dakera-ai/dakera:latest on port 3300, not dakera/dakera:latest on port 3000. The Docker Compose reference and cloud deployment snippets (AWS/GCP/Azure/Fly.io) on the same docs site also standardize on ghcr.io/dakera-ai/dakera:latest and port 3300. I did find one outlier — the Dakera-AI/dakera-py Python SDK README shows a -p 3000:3000 example — but that's a client SDK repo, not the server deployment docs, so it may just be stale/inconsistent with the current docs.

MCP launcher — Across five independent sources (Dakera's own dakera.ai/docs/mcp-server, the dakera-mcp Glama listing, crates.io, and GitHub releases), dakera-mcp is distributed exclusively via cargo install dakera-mcp, npm install -g @dakera-ai/dakera-mcp / `npx `@dakera-ai/dakera-mcp, Homebrew (brew install dakera-ai/tap/dakera-mcp), and Docker (ghcr.io/dakera-ai/dakera-mcp). None of these sources mention a PyPI package, and uvx specifically resolves packages from PyPI — I couldn't find any indication that dakera-mcp is published there. If uvx dakera-mcp isn't backed by a real PyPI package, it would fail at runtime for anyone following the README.

Since you may have closer/insider knowledge of the project (I noticed your GitHub handle associated with Dakera's releases), could you confirm whether there's a PyPI package and a dakera/dakera:latest Docker Hub image that simply isn't reflected in the current public docs yet? If so, a quick link would help resolve the discrepancy; otherwise, it'd be safer to align the README with what's currently documented (ghcr.io/dakera-ai/dakera:latest on port 3300, and npx @dakera-ai/dakera-mcp`` or cargo install dakera-mcp for the MCP server).

Looking forward to your tool-count verification, and no concerns on narrowing the PR scope to core persistent memory — graph/entity memory and scope-based retrieval as follow-up work sounds reasonable.

uvx is the Python package runner (uv's equivalent of pipx) and expects a PyPI
package. dakera-mcp is published as an npm package (@dakera-ai/dakera-mcp),
so the correct launcher is npx. Updated mcp_agent.config.yaml and both
prose references in main.py and README.md.
…b/3000)

The correct self-hosting command is the GitHub Container Registry image on
port 3300, as documented at dakera.ai. Updated all three files.
@ferhimedamine

Copy link
Copy Markdown
Author

Fixed. CodeRabbit was correct — updated to ghcr.io/dakera-ai/dakera:latest on port 3300 in all three files. Removed the incorrect earlier comment defending the Docker Hub image.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
@ferhimedamine

Copy link
Copy Markdown
Author

Thanks for independently researching this — your conclusions match our corrections:

Docker image and port: Our PR already uses ghcr.io/dakera-ai/dakera:latest on port 3300 throughout the README and config (pushed in commit b05edae). The earlier dakera/dakera:latest + port 3000 were wrong and have been removed.

MCP launcher: The README already says npx @dakera-ai/dakera-mcp. There is no uvx dakera-mcpdakera-mcp is NOT on PyPI. The @dakera-ai/dakera-mcp npm package is the only supported launcher. That was corrected as well.

Tool count: The table in the README lists 14 MCP tools that the Dakera MCP server exposes.

Both issues have been resolved in the current head of this PR. Your research confirms the corrections are accurate.

@ferhimedamine

Copy link
Copy Markdown
Author

Fixed the tool count issue (CodeRabbit review): the README now says 'Key Dakera tools exposed via MCP:' and lists all 14 tools in the table. The ambiguous 'access to 14 Dakera tools' line was removed to eliminate any mismatch.

Address issue lastmile-ai#713 review (hegu-1) and CodeRabbit pre-merge warnings:
- Model memory as a project-continuity layer, not a transcript dump: the
  agent only PROMOTES typed, scoped records (type:/project:/status: tags)
  via an explicit MEMORY CONTRACT injected into each agent.
- Add scope-filtered retrieval (dakera_batch_recall by project + status)
  and an entity/knowledge-graph phase (dakera_extract_entities +
  dakera_knowledge_graph) — the two lastmile-ai#12 capabilities the earlier draft
  omitted (fixes CodeRabbit 'Linked Issues' warning).
- Docstring every function (main() included) -> 100% coverage (fixes
  CodeRabbit 'Docstring Coverage' warning).
- README documents the contract, tiers, and per-phase tool usage.
@ferhimedamine

Copy link
Copy Markdown
Author

Pushed e0e9e49 — reworks the example from a flat store/recall loop into an explicit memory contract, addressing both pre-merge warnings and the design feedback in #713.

What changed

  • Typed, promoted memory (not a transcript dump). Each agent is given an explicit contract and only promotes durable facts, tagged so they can be scoped later:
    type:<decision|convention|failed_path|bug_fix|open_question>
    project:<scope>
    status:<accepted|candidate|superseded>
    
    accepted facts are stored at importance 0.8–0.9 so they survive decay.
  • Scope-filtered retrieval (fixes the Linked Issues warning) — Phase 2 uses dakera_batch_recall filtered by project:… + status:accepted, so a fresh (restarted) agent pulls back only durable facts for this project instead of a semantic soup.
  • Entity / knowledge-graph memory (fixes the Linked Issues warning) — Phase 3 adds dakera_extract_entities + dakera_knowledge_graph, covering the graph/entity tier from Long-term memory support #12.
  • Docstring coverage → 100% (fixes the Docstring Coverage warning) — every function, including main(), is now documented. ruff check and ruff format --check pass clean.

README documents the contract, the three tiers, and per-phase tool usage.

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 3, 2026

Copy link
Copy Markdown

@ferhimedamine Thanks for the detailed writeup on the rework — the memory contract with typed/promoted facts, scope-filtered dakera_batch_recall, and the added entity/knowledge-graph tier sound like solid responses to the design feedback in #713. Kicking off a fresh review of the updated code now.

✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (2)
examples/usecases/persistent_memory_agent/main.py (2)

57-75: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Scope/status tag filter is duplicated across three phases instead of a shared constant.

The ["{PROJECT_SCOPE}", "status:accepted"] filter pattern is hardcoded three separate times: once inside MEMORY_CONTRACT (Line 62), once inline in recall_phase()'s instruction (Lines 118-119), and once inline in knowledge_graph_phase()'s instruction (Line 148). If the tag scheme ever changes (e.g., an additional required tag), all three call sites need to be updated in lockstep.

♻️ Suggested consolidation
+ACCEPTED_FACTS_TAGS = f'["{PROJECT_SCOPE}", "status:accepted"]'
+
 MEMORY_CONTRACT = f"""\
 ...
 1. At the start, call dakera_session_start, then dakera_batch_recall with
-   tags ["{PROJECT_SCOPE}", "status:accepted"] to load what is already known.
+   tags {ACCEPTED_FACTS_TAGS} to load what is already known.
 ...
 """

Then reference ACCEPTED_FACTS_TAGS in recall_phase() and knowledge_graph_phase() instructions as well.

Also applies to: 116-122, 146-152

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@examples/usecases/persistent_memory_agent/main.py` around lines 57 - 75, The
scope/status filter is duplicated in the memory contract and in both phase
instructions, so centralize it into a shared constant such as
ACCEPTED_FACTS_TAGS. Update MEMORY_CONTRACT, recall_phase(), and
knowledge_graph_phase() to reference that constant instead of hardcoding
["{PROJECT_SCOPE}", "status:accepted"], so any future tag changes only need one
update.

135-162: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

No guidance for the case where no accepted facts exist yet.

Unlike recall_phase() (Lines 120-122), which explicitly tells the agent to flag missing facts as open questions rather than inventing them, knowledge_graph_phase()'s instruction has no equivalent fallback if dakera_batch_recall returns nothing (e.g., first-ever run, or run out of order). The agent could hallucinate entities/relationships instead of reporting "no data yet."

✏️ Suggested addition
 1. Call dakera_batch_recall with tags ["{PROJECT_SCOPE}", "status:accepted"].
+   If no facts are returned, report that there is nothing to graph yet rather
+   than inventing entities.
 2. For each recalled fact, call dakera_extract_entities to pull out frameworks,
    organizations, and architectural concepts as graph entities.
 3. Call dakera_knowledge_graph to inspect how those entities relate.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@examples/usecases/persistent_memory_agent/main.py` around lines 135 - 162,
The Agent instruction in knowledge_graph_phase() lacks a fallback when
dakera_batch_recall returns no accepted facts, so add an explicit step telling
graph_explorer to report that no entity/relationship data is available yet and
not to invent entities. Update the instruction text near the
dakera_batch_recall, dakera_extract_entities, and dakera_knowledge_graph steps
so the agent first checks for empty recall results and, if none are found,
summarizes the absence of knowledge graph data as an open question instead of
proceeding with assumptions.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@examples/usecases/persistent_memory_agent/main.py`:
- Around line 57-75: The scope/status filter is duplicated in the memory
contract and in both phase instructions, so centralize it into a shared constant
such as ACCEPTED_FACTS_TAGS. Update MEMORY_CONTRACT, recall_phase(), and
knowledge_graph_phase() to reference that constant instead of hardcoding
["{PROJECT_SCOPE}", "status:accepted"], so any future tag changes only need one
update.
- Around line 135-162: The Agent instruction in knowledge_graph_phase() lacks a
fallback when dakera_batch_recall returns no accepted facts, so add an explicit
step telling graph_explorer to report that no entity/relationship data is
available yet and not to invent entities. Update the instruction text near the
dakera_batch_recall, dakera_extract_entities, and dakera_knowledge_graph steps
so the agent first checks for empty recall results and, if none are found,
summarizes the absence of knowledge graph data as an open question instead of
proceeding with assumptions.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 89e7304f-fd36-4c94-8cf8-c2007c81124d

📥 Commits

Reviewing files that changed from the base of the PR and between 8701ae4 and e0e9e49.

📒 Files selected for processing (2)
  • examples/usecases/persistent_memory_agent/README.md
  • examples/usecases/persistent_memory_agent/main.py
✅ Files skipped from review due to trivial changes (1)
  • examples/usecases/persistent_memory_agent/README.md

The prereqs used port 3300 and a bare `docker run ghcr.io/dakera-ai/dakera:latest`.
Per Dakera's actual setup the REST API listens on port 3000, and the server
requires the MinIO object store provisioned by the dakera-ai/dakera-deploy
docker-compose stack — a standalone `docker run` won't work. Point the example
at dakera-deploy and use http://localhost:3000 throughout (README + main.py).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Long-term memory support

1 participant