{"slug": "mnemo-a-local-first-learning-agent-that-remembers-without-leaking", "title": "mnemo — A local-first learning agent that remembers, without leaking", "summary": "A developer built **mnemo**, a local-first learning-management agent that tracks topics, sequences them by prerequisites, and schedules spaced-repetition reviews using the SM-2 algorithm, all running entirely on-device through a Hermes-3 agent orchestration loop. The system addresses privacy and safety concerns in AI tutoring by storing all data locally in SQLite and implementing a two-gate approval system in the schema—resources start with `approved = 0` and are further filtered through a `source_allowlist` table at the SQL layer—preventing unverified content from reaching young learners. The agent exposes seven MCP tools for Hermes-3 to drive adaptive study sessions, with 69 passing tests covering the SM-2 algorithm, integration, API endpoints, and safety scenarios.", "body_md": "*This is a submission for the Hermes Agent Challenge: Build With Hermes Agent*\n\n**mnemo** is a learning-management agent for kids and self-directed learners. It tracks topics, sequences them by prerequisites, schedules spaced-repetition reviews (SM-2), generates quizzes, and adapts the plan over time through a Hermes-driven feedback loop.\n\nThe problem: hosted AI tutors send a child's mistakes, struggles, and reading level to someone else's servers. Browser-only chat loops forget everything the moment the tab closes — no cross-session adaptation, no long-term plan. And LLMs happily hallucinate URLs and quiz answers, so nothing stops an unreviewed link from reaching a 6th-grader.\n\nmnemo's answer is a three-part stance baked into the schema, not bolted on:\n\n`events`\n\ntable.`resource`\n\nstarts `approved = 0`\n\nin the schema. A second gate joins a `source_allowlist`\n\ntable at the SQL layer — even an approved resource is suppressed if its origin isn't a trusted source (`khan-academy`\n\n, `ck12`\n\n, internal-curriculum). Both gates have dedicated scenario tests.Pitch deck (single-file Reveal.js, no build step):\n\n** pitch.html** — 6 slides covering the problem, the Hermes differentiator, architecture, the two-gate safety model, and a realistic dashboard mockup of the shipped UI.\n\nQuick local run:\n\n```\npython3 -m venv .venv && source .venv/bin/activate\npip install -r requirements.txt\nsqlite3 learning.db < migrations/001_init.sql\nsqlite3 learning.db < migrations/002_quiz_bank.sql\nsqlite3 learning.db < migrations/003_source_allowlist.sql\npython -m uvicorn webapp.app:app --reload --port 8000\n```\n\nOpen `http://localhost:8000`\n\nfor the dashboard, or run a Hermes session end-to-end:\n\n```\nollama pull hermes3\ncurl -X POST http://localhost:8000/api/agent/session \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"learner_id\": 1, \"backend\": \"ollama\"}'\n```\n\nThe dashboard streams each tool call the agent makes, then refreshes progress and the review queue automatically.\n\nRepository: [gitea.com/rohithtp/mnemo](https://gitea.com/rohithtp/mnemo)*(replace with actual URL on publish)*\n\nKey directories:\n\n`webapp/`\n\n— FastAPI REST + SSE + static dashboard (25+ endpoints)`mcp_server/`\n\n— stdio MCP server exposing 7 tools to Hermes`src/agent/`\n\n— Hermes session orchestrator, allowlist, memory, insights`src/learning/`\n\n— SM-2 algorithm, sequencing logic, safety/privacy gates`migrations/`\n\n— 3 SQL files, schema-version tracked`tests/`\n\n— 69 passing tests (SM-2 unit + integration + API + scenario)`docs/`\n\n— full build plan, decisions log, 25-risk register, two-profile docs| Layer | Choice |\n|---|---|\n| Database |\nSQLite (WAL mode) on-device · libSQL/Turso documented as forward path |\n| Backend |\nFastAPI + uvicorn + SSE streaming |\n| Agent runtime |\nHermes-3 via Ollama (local) · HuggingFace Inference API fallback |\n| Tool surface |\nstdio MCP (Hermes) + WebMCP progressive enhancement (Chrome 146+) |\n| Frontend | Vanilla JS + plain CSS — no framework, no build step |\n| Spaced repetition |\nSM-2 algorithm, implemented as pure functions |\n| Tests |\npytest — 15 SM-2 unit + 18 integration + 31 API + 5 scenario = 69 passing\n|\n| Deployment |\nDockerfile + systemd unit |\n| Language | Python 3.11+ (3.14 tested) |\n\nAll Python deps are pinned in `requirements.txt`\n\n. The Hermes-3 model digest is pinned by SHA256 and asserted at startup so a silent upstream change can't drift the agent's behaviour mid-session (R02 mitigation).\n\nHermes-3 is the orchestration brain for the whole adaptive loop. The reason it had to be Hermes specifically, not a stateless prompt loop, comes down to four capabilities:\n\n**1. Tool-calling over a real schema.** `src/agent/session.py`\n\nruns a tool-calling loop where Hermes drives a study session by calling the 7 MCP tools (`get_progress_summary`\n\n, `get_ready_topics`\n\n, `start_topic`\n\n, `get_next_review_items`\n\n, `record_review_result`\n\n, `recommend_resources`\n\n, `generate_quiz`\n\n). Each call is logged to the `events`\n\ntable with arguments and result. The agent isn't generating advice in prose — it's writing to SQLite through audited tools.\n\n**2. Cross-session memory.** Every session writes a `memory_note`\n\nevent summarising patterns the agent noticed. The next session's system prompt is injected with the most recent notes for that learner, so Hermes \"remembers\" that this kid struggles with unlike denominators across sessions, not just within one tab. Memory notes are per-learner and stay on-device.\n\n**3. Pattern analysis + human-approvable proposals.** Phase 6 added `POST /api/agent/insights/{learner_id}`\n\n. Hermes reads the event trail across a configurable window, computes `avg_quality`\n\n, `days_active`\n\n, resource-type variety, and emits structured proposals like `{\"type\": \"pace\", \"direction\": \"increase\", \"reason\": \"avg quality above 4.0\"}`\n\n. Each proposal is stored with `resolved_at = NULL`\n\nand surfaces in the dashboard with Approve / Dismiss buttons. The agent never silently applies pace changes — a human resolves each proposal.\n\n**4. Allowlist-enforced tool boundary.** `src/agent/allowlist.py`\n\ndefines `AGENT_ALLOWLIST`\n\nand `AGENT_DENY_LIST`\n\nas constants. `set_learner_preferences`\n\nis on the deny-list, so even if the model is jailbroken into trying to mutate preferences directly, the session orchestrator raises `PermissionError`\n\nbefore the tool runs. This is the difference between \"the UI hides it\" and \"the agent cannot do it.\"\n\nThe combination — local model + persistent memory + audited tool calls + human-in-the-loop on every state-mutating proposal — is what makes mnemo trustable as a coach for a child, not just for an adult tinkerer. That posture is what the Hermes agent makes practical; a hosted stateless chatbot can't enforce any of it.", "url": "https://wpnews.pro/news/mnemo-a-local-first-learning-agent-that-remembers-without-leaking", "canonical_source": "https://dev.to/rohithtp/mnemo-a-local-first-learning-agent-that-remembers-without-leaking-d49", "published_at": "2026-05-31 09:20:10+00:00", "updated_at": "2026-05-31 09:42:18.655340+00:00", "lang": "en", "topics": ["ai-agents", "ai-safety", "ai-products", "ai-tools", "large-language-models"], "entities": ["Hermes Agent", "mnemo", "Khan Academy", "CK-12"], "alternates": {"html": "https://wpnews.pro/news/mnemo-a-local-first-learning-agent-that-remembers-without-leaking", "markdown": "https://wpnews.pro/news/mnemo-a-local-first-learning-agent-that-remembers-without-leaking.md", "text": "https://wpnews.pro/news/mnemo-a-local-first-learning-agent-that-remembers-without-leaking.txt", "jsonld": "https://wpnews.pro/news/mnemo-a-local-first-learning-agent-that-remembers-without-leaking.jsonld"}}