Architecture
Section titled “Architecture”This document provides a deep technical overview of YouLab’s architecture. It’s written for developers and technical evaluators who want to understand how the system works.
System Overview
Section titled “System Overview”YouLab is built around a simple principle: AI tutors should be transparent about what they know. The architecture reflects this through explicit data flows and version-controlled state.
┌─────────────────────────────────────────────────────────────────────────┐│ OpenWebUI ││ (Chat Frontend + Auth) ││ ┌─────────────────────────────────────────────────────────────────┐ ││ │ Youlab Pipe: Extracts user_id, chat_id, forwards to backend │ ││ └─────────────────────────────────────────────────────────────────┘ │└────────────────────────────────┬────────────────────────────────────────┘ │ POST /chat/stream (SSE) ▼┌─────────────────────────────────────────────────────────────────────────┐│ Youlab Server ││ (FastAPI + Agno Agent) ││ ││ ┌──────────────────────────────────────────────────────────────────┐ ││ │ Per-Request Agent │ ││ │ ┌─────────────────────────────────────────────────────────────┐ │ ││ │ │ Instructions: │ │ ││ │ │ • Base system prompt │ │ ││ │ │ • CLAUDE.md from workspace (if exists) │ │ ││ │ │ • Memory blocks for this student │ │ ││ │ └─────────────────────────────────────────────────────────────┘ │ ││ │ ┌─────────────────────────────────────────────────────────────┐ │ ││ │ │ Tools (workspace-scoped): │ │ ││ │ │ • FileTools → read/write files in /workspace │ │ ││ │ │ • ShellTools → execute commands in /workspace │ │ ││ │ │ • HonchoTools → query conversation history │ │ ││ │ │ • MemoryBlockTools → propose profile edits │ │ ││ │ │ • LaTeXTools → generate PDF notes │ │ ││ │ └─────────────────────────────────────────────────────────────┘ │ ││ └──────────────────────────────────────────────────────────────────┘ ││ ││ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────────────┐ ││ │ API Routers │ │ Background │ │ Workspace Sync │ ││ │ • /blocks │ │ Scheduler │ │ • OpenWebUI KB ↔ │ ││ │ • /background │ │ • Cron tasks │ │ Local files │ ││ │ • /workspace │ │ • Idle tasks │ │ │ ││ │ • /notes │ │ │ │ │ ││ └────────┬────────┘ └────────┬────────┘ └────────────┬────────────┘ ││ │ │ │ │└───────────┼────────────────────┼────────────────────────┼───────────────┘ │ │ │ ▼ ▼ ▼┌───────────────────┐ ┌─────────────────┐ ┌────────────────────────────┐│ Dolt │ │ Honcho │ │ OpenWebUI API ││ (Memory Blocks) │ │ (Messages) │ │ (Knowledge Base) ││ │ │ │ │ ││ • memory_blocks │ │ • Persistence │ │ • File upload/download ││ • background_* │ │ • Dialectic │ │ • KB management ││ • user_activity │ │ queries │ │ ││ │ │ │ │ │└───────────────────┘ └─────────────────┘ └────────────────────────────┘Request Lifecycle
Section titled “Request Lifecycle”Chat Request Flow
Section titled “Chat Request Flow”When a student sends a message in OpenWebUI:
-
OpenWebUI → Pipe (
src/youlab/pipe.py:45-130)- OpenWebUI invokes
Pipe.pipe()with message body, user info, metadata, and event emitter - Pipe extracts
user_idfrom__user__["id"]andchat_idfrom__metadata__["chat_id"] - Formats messages as
[{"role": "user", "content": "..."}]
- OpenWebUI invokes
-
Pipe → Server (
src/youlab/pipe.py:86-104)- Opens SSE connection to
POST /chat/stream - Sends JSON payload:
{user_id, chat_id, messages} - Receives and forwards SSE events to OpenWebUI
- Opens SSE connection to
-
Server: Agent Construction (
src/youlab/server.py:167-297)- Resolves workspace path (shared or per-user)
- Loads
CLAUDE.mdfrom workspace if present - Builds memory context from Dolt blocks
- Composes instructions: base + CLAUDE.md + memory
- Creates fresh Agno agent with workspace-scoped tools
-
Server: Execution (
src/youlab/server.py:324-384)- Persists user message to Honcho (fire-and-forget)
- Streams agent response via
agent.arun(stream=True) - Yields SSE events for each chunk
- Persists assistant response to Honcho
- Updates user activity timestamp in Dolt
-
Pipe → OpenWebUI (
src/youlab/pipe.py:132-167)- Converts SSE events to OpenWebUI format
- Status events show progress
- Message events display response chunks
- Done event completes the stream
Key Design Decision: Per-Request Agents
Section titled “Key Design Decision: Per-Request Agents”Youlab creates a fresh agent for every chat request rather than maintaining stateful agents:
Benefits:
- No agent state to synchronize across requests
- Memory blocks are always fresh from Dolt
- Tools are scoped to current user’s workspace
- Simpler error recovery (failed requests don’t corrupt state)
Trade-offs:
- Can’t maintain in-memory conversation context (relies on message history in request)
- Agent instantiation overhead per request (mitigated by fast Agno initialization)
Data Stores
Section titled “Data Stores”Dolt: Version-Controlled Memory
Section titled “Dolt: Version-Controlled Memory”Dolt is a MySQL-compatible database with git-like versioning. YouLab uses it for:
Memory Blocks (memory_blocks table)
CREATE TABLE memory_blocks ( user_id VARCHAR(255) NOT NULL, label VARCHAR(100) NOT NULL, title VARCHAR(255), body TEXT, schema_ref VARCHAR(255), updated_at TIMESTAMP, PRIMARY KEY (user_id, label));Every update creates a Dolt commit with author attribution. History is queryable via dolt_history_memory_blocks.
Proposals as Branches
When an agent proposes a memory block edit:
- Creates branch
agent/{user_id}/{label}from main - Commits proposed change to branch
- Stores metadata (reasoning, confidence) in commit message as JSON
- On approval:
DOLT_MERGE→ main - On rejection:
DOLT_BRANCH('-D')deletes branch
This leverages Dolt’s native branching rather than application-level state management.
Background Tasks (background_tasks, task_runs, user_activity tables)
Task definitions, execution history, and user activity tracking for idle triggers.
Honcho: Conversation Memory
Section titled “Honcho: Conversation Memory”Honcho provides two capabilities:
Message Persistence
- Every message persisted with peer identity (student or tutor)
- Sessions scoped by
chat_{chat_id} - Metadata includes user_id and chat_id for correlation
Dialectic Queries
- Agents ask questions like “What has this student struggled with?”
- Honcho’s backend processes conversation history through LLM
- Returns synthesized insights without exposing raw messages
Honcho enables cross-session memory without stuffing conversation history into prompts.
Workspaces: Per-User Filesystems
Section titled “Workspaces: Per-User Filesystems”Students get isolated workspaces:
/data/youlab/users/{user_id}/workspace/├── essays/│ └── draft-v1.md├── notes/│ └── research.md└── CLAUDE.md # Optional project instructionsWorkspace Modes:
- Isolated (default): Each user gets their own workspace
- Shared: All users share a single workspace (set
RALPH_AGENT_WORKSPACE)
FileTools and ShellTools are scoped to the workspace directory, preventing path traversal.
Tool System
Section titled “Tool System”Agents are equipped with five toolkits:
FileTools + ShellTools (Agno Built-in)
Section titled “FileTools + ShellTools (Agno Built-in)”Standard file and shell operations scoped to workspace. Enable “Claude Code-like” capabilities for technical tutoring.
HonchoTools (src/youlab/tools/honcho_tools.py)
Section titled “HonchoTools (src/youlab/tools/honcho_tools.py)”Tool: query_student(question: str) -> str
Queries Honcho’s dialectic API for insights about the current student. The tool extracts user_id from Agno’s RunContext (injected via dependencies parameter).
MemoryBlockTools (src/youlab/tools/memory_blocks.py)
Section titled “MemoryBlockTools (src/youlab/tools/memory_blocks.py)”Tools:
list_memory_blocks()- Returns available block labelsread_memory_block(label)- Returns block content as markdownpropose_memory_edit(label, old_string, new_string, reasoning)- Creates proposal
The propose_memory_edit tool implements Claude Code-style surgical string replacement. It:
- Validates
old_stringexists in current content - Checks uniqueness (or uses
replace_all=True) - Creates Dolt branch with proposed change
- Returns success message (agent never sees approval status)
LaTeXTools (src/youlab/tools/latex_tools.py)
Section titled “LaTeXTools (src/youlab/tools/latex_tools.py)”Tool: render_notes(title: str, content: str) -> str
Generates PDF from LaTeX content:
- Writes LaTeX to temp file in workspace
- Compiles with Tectonic (subprocess)
- Base64 encodes PDF
- Returns HTML with embedded PDF.js viewer
The HTML is returned in a code block that OpenWebUI renders as an artifact.
Background Tasks
Section titled “Background Tasks”The background system enables scheduled and event-driven agent execution:
Components
Section titled “Components”- Registry (
src/youlab/background/registry.py): In-memory task definitions with Dolt persistence - Scheduler (
src/youlab/background/scheduler.py): Async loop checking triggers - Executor (
src/youlab/background/executor.py): Runs agents for users in batches
Trigger Types
Section titled “Trigger Types”CronTrigger: Standard cron expressions
CronTrigger(schedule="0 3 * * *") # Daily at 3 AMIdleTrigger: Fires after user inactivity
IdleTrigger(idle_minutes=1440, cooldown_minutes=2880) # 24h idle, 48h cooldownCooldown prevents repeated triggers for the same user.
Execution Flow
Section titled “Execution Flow”- Scheduler checks triggers every 60 seconds
- For eligible tasks, queries Dolt for target users
- Executor processes users in batches (default: 5 concurrent)
- Each user gets fresh agent with task’s system prompt and tools
- Run history persisted to Dolt for auditability
API Endpoints
Section titled “API Endpoints”Chat API
Section titled “Chat API”| Endpoint | Method | Description |
|---|---|---|
/chat/stream | POST | Main chat endpoint with SSE streaming |
/health | GET | Health check |
Memory Blocks API (/users/{user_id}/blocks)
Section titled “Memory Blocks API (/users/{user_id}/blocks)”| Endpoint | Method | Description |
|---|---|---|
/ | GET | List all blocks with pending counts |
/{label} | GET | Get single block |
/{label} | PUT | Update block (user-initiated) |
/{label} | DELETE | Delete block |
/{label}/history | GET | Get version history |
/{label}/versions/{sha} | GET | Get content at version |
/{label}/restore | POST | Restore to previous version |
/{label}/diffs | GET | Get pending proposals |
/{label}/propose | POST | Create proposal (agent-initiated) |
/{label}/diffs/{id}/approve | POST | Approve proposal |
/{label}/diffs/{id}/reject | POST | Reject proposal |
Background Tasks API (/background)
Section titled “Background Tasks API (/background)”| Endpoint | Method | Description |
|---|---|---|
/tasks | GET | List all tasks |
/tasks | POST | Create/update task |
/tasks/{name} | GET | Get task |
/tasks/{name} | DELETE | Delete task |
/tasks/{name}/enable | POST | Enable task |
/tasks/{name}/disable | POST | Disable task |
/tasks/{name}/run | POST | Trigger immediate execution |
/tasks/{name}/runs | GET | List execution history |
/runs/{id} | GET | Get run details |
Workspace API (/users/{user_id}/workspace)
Section titled “Workspace API (/users/{user_id}/workspace)”| Endpoint | Method | Description |
|---|---|---|
/files | GET | List workspace files |
/files/{path} | GET | Download file |
/files/{path} | PUT | Upload file |
/files/{path} | DELETE | Delete file |
/sync | POST | Trigger KB sync |
Notes Adapter (/api/you/notes)
Section titled “Notes Adapter (/api/you/notes)”Bridges OpenWebUI’s TipTap editor to Dolt memory blocks:
| Endpoint | Method | Description |
|---|---|---|
/ | GET | List blocks as notes |
/{id} | GET | Get note with version history |
/{id}/update | POST | Update note content |
Configuration
Section titled “Configuration”All Youlab settings use the RALPH_ prefix and are loaded via Pydantic Settings:
Required:
RALPH_OPENROUTER_API_KEY- API key for model access
Model:
RALPH_OPENROUTER_MODEL- Model ID (default:anthropic/claude-sonnet-4-20250514)
Storage:
RALPH_USER_DATA_DIR- Base for per-user data (default:/data/youlab/users)RALPH_AGENT_WORKSPACE- Shared workspace path (optional)
Dolt:
RALPH_DOLT_HOST,RALPH_DOLT_PORT,RALPH_DOLT_USER,RALPH_DOLT_PASSWORD,RALPH_DOLT_DATABASE
Honcho:
RALPH_HONCHO_WORKSPACE_ID- Workspace name (default:youlab)RALPH_HONCHO_ENVIRONMENT- Environment (default:demo)RALPH_HONCHO_API_KEY- API key (required for production)
OpenWebUI Sync:
RALPH_OPENWEBUI_URL- OpenWebUI base URLRALPH_OPENWEBUI_API_KEY- API key for KB sync
Project Structure
Section titled “Project Structure”src/youlab/├── __init__.py # Exports Pipe for OpenWebUI├── pipe.py # OpenWebUI pipe (HTTP client)├── server.py # FastAPI app + chat endpoint├── config.py # Pydantic settings├── dolt.py # Dolt client (800+ lines)├── memory.py # Memory context builder├── honcho.py # Honcho client│├── api/│ ├── blocks.py # Memory blocks REST API│ ├── background.py # Background tasks API│ ├── workspace.py # Workspace file API│ └── notes_adapter.py # OpenWebUI notes bridge│├── tools/│ ├── __init__.py # Exports all toolkits│ ├── memory_blocks.py # MemoryBlockTools│ ├── honcho_tools.py # HonchoTools│ └── latex_tools.py # LaTeXTools│├── background/│ ├── models.py # Task, Run, Trigger models│ ├── registry.py # Task registration│ ├── scheduler.py # Trigger checking loop│ ├── executor.py # Agent execution│ └── tools.py # Tool factory for tasks│└── sync/ ├── workspace_sync.py # Bidirectional file sync ├── openwebui_client.py # OpenWebUI API client ├── knowledge.py # KB management └── models.py # Sync state modelsAgent Configuration System
Section titled “Agent Configuration System”Agents are defined declaratively in TOML files. This enables course designers to create tutoring experiences without writing code. See Agent Configuration for the full schema reference and examples.
At startup, the system:
- Scans
config/courses/*/agents.tomlfor all course definitions - Validates each file against the schema
- Detects conflicts: If two courses define
[[block]]with the same label but different schemas, startup fails with an error - Registers agents, blocks, and tasks in the runtime registry
Blocks can be shared across courses—if a student already has a block from another course, it’s reused rather than overwritten, enabling shared context across tutoring domains.
Evolution from Legacy
Section titled “Evolution from Legacy”YouLab evolved from a Letta-based architecture (src/youlab_server/):
| Aspect | Legacy (Letta) | Current (Youlab/Agno) |
|---|---|---|
| Agent Framework | Letta | Agno |
| Memory Storage | Git repos + Letta blocks | Dolt database |
| Configuration | Complex TOML courses | Simplified agents.toml |
| Diff Workflow | JSON files | Dolt branches |
| Tools | Custom Letta tools | Agno toolkits |
| Background | Multi-agent orchestration | Simple scheduler with TOML tasks |
Why the change?
The Letta architecture provided powerful primitives (archival memory, tool execution, structured blocks) but added complexity:
- Separate Letta server to manage
- Complex memory rotation strategies
- TOML configuration sprawl
- Multiple abstraction layers
Youlab simplifies to:
- Single FastAPI server
- Per-request agents (no state management)
- Workspace files + Dolt blocks
- Direct tool execution
The core insight—transparent, user-controlled memory—remains. The implementation became simpler.
Related
Section titled “Related”- Thoughts/Introducing Youlab - The vision and motivation behind YouLab