Skip to content

tetratorus/dory

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

27 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Dory

Minimal agent with forgetting (500 lines).

Agents today are bad at forgetting, and worse at remembering that they forgot. Forgetting is a good thing actually. Evolution didn't select for photographic memory in humans for a reason: when you're planning a trip to Japan and you're picking a seat on the flight, you don't need sushi restaurants in your head — and later, when you're planning meals, you can rehydrate that context on demand. Dory does the same: old context gets compacted — details fade but the gist remains, and the original is always one UNCOMPACT away.

agent.py             — the loop
SOUL.md              — purpose + tool format (immutable)
SEGMENT.md           — chunking instructions for compactor (agent-editable)
SUMMARISE.md         — summarisation instructions for compactor (agent-editable)
blobs/               — content-addressed blob store (shared)
chats/<id>.md        — chat rooms (one file per room, append-only)
agents/<self>/       — per-identity dir; <self> = hash(SOUL + boot ts)
  ├ LIFE.md          — append-only event log (every turn, every tool)
  ├ MEMORY.md        — agent-editable working notes (capped at 4000ch)
  ├ context.md       — last live context (resume point)
  └ subs/            — chat subscriptions (symlinks into chats/)

Design

  • SOUL / LIFE / MEMORY. SOUL is the agent's purpose (immutable). LIFE is the append-only event log, per-identity; the last 50 lines are injected each turn. MEMORY is the agent's own scratchpad — learned preferences and notes it edits with EDIT_FILE.

  • SEGMENT / SUMMARISE. Instructions for the compactor, stashed as blob refs in the agent's prompt (so they don't bloat it). The agent can UNCOMPACT or READ_FILE them, and even edit them to tune its own compaction strategy.

  • Prompt order. Each turn: `SOUL + segment_ref + summarise_ref

    • harness + context + MEMORY + LIFE`. Stable prefix first, append-only context next, volatile tail last — optimised for LLM prompt caching.
  • Blobs are the substrate. Every LLM response and every tool result is stashed as a blob under blobs/; the ref is appended to LIFE.md. Blobs are content-addressed (filename = sha256 prefix of the blob) and have a tiny frontmatter: at, gist, parent. Live memory holds content inline for free access; older content may only appear as a ◱hash=... gist=...◲ reference after compaction.

  • Chunked compaction. When context exceeds the limit, compaction runs in the background. Phase 1: an LLM call identifies conceptual chunks in the first 2/3 of context. Phase 2: each chunk is compacted in parallel — the compactor sees the full agent state (soul, harness, memory, life) and returns a summary, a relevance score (1–10), and a gist. Chunks are swapped in from least to most relevant until eligible content is compressed to 50%. The recent 1/3 of context is never touched. Originals are stashed as blobs with LLM-generated gists; UNCOMPACT rehydrates any ref inline.

  • Graceful forgetting. Compaction is semantic, not truncation. Old turns lose resolution but stay reachable via blob refs. The agent can UNCOMPACT any ref to pull it back into context.

  • Chat = file. Each room is chats/<id>.md, append-only. Lines are tagged [USER:name ts] or [AGENT:name ts]. Agents subscribe by symlinking files into agents/<name>/; the harness tails that dir each turn. SAY <id> <msg> appends to the right file. External transports (Telegram, stdin, etc.) are independent bridge processes that read/write the same files — the agent doesn't know or care.

Running

pip install -r requirements.txt
export DEEPSEEK_API_KEY=...
python agent.py            # fresh boot: new identity
python agent.py <hash>     # resume an existing agent by its <self> hash

Defaults to deepseek-v4-pro via api.deepseek.com. Reasoning content is captured and prepended to context inside <reasoning> tags. Pass --verbose to print LIFE events to stdout.

Each fresh boot generates a new content-addressed identity: the agent stashes a blob of its SOUL plus boot timestamp, and uses that hash as its name. Two agents with the same SOUL booted at different times get distinct identities; their state lives under agents/<self>/.

Edit SOUL.md to give the agent a purpose before running. Talk to a running agent with python chat.py [room_id] (defaults to room dev). Wipe runtime state with python clean.py.

About

A forgetful agent

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages