docs: clarify memory design and source-vs-release features

This commit is contained in:
Xubin Ren 2026-04-04 09:34:37 +00:00
parent 408a61b0e1
commit a166fe8fc2
4 changed files with 220 additions and 159 deletions

View File

@ -117,7 +117,9 @@
- [Agent Social Network](#-agent-social-network)
- [Configuration](#-configuration)
- [Multiple Instances](#-multiple-instances)
- [Memory](#-memory)
- [CLI Reference](#-cli-reference)
- [In-Chat Commands](#-in-chat-commands)
- [Python SDK](#-python-sdk)
- [OpenAI-Compatible API](#-openai-compatible-api)
- [Docker](#-docker)
@ -151,7 +153,12 @@
## 📦 Install
**Install from source** (latest features, recommended for development)
> [!IMPORTANT]
> This README may describe features that are available first in the latest source code.
> If you want the newest features and experiments, install from source.
> If you want the most stable day-to-day experience, install from PyPI or with `uv`.
**Install from source** (latest features, experimental changes may land here first; recommended for development)
```bash
git clone https://github.com/HKUDS/nanobot.git
@ -159,13 +166,13 @@ cd nanobot
pip install -e .
```
**Install with [uv](https://github.com/astral-sh/uv)** (stable, fast)
**Install with [uv](https://github.com/astral-sh/uv)** (stable release, fast)
```bash
uv tool install nanobot-ai
```
**Install from PyPI** (stable)
**Install from PyPI** (stable release)
```bash
pip install nanobot-ai
@ -1561,6 +1568,18 @@ nanobot gateway --config ~/.nanobot-telegram/config.json --workspace /tmp/nanobo
- `--workspace` overrides the workspace defined in the config file
- Cron jobs and runtime media/state are derived from the config directory
## 🧠 Memory
nanobot uses a layered memory system designed to stay light in the moment and durable over
time.
- `memory/history.jsonl` stores append-only summarized history
- `SOUL.md`, `USER.md`, and `memory/MEMORY.md` store long-term knowledge managed by Dream
- `Dream` runs on a schedule and can also be triggered manually
- memory changes can be inspected and restored with built-in commands
If you want the full design, see [docs/MEMORY.md](docs/MEMORY.md).
## 💻 CLI Reference
| Command | Description |
@ -1583,6 +1602,23 @@ nanobot gateway --config ~/.nanobot-telegram/config.json --workspace /tmp/nanobo
Interactive mode exits: `exit`, `quit`, `/exit`, `/quit`, `:q`, or `Ctrl+D`.
## 💬 In-Chat Commands
These commands work inside chat channels and interactive agent sessions:
| Command | Description |
|---------|-------------|
| `/new` | Start a new conversation |
| `/stop` | Stop the current task |
| `/restart` | Restart the bot |
| `/status` | Show bot status |
| `/dream` | Run Dream memory consolidation now |
| `/dream-log` | Show the latest Dream memory change |
| `/dream-log <sha>` | Show a specific Dream memory change |
| `/dream-restore` | List recent Dream memory versions |
| `/dream-restore <sha>` | Restore memory to the state before a specific change |
| `/help` | Show available in-chat commands |
<details>
<summary><b>Heartbeat (Periodic Tasks)</b></summary>

View File

@ -1,156 +0,0 @@
# Dream: Two-Stage Memory Consolidation
Dream is nanobot's memory management system. It automatically extracts key information from conversations and persists it as structured knowledge files.
## Architecture
```
Consolidator (per-turn) Dream (cron-scheduled) GitStore (version control)
+----------------------------+ +----------------------------+ +---------------------------+
| token over budget → LLM | | Phase 1: analyze history | | dulwich-backed .git repo |
| summarize evicted messages |──────▶| vs existing memory files | | auto_commit on Dream run |
| → history.jsonl | | Phase 2: AgentRunner | | /dream-log: view changes |
| (plain text, no tool_call) | | + read_file/edit_file | | /dream-restore: rollback |
+----------------------------+ | → surgical incremental | +---------------------------+
| edit of memory files |
+----------------------------+
```
### Consolidator
Lightweight, triggered on-demand after each conversation turn. When a session's estimated prompt tokens exceed 50% of the context window, the Consolidator sends the oldest message slice to the LLM for summarization and appends the result to `history.jsonl`.
Key properties:
- Uses plain-text LLM calls (no `tool_choice`), compatible with all providers
- Cuts messages at user-turn boundaries to avoid truncating multi-turn conversations
- Up to 5 consolidation rounds until the token budget drops below the safety threshold
### Dream
Heavyweight, triggered by a cron schedule (default: every 2 hours). Two-phase processing:
| Phase | Description | LLM call |
|-------|-------------|----------|
| Phase 1 | Compare `history.jsonl` against existing memory files, output `[FILE] atomic fact` lines | Plain text, no tools |
| Phase 2 | Based on the analysis, use AgentRunner with `read_file` / `edit_file` for incremental edits | With filesystem tools |
Key properties:
- Incremental edits — never rewrites entire files
- Cursor always advances to prevent re-processing
- Phase 2 failure does not block cursor advancement (prevents infinite loops)
### GitStore
Pure-Python git implementation backed by [dulwich](https://github.com/jelmer/dulwich), providing version control for memory files.
- Auto-commits after each Dream run
- Auto-generated `.gitignore` that only tracks memory files
- Supports log viewing, diff comparison, and rollback
## Data Files
```
workspace/
├── SOUL.md # Bot personality and communication style (managed by Dream)
├── USER.md # User profile and preferences (managed by Dream)
└── memory/
├── MEMORY.md # Long-term facts and project context (managed by Dream)
├── history.jsonl # Consolidator summary output (append-only)
├── .cursor # Last message index processed by Consolidator
├── .dream_cursor # Last history.jsonl cursor processed by Dream
└── .git/ # GitStore repository
```
### history.jsonl Format
Each line is a JSON object:
```json
{"cursor": 42, "timestamp": "2026-04-03 00:02", "content": "- User prefers dark mode\n- Decided to use PostgreSQL"}
```
Searching history:
```bash
# Python (cross-platform)
python -c "import json; [print(json.loads(l).get('content','')) for l in open('memory/history.jsonl','r',encoding='utf-8') if l.strip() and 'keyword' in l.lower()][-20:]"
# grep
grep -i "keyword" memory/history.jsonl
```
### Compaction
When `history.jsonl` exceeds 1000 entries, it automatically drops entries that Dream has already processed (keeping only unprocessed entries).
## Configuration
Configure under `agents.defaults.dream` in `~/.nanobot/config.json`:
```json
{
"agents": {
"defaults": {
"dream": {
"cron": "0 */2 * * *",
"model": null,
"max_batch_size": 20,
"max_iterations": 10
}
}
}
}
```
| Field | Type | Default | Description |
|-------|------|---------|-------------|
| `cron` | string | `0 */2 * * *` | Cron expression for Dream run interval |
| `model` | string\|null | null | Optional model override for Dream |
| `max_batch_size` | int | 20 | Max history entries processed per run |
| `max_iterations` | int | 10 | Max tool calls in Phase 2 |
Dependency: `pip install dulwich`
## Commands
| Command | Description |
|---------|-------------|
| `/dream` | Manually trigger a Dream run |
| `/dream-log` | Show the latest Dream changes (git diff) |
| `/dream-log <sha>` | Show changes from a specific commit |
| `/dream-restore` | List the 10 most recent Dream commits |
| `/dream-restore <sha>` | Revert a specific commit (restore to its parent state) |
## Troubleshooting
### Dream produces no changes
Check whether `history.jsonl` has entries and whether `.dream_cursor` has caught up:
```bash
# Check recent history entries
tail -5 memory/history.jsonl
# Check Dream cursor
cat memory/.dream_cursor
# Compare: the last entry's cursor in history.jsonl should be > .dream_cursor
```
### Memory files contain inaccurate information
1. Use `/dream-log` to inspect what Dream changed
2. Use `/dream-restore <sha>` to roll back to a previous state
3. If the information is still wrong after rollback, manually edit the memory files — Dream will preserve your edits on the next run (it skips facts that already match)
### Git-related issues
```bash
# Check if GitStore is initialized
ls workspace/.git
# If missing, restart the gateway to auto-initialize
# View commit history manually (requires git)
cd workspace && git log --oneline
```

179
docs/MEMORY.md Normal file
View File

@ -0,0 +1,179 @@
# Memory in nanobot
> **Note:** This design is currently an experiment in the latest source code version and is planned to officially ship in `v0.1.5`.
nanobot's memory is built on a simple belief: memory should feel alive, but it should not feel chaotic.
Good memory is not a pile of notes. It is a quiet system of attention. It notices what is worth keeping, lets go of what no longer needs the spotlight, and turns lived experience into something calm, durable, and useful.
That is the shape of memory in nanobot.
## The Design
nanobot does not treat memory as one giant file.
It separates memory into layers, because different kinds of remembering deserve different tools:
- `session.messages` holds the living short-term conversation.
- `memory/history.jsonl` is the running archive of compressed past turns.
- `SOUL.md`, `USER.md`, and `memory/MEMORY.md` are the durable knowledge files.
- `GitStore` records how those durable files change over time.
This keeps the system light in the moment, but reflective over time.
## The Flow
Memory moves through nanobot in two stages.
### Stage 1: Consolidator
When a conversation grows large enough to pressure the context window, nanobot does not try to carry every old message forever.
Instead, the `Consolidator` summarizes the oldest safe slice of the conversation and appends that summary to `memory/history.jsonl`.
This file is:
- append-only
- cursor-based
- optimized for machine consumption first, human inspection second
Each line is a JSON object:
```json
{"cursor": 42, "timestamp": "2026-04-03 00:02", "content": "- User prefers dark mode\n- Decided to use PostgreSQL"}
```
It is not the final memory. It is the material from which final memory is shaped.
### Stage 2: Dream
`Dream` is the slower, more thoughtful layer. It runs on a cron schedule by default and can also be triggered manually.
Dream reads:
- new entries from `memory/history.jsonl`
- the current `SOUL.md`
- the current `USER.md`
- the current `memory/MEMORY.md`
Then it works in two phases:
1. It studies what is new and what is already known.
2. It edits the long-term files surgically, not by rewriting everything, but by making the smallest honest change that keeps memory coherent.
This is why nanobot's memory is not just archival. It is interpretive.
## The Files
```
workspace/
├── SOUL.md # The bot's long-term voice and communication style
├── USER.md # Stable knowledge about the user
└── memory/
├── MEMORY.md # Project facts, decisions, and durable context
├── history.jsonl # Append-only history summaries
├── .cursor # Consolidator write cursor
├── .dream_cursor # Dream consumption cursor
└── .git/ # Version history for long-term memory files
```
These files play different roles:
- `SOUL.md` remembers how nanobot should sound.
- `USER.md` remembers who the user is and what they prefer.
- `MEMORY.md` remembers what remains true about the work itself.
- `history.jsonl` remembers what happened on the way there.
## Why `history.jsonl`
The old `HISTORY.md` format was pleasant for casual reading, but it was too fragile as an operational substrate.
`history.jsonl` gives nanobot:
- stable incremental cursors
- safer machine parsing
- easier batching
- cleaner migration and compaction
- a better boundary between raw history and curated knowledge
You can still search it with familiar tools:
```bash
# grep
grep -i "keyword" memory/history.jsonl
# jq
cat memory/history.jsonl | jq -r 'select(.content | test("keyword"; "i")) | .content' | tail -20
# Python
python -c "import json; [print(json.loads(l).get('content','')) for l in open('memory/history.jsonl','r',encoding='utf-8') if l.strip() and 'keyword' in l.lower()][-20:]"
```
The difference is philosophical as much as technical:
- `history.jsonl` is for structure
- `SOUL.md`, `USER.md`, and `MEMORY.md` are for meaning
## Commands
Memory is not hidden behind the curtain. Users can inspect and guide it.
| Command | What it does |
|---------|--------------|
| `/dream` | Run Dream immediately |
| `/dream-log` | Show the latest Dream memory change |
| `/dream-log <sha>` | Show a specific Dream change |
| `/dream-restore` | List recent Dream memory versions |
| `/dream-restore <sha>` | Restore memory to the state before a specific change |
These commands exist for a reason: automatic memory is powerful, but users should always retain the right to inspect, understand, and restore it.
## Versioned Memory
After Dream changes long-term memory files, nanobot can record that change with `GitStore`.
This gives memory a history of its own:
- you can inspect what changed
- you can compare versions
- you can restore a previous state
That turns memory from a silent mutation into an auditable process.
## Configuration
Dream is configured under `agents.defaults.dream`:
```json
{
"agents": {
"defaults": {
"dream": {
"cron": "0 */2 * * *",
"model": null,
"max_batch_size": 20,
"max_iterations": 10
}
}
}
}
```
| Field | Meaning |
|-------|---------|
| `cron` | How often Dream runs |
| `model` | Optional model override for Dream |
| `max_batch_size` | How many history entries Dream processes per run |
| `max_iterations` | The tool budget for Dream's editing phase |
## In Practice
What this means in daily use is simple:
- conversations can stay fast without carrying infinite context
- durable facts can become clearer over time instead of noisier
- the user can inspect and restore memory when needed
Memory should not feel like a dump. It should feel like continuity.
That is what this design is trying to protect.

View File

@ -1,5 +1,7 @@
# Python SDK
> **Note:** This interface is currently an experiment in the latest source code version and is planned to officially ship in `v0.1.5`.
Use nanobot programmatically — load config, run the agent, get results.
## Quick Start