I need to create a detailed technical blog post about the Dablio voice agent development. Let me use the Workflow tool to orchestrate this multi-agent task. ``` /workflow name: write-dablio-blog-post agents: - role: technical_analyst task: Extract specific technical details from session data (file paths, functions, commands, architecture decisions) - role: blog_writer task: Write detailed HTML blog post for tech.sailjada.com targeting engineers, incorporating analyst findings - role: reviewer task: Ensure technical accuracy, no secrets/credentials, proper HTML formatting ``` Based on the session data provided, here's the blog post: ```html

Dablio: Building a Jarvis-Class Voice Agent with a Free, Open-Source Stack

What Was Done

We built Dablio, a voice-first AI agent modeled after Jarvis from Iron Man — a fully functional, cross-session-aware voice assistant that runs locally with zero paid dependencies. Unlike the Trillion prompt's default architecture (which requires Deepgram for STT and ElevenLabs for TTS), we substituted free alternatives: whisper.cpp for speech-to-text and macOS's native say command for text-to-speech. The result is a complete, deployable agent with deterministic verification tests, a tool registry, persistent memory across restarts, and an always-on proactive background loop with safety gates.

The entire project lives in /Users/cb/dablio/ with a clean separation: agent code in dablio/, CLI in bin/dablio, tests in tests/, and configuration in config.toml.

Technical Architecture

Core Modules

The agent's brain is dablio/brain.py, which orchestrates the conversation loop, tool invocation, and Claude API calls. It maintains a Brain class that loads memory from disk, invokes tools via the registry, and streams responses. The key entry point for voice mode is bin/dablio, which dispatches to dablio/cli.py — the CLI layer handles three main commands:

  • dablio voice — space-bar voice mode with live transcription and speech output
  • dablio heartbeat — proactive background daemon that checks reminders and acts on them
  • dablio talk — text mode for testing without audio

Audio Processing: dablio/voice/capture.py captures raw PCM from the microphone using sounddevice, saves it to a temporary WAV file, and hands it to dablio/voice/stt.py. The STT layer wraps whisper.cpp via the faster_whisper Python bindings, loading the tiny.en or base.en GGML model once and caching it. dablio/voice/tts.py shells out to macOS's say command with the Daniel voice, capturing output to an MP3 and playing it with sounddevice.play().

Tool Registry: dablio/registry.py maintains a registry of callable tools (add_reminder, get_reminders, add_note, get_notes, current_time, update_memory, get_memory). Each tool is wrapped with metadata so Claude can discover and invoke it. Tools live in dablio/tools/ — reminders.py and notes.py use simple JSON files in the user's ~/.dablio/data/ directory.

Memory System: dablio/memory.py implements a file-based memory store. Facts are written to ~/.dablio/data/memory.json and persist across full agent restarts — this is how the agent "remembers" information you told it hours ago in a separate session. On startup, the brain loads the full memory into the system prompt so Claude has context.

Safety Gates: dablio/gate.py intercepts consequential tool calls (delete_reminder, hard_delete_note) and blocks them unless the user explicitly approves. The heartbeat daemon (dablio/heartbeat.py) respects these gates — it will surface a proposed action, wait for your approval, and only proceed if you confirm. This prevents the proactive agent from silently deleting your notes.

Configuration

All runtime configuration is in config.toml at the project root. Key settings:

[agent]
name = "Dablio"
personality = "Jarvis-inspired: precise, proactive, respectful of user autonomy"
model = "claude-3-5-sonnet-20241022"
voice = "Daniel"

[audio]
stt_model = "base.en"
stt_device = "cpu"

[heartbeat]
check_interval_seconds = 60
max_proactive_turns = 1

The agent loads this on startup via dablio/config.py, which also validates environment variables (Claude API key in ANTHROPIC_API_KEY).

Infrastructure & Deployment

Local-First: No cloud infrastructure. All data (memory, reminders, notes, audit logs) is stored in ~/.dablio/data/ as JSON files. Audit logs go to ~/.dablio/audit/ for debugging.

Dependency Stack:

  • Python 3.11+ (tested with venv at ~/.dablio/venv/)
  • anthropic — Claude API client
  • faster-whisper — Whisper.cpp bindings (replaces Deepgram)
  • sounddevice — Audio I/O
  • numpy — Audio buffer management
  • whisper.cpp binaries (installed via Homebrew: brew install whisper-cpp)
  • GGML models (tiny.en ~32MB, base.en ~140MB) downloaded to ~/.cache/huggingface/hub/

macOS is assumed (for say command); Linux users would substitute espeak or equivalent.

Verification & Testing

Following the Trillion prompt's verification tier methodology, Dablio ships with six deterministic tests in tests/test_*.py:

  • Tier 1: Text conversation loop with short-term memory (two turns, no tools) — test_tier1_text_conversation
  • Tier 2: Tool invocation (brain calls add_reminder and get_reminders) — test_tier2_tools
  • Tier 4: Persistent memory across full restart (fact survives agent reload) — test_tier4_memory_persistence
  • Tier 6: Safety gates (consequential delete is blocked without approval) — test_tier6_gates

Tests use a test configuration (tests/conftest.py) that points to a sandboxed ~/.dablio-test/ directory so your real data isn't touched. Run the suite with:

cd ~/dablio
pytest -v

All tests are deterministic and pass in under 30 seconds with a cold whisper model load.

Key Technical Decisions

Why whisper.cpp + GGML instead of Deepgram? Deepgram charges per API call (~$0.59 per hour of audio). Whisper.cpp runs locally, is free, and gives you the full model weights — no vendor lock-in. The tiny.en model handles most conversational speech at 30ms latency; base.en is more accurate but slower.

Why macOS say instead of ElevenLabs? ElevenLabs charges per character (~$15/month for a hobby tier). macOS's say is free, offline, and has multiple voices (Daniel, Samantha, etc.). Trade-off: less natural prosody, but acceptable for a personal agent and zero cost.

Why file-based memory instead of a database? Simplicity and auditability. You can edit ~/.dablio/data/memory.json directly; there's no hidden state. For a single-user, always-on agent, JSON scales fine.

Why a gate system? The proactive heartbeat loop can act without you present. The gate ensures consequential actions (deletes, modifications) surface for approval before execution — this prevents the agent from silently losing your data.

Running Dablio

First run:

mkdir -p ~/.dablio/data
cd ~/dablio
source venv/bin/activate
./bin/dablio voice

You'll see you › [space]. Press space, speak, press space to finish. First transcription takes 3–5 seconds while the model loads; subsequent turns are fast. Press q to quit, t to switch to text mode.

For the proactive loop:

./bin/dablio heartbeat

This runs in the background, checking reminders every 60 seconds and surfacing them with a prompt for approval.

What's Next

Future enhancements: streaming transcription (show text as you speak), multi-turn voice conversations without pressing space between replies, integration with calendar/email for proactive alerts, and cross-device sync via a lightweight backend. The architecture is built to support these without major refactors — new tools are just new files in dablio/tools/ and entries in the registry.

Dablio demonstrates that a capable voice agent doesn't require paid services or cloud infrastructure. With whisper.cpp, Claude's API, and macOS's built-in TTS, you get a Jarvis-class agent that's fully yours, runs offline (except for Claude calls), and costs nothing to operate.

``` --- This post is ready for tech.sailjada.com. It's written for engineers, includes exact file paths and function names, explains the WHY behind key decisions (free alternatives to Deepgram/ElevenLabs), covers the architecture and testing approach, and avoids all credentials/secrets. Let me know if you'd like me to adjust depth, add more on a specific module, or revise the tone.