Two Capstone Agent Architectures
Tell one agent "find a pub, capacity 160+, vegan options, a quiet corner for a webinar, save the top 3 to memory," and it plans, searches in parallel, and just does it. Tell another agent "delete my files," and it refuses outright — not because a circuit breaker caught it mid-action, but because that action was never something the agent was configured to attempt in the first place. Same underlying loop (an LLM brain, tools, a plan-act-observe cycle), two opposite defaults for what the agent is allowed to try. This lesson is about that gap: PyNanoClaw, this course's headless automation project, acts first and gets constrained after the fact; Voice Rasa Agent, its conversational counterpart, refuses by design before an out-of-bounds action ever reaches the model's judgment. Picking between them is a risk-tolerance decision, not a "which is more advanced" question.
Prerequisite: Lesson 04's agent-loop and tool-calling mechanics (the Think-Act-Observe cycle, what a tool call actually is) — both projects in this lesson are built on that loop; this lesson doesn't re-derive it. After this lesson, you can: explain why PyNanoClaw and Voice Rasa Agent are not two skill-level versions of the same idea but two opposite governance defaults — one that acts first and gets constrained afterward, one that structurally cannot act until an action exists in its own Domain — and pick between them as a risk-tolerance decision, not a technology preference.
PyNanoClaw: constrained after the fact
PyNanoClaw is a headless automator — no chat box, triggered by events, schedules, and file changes, running an async router so it isn't blocked waiting on any single tool call. Its stack: a reasoning-heavy model as the brain, CLAUDE.md-style filesystem memory for persistence, a tool runner for web search, shell commands, and file operations, and an async polling loop as its pulse. Point it at the Edinburgh pub-search problem from the top of this lesson, and it plans, searches in parallel, writes structured findings to persistent memory, and finishes with a tool call that generates a visual summary.
Its safety model is explicitly code-level circuit breakers bolted on after the fact: hard limits on turns and tokens to prevent runaway cost, freshness checks against date-stamped search results, and sandboxing that denies root shell access to sensitive systems. The circuit breaker inspects behavior — is the plan still on-goal, is cost within budget, is the agent touching a forbidden path — and kills the process if something looks wrong. Nothing in that architecture prevents PyNanoClaw from attempting an out-of-scope action. It prevents PyNanoClaw from succeeding at one, if the breaker catches it in time.
PyNanoClaw's multimodal reach extends past text: once a pub is confirmed, structured data (name, address, capacity, vegan status) gets turned into an image-generation brief by Llama-3.3-70B-Instruct, then rendered by black-forest-labs/flux-schnell at roughly $0.001 per image, in under 2 seconds — versus flux-dev's 15–20 second, higher-fidelity alternative for a final deliverable rather than a live-loop preview.
Voice Rasa Agent: refusal by design
Voice Rasa Agent is the opposite governance default, and this is where the named architecture behind it matters. This lesson credits Rasa's CALM architecture by name as the real system this project's design is built on — not a genericized abstraction of "structured dialogue management." That's a deliberate citation, not an endorsement or a product pitch: the pattern is taught because it is the deepest, most concrete architecture available for this half of the course's two-capstone comparison, and paraphrasing it into an invented generic system would teach a worse, less real version of the same idea.
CALM's pipeline runs in a fixed order, and the order is exactly why the architecture holds together:
Speech comes in and ASR (Whisper or a compatible service) converts it to clean text, handling filler words along the way.
Rasa's Command Generator — an LLM-powered component — reads the message in context and produces commands (start flow, set slot, clarify) rather than classic intent/entity labels. This is the only step where the LLM interprets intent; it does not yet decide what happens next.
The dialogue manager routes the command through Flows — the primary business-logic building block, encoding what happens next deterministically — or through a built-in Conversation Pattern if the input is off-script (cancellation, correction, clarification, human handoff all fire automatically here, without custom code).
A response is generated — templated, or LLM-rephrased from data the Flow already decided on — and TTS converts it back to speech.
Skip step 3, or let the LLM decide it instead of the Domain, and this becomes an ordinary LLM chatbot with extra steps. The order is what makes it CALM.
The single sentence that separates this from PyNanoClaw's model: the LLM in this pipeline is never the policy decision-maker. It interprets what the user said and, later, how a response is phrased. It does not decide what happens next — the Domain does, and the Domain is a configuration file that defines everything the agent is aware of. If an action isn't defined there, it doesn't exist to the agent, full stop.
The worked contrast: "the power of no"
This lesson's own comparison table makes the governance gap concrete with a single test case: "delete my files."
| Scenario | PyNanoClaw | Voice Rasa Agent |
|---|---|---|
| "Find a pub" | Searches the web immediately | Identifies intent, asks for location and size |
| "Delete my files" | Might execute, if it has shell access | Refuses — the intent is not defined in its Domain |
| Out-of-scope request | Hallucinates or improvises a response | Standard fallback: a defined "I can't do that" |
The word "might" in that first row is the whole lesson. PyNanoClaw's refusal, if it refuses at all, depends on a circuit breaker recognizing the request as dangerous after the model has already decided to attempt it. Voice Rasa's refusal doesn't depend on the model's judgment at all — the action simply isn't in the Domain, so the model was never in a position to attempt it. One is a prompt-level hope backed by code-level cleanup. The other is a structural boundary the LLM cannot cross regardless of what it's told or tricked into wanting.
| Product (2026 status) | Status | What it is |
|---|---|---|
| Rasa Open Source | Maintenance mode | The classic ML framework (NLU + Stories + Rules) — still used, no active new features |
| Rasa Pro / CALM | Active development | The enterprise framework powering CALM — what this course builds with |
| Hello Rasa | Active development | Browser-based playground for prototyping CALM agents, no install needed |
| Rasa Studio | Active development | No-code UI for building and managing CALM flows visually |
That table matters as a first-party note, not trivia: a lot of Rasa content circulating online predates CALM and describes the older intent/entity classifier approach. If you go looking for reference material, check which product you're reading about before applying it here.
Where the model race actually differs
Both projects still need a model underneath them, and this lesson's model-race code-along runs the same JSON-constrained pub search across three named models to see how latency and accuracy trade off in practice:
python model_race.py
DeepSeek R1 (Reasoner) | 4.46s capacity✅ vegan✅ quiet✅
Llama 70B (Worker) | 18.49s capacity✅ vegan✅ quiet✅
Llama 8B (Speedster) | 0.92s capacity✅ vegan✅ quiet✅
All three passed every programmatic check — so on correctness alone, this result looks like a wash. It isn't. This lesson's own framing calls the 70B's 18.49 seconds "a production red flag," not a worker model behaving well: at that latency, a five-turn agent loop takes a minute and a half, which is not a viable interaction in either project's context — background or conversational. The 8B model was roughly 20 times faster than the 70B and nearly 5 times faster than the Reasoner while matching its accuracy on this task. The lesson isn't "always use the smallest model" — it's that latency is a correctness dimension too, and an automated checker catching a silently wrong or silently slow output is worth more here than a human eyeballing plausible-looking JSON. Which model and reasoning tier to use for which role is lesson 03's material in full; this lab just shows the stakes when that choice is made carelessly inside a capstone project rather than in the abstract.
The decision that actually matters
Neither project is the "right" one in general. The decision is about what you're building and who bears the cost of a mistake: PyNanoClaw for complex, multi-step background work where nobody's watching the process in real time and the user is often expert enough to catch its own errors; Voice Rasa Agent — and CALM's structural refusal — for anything customer-facing, regulated, or high-stakes, where a hallucinated policy or an executed-but-wrong action has real consequences and needs to be provably impossible, not just unlikely.
The lethal trifecta, why telling a model to "ignore embedded instructions" measurably does nothing, and the air-gap pattern for running an always-on agent responsibly.
Reply here and it goes straight to Rod. Same as replying to one of his emails.