The next evolution of my Agentic AI Workflow Framework

[1] The SCHEMA — Teaching AI Exactly What Shape to Answer In

In my previous post (here), and the demo video (here), I showed how we use the new Coda GO AI side-panel to run sophesticated Agentic Workflows.

The approach worked, but it leaned on a sidebar AI agent with full MCP access to the document, reading pseudo-code SKILLS pages and improvising its way through tables.

That was very powerful, but also has a lot of surface area for things to drift. It burned way more tokens than really necessary. It was a lot slower as a result. And it needed a LOT of extra SKILL and PROMPT engineering to keep it deterministic (hence the ‘pseudo-code’ style).

Over the last month, my clients and I have evolved this approach further, to make it run 10x faster, use 60% less tokens, behave way more reliably and deterministically. But still not need users to write any formulas or packs.

Because we are geeky, we called it The Agentix Framework (geeks love brands ending in X or Z). We demonstrate it here on Coda, but the same framework has been used in Notion, Excel, Google Sheets, Airtable, and should be applicable to ANY no-code platform.

This new approach takes a different path: no external agent, no MCP loop, no pseudo-code interpreter. Just Coda’s own native AI columns, driven by pages you already know how to write. This chapter is about the first of four ideas that make that possible: The SHEMA.

The Real Problem Was Never the Logic

Anyone who has ever asked an AI for “the customer’s order status” knows the answer comes back differently every time — a sentence here, a bulleted list there, a field renamed or quietly dropped. That is fine in a chat window. But it is fatal inside a workflow, where the next step is a formula reading a specific field, not a person parsing a sentence. The hard problem was never getting the AI to reason correctly — it was getting it to hand back its answer in an exact, predictable shape, every single time.

The SCHEMA: A Human-Written Contract, Nothing More

A SCHEMA is just plain text on a normal Coda page — no code, no JSON, nothing a developer would need to write. It’s the same shorthand you’d use to sketch column headers for a spreadsheet:

Order_id: the order identifier
Customer_Name: full name of the customer
Order_Status (options: pending, shipped, delivered, cancelled): current status of the order
LINE_ITEMS (list): one entry per product ordered
- Product_Name: name of the product ordered
- Quantity: number of units ordered

Each ‘field’ is a name plus a short “micro-prompt” describing exactly what belongs there.
(options: ...) locks a field to a fixed vocabulary, so you never get five different variations of the same status.
(list) with it’s indented sub-fields defines a repeating group — one row per item, exactly like a table.

One SCHEMA, Two Audiences

The trick is that this text does double duty:

  • A business owner reads it and knows precisely what will come back. And — unmodified, word for word
  • it’s also the literal instruction handed to the AI. There is no separate spec document that quietly goes stale next to the “real” prompt. The documentation is the prompt.

Real Workflow Example

Here is the actual SCHEMA driving the first step of the Pump Configuration workflow from my previous post:

FlowRate: maximum sustained flow, converted to litres per minute
StaticHead: vertical pumping distance, converted to metres
FluidType(options: Clean, Mildly abrasive, Abrasive, Highly abrasive): industry standard classification
SolidContent: percentage solids by volume, numeric only
ConfidenceRating (options: High, Medium, Low): as defined in the instructions
NextAction: always set this to exactly “Headrace”

Notice what each micro-prompt is quietly doing. “Converted to litres per minute” forces unit normalization no matter how the source engineering report phrased the flow rate. “Classify as one of: Clean, Mildly abrasive…” forces a judgment call from a fixed vocabulary instead of the AI inventing its own adjectives.
NextAction is just another field; the workflow’s next step is decided by the same declarative mechanism as everything else.

Nested Lists for Real Structure

The same notation scales to genuinely complex output. Here’s the SCHEMA for the Bill of Materials step:

Customer_ID: the customer reference number
PARTS (list): one entry for each part belonging to the selected Headrace, PumpEngine, or Tailrace component
- PART_NUMBER: the part number from the parts list
- Component: the component code this part belongs to
- PartName: the part name from the parts list
- Notes: the notes text from the parts list
PartCount: the total number of parts returned

However many parts the AI finds — four, forty, whatever the reference data holds — this one indented block tells it to return every single one in exactly this shape. No loop to write, no clever prompt-chaining trick. Just the same “one row per X” pattern you already reach for in a spreadsheet.

Why This Beats Clever “Prompt Engineering”

None of this precision comes from prompt-engineering tricks. It comes from being specific, in plain English, about shape and vocabulary.

And because the SCHEMA only defines what comes back — the how stays entirely in the Skill instructions and the Reference decision tables — you can revise a schema without touching the logic underneath it, the same way you’d redesign a report template without touching the calculation engine behind it.

The Key Benefits

Predictable output, every run. The AI hands back the same fields, in the same shape, whether it’s the first call or the thousandth — no parsing a sentence to find the answer.

One document, not two. The SCHEMA page is simultaneously the human-readable spec and the literal AI instruction — nothing to keep in sync.

Forced vocabulary, not invented adjectives. (options: …) eliminates the drift you get from free-text classification.

Repeating structure without writing a loop. (list) handles any number of items in one declarative block.

Logic and shape stay separate. SCHEMA defines what comes back; Skill and Reference pages define how it’s computed — change one without touching the other.

No developer required. Anyone who can sketch spreadsheet column headers can write a SCHEMA page.

Next Up…

My next post will cover the other half of this magic: The MEMORY — how each step’s answer is safely folded into a running record, so every later agent in the chain knows exactly what’s already been decided, without wading through a full conversation history to find out.

Then after, that I will cover the other ingredients of the framework: The NEXT ACTION element, and why we use JSON for internal & external information exchange.

respect,
:lobster:Max

5 Likes

[2] MEMORY: A Sticky-Note, Not a Filing Cabinet

My previous post covered The SCHEMA — how a plain-English page tells the AI exactly what shape to hand back.

But a real workflow isn’t one call, it’s a chain of them.

The Headrace step needs to know what ExtractParameters found.
The Validate step needs to know what Headrace, PumpEngine, and Tailrace steps all decided.

Something has to carry that forward. In the old “CodaClaw” approach, that was a live table set (TASK, CONFIGURATION, HISTORY) that the side-bar agent read and rewrote row by row, using the MCP server, deciding for itself what to delete, what to insert, what to update.

The new Agentix Framework replaces all of that with one idea: The MEMORY.

The Problem With Carrying State Forward

Every extra step in a chain is another chance for the AI to lose track of something it already decided, contradict an earlier answer, or simply forget a field existed. The more moving parts your state lives in — three tables, a dozen rows, a running conversation — the more surface area there is for that kind of drift.

What you actually want is dead simple: one place that always holds everything decided so far, and a rule so mechanical it never needs the AI’s judgment to apply correctly.

The MEMORY: A Single JSON Object, Merged Forward

In Agentix, a workflow’s entire state lives in one ordinary page, holding one JSON object.
Each agent step only has to emit what it itself just figured out — not the whole history. A plain CFL formula does the rest:

MEM.ParseJSON()._merge(Result.ParseJSON())

Read left to right: take what Memory already knows, layer this step’s new Result on top, and where a key exists in both, the new one wins. That’s the entire mechanism. No table design, no relational joins, no agent deciding which row to delete before inserting a fresh one.

Watching Outcomes Accumulate

Here’s the Pump Configuration chain in practice. Step 1 (ExtractParameters) emits:

{"FlowRate":"420","StaticHead":"75","FluidType":"Abrasive", ..., "NextAction":"Headrace"}

Step 2 (Headrace) never has to repeat any of that — its own SCHEMA only asks for two new fields:

{"HeadraceComponent":"HR-300","HeadraceReason":"...","NextAction":"PumpEngine"}

The merge folds them together automatically. By the time Validate runs three steps later, the full picture — flow rate, fluid type, all three selected components — are sitting there waiting, without any step having had to echo it back. Each SCHEMA stays exactly as small and focused as I previously described, precisely because MEMORY is quietly doing the accumulation underneath it.

One Object, Three Jobs

The same object is available for 3 roles for every AGENT:

  • as Data (what goes into the next prompt),
  • as Memory (what gets read before merging),
  • and as Storage (where the merged result lands).

It’s the agent’s Sticky Note and its briefing document at once — nothing to keep in sync by hand.

A Clean Start, Every Time

A workflow’s first step is flagged with a checkbox;Begin?.
If set, the AGENT clears the Memory/Storage page before anything else happens, so a new run never inherits a stray field from the last one.

The Key Benefits

  • Small, precise schemas stay small. Each step only describes what it computes, not everything already known — the token-efficiency and precision benefits from Chapter 1 hold all the way down a long chain, not just for a single call.

  • No database to design. One JSON blob replaces a set of tables an agent would otherwise have to choreograph row-by-row — there’s nothing to model, migrate, or get out of sync.

  • State changes are deterministic, not agent-narrated. The merge is a formula, not a task the AI has to remember to perform correctly — an entire class of “forgot to update the tracking row” bugs simply can’t happen.

  • One current snapshot, always. No digging through a long transcript or scattered table rows to answer “what do we know right now” — it’s one page, one object, always up to date.

  • New steps drop in for free. Adding a step to an existing chain (we did exactly this with a Bill-of-Materials step) needs no schema or table changes elsewhere — just point its Data/Memory/Storage at the same page everyone else already uses.

  • Business users can just read it. Memory is a plain page, viewable as JSON or as YAML — not a hidden execution trace buried inside an agent’s session that only a developer could inspect.

Next Up..

My next post will cover the piece that turns this into a genuine workflow rather than a string of one-off calls: NextAction — how the AI’s own structured answer decides what runs next, including how the chain handles failure and retries itself back onto a valid path.

respect,
:lobster:Max

4 Likes

i love this part:

thanks for sharing your ideas @Max_OBrien

4 Likes

[3] NextAction: Letting the Answer Decide What Happens Next

My previous posts covered The SCHEMA (the shape of the answer) and The MEMORY (how answers accumulate). This ‘chapter’ covers the piece that turns a chain of one-off calls into an actual workflow: NextAction — and how the same declarative approach lets the AI handle branching, looping, and even correcting its own mistakes.

How to Decide What Runs Next

Every workflow tool needs an answer to “what happens after this step?” Traditionally that means ‘a state machine’, or, in the CodaClaw approach, imperative pseudo-code baked into the SKILL or prompts: while result = "Illegal" and attempt < 5, if thisrow.Status = "ERROR" then stop, written by hand and interpreted turn by turn by the side-bar AI agent.

The new “Agentix Framework” needs none of that. It just needs one more field in the SCHEMA and in the MEMORY.

NextAction Is Just Another Field

Recall that a SCHEMA is a list of fields with micro-prompts.
NextAction is nothing special — it’s defined exactly the same way as every other field:

NextAction: always set this to exactly “Headrace”

And from the Validate step:

NextAction: if ValidationResult is Illegal, set this to exactly “Backtrack”; if ValidationResult is Legal, set this to exactly “BillOfMaterials”

There’s no separate control-flow language to learn. The same plain-English notation that defines what data comes back also defines what happens next.

One Action CFL, Reading Its Own Output

The mechanism that acts on this is almost embarrassingly simple. Once a step’s answer comes back, an action CFL reads the NextAction value out of it and, if it’s not blank, calls whichever Agent row shares that name:

WithName(Result.ParseJSON("$.NextAction"), NEXT,
  SwitchIf(NEXT.IsNotBlank(), AGENTZ.Filter(Name=NEXT).Call)
)

That’s the entire orchestrator. A blank NextAction simply means the workflow is done — there’s no separate “stop” instruction to write or forget.

Self-Healing: The Retry Loop

Even a precise SCHEMA occasionally produces a malformed response — a stray character, a broken bracket.
Occasionaly (aound 1 in 100) the LLM fails to respond in pure JSON.
Rather than trust the AI to grade its own homework, each step retries up to N times (currently N=3), checking one mechanical thing via CFL:

  • does the response contain JSON between ‘{’ and ‘}’.

Simple, cheap, and it catches the overwhelming majority of failures without any judgment call at all.

Self-Correction: Validate and Backtrack

The flagship example is the Pump Configuration workflow’s Validate/Backtrack loop.

When Validate finds an illegal combination of components, NextAction routes to Backtrack.

Backtrack reasons about which section is actually at fault, revises just that one component, and sets NextAction back to "Validate" — looping until the configuration is legal, or until a retry ceiling is reached:

NextAction: if RetryCount is less than 3, set this to exactly “Validate”; if RetryCount is 3 or more, set this to an empty string “” (stop retrying after 3 attempts)

That’s a genuine bounded loop with a termination condition — expressed as a single sentence inside a SCHEMA field, not a hard-coded counter buried in a script.

Branching and Looping, Without a Flowchart

In this way, NextAction gives us sequencing, conditional branching, and bounded retry loops:

  • the three building blocks of any real workflow

Using nothing but the same plain-English micro-prompts in our SCHEMA text.
No orchestration engine, no separate scripting layer, no pseudo-code interpreter standing between the business logic and the AI executing it.

The Key Benefits

  • Control flow is just data. The next step is a value sitting in the same JSON payload as everything else — no separate flowchart tool or orchestration layer required.
  • Termination is automatic. A blank NextAction ends the workflow — nothing to explicitly code as a “stop” case.
  • Self-correction is declarative too. Backtrack’s retry ceiling is one more micro-prompt, not a loop counter hidden in code.
  • Validation stays lightweight. The malformed-JSON retry check is a single mechanical test, not a judgment call asked of the AI.
  • New branches slot in for free. Point any NextAction value at an Agent row’s Name and it joins the chain — exactly how the Bill-of-Materials step was added with one schema edit.
  • Fully auditable. Every NextAction decision is written straight into the merged Memory JSON — the whole decision trail sits in plain text, not buried in a private agent reasoning trace.

Up Next…

My next post looks at the other end of the pipeline: turning this JSON into something a business manager can actually read at a glance, on demand, without ever opening a developer tool.

respect,
:lobster:Max

3 Likes

[4] JSON: The Universal Currency Between Agents (and Everything Else)

Every mechanism covered so far: SCHEMA’s precision, MEMORY’s persistance, NextAction’s branching, depends on one quiet assumption: that the answer coming back is something a formula can reliably take apart. That assumption has a name. It is called JSON (JavaScript Object Notation)
This post is about why that specific choice matters, both inside the workflow and at its edges.

Why JSON, Not Text

The instruction given to every Agentic step is blunt: return exactly one JSON object, nothing else, no narrative, no explanation, no markdown. That’s not a style preference. Every mechanism in this series only works because the answer is structured data, and not a paragraph someone (or something) has to re-read to extract meaning from.
A merge, a field lookup, a branch decision: all of it needs an unambiguous, machine-addressable answer to operate on.

JSON: The QR Code for Data

JSON is like a QR code. It holds the data precisely and losslessly: for example…

{“Order_id”:“ORD-8821377”,“Customer_Name”:“Sarah Chen”,“Order_Status”:“pending”,“Total_Amount”:“$298.50”,“LINE_ITEMS”:[{“Product_Name”:“Wireless mouse”,“Quantity”:“5”,“Unit_Price”:“$25.99”},{“Product_Name”:“USB-C hub”,“Quantity”:“1”,“Unit_Price”:“$78.56”},{“Product_Name”:“Laptop Stand”,“Quantity”:“1”,“Unit_Price”:“$89.99”}],“Flagged_for_Review”:“yes”,“Review_Reason”:“Payment declined once, address changed twice, and urgent delivery request.”}

At a glance it’s just a dense block of symbols, not meant for a human to read easily, any more than you’d squint at a QR code’s pattern of dots and expect to read the web page. But hand it to a machine, and it’s instantly, perfectly legible. That trade-off is the whole point: machine-perfect precision instead of human readability.

ParseJSON(): The Universal Decoder

ParseJSON() is the scanner that turns the pattern of dots back into something usable. It shows up everywhere in the Agentix Framework:

  • pulling a single field out of a result:
Result.ParseJSON("$.NextAction")
  • or treating an entire object as a native structure to merge with another:
MEM.ParseJSON()._Merge(Result.ParseJSON())
  • or to collect a list of objects from within the structure
MEM.ParseJSON("$..unit_price")

One generic tool handles both a single field and a deeply nested object with an array of parts inside it. There’s no special-case parser needed per schema, the same decoder works on anything JSON, because JSON is always structured the same predictable way.

JSON as the Lingua Franca Everything Else Speaks

This isn’t just an internal convenience. JSON happens to be the native format used by almost every REST API, webhook, and SaaS integration in existence.

Because our agent’s internal currency is already JSON, handing a finished result to an outside system, or receiving one from an inbound automation, needs no translation step. The exact object that merged its way through Headrace, PumpEngine, Tailrace, and Validate can be posted straight to an external system exactly as-is.

Whereas agents that communicate in natural-language paragraphs: need an entire extraction layer just to get a usable value out before any external system could touch it. JSON removes that layer completely, because every party — internal steps and external systems alike — already agrees on the same alphabet.

YAML: The Human-Readable Twin

That machine-fluency does have a cost: nobody wants to read a wall of {"PART_NUMBER":"SCR-3001","Component":"HR-300",...} in a business process.

So we also keep a YAML rendering on tap, generated from the exact same JSON (deterministically, via a dedicated conversion formula).

Extending the metaphor: this is the moment you scan the QR code and it displays the web page on your screen: same data, translated for human eyes, without ever touching what’s actually stored or transmitted.

(YAML started out as “Yet Another Markup Language” - but was renamed later).

The Key Benefits

  • One unambiguous currency. Every step, every merge, every branch decision speaks exactly the same format — nothing is left to interpretation.

  • One decoder for everything. ParseJSON() and _Merge() handle a single field or a deeply nested list with equal ease, so no bespoke parsing per schema is needed.

  • Zero-friction interoperability. Because JSON is also the native language of virtually every external API, results move in and out of the workflow without a translation layer.

  • Lossless and precise. Unlike a text summary, JSON preserves every field and every nested value exactly as generated — nothing gets paraphrased away.

  • Human readability is a separate, optional layer. YAML rendering exists purely for people, computed on demand, without ever changing the underlying wire format machines rely on.

Up Next

I will post about the way this Framework can handle data in all its various forms:

  • Tables and Relational Databases
  • Markdown text and tables of data
  • HTML scrapes of entire web pages
  • JSON objects
  • YAML structures
  • or just plain old ASCII or UTF text

respect,
:lobster:Max

3 Likes

see @Bill_French’s review and comparison of my approach and his alternative Graph Engine framework (here).

Thanks for the extremely thoughtful analysis, Bill, very informative.

:lobster:Max

2 Likes