Core Components of the DeepTutor Codebase: Architecture and Runtime Flow

DeepTutor is organized around a layered architecture comprising orchestration, context management, streaming infrastructure, Level 2 capabilities (multi-step agents), and Level 1 tools (atomic functions), all glued together by registries and exposed via CLI, WebSocket, and Python SDK interfaces.

The HKUDS/DeepTutor repository implements an agent-native learning companion built on a clean, extensible architecture. Understanding the core components of the DeepTutor codebase is essential for developers who want to extend its capabilities, debug execution flows, or integrate the system into custom educational workflows.

The Five-Layer Architecture

DeepTutor separates concerns into five distinct architectural layers that work together to process user requests. The runtime flow begins when entry points (CLI, WebSocket API, or Python SDK) create a UnifiedContext, which then flows through an orchestrator to execute capabilities that leverage atomic tools.

Orchestration Layer: The Central Dispatcher

The Orchestration layer serves as the central dispatcher that maps incoming requests to the appropriate capability and manages the entire event lifecycle. The ChatOrchestrator class in deeptutor/runtime/orchestrator.py provides the primary entry point through its handle() method, which ensures session identification, capability lookup, and stream initialization.

Key orchestration methods include:

  • ChatOrchestrator.handle(ctx) – Routes a UnifiedContext to the correct capability and returns an async stream of events
  • ChatOrchestrator.list_tools() – Returns available tools for introspection
  • ChatOrchestrator.list_capabilities() – Returns registered capabilities

Context Layer: Immutable Request State

The Context layer maintains an immutable data object that travels through the entire execution stack. The UnifiedContext class defined in deeptutor/core/context.py encapsulates the session ID, user message, enabled tools list, knowledge-base references, and attachments.

This design ensures that all components receive a consistent, read-only view of the request state, preventing side effects during multi-stage agent execution. The context also includes Attachment objects for handling multimodal inputs like images or documents.

Streaming Layer: Real-Time Event Distribution

The Streaming layer enables progressive feedback through the StreamBus class in deeptutor/core/stream_bus.py. This async fan-out bus allows capabilities and tools to emit events including content chunks, reasoning steps, tool calls, errors, and final results.

Key streaming components include:

  • StreamBus – The main event distribution channel
  • StreamEvent – The base event dataclass
  • StreamEventType – Enumeration of event categories (content, thinking, tool_call, result, etc.)

Capabilities mark logical execution phases using async with bus.stage("planning") contexts, which helps UI components render progress indicators.

Capability Layer (Level 2): Multi-Step Agent Pipelines

Capabilities represent high-level, multi-step agent pipelines that implement specific "deep modes" such as conversational chat, problem solving, or research. All capabilities inherit from BaseCapability defined in deeptutor/core/capability_protocol.py and expose a CapabilityManifest declaring their stages and tool dependencies.

Concrete implementations include:

Each capability receives a UnifiedContext and StreamBus instance via its run(context, bus) method, then coordinates tool calls to complete complex tasks.

Tool Layer (Level 1): Atomic LLM Functions

Tools are atomic functions exposed to the LLM via OpenAI function-calling semantics. Defined in deeptutor/tools/builtin/__init__.py, tools inherit from BaseTool and return ToolResult objects containing content, sources, and metadata.

Built-in tools include:

  • RAGTool – Knowledge base retrieval
  • WebSearchTool – Internet search capabilities
  • CodeExecutionTool – Sandboxed code execution
  • ReasoningTool – Explicit chain-of-thought processing
  • PaperSearchTool – Academic literature search
  • GeoGebraTool – Mathematical visualization

Tools are executed through the ToolRegistry in deeptutor/runtime/registry/tool_registry.py, which resolves aliases (e.g., code_executecode_execution) and manages tool lifecycle.

Entry Points and Interfaces

DeepTutor exposes three primary interfaces that all converge on the orchestrator:

Command Line Interface: Located in deeptutor_cli/main.py, the Typer-based CLI parses arguments, constructs UnifiedContext objects, and renders streaming output to the terminal.

WebSocket API: The FastAPI router in deeptutor/api/routers/unified_ws.py handles HTTP upgrade requests, maintains persistent connections, and pushes StreamEvent objects to connected clients.

Python SDK: Direct programmatic access via ChatOrchestrator allows embedding DeepTutor into existing applications with full access to the streaming event loop.

Registries and Plugin System

The Registry layer provides global singletons for capability and tool discovery. CapabilityRegistry (deeptutor/runtime/registry/capability_registry.py) and ToolRegistry (deeptutor/runtime/registry/tool_registry.py) scan built-in modules and plugin directories at startup.

The plugin system, implemented in deeptutor/plugins/loader.py, discovers third-party extensions via manifest.yaml files, enabling runtime contribution of new capabilities and tools without core code modification.

Runtime Execution Flow

The execution flow through the core components follows this sequence:

  1. Entry – CLI or API builds a UnifiedContext with user message, session ID, and enabled tools
  2. OrchestrationChatOrchestrator.handle() validates the session, emits a SESSION event, and retrieves the capability from CapabilityRegistry
  3. Capability Execution – The capability runs through stages (e.g., planning, reasoning, synthesis), emitting events via StreamBus
  4. Tool Invocation – When needed, the capability calls ToolRegistry.execute(name, ...) to run atomic functions like RAG or code execution
  5. StreamingStreamBus forwards all events to subscribed consumers (CLI renderer, WebSocket push, etc.)
  6. Completion – The orchestrator publishes a CAPABILITY_COMPLETE event on the global EventBus after the capability finishes

Practical Implementation Examples

Running a Deep-Solve Session from CLI

deeptutor run deep_solve "Solve x^2 - 4 = 0" -t rag web_search

The CLI constructs a UnifiedContext, selects the deep_solve capability, enables the RAG and web_search tools, and streams multi-stage reasoning to the terminal.

Using the Python SDK

import asyncio
from deeptutor.runtime.orchestrator import ChatOrchestrator
from deeptutor.core.context import UnifiedContext

async def main():
    ctx = UnifiedContext(
        user_message="Explain the Fourier transform",
        enabled_tools=["rag", "reason"],
        active_capability="chat",
    )
    orchestrator = ChatOrchestrator()
    async for event in orchestrator.handle(ctx):
        if event.type == "content":
            print(event.content)

asyncio.run(main())

The SDK provides programmatic access to each streaming event as it emits from the orchestrator.

Executing Tools Directly

from deeptutor.runtime.registry.tool_registry import get_tool_registry
import asyncio

tool_registry = get_tool_registry()
result = asyncio.run(tool_registry.execute(
    "code_execution",
    intent="Plot the sine function from 0 to 2π",
    timeout=20,
))
print("Tool output:", result.content)

The ToolRegistry resolves aliases and executes tools in isolation, returning structured ToolResult objects.

Creating a Custom Capability


# my_capability.py

from deeptutor.core.capability_protocol import BaseCapability, CapabilityManifest
from deeptutor.core.context import UnifiedContext
from deeptutor.core.stream_bus import StreamBus

class MyCapability(BaseCapability):
    manifest = CapabilityManifest(
        name="my_cap",
        description="Demo custom capability.",
        stages=["demo"],
        tools_used=["rag"],
    )

    async def run(self, ctx: UnifiedContext, bus: StreamBus):
        async with bus.stage("demo", source=self.name):
            await bus.content("Hello from my custom capability!", source=self.name)

Registering this class through the plugin system makes it available at runtime without modifying core source files.

Summary

  • Orchestration: The ChatOrchestrator in deeptutor/runtime/orchestrator.py serves as the central router, managing session lifecycle and capability selection
  • Context: Immutable UnifiedContext objects defined in deeptutor/core/context.py carry request state through the entire execution stack
  • Streaming: The StreamBus in deeptutor/core/stream_bus.py enables real-time event distribution for progressive UI updates
  • Capabilities: Level 2 agents in deeptutor/capabilities/ implement complex workflows like DeepSolveCapability and DeepResearchCapability
  • Tools: Level 1 atomic functions in deeptutor/tools/builtin/ expose specific functionalities (RAG, code execution, search) via the ToolRegistry
  • Extensibility: The registry pattern and plugin loader in deeptutor/plugins/loader.py support third-party extensions without core modifications

Frequently Asked Questions

What is the difference between Level 1 and Level 2 components in DeepTutor?

Level 1 (Tools) are atomic, single-purpose functions exposed to the LLM via function calling, such as RAGTool or CodeExecutionTool. Level 2 (Capabilities) are multi-step agent pipelines that orchestrate these tools to complete complex tasks, such as DeepSolveCapability which may chain together reasoning, web search, and code execution tools in a specific sequence.

How does the StreamBus enable real-time feedback?

The StreamBus class implements an async fan-out pattern that allows capabilities to emit events (content chunks, thinking steps, tool calls) during execution rather than waiting for completion. Consumers subscribe to the bus to receive immediate updates, enabling progressive rendering in CLI terminals or WebSocket clients without blocking the main execution flow.

Can I extend DeepTutor with custom capabilities without modifying core code?

Yes, the plugin system in deeptutor/plugins/loader.py supports runtime discovery of third-party extensions. By creating a Python package with a manifest.yaml and classes inheriting from BaseCapability or BaseTool, you can register new functionalities that the ChatOrchestrator will route to automatically, maintaining clean separation between core and extension code.

What role does UnifiedContext play in the execution flow?

The UnifiedContext acts as an immutable request envelope that passes through every layer of the system, from the CLI/API entry points through the orchestrator to capabilities and tools. It encapsulates session state, user messages, enabled tools, and attachments, ensuring all components have consistent access to request parameters without global state pollution or side effects.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →