← Documentation index Microdesign › Praxis Engine

Praxis Engine

the Greek pentad that thinks, executes, remembers, rescues, recognises
Microdesign v1.0 — 25 May 2026
Aligned with ADR 0161.
Replaces the legacy step-by-step PLANNER.

Audience: people who want to understand how Metnos thinks,
why it got six times faster, and how it stopped calling the cloud.
Reading time: 18 minutes.

Contents

  1. One-liner: five deities, one engine
  2. The real problem: the step-by-step PLANNER
  3. The myth that drove the refactor
  4. The pentad, deity by deity
  5. The cascade across a turn
  6. Praxis, the memory that grows
  7. Mētis, the cunning adviser
  8. Noûs, the doubt-free executor
  9. Pronoia, the providence that sees first
  10. Aporia, the dead-end that becomes a proposal
  11. Bench: honest numbers
  12. What changes for the user
  13. Why it beats OpenClaw and LangGraph
  14. Open questions
  15. References
The Greek pentad of Metnos cognition: Mētis with scroll, Noûs with book and halo, Praxis at center with luminous orb and starred mantle, Pronoia with solar shield, Aporia in dark mantle
The five deities of the pentad: Mētis (advisor with scroll), Noûs (intellect with book and halo), Praxis (memory at center, starred mantle), Pronoia (providence with solar shield), Aporia (honest dead-end, dark mantle).

1. One-liner: five deities, one engine

When a request arrives, Metnos no longer calls the LLM planner five or six times in a row. It hands the turn to five modules named after five Greek deities: Mētis advises, Noûs executes, Praxis remembers, Pronoia rescues when something stumbles, Aporia honestly admits when we're stuck. Together they answer in twelve seconds where the legacy planner needed seventy-six — without ever asking the cloud for help.

2. The real problem: the step-by-step PLANNER

Until 24 May 2026 Metnos worked like this: at every turn, the planner (a local Gemma 4 26B) was called repeatedly — one step, the observation, the next step, the next observation, and so on. A five-step pipeline meant five or six model calls. Each call took ten to fifteen seconds. Net result: one turn averaged a minute and a quarter. And it didn't always finish.

The real problem wasn't slowness: it was stochasticity. The same intent — “find spam mail and move it to trash” — repeated five times under identical conditions, gave results {0, 1, 0, 1, 1}. Three times out of five the planner “lost the plot” halfway through and changed direction. Raising the reasoning budget didn't help: the problem was structural. Each model call is a die roll; rolling six dice and hoping they all come up six is unreasonable.

A third, subtler problem compounded the first two: the search space. The planner saw two hundred tools every turn and had to rebuild the right sequence from scratch, even for sequences it had seen a thousand times (“find mail, classify spam, move to trash”). Rediscovering the obvious is wasteful — but the system had no memory.

3. The myth that drove the refactor

Zeus married Mētis, goddess of cunning counsel. But when she became pregnant, Zeus swallowed her whole — so as to keep her wisdom inside him forever, without ever again needing to ask outside advice. Since then Mētis lives in Zeus's belly: she whispers strategies from within, but she is no longer seen. — Hesiod, Theogony, vv. 886-900

The 25 May 2026 refactor took this image literally. The step-by-step PLANNER gets swallowed by a new module called Praxis (πράξις, “learned practice”). The language model doesn't disappear: it still lives inside Praxis as Mētis (Μῆτις, “cunning counsel”). But its role changes radically:

This is Zeus swallowing Mētis: the wisdom lives inside Praxis, but it is only invoked when accumulated experience is insufficient. As the catalog matures, Mētis stays silent for days. The system becomes its own adviser — it learns not to ask any more.

But the myth doesn't end there. Two more deities enter the stage when things go wrong:

Pronoia, according to some sources Prometheus's mother, is divine providence. Her name literally means “intellect-before”: pro-noûs. She sees the obstacle while the others are already stumbling into it. She intervenes after the plan has failed, not before. — Aeschylus, Prometheus Bound
Aporia (ἀπορία) means “perplexity, dead-end”. In the Socratic dialogues it's the moment when the interlocutor realises they don't know — and that realisation, far from being a defeat, is the beginning of real inquiry. — Plato, Meno, 80a-d

Pronoia steps in when Mētis's plan fails: she figures out the kind of error, proposes an alternative, retries. Aporia steps in when even Pronoia can't save the turn: she honestly admits we can't proceed, tells the user what's needed to break out of the impasse, and keeps a record of the gap for the future.

4. The pentad, deity by deity

Μῆτις

Mētis

cunning adviser

File: runtime/praxis_propose.py (~229 LOC).

What she does: takes the query and proposes an entire multi-step pipeline in one LLM call (local Gemma 4 26B, wise tier, reasoning enabled). Output: structured JSON constrained by a GBNF grammar — list of steps, ${FILLER:name} placeholders, final-message template.

Greek flavour: cunning, situated, technical. She knows “the trick” for getting out of the situation; she isn't a general solver. That's why she proposes one plan, not ten alternatives.

When she speaks: only when Praxis doesn't already have the answer. Once per turn, never twice. Time: about 15 seconds.

νοῦς

Noûs

pure intellect that executes

File: runtime/praxis_executor.py (~430 LOC).

What he does: receives the framework from Mētis and realises it, step by step, fully deterministically. No language model in the loop: only data routing. Expands ${FILLER:name} (a small fast-tier LLM on the fly if needed), expands ${stepN.field} from previous-step results, invokes the executor, hands the result to Vaglio, accumulates. At the end he renders the final message.

Greek flavour: clear, faithful, doubt-free. The mind that sees and acts, not the one that doubts. That's why he never revises the plan: he realises it.

When he works: always, on every plan (whether it comes from Praxis, from Mētis, or from Pronoia).

πρᾶξις

Praxis

memory of what worked

File: runtime/praxis.py (~560 LOC) + sqlite ~/.local/share/metnos/praxis.sqlite.

What she does: keeps memory of every framework that worked, indexed by intent signature. When a turn arrives, she computes the intent fingerprint (verb + object + keywords) and looks it up in O(1) on the skills table. Hit? Used as-is. Miss? Call Mētis. When a plan succeeds twice on the same intent, she auto-promotes it to active skill; when it fails three times in a row, she anti-skills it for thirty days.

Greek flavour: for Aristotle (Nicomachean Ethics) praxis is action grown through repeated experience. Not contemplation, not production: the know-how that becomes second nature. That's why here it's a memory that learns from feedback.

When she acts: at the start of every turn (matching attempt) and at the end (observation recording).

Πρόνοια

Pronoia

providence that sees first

File: runtime/pronoia.py (~250 LOC).

What she does: intervenes only if Mētis's plan fails. Classifies the error into one of three orthogonal classes (wrong tool, wrong args, missing input), picks a specialised prompt per class, and re-invokes Mētis with the instruction to exclude the failed tool and propose an alternative. If Pronoia saves the turn, Praxis records it and next time we start straight from the correct plan.

Greek flavour: Pronoia (pro-noûs) is intellect-before, divine foresight that sees the obstacle while the others are already stumbling. Mother of Prometheus in some sources, hence grandmother of fire stolen from the gods: a deity that unlocks, that opens new paths.

When she intervenes: at most once per turn. If Pronoia herself fails, she gives the floor to Aporia.

ἀπορία

Aporia

recognised dead-end

File: runtime/aporia.py (~210 LOC) + sqlite ~/.local/share/metnos/aporiae.sqlite.

What she does: final honest instance. When even Pronoia fails, Aporia doesn't pretend: she classifies the gap into one of four categories (needs user action, missing executor, missing skill, missing data), generates a final message telling the user exactly what's needed to break the impasse, and logs the persistent gap in sqlite. If the user later resolves it (enables a skill, shares location, builds an index), the query returns to the normal Praxis cascade and typically becomes an active skill within two or three confirmations.

Greek flavour: for Plato aporia is not defeat but a starting point. It's the Socratic moment in which the interlocutor recognises ignorance — and that recognition is the beginning of inquiry. That's why Aporia not only admits the limit: she turns it into an evolutionary proposal.

When she acts: rarely. Only when the cascade has gone all the way down without a solution. Everything deterministic, zero LLM inside Aporia.

5. The cascade across a turn

Here is the full path, from the user's hello to the final answer. Read top to bottom: each layer is tried only if the one above didn't already resolve.

        USER: "find spam mail and move it to trash"
             |
             v
   +-------------------+
   | fast_path L0      |  regex on banal patterns ("what time", "where am i")
   | (zero LLM)        |  --> match? answer in 50ms and end
   +-------------------+
             | (no match)
             v
   +-------------------+
   | intent_extractor  |  LLM fast tier ~370ms
   | verb + object +   |  --> (verb="move", object="messages",
   | keywords          |        keywords=["spam","trash"])
   +-------------------+
             |
             v
+--------------------------------------------------------------+
|   PRAXIS ENGINE (the three internal deities)                 |
|                                                              |
|  +----------------+                                          |
|  | Praxis         |  sha[:16] hash of intent_sig             |
|  | try_match      |  --> cache hit O(1)? jump to Nous        |
|  | (sqlite)       |                                          |
|  +----------------+                                          |
|         | (miss)                                             |
|         v                                                    |
|  +----------------+                                          |
|  | Metis          |  LLM wise 1-shot, ~15s                   |
|  | propose        |  --> framework JSON {steps, fillers,     |
|  | (Gemma 26B)    |       final_message}                     |
|  +----------------+                                          |
|         |                                                    |
|         v                                                    |
|  +----------------+                                          |
|  | Nous           |  for each step:                          |
|  | execute        |    - resolve from_step + FILLER          |
|  | (deterministic)|    - invoke_executor                     |
|  +----------------+    - vaglio.judge                        |
|         |              - accumulate                          |
|         v                                                    |
|  +----------------+                                          |
|  | render final   |  template "Found {N} mails, moved"      |
|  +----------------+                                          |
+--------------------------------------------------------------+
             |
             v  (ok? yes --> Praxis records + replies to user)
             |
             |  (error? yes --> Pronoia enters)
             v
   +-------------------+
   | Pronoia           |  classifies: wrong_tool / wrong_args /
   | classify + retry  |    missing_input
   |                   |  --> re-proposes framework excluding failed_tool
   |                   |  --> re-runs via Nous
   +-------------------+
             |
             v  (ok? yes --> reply)
             |
             |  (still error? --> Aporia enters)
             v
   +-------------------+
   | Aporia            |  classifies root_cause: user_action_required /
   | honest dead-end   |    missing_executor / missing_skill / missing_data
   |                   |  --> log in aporiae.sqlite
   |                   |  --> "Can't resolve: X. To proceed: Y."
   +-------------------+
             |
             v
        USER: final message (reply or action request)
For the technically inclined. Filler resolution and consumer_arg passthrough are the two mechanisms that make the framework executable without further model calls. The first is an ad-hoc fast-tier LLM for a single slot (e.g. “what's the trash folder name?” → “Trash” in half a second); the second is pure key-name lookup in the previous step's result, fully deterministic.

6. Praxis, the memory that grows

The engine's heart is the sqlite database. Three tables, all indexed for O(1) access:

TableWhat it holdsWhen it's written
skills Pipelines that worked, indexed by intent hash. Each: framework JSON, usage counters, success rate, version. Auto-promoted when a pipeline has at least 2-3 consecutive successes on the same intent.
anti_skills Pipelines that failed repeatedly. Each: reason, TTL 30 days. When a pipeline fails three times in a row on the same intent. After 30 days, retried.
observations Complete per-turn log: intent, executed framework, verdict, latency. Append-only. Always, end-of-turn. Source of truth for audit and promotion/anti-skill decisions.

The interesting phenomenon is that the system gets faster over time, by itself. The first time you say “find spam mail and move it to trash”, Mētis spends her good fifteen seconds generating the framework. The second time, Praxis already has the same exact intent fingerprint in cache: the pipeline starts in three milliseconds. From that point on, that specific request never touches the language model again.

The user notices. After two or three turns on the same family of requests, perceived speed changes abruptly: from “tens of seconds” to “instant”. No cache to flush, no setting to enable: Praxis learns from feedback, period.

Feedback is the grammar of memory

Below every chat reply, the user sees three small buttons: ✓ green (“correct answer”), ✗ red (“wrong answer”), ↺ orange (“retry”). Those three glyphs are the language by which Praxis learns:

Nothing has to be pressed: if the user gives no feedback and a pipeline finishes error-free two turns in a row, Praxis promotes it anyway (auto-promote). Explicit feedback accelerates and refines, but the system also learns from silence.

7. Mētis, the cunning adviser

Mētis is the only deity in the pentad that actually talks to the language model. Her job: given a query and an extracted intent, produce in one call the entire plan to execute. No step-by-step reasoning, no iterative tool-calls: just one big JSON object describing the whole pipeline.

{
  "steps": [
    {"tool": "find_messages",
     "args": {"folder": "INBOX", "query": "is:unread"}},
    {"tool": "classify_entries",
     "args": {"from_step": 1, "category": "spam"}},
    {"tool": "filter_entries",
     "args": {"from_step": 2, "where": {"category": "spam"}}},
    {"tool": "move_messages",
     "args": {"from_step": 3, "dst_folder": "${FILLER:trash_folder}"}}
  ],
  "fillers": {
    "trash_folder": {
      "prompt": "What's the trash folder name for this account?",
      "default": "Trash",
      "tier": "fast"
    }
  },
  "final_message": "Moved ${step4.ok_count} mails to trash."
}

The model call is constrained by a deterministic GBNF grammar: the model cannot produce malformed output because the decoder rejects in real time any token that violates the schema. Average time: 15 seconds on the local Gemma 26B, with reasoning enabled (2048-token budget). Cost: zero. Six times faster than the legacy step-by-step mode.

Why one shot beats six. The model “loses the plot” when it has to decide step by step because each decision is a variance point. With one shot it sees the whole problem at once, builds internal coherence in the plan (e.g. remembering that step 4 needs step 3's output), and produces a far more stable plan. Bench n=10 on the same query: 9/10 identical plans, 1/10 slightly different but equivalent.

Anti-fixation: Mētis doesn't repeat the same mistake

If Pronoia calls Mētis for a re-propose after a failure, she passes the list of tools that failed in the previous turn. The prompt includes an explicit instruction: “DO NOT propose these tools, they already failed”. So Mētis doesn't loop in circles, and every failure becomes an opportunity to explore a different route.

8. Noûs, the doubt-free executor

Noûs is the most rigorous module: zero language model in the main loop. Only deterministic Python. His job:

  1. For each step, resolve from_step: N references (extract entries from step N).
  2. Resolve ${FILLER:name} by calling a small fast-tier LLM (one per filler, half a second each) or by using the default if Praxis already cached the answer.
  3. Resolve ${stepN.field} in the final-message template by extracting the field from the step's result.
  4. Validate args (requires_one_of, forbid_placeholder_values) before invoking the executor.
  5. Invoke the executor and collect the result.
  6. Pass the result to Vaglio (the safety judge).
  7. Accumulate into step history.

This loop is deterministic by construction. No “plot loss”, no plan reconsideration. If a step fails, Noûs doesn't improvise: he stops and gives the floor to Pronoia.

A hidden virtue: reproducibility. The same framework executed by Noûs on the same data snapshot always produces the same exact outcome. That means past turns can be replayed pixel by pixel, and E2E tests can assert bit-by-bit. Impossible with a stochastic PLANNER.

9. Pronoia, the providence that sees first

Pronoia is the safety net. She activates only if Noûs hit an error while executing Mētis's plan. Her first job: figure out what kind of error it is.

ClassWhenRecovery strategy
wrong_tool The chosen tool was unfit: crashed, semantically wrong, or hallucinated an argument. Re-invokes Mētis saying “this tool is off the table, propose a different plan”.
wrong_args The tool was right but args were malformed: spinning pipeline, cap_steps reached, from_step pointing to nothing. Re-invokes Mētis with the instruction “canonical args, explicit from_step”.
missing_input A backend doesn't respond, an index isn't built, a directory doesn't exist, a filter produces zero results. Suggests a setup step (build the index, mount the share) or hands off to Aporia if the user is needed.
out_of_scope Structurally unrecoverable error: the user must do something physically (e.g. share location on Telegram) or a capability is entirely missing. Pronoia does not intervene: she hands off to Aporia immediately.

Classification is deterministic: textual markers, no language model. Only the framework re-proposal uses Mētis again (because that's an intrinsically creative act).

Budget: Pronoia intervenes at most once per turn. If even the alternative plan fails, she hands off to Aporia. No infinite loops, no “one and a half” further attempts.

10. Aporia, the dead-end that becomes a proposal

Aporia is the subtlest piece of the system. She is not an “error handler”: she is an honest epistemic recognition. By the time her turn comes, the whole cascade has tried and failed. Aporia doesn't try to hide it: she classifies the gap, names it clearly to the user, and suggests what would be needed to get out.

CategoryReal exampleSuggested action
user_action_required “Where am I?” without shared location on Telegram. “To tell you where you are, share your location here in chat (paperclip → Location).”
missing_executor New vocabulary, intent not covered by the catalog. “I can try to synthesise a new executor for this. Confirm?” (synth request)
missing_skill “Search Drive” but the Google Drive skill isn't enabled. “Google Drive skill not enabled. Would you like to configure it? Requires OAuth.”
missing_data “Find 2019 photos” but the image index has never been built. “Photo index missing. Would you like me to build it now? About ten minutes.”

Everything deterministic, zero LLM inside Aporia. The gap is logged in aporiae.sqlite: the next day, the occurrence count is visible on the admin dashboard /admin/aporiae, and we can decide whether it's time to close it structurally (synthesise the missing executor, enable the skill, build the index).

Aporia is evolutionary. When the user resolves the gap (shares location, enables the skill, builds the index), the same query returns to the normal Praxis cascade and typically becomes an active skill after two or three confirmations. Every recognised dead-end is a growth opportunity for the system. Aporia's “not knowing” is the first step of future “knowing”.

11. Bench: honest numbers

Test set: 35 real queries, collected from production turn logs + smoke battery + E2E sample. They cover mail, files, web, location, time, multi-turn dialogues, scheduling, contact management.

ModeCoverageMean latencyModelCost
Legacy step-by-step PLANNER ~33/35 estimated 76.3 s Anthropic Opus 4.7 $$
Praxis + Pronoia + Aporia 33/35 (94%) 12.5 s Local Gemma 4 26B 0

Six times faster. Equivalent coverage. Zero cost (model runs on Strix Halo 96GB unified). Agent codebase reduced by 55% (about 8000 LOC of full PLANNER removed).

Epistemic honesty. Two queries out of 35 fail in both modes. They're real out_of_scope cases: without shared location we can't tell where you are; without the Drive skill enabled we can't search there. In Praxis mode those two are caught by Aporia, who tells the user what's needed. In legacy mode the PLANNER just failed or looped. Neither side “wins” falsely: the failure is classified and the user knows what to do.

12. What changes for the user

The visible difference, from the user side, comes in three points:

1. First requests are faster

Even on first hit, Praxis doesn't call the step-by-step planner: it makes a single 1-shot call to Mētis. Result: 12 seconds instead of 76. The feel is of a system that “takes a moment” instead of one that “hangs for a minute”.

2. Repeated requests are instant

After two or three turns of the same request family, the skill is active and subsequent turns start as cache hits. The answer arrives before you finish reading the message you just sent: under a second, often under 500 ms. That's the difference between “assistant” and “immediate reaction”.

3. Failures become informative

When something can't be done, Aporia says exactly what's needed. No vague “generic error” messages, no loops of pointless attempts: one clear sentence, one operative suggestion, and (if appropriate) a dialog to resolve the gap on the spot.

13. Why it beats OpenClaw and LangGraph

OpenClaw, Lobster, LangGraph and friends are frameworks for writing “LLM agent workflows” in YAML or Python. They partially address the same problem: reduce LLM-in-loop variance by giving structure to tasks. But they have limits that Praxis doesn't:

AspectOpenClaw / LangGraphPraxis Metnos
Workflow origin hand-written YAML, manual maintenance optional seed, but they grow automatically from ✓ ✗ ↻ feedback
Learning no yes: cache populated from successful turns
Anti-error no anti_skills table excludes failed paths for 30 days
Structured recovery generic retries Pronoia with 3 orthogonal classes + targeted re-propose
Honest dead-end infinite retry or crash Aporia classifies + suggests user action
Audit none, or text logs observations sqlite, /admin/changes UI
Vocab compliance free names Naming Authority (verb + object + qualifier) guaranteed
Decay no 30-day efficacy ager

The analogy that clarifies everything

Traditional LLM-in-loop frameworks (OpenClaw, LangGraph) are like artists with a blank canvas: they reinvent the composition every turn. Beautiful creativity, but hallucinating a brush instead of a pencil is a real risk, and each artwork is expensive.

Praxis is like a craftsman with a sketchbook: Mētis sketches the plan once (concentrated, constrained creativity), and from then on the craftsman opens the book to the right page. Model creativity is still there, but it's confined to the moment of invention: execution is faithful reproduction of the sketch. That avoids execution-time hallucinations, gains speed, gains zero cost when the sketchbook is already rich.

The LLM remains essential but changes role. It's no longer the orchestra conductor: it's the composer-adviser we call only when a new piece is needed. The orchestra (Noûs) plays on its own, and the conductor (Praxis) knows which pieces are ready and which still need writing.

14. Open questions

15. References

Companion reading for fans of the history of ideas. Detienne and Vernant show that Greek mētis is not abstract “intelligence”: it's the situated cunning of the fisherman, the blacksmith, the navigator. They know “the trick” for getting out of a specific situation. That's exactly the spirit in which praxis_propose.py proposes frameworks: not general solvers, but technical insights for the concrete case.