Building Your First Agent With Bub: A Hands-On Tutorial
Step-by-step tutorial to create a Bub agent from scratch — install, chat, write custom plugins, run as a gateway, and compare with traditional frameworks.
Building Your First Agent With Bub: A Hands-On Tutorial
In this tutorial, we'll walk through creating a practical Bub agent from scratch. You'll see firsthand why the hook-first architecture makes agent development more transparent and customizable than traditional frameworks.
🚀 Want to deploy Bub yourself?
Docker configs, system requirements, and installation guides — all on one page.
View Bub Tool Page →Step 1: Install Bub
Bub installs via pip with zero heavy dependencies. The runtime is intentionally small — no bloated framework installs.
pip install bub
Once installed, verify it works:
bub --help
You'll see commands for chat (interactive), run (one-shot), gateway (channel listener), install, and update.
Step 2: Start a Chat Session
Launch Bub in interactive mode — this is the fastest way to understand its "tape-based" context system:
bub chat
Bub will prompt you for a model key. It defaults to OpenRouter's free tier model, so you can start without any paid API key. Try asking it to summarize a topic or write a short script — you'll notice the responses are conversational but grounded in the tape context, which rebuilds from append-only records each turn.
Step 3: Understand the Turn Pipeline
Every message in Bub goes through a well-defined pipeline:
resolve_session → load_state → build_prompt → run_model → save_state → render_outbound → dispatch_outbound
Each of these stages is a pluggy hook. You can inspect which hooks are registered with:
bub hooks
Step 4: Write a Custom Plugin
Let's create a simple "note-taker" plugin that logs every user message to a file. Create a file note_taker.py:
import datetime
from bub import hookimpl
from bub.envelope import content_of
class NoteTakerPlugin:
def __init__(self, log_path="/tmp/bub_notes.log"):
self.log_path = log_path
@hookimpl
def save_state(self, message, session_id, state):
content = content_of(message)
if content:
with open(self.log_path, "a") as f:
f.write(f"[{datetime.datetime.now()}] [{session_id}] {content}\n")
return state
note_taker = NoteTakerPlugin()
To register this plugin, add it to your Bub config's entry points:
[project.entry-points."bub"] note_taker = "note_taker:note_taker"
Now every message gets logged automatically — without modifying any Bub source code.
Step 5: Run Bub as a Gateway
Bub's gateway mode turns your agent into a long-running service that listens on channels like Telegram:
BUB_TELEGRAM_TOKEN=your_token_here bub gateway
The same plugin you wrote for CLI works on Telegram too — adapters change the surface, not the agent logic.
Step 6: Try the One-Shot Mode
For scripting and automation, Bub's run command executes a single turn and exits:
bub run "What are the latest AI news topics?"
This is perfect for cron jobs, CI pipelines, or integrating agent capabilities into existing workflows.
Why We Like Bub
| Aspect | Bub | Traditional Frameworks |
|---|---|---|
| Architecture | Hook-first, fully pluggable | Class-based, configuration-driven |
| Context | Append-only tape (immutable) | Mutable session state |
| Multi-channel | Same pipeline, different adapters | Separate integrations per channel |
| Footprint | ~5 KLOC Python | Often 50-200 KLOC |
🚀 Ready to build with Bub?
Get Docker configs, system requirements, and installation guides on the tool page.
View Bub Tool Page →Bub's approach to agent building is refreshingly pragmatic. By keeping the runtime small and making every stage a hook, it empowers developers to build exactly what they need — no more, no less. Whether you're prototyping a simple assistant or deploying a multi-channel production agent, Bub gives you the foundation without the framework.