Nick Duong · Architecture notes
← ReliBot & MyClawBot: full notes

Cheatsheet · Agent memory architecture

ReliBot & MyClawBot

The whole system on one page: numbers, flows, design decisions and lessons. Hover or tap an underlined acronym for its meaning.

Full notes with diagrams →
80+tools · 16 domains
12sub-agents
3-layerauto-recall
2,200+tests

Engineers
CLI · Teams · Web chat

Bridge
auth · rate limit · injection scan

Root agent (Google ADK)
Gemini Pro → Flash fallback

80+ tools / 16 domains
GitHub · Splunk · Dynatrace · Scanner

Memory
SQLite: notes · vectors · graph · conversations

Heartbeat (scheduled)
nightly maintenance + memory flush

In one breath

  • ReliBot (The Hartford): incident-response agent for SRE and dev teams. Asked in Teams, CLI or web chat, it pulls Splunk logs, Dynatrace problems, recent GitHub deploys and scanner results, and answers with evidence.
  • MyClawBot (personal): 12 bot instances on Telegram and WhatsApp, built on the Claude Agent SDK. The proving ground for the same memory, bridge and security patterns.
  • What sets them apart: long-term memory that recalls past fixes, and a fail-closed tool policy on every call.

Key numbers

Tools 80+ in 16 domains (ReliBot) · 228 MCP tools (MyClawBot)
Sub-agents 12, called as AgentTool
Tests 2,200+ (ReliBot) · ~3,500 (MyClawBot, gate < 60 s)
Embeddings 384-d, local (bge-small / MiniLM)
Session rotation 50K tokens or 50 messages
Tool limits 30 s timeout · max 5 calls per tool per agent
Retry backoff 1 / 3 / 8 s · breaker opens after 2 failures
Conversation TTL 90 days

Request path

  1. Channel: Teams (Bot Framework JWT/JWKS + tenant check), CLI, web chat (WebSocket).
  2. Bridge: per-user rate limit → input injection scan → session rotation.
  3. ADK callbacks around the root agent:
    • before_model_callback: inject L1/L2/L3 recall
    • before_tool_callback: fail-closed allowlist + call cap
    • after_tool_callback: scan tool output for injection
  4. Models: Vertex AI Gemini 3 Pro → 3 Flash → 3.1 Flash → 3.1 Flash-Lite.
  5. Post-turn: log the conversation, auto-save incident fixes and insights.

Recall layers (ReliBot)

Layer Holds Budget
L1 Core MEMORY.md: preferences, standing decisions ≤ 8K chars, always on
L2 Recent this chat, by chat_key 4 days, ≤ 20K chars
L3 Entity services or people mentioned ≤ 6 entities, ≤ 9.6K chars
Search everything else on demand (search_notes)
  • L1–L3 are pure SQL, no LLM call, injected on the first model call only.
  • L3 matching is a substring / word-boundary check against a cached top-1K entity list: microseconds.

Ranking formulas

  • Hybrid: 0.7 · vector + 0.3 · BM25
  • Graph mode: 0.5 · hybrid + 0.3 · graph + 0.2 · entity
  • MyClawBot adds: × recency decay (30-day half-life) → MMR re-rank (λ = 0.7) over a 3× over-fetch.
  • Caching the embedding matrix took search from 4.6 s to 58 ms.

Write path

  • During the turn: the model calls save_memory.
  • Post-turn auto-save: ≥ 2 incident tools used and nothing saved → incident-resolutions/.
  • Insights: answer ≥ 500 chars or > 2 turns → insights/.
  • Heartbeat flush (idle 15 min or nightly): Gemini Flash pass 1 → Context · Decisions · Lessons · Actions; pass 2 → facts, SHA-1 dedup.
  • Indexing on save: chunk 1,500 chars → local ONNX embedding + FTS5 triggers + regex entities (sync) + LLM entity extraction on an asyncio.Queue (async).

Storage

  • ReliBot: one SQLite file in WAL mode: notes + FTS5, chunks + sqlite-vec, entities / relationships, conversations + FTS5, incidents.
  • MyClawBot: a markdown vault is the source of truth (git-trackable). The SQLite index is rebuildable, and unchanged files are skipped by content hash.
  • Why SQLite: each instance is team-scoped (~2.5K chunks, ~500 notes). Millisecond queries, zero ops, one file to back up.

Guardrails

  • Fail-closed tool allowlist, enforced in code.
  • Call caps in code: Gemini sub-agents called search_notes 15+ times and prompt instructions didn't stop them.
  • Memory is an injection persistence vector: text saved once is replayed every turn. So MEMORY.md is sanitized, tool output is scanned, and compression input is fenced as quoted data.
  • Isolation: one subprocess and data directory per team instance, run by a manager process.

Reliability

  • Failover chain with retry backoff 1 / 3 / 8 s.
  • Circuit breaker opens after 2 failures, so a bad model is skipped instead of paying its timeout on every request.
  • Replies served by a fallback are tagged [fallback: model].
  • Bounded growth: session rotation (summary written first), 90-day TTL, SHA-1 fact dedup.

ReliBot vs MyClawBot

ReliBot MyClawBot
Runtime ADK + Gemini Claude Agent SDK, Haiku / Sonnet / Opus router
Source of truth SQLite notes markdown vault
Recall injected into system_instruction the user message
Tools ADK function tools in-process MCP @tool
Idle flush Gemini Flash, 15 min Claude, 10 min

Prompt-caching rule

  • Caching is prefix-based: per-turn recall in the system prompt breaks the cache every turn.
  • MyClawBot keys the system prompt on file mtimes and puts dynamic context in the user turn.
  • Volatile content goes last.

Design decisions → at scale

Today Why At scale
SQLite + sqlite-vec team-scoped, zero ops Postgres + pgvector, tenant_id + row-level security
Keep BM25 next to vectors rare tokens: FTS5 38/40 recall@10 vs hybrid 0–2/40 reciprocal-rank fusion or exact-match boost
Deterministic recall + on-demand search injecting everything costs too much; models forget to call memory tools tune layer budgets per tenant
Caps in code, not prompt prompts didn't stop runaway calls same

Token-metering lessons

  1. Cumulative counter summed as a delta. On resumed sessions the SDK's total_cost_usd is a running total. Recorded $561.97 vs $144.12 actual: 3.9× (7× on one bot).
  2. Cached tokens. input_tokens excludes cache reads and writes, so rotation saw 2–8 tokens while real context passed 300K.

Rules for a metering pipeline:

  • idempotent usage events (event_id), idempotency keys on billing calls
  • know counter vs gauge vs delta for every provider field
  • price input, output, cache-write and cache-read separately
  • late or duplicate events: dedup by event_id, close periods with a grace window
  • reconcile three ways: usage store vs billing system vs provider invoice

Testing

  • LLM mocked at the boundary.
  • Storage tests use a real SQLite in tmp_path: mocking the database hides the bugs that matter.
  • Deterministic fast gate on every change, full suite before merge.

Acronyms on this sheet

Underlined acronyms show these definitions on hover or tap. Full glossary →

ADK
Agent Development Kit Google's open-source framework for building agents and multi-agent systems. ReliBot and GitHubScanner run on it.
BM25
Best Match 25 The classic keyword-ranking formula behind SQLite FTS5. Strong on exact tokens such as error codes and CVE IDs.
CLI
Command-Line Interface A terminal tool.
FTS5
Full-Text Search, version 5 SQLite's built-in full-text index.
JWKS
JSON Web Key Set The published public keys used to verify JWT signatures, such as Microsoft's keys for Teams bots.
JWT
JSON Web Token A signed token that carries identity claims.
LLM
Large Language Model A model such as Gemini or Claude that reads and writes text. Every agent here is an LLM plus tools.
MCP
Model Context Protocol An open standard for exposing tools and data to LLM agents. MyClawBot's tools are in-process MCP tools.
MMR
Maximal Marginal Relevance A re-ranking step that trades relevance against diversity, so the top results aren't near-duplicates.
ONNX
Open Neural Network Exchange A portable model format. ReliBot runs its embedding model locally through ONNX, with no API call.
SDK
Software Development Kit A library for building on a platform, such as the Claude Agent SDK.
SHA-1
Secure Hash Algorithm 1 A 160-bit hash. Used here for deduplication and snippet-integrity checks, not for security.
SQL
Structured Query Language The standard language for querying relational databases.
SRE
Site Reliability Engineering The discipline (and teams) that keep production running. ReliBot's main users.
TTL
Time To Live How long a record or cache entry is kept before it expires.
WAL
Write-Ahead Logging A SQLite journal mode where readers don't block the writer.

More quick reference