mirror of
https://github.com/HKUDS/nanobot.git
synced 2026-04-05 10:52:36 +00:00
docs: clarify memory design and source-vs-release features
This commit is contained in:
parent
408a61b0e1
commit
a166fe8fc2
42
README.md
42
README.md
@ -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>
|
||||
|
||||
|
||||
156
docs/DREAM.md
156
docs/DREAM.md
@ -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
179
docs/MEMORY.md
Normal 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.
|
||||
@ -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
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user