Skip to main content

Developer blog

How the chatbot works: a mouth, not a brain

The data-mapping assistant's chat compiles four chip answers into a recipe from a fixed table of 384 templates. The executor's model picks each action from the valid menu, and nothing is final until a human confirms every card.

ShareLinkedIn
data mappingchatbot designdeterministic intakeLaw 25recipe templateshuman review
On this page
  1. The most interesting screen is not the chat
  2. Two doors into the same table
  3. Authors recipes; never executes
  4. The review loop: nothing is final until you confirm
  5. Why deterministic, and where the AI actually lives
  6. One thread left open

Key takeaways

  • The intake spends zero LLM tokens. Four chip answers select one of 384 hand-authored recipe templates from a fixed lookup table: 4 sizes x 6 sectors x 4 regions x 4 data profiles. Nothing is generated at runtime.
  • The chatbot only authors recipes. Execution goes through a single seam, window.execute_mapping_workflow(recipe, opts), which speaks the same contract as the MCP execute_mapping_workflow tool (see “How to use the MCP server”).
  • The trust story is three layers: deterministic intake, a learned executor that can only pick among valid menu actions (it cannot invent a vendor), and a mandatory review loop where every auto-placed item gets a confirm-or-fix card. Export is locked until every card is confirmed.

The most interesting screen is not the chat

When you finish the intake on the data-mapping guide, the first thing you see is not a summary and not a celebration. It is a queue of review cards, one per item the assistant placed on your canvas. Confirm, rename, retype, fill in the details, or remove it. Every confirmation lands as a structured record.

This is the whole design in miniature: the chat talks, the system drafts, and you decide. I want to walk you through the first two parts, because they are deliberately dumber than you might expect, and that is the point.

Two doors into the same table

The assistant offers two intake modes at movahedi.ca/tools/data-mapping-guide/. Quick mode is four taps on chips. Guided chat asks the same four questions conversationally, and you answer them the same way: by tapping chips. There is no free-text intake. The four answers are size (1-10, 11-50, 51-200, 200+), sector (retail, services, health, tech, manufacturing, nonprofit), region (quebec, canada, canada-us, international), and data profile (contact, contact-payment, contact-marketing, or all four).

Both modes converge on a four-part key, something like 51-200|tech|canada|contact-marketing, and that key is a lookup into a fixed table. The table holds 384 hand-authored recipe templates, 4 x 6 x 4 x 4, shipped as templates.js in the open-source repo github.com/movahedi-ca/AI-data-map (MIT). Selection is a pure lookup. No prompt, no temperature, no tokens. During the intake the page makes no inference calls at all.

A small health clinic in Canada and the US, handling contact data plus payments plus marketing, gets its template by table address, not by inference. Here is what one of those templates looks like, trimmed but internally coherent (every node referenced is created):

{
  "recipe_id": "t-1-health-caus-all-01",
  "schema_version": "1.0.0",
  "items": [
    {"intent": "add_collection_point", "params": {"node_id": "n1", "label": "Patient intake form"}},
    {"intent": "add_node", "params": {"node_id": "n2", "type": "system", "label": "Clinic records system"}},
    {"intent": "add_node", "params": {"node_id": "n3", "type": "thirdparty", "label": "Payment processor"}},
    {"intent": "connect", "params": {"a": "n1", "b": "n2", "cat": "contact"}},
    {"intent": "connect", "params": {"a": "n2", "b": "n3", "cat": "payment"}},
    {"intent": "set_retention", "params": {"node_id": "n2", "record_type": "Business records", "verify_only": true}}
  ]
}

A few vocabulary notes, since this is a contract: a collection point is a kind of node (where personal data first comes in), so add_collection_point and add_node are siblings, not duplicates. In connect, cat is the flow category (contact, payment, marketing). verify_only means the template declines to assert a retention period and marks the item for human verification instead. Note the discipline in that last line: Quebec Law 25 is never used to source a numerical retention period. Ranges only, with the statute cited, or verify_only. The table was authored with that rule baked in.

Authors recipes; never executes

There is a contract, stated bluntly in the source: the chatbot authors recipes; it never executes. Execution goes through one seam on the page, window.execute_mapping_workflow(recipe, opts) (opts.onStep fires per executed step so the page can narrate and paint), and that function speaks the same recipe contract as the MCP execute_mapping_workflow tool: {recipe_id, schema_version, items[]}, where each item is {intent, params}.

This split is load-bearing, and it resolves the obvious tension: if the recipe lists every intent, what is left for the model to decide? The recipe says what to build; the executor’s model decides how, one step at a time. Each recipe item carries a completion predicate, and the System-1 model picks the concrete action for each step as the argmax over that step’s valid menu. The applied action always comes from the model; it is never bypassed, and it can never be anything but a valid menu entry. That is the whole reason the hallucination critique does not transfer to the learned part of this system: an LLM drafting your inventory can invent a vendor you never had, because its output vocabulary is open. This model picks among actions it was given. It cannot invent a vendor, a flow, or a retention period. The companion article “How the AI model works, and the work that went into it” covers the model itself.

The post-map refinement commands honor the same contract. “Add our CRM as a system”, “rename X to Y”, “remove the analytics flow”: the chat parses these with fixed deterministic patterns (no LLM, no tokens) and routes them through the executor seam as corrections, never by mutating the canvas directly. Destructive ones ask for confirmation in the thread first. “What is missing from my map?” runs a deterministic gap analysis: unconnected nodes, unconfirmed items, missing destruction or retention coverage. It lists them. It does not invent new ones.

The review loop: nothing is final until you confirm

After execution, every auto-placed item becomes a review card. You confirm it or you fix it: rename, retype, fill in the details, remove it. The confirmations are recorded as structured records, not as a thumbs-up in a chat log. The results screen, with the map, the table view, and the Excel export, only appears once every card is confirmed. Export is locked behind that gate. Nothing is final until the human confirms.

A template gets you most of the way to a plausible inventory skeleton for your sector and size band. Plausible is not the same as true. The review loop is where your map stops being generic and starts being yours. If that sounds like a concession, it is. A fixed table trades one failure mode for another: it cannot hallucinate, but it can be generic, and a hand-authored table can also be wrong in the same way for everyone in the same bucket. The mandatory review exists precisely because the authors knew that. The templates are versioned source in the public repo, every change a commit, but the backstop is and remains the person reading the cards.

There is also a paste-a-recipe path for power users. A pasted recipe does not bypass the table’s discipline by smuggling in arbitrary content: it is validated structurally (plain-language errors, never raw parser output), then executed through the same seam and subjected to the same mandatory review cards. The side door leads into the same room.

Why deterministic, and where the AI actually lives

This is a compliance-adjacent tool. Quebec Law 25 asks organizations to keep a data inventory, and the inventory is a statement of fact about what the organization actually does with personal information. An LLM drafting your inventory can hallucinate a vendor you never had, invent a data flow that does not exist, or smooth over a retention gap with confident prose. A fixed template table cannot do any of that, because it cannot do anything that is not in the table.

So the AI in this system sits where it belongs: in the executor, picking each next action from a valid menu, unable to invent anything outside it. The chat itself stays deliberately non-generative. Deterministic intake plus a constrained learned executor plus mandatory human review is the whole trust story. Stated honestly, most of the weight is on that last layer, and the system is designed around that fact rather than around hiding it.

One thread left open

The review records are an interesting dataset: over time they will show whether the executor’s picks keep getting better, or whether humans keep correcting the same mistakes the same way. If it is the latter, the templates need rewriting, not the model. If it is the former, the table plus the model is genuinely compounding. Whether those records should ever be published is a separate question, and for a privacy tool it is not a small one: they are users’ corrections to compliance artifacts.

Either way, the uncomfortable part is the same: the system only works because a person still reads every card. The day nobody does, deterministic or not, you have a compliance artifact nobody owns.