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 aUnifiedContextto the correct capability and returns an async stream of eventsChatOrchestrator.list_tools()– Returns available tools for introspectionChatOrchestrator.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 channelStreamEvent– The base event dataclassStreamEventType– 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:
ChatCapability(deeptutor/capabilities/chat.py) – Default conversational modeDeepSolveCapability(deeptutor/capabilities/deep_solve.py) – Multi-stage problem solving with planning and verificationDeepQuestionCapability(deeptutor/capabilities/deep_question.py) – Practice question generationDeepResearchCapability(deeptutor/capabilities/deep_research.py) – Multi-agent research orchestration
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 retrievalWebSearchTool– Internet search capabilitiesCodeExecutionTool– Sandboxed code executionReasoningTool– Explicit chain-of-thought processingPaperSearchTool– Academic literature searchGeoGebraTool– Mathematical visualization
Tools are executed through the ToolRegistry in deeptutor/runtime/registry/tool_registry.py, which resolves aliases (e.g., code_execute → code_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:
- Entry – CLI or API builds a
UnifiedContextwith user message, session ID, and enabled tools - Orchestration –
ChatOrchestrator.handle()validates the session, emits aSESSIONevent, and retrieves the capability fromCapabilityRegistry - Capability Execution – The capability runs through stages (e.g., planning, reasoning, synthesis), emitting events via
StreamBus - Tool Invocation – When needed, the capability calls
ToolRegistry.execute(name, ...)to run atomic functions like RAG or code execution - Streaming –
StreamBusforwards all events to subscribed consumers (CLI renderer, WebSocket push, etc.) - Completion – The orchestrator publishes a
CAPABILITY_COMPLETEevent on the globalEventBusafter 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
ChatOrchestratorindeeptutor/runtime/orchestrator.pyserves as the central router, managing session lifecycle and capability selection - Context: Immutable
UnifiedContextobjects defined indeeptutor/core/context.pycarry request state through the entire execution stack - Streaming: The
StreamBusindeeptutor/core/stream_bus.pyenables real-time event distribution for progressive UI updates - Capabilities: Level 2 agents in
deeptutor/capabilities/implement complex workflows likeDeepSolveCapabilityandDeepResearchCapability - Tools: Level 1 atomic functions in
deeptutor/tools/builtin/expose specific functionalities (RAG, code execution, search) via theToolRegistry - Extensibility: The registry pattern and plugin loader in
deeptutor/plugins/loader.pysupport 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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →