Build AI automation where the LLM fills one typed step and the engine walks a fixed sequence — so an agent can't choose to do something you didn't plan. About one switch statement.
Last tested: June 2026. See Changelog at the bottom.
If this saves you setup time, follow @renezander030 — production notes on shipping AI agents outside demos.
Reference implementation (Go, MIT): github.com/renezander030/draftcat
| Question | Answer |
|---|---|
| Does the agent pick its next action? | No. The engine walks a fixed list of typed steps. |
| What does the LLM actually do? | Fills one ai step: structured output, schema-validated, budget-checked. |
| How do side effects happen? | Only through a deterministic step (plain Go) or a human approval step. |
| What happens on a bad step? | It errors and the pipeline halts — no "the agent retried something weird." |
An autonomous agent loops "think, choose a tool, act" and chooses the next action itself. That is exactly the part you cannot audit. Flip it:
- The pipeline is a fixed sequence of typed steps, declared up front — not chosen at runtime.
- Three step types only:
deterministic(plain Go),ai(LLM inference, schema-validated),approval(human gate). - The LLM never selects what runs next. It produces structured output for its one step; the engine decides the order, because the order is hard-coded in config.
Declare the pipeline as a fixed list of steps in YAML. The LLM shows up at exactly the steps you marked ai, and nowhere else.
pipelines:
- name: invoice-due-diligence
schedule: 1h
steps:
- {name: parse-pdf, type: deterministic, action: pdf_extract}
- {name: extract, type: ai, skill: extract-line-items}
- {name: verify, type: deterministic, action: pdf_verify_cite}
- {name: review, type: approval, channel: telegram}The order is in the file, not in the model's head. Swap, reorder, or delete a step by editing config, never by re-prompting.
type StepConfig struct {
Name string `yaml:"name"`
Type string `yaml:"type"` // deterministic, ai, approval
Action string `yaml:"action"` // deterministic action name
Skill string `yaml:"skill"` // reference to skills/<name>.yaml
}for _, step := range pipeline.Steps {
switch step.Type {
case "deterministic":
// plain Go: fetch, parse, dedup, write. No model involved.
case "ai":
// LLM inference with a skill template; output is schema-validated
// and budget-checked before it's allowed to continue.
case "approval":
// pause and wait for a human to approve / edit / reject.
}
}There is no branch where a model returns "next, call tool X." The loop is the authority; the model is a typed function inside one case.
- Auditable. The run is the step list plus each step's output. You can diff two runs; you cannot diff two free-roaming agent loops.
- Testable. A fixed pipeline dry-runs against fixtures (
draftcat test <pipeline>). An agent that picks its own path doesn't have a stable path to test. - Bounded blast radius. A wrong LLM output is contained to its step's schema; it can't decide to call a delete tool you never wired in.
- Still flexible where it matters. The content is LLM-driven (the
aistep), the control flow is not. You keep the writing, lose the roulette.
Use an autonomous agent when exploration is the point. Use a fixed step pipeline when a wrong choice emails a real customer.
$ draftcat test invoice-due-diligence
[step parse-pdf] deterministic ok
[step extract] ai ok (schema valid, 812 tok)
[step verify] deterministic ok
[step review] approval — would prompt operator (dry-run)
Pass criteria: every step runs in declared order, the ai step's output validates against its schema, and the approval step pauses rather than auto-sending.
This is Production AI Automation Notes #10. The series, on shipping AI agents outside demos:
- Agent Approval Gates — the per-step approval contract
- Token Budgets
- SQLite dedup + crash safety
- Prompt-injection defense
- PDF cite verification
- LLM cost tracking in Go
- Deterministic step pipelines (this entry)
Reference implementation for the Go entries: draftcat (MIT).
Follow @renezander030 for new entries.
- The approval-step contract this builds on: Production AI Automation Notes #1.
- Reference engine (the
runPipelineswitch above): draftcatmain.go.
Running fixed pipelines vs an autonomous loop? Comment with your step types, how you validate the ai step output, and where you draw the line between "let the model choose" and "the config chooses."
- Initial entry. Skipped the hardware-by-model matrix (not hardware-bound) and a new companion repo (draftcat already carries the runnable engine).