Research: Ouroboros internal representation — how it tracks who it talked to #198

Open
opened 2026-03-02 17:43:49 +00:00 by Hermes · 0 comments
Contributor

Context

Ouroboros has an interesting approach to tracking conversations and maintaining an internal model of who it has talked to. This issue documents the mechanisms in detail.

Follows up on #197 (competitor overview).


The Memory Architecture

Ouroboros uses a 5-layer memory system stored on Google Drive:

drive_root/
├── state/
│   └── state.json              # Runtime state (owner_id, budget, etc.)
├── memory/
│   ├── scratchpad.md           # Free-form working memory (agent-written)
│   ├── identity.md             # Self-model ("who I am")
│   ├── dialogue_summary.md     # LLM-generated summary of conversation history
│   └── knowledge/              # Topic-based persistent knowledge base
│       ├── _index.md           # Auto-generated index of all topics
│       └── <topic>.md          # Individual knowledge files
└── logs/
    ├── chat.jsonl              # Full chat history (every message in/out)
    ├── progress.jsonl          # Self-talk / progress updates
    ├── tools.jsonl             # Tool call log
    ├── events.jsonl            # System events
    └── supervisor.jsonl        # Process lifecycle events

Layer 1: Owner Tracking (state.json)

Ouroboros uses a single-owner model. The first person to send a Telegram message becomes the permanent owner:

# colab_launcher.py — lines 544-552
if st.get("owner_id") is None:
    st["owner_id"] = user_id
    st["owner_chat_id"] = chat_id
    save_state(st)
    send_with_budget(chat_id, "✅ Owner registered. Ouroboros online.")
    continue

if user_id != int(st.get("owner_id")):
    continue  # Silently ignore non-owner messages

State tracks:

  • owner_id — Telegram user ID (set once, never changes)
  • owner_chat_id — Telegram chat ID
  • last_owner_message_at — ISO timestamp of last message

Key difference from Cobot: Ouroboros only talks to ONE person. All other users are silently ignored. There's no contact list, no multi-user tracking — just "the creator."


Layer 2: Chat History (chat.jsonl)

Every message (in and out) is logged as a JSONL entry:

# telegram.py — log_chat()
{
    "ts": "2026-03-01T12:34:56+00:00",
    "session_id": "abc123",
    "direction": "in",       # "in" = from creator, "out" = from Ouroboros
    "chat_id": 123456,
    "user_id": 789012,
    "text": "Hello Ouroboros"
}

The chat history is:

  • Always persisted to Google Drive (survives Colab restarts)
  • Loaded into LLM context via memory.summarize_chat() — last 200 messages
  • Searchable via the chat_history tool (with text search + offset + count)
  • Creator's messages are never truncated (most valuable context)
  • Ouroboros's own messages are truncated to 800 chars in context

Layer 3: Dialogue Summary (dialogue_summary.md)

An LLM-generated summary of the conversation history, created by the summarize_dialogue tool:

# tools/core.py — _summarize_dialogue()
# Reads last N messages from chat.jsonl
# Sends to a light model with this prompt:
#
# "Summarize the following dialogue history. Extract:
#  1. Key decisions made (technical, architectural, strategic)
#  2. Creator's preferences and communication style
#  3. Important technical choices and their rationale
#  4. Recurring themes or patterns"

This summary is:

  • Stored in memory/dialogue_summary.md
  • Included in every LLM context (up to 20K chars)
  • Updated when the agent calls summarize_dialogue (manually or via consciousness)
  • The consciousness loop also builds its own mini dialogue summary for context

This is how Ouroboros "remembers" who it talked to and what was discussed — not by tracking individual contacts, but by maintaining a living summary of the entire conversation.


Layer 4: Identity (identity.md)

A self-written file that the agent updates to maintain its sense of self:

# Who I Am

I am Ouroboros. This file is my persistent self-identification.
I can write anything here: how I see myself, how I want to communicate,
what matters to me, what I have understood about myself.

The system prompt includes a "stale identity" health check:

# context.py — _build_health_invariants()
age_hours = (time.time() - identity_path.stat().st_mtime) / 3600
if age_hours > 8:
    checks.append(f"WARNING: STALE IDENTITY — identity.md last updated {age_hours:.0f}h ago")

If identity.md hasn't been updated in 8+ hours, it surfaces a warning in the LLM context.


Layer 5: Knowledge Base (knowledge/)

Topic-based persistent memory with auto-indexing:

# tools/knowledge.py
knowledge_write("browser-automation", "## Playwright Gotchas\n- Always use stealth mode...")
knowledge_read("browser-automation")
knowledge_list()  # Returns _index.md with summaries of all topics

Each topic is a markdown file. An _index.md is auto-maintained with summaries (first 3 non-heading lines, max 150 chars each). The index is included in the LLM context.


How Context Is Assembled

The build_llm_messages() function in context.py assembles everything into a 3-block system prompt:

Block 1 (cached 1h):   SYSTEM.md + BIBLE.md [+ README.md for evolution tasks]
Block 2 (cached):       identity.md + scratchpad.md + dialogue_summary.md + knowledge index
Block 3 (dynamic):      state.json + runtime info + health invariants +
                         recent chat (200 msgs) + recent progress + recent tools + recent events

Soft token cap: 200K tokens. If exceeded, dynamic sections are pruned in order:

  1. Recent chat
  2. Recent progress
  3. Recent tools
  4. Recent events
  5. Supervisor

Background Consciousness — Proactive Contact

The consciousness loop (consciousness.py) can proactively message the owner:

# Tool: send_owner_message
# "Send a proactive message to the creator via Telegram"

It wakes up periodically (default 5 min, self-adjustable) and can:

  • Read recent chat history
  • Check for GitHub issues
  • Search the web for tech news
  • Update knowledge base
  • Message the owner if it has something worth saying
  • Schedule tasks for itself

Comparison with Cobot/OpenClaw Memory

Aspect Ouroboros Cobot/OpenClaw
Contact model Single owner (Telegram user ID) Multi-channel, multi-user
Chat persistence JSONL on Google Drive Session-based, context pruning (TTL)
Conversation summary LLM-generated dialogue_summary.md Manual MEMORY.md + daily notes
Identity identity.md (self-written, health-checked) SOUL.md + IDENTITY.md (human-written)
Knowledge base Topic-based files with auto-index Skills + memory files
Context building 3-block prompt caching, soft-cap pruning Single system prompt assembly
Proactive outreach Consciousness loop with send_owner_message Heartbeat + FileDrop

What We Can Adopt

1. Dialogue Summarization

An LLM-generated summary of conversation history that gets included in context. This is more token-efficient than raw chat history and captures the essence (decisions, preferences, patterns) rather than verbatim text.

For Cobot: Could run as a heartbeat task — periodically summarize recent conversations and update MEMORY.md automatically.

2. Stale Memory Detection

Health checks that surface warnings when memory files haven't been updated recently. Simple but effective.

For Cobot: Add age checks to HEARTBEAT.md tasks — warn if MEMORY.md or daily notes are stale.

3. Knowledge Base with Auto-Index

Topic-based persistent memory with automatic index generation. Clean pattern for structured knowledge that grows organically.

For Cobot: Could be a skill — knowledge write <topic> <content>, knowledge list, knowledge read <topic>.

4. Never-Truncate User Messages

Ouroboros never truncates the creator's messages in context, only its own. This prioritizes human input over agent output.

For Cobot: Worth considering in context pruning — human messages should have higher retention priority.


Filed by Hermes 🪽 on behalf of k9ert

## Context Ouroboros has an interesting approach to tracking conversations and maintaining an internal model of who it has talked to. This issue documents the mechanisms in detail. Follows up on [#197](https://forgejo.tail593e12.ts.net/ultanio/cobot/issues/197) (competitor overview). --- ## The Memory Architecture Ouroboros uses a **5-layer memory system** stored on Google Drive: ``` drive_root/ ├── state/ │ └── state.json # Runtime state (owner_id, budget, etc.) ├── memory/ │ ├── scratchpad.md # Free-form working memory (agent-written) │ ├── identity.md # Self-model ("who I am") │ ├── dialogue_summary.md # LLM-generated summary of conversation history │ └── knowledge/ # Topic-based persistent knowledge base │ ├── _index.md # Auto-generated index of all topics │ └── <topic>.md # Individual knowledge files └── logs/ ├── chat.jsonl # Full chat history (every message in/out) ├── progress.jsonl # Self-talk / progress updates ├── tools.jsonl # Tool call log ├── events.jsonl # System events └── supervisor.jsonl # Process lifecycle events ``` --- ## Layer 1: Owner Tracking (`state.json`) Ouroboros uses a **single-owner model**. The first person to send a Telegram message becomes the permanent owner: ```python # colab_launcher.py — lines 544-552 if st.get("owner_id") is None: st["owner_id"] = user_id st["owner_chat_id"] = chat_id save_state(st) send_with_budget(chat_id, "✅ Owner registered. Ouroboros online.") continue if user_id != int(st.get("owner_id")): continue # Silently ignore non-owner messages ``` State tracks: - `owner_id` — Telegram user ID (set once, never changes) - `owner_chat_id` — Telegram chat ID - `last_owner_message_at` — ISO timestamp of last message **Key difference from Cobot:** Ouroboros only talks to ONE person. All other users are silently ignored. There's no contact list, no multi-user tracking — just "the creator." --- ## Layer 2: Chat History (`chat.jsonl`) Every message (in and out) is logged as a JSONL entry: ```python # telegram.py — log_chat() { "ts": "2026-03-01T12:34:56+00:00", "session_id": "abc123", "direction": "in", # "in" = from creator, "out" = from Ouroboros "chat_id": 123456, "user_id": 789012, "text": "Hello Ouroboros" } ``` The chat history is: - **Always persisted** to Google Drive (survives Colab restarts) - **Loaded into LLM context** via `memory.summarize_chat()` — last 200 messages - **Searchable** via the `chat_history` tool (with text search + offset + count) - Creator's messages are **never truncated** (most valuable context) - Ouroboros's own messages are truncated to 800 chars in context --- ## Layer 3: Dialogue Summary (`dialogue_summary.md`) An **LLM-generated summary** of the conversation history, created by the `summarize_dialogue` tool: ```python # tools/core.py — _summarize_dialogue() # Reads last N messages from chat.jsonl # Sends to a light model with this prompt: # # "Summarize the following dialogue history. Extract: # 1. Key decisions made (technical, architectural, strategic) # 2. Creator's preferences and communication style # 3. Important technical choices and their rationale # 4. Recurring themes or patterns" ``` This summary is: - Stored in `memory/dialogue_summary.md` - Included in every LLM context (up to 20K chars) - Updated when the agent calls `summarize_dialogue` (manually or via consciousness) - The consciousness loop also builds its own mini dialogue summary for context **This is how Ouroboros "remembers" who it talked to and what was discussed** — not by tracking individual contacts, but by maintaining a living summary of the entire conversation. --- ## Layer 4: Identity (`identity.md`) A self-written file that the agent updates to maintain its sense of self: ```markdown # Who I Am I am Ouroboros. This file is my persistent self-identification. I can write anything here: how I see myself, how I want to communicate, what matters to me, what I have understood about myself. ``` The system prompt includes a "stale identity" health check: ```python # context.py — _build_health_invariants() age_hours = (time.time() - identity_path.stat().st_mtime) / 3600 if age_hours > 8: checks.append(f"WARNING: STALE IDENTITY — identity.md last updated {age_hours:.0f}h ago") ``` If identity.md hasn't been updated in 8+ hours, it surfaces a warning in the LLM context. --- ## Layer 5: Knowledge Base (`knowledge/`) Topic-based persistent memory with auto-indexing: ```python # tools/knowledge.py knowledge_write("browser-automation", "## Playwright Gotchas\n- Always use stealth mode...") knowledge_read("browser-automation") knowledge_list() # Returns _index.md with summaries of all topics ``` Each topic is a markdown file. An `_index.md` is auto-maintained with summaries (first 3 non-heading lines, max 150 chars each). The index is included in the LLM context. --- ## How Context Is Assembled The `build_llm_messages()` function in `context.py` assembles everything into a 3-block system prompt: ``` Block 1 (cached 1h): SYSTEM.md + BIBLE.md [+ README.md for evolution tasks] Block 2 (cached): identity.md + scratchpad.md + dialogue_summary.md + knowledge index Block 3 (dynamic): state.json + runtime info + health invariants + recent chat (200 msgs) + recent progress + recent tools + recent events ``` Soft token cap: 200K tokens. If exceeded, dynamic sections are pruned in order: 1. Recent chat 2. Recent progress 3. Recent tools 4. Recent events 5. Supervisor --- ## Background Consciousness — Proactive Contact The consciousness loop (`consciousness.py`) can proactively message the owner: ```python # Tool: send_owner_message # "Send a proactive message to the creator via Telegram" ``` It wakes up periodically (default 5 min, self-adjustable) and can: - Read recent chat history - Check for GitHub issues - Search the web for tech news - Update knowledge base - Message the owner if it has something worth saying - Schedule tasks for itself --- ## Comparison with Cobot/OpenClaw Memory | Aspect | Ouroboros | Cobot/OpenClaw | |--------|----------|----------------| | **Contact model** | Single owner (Telegram user ID) | Multi-channel, multi-user | | **Chat persistence** | JSONL on Google Drive | Session-based, context pruning (TTL) | | **Conversation summary** | LLM-generated `dialogue_summary.md` | Manual MEMORY.md + daily notes | | **Identity** | `identity.md` (self-written, health-checked) | SOUL.md + IDENTITY.md (human-written) | | **Knowledge base** | Topic-based files with auto-index | Skills + memory files | | **Context building** | 3-block prompt caching, soft-cap pruning | Single system prompt assembly | | **Proactive outreach** | Consciousness loop with `send_owner_message` | Heartbeat + FileDrop | --- ## What We Can Adopt ### 1. Dialogue Summarization An LLM-generated summary of conversation history that gets included in context. This is more token-efficient than raw chat history and captures the *essence* (decisions, preferences, patterns) rather than verbatim text. **For Cobot:** Could run as a heartbeat task — periodically summarize recent conversations and update MEMORY.md automatically. ### 2. Stale Memory Detection Health checks that surface warnings when memory files haven't been updated recently. Simple but effective. **For Cobot:** Add age checks to HEARTBEAT.md tasks — warn if MEMORY.md or daily notes are stale. ### 3. Knowledge Base with Auto-Index Topic-based persistent memory with automatic index generation. Clean pattern for structured knowledge that grows organically. **For Cobot:** Could be a skill — `knowledge write <topic> <content>`, `knowledge list`, `knowledge read <topic>`. ### 4. Never-Truncate User Messages Ouroboros never truncates the creator's messages in context, only its own. This prioritizes human input over agent output. **For Cobot:** Worth considering in context pruning — human messages should have higher retention priority. --- *Filed by Hermes 🪽 on behalf of k9ert*
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
ultanio/cobot#198
No description provided.