{"course":{"slug":"agents-and-automation","title":"Agents and Automation","url":"https://addaly.com/learn/agents-and-automation","level":"intermediate","track":"build"},"lesson":{"slug":"build-one-real-thing","title":"Build the boring version first","url":"https://addaly.com/learn/agents-and-automation/build-one-real-thing","position":64,"of":72,"minutes":9,"answer":"Most automation that survives is a deterministic pipeline with one model call and a real validator.","keyIdea":"Most automation that survives is a deterministic pipeline with one model call and a real validator."},"body":{"format":"markdown","text":"## A real task\n\nA building-supplies business in Lagos receives about sixty supplier invoices a month as PDF attachments. Someone opens each one and retypes eight fields into a spreadsheet. It takes four hours a month and the errors are silent until reconciliation.\n\nThe tempting design: an agent with an email tool, a PDF tool and a Sheets tool, told \"keep the invoice sheet up to date.\"\n\nDon't. Here is the design that survives.\n\n## The pipeline\n\n**1. Trigger**: new email arriving in a labelled inbox. Deterministic. Gmail push, n8n, Zapier, whatever you already run.\n\n**2. Filter**: is there a PDF attachment under 10 MB from a known supplier domain? Deterministic. An `if` statement. Everything else goes to a human folder, unopened.\n\n**3. Extract**: **one model call.** PDF in, strict JSON out:\n\n```json\n{\n  \"supplier\": \"string\",\n  \"invoice_number\": \"string\",\n  \"date\": \"YYYY-MM-DD\",\n  \"currency\": \"NGN | USD | EUR\",\n  \"subtotal\": 0.00, \"tax\": 0.00, \"total\": 0.00,\n  \"line_items\": [{ \"description\": \"string\", \"qty\": 0, \"unit_price\": 0.00 }]\n}\n```\n\n**4. Validate**: deterministic, and this step is where the reliability actually comes from:\n\n```python\nassert round(subtotal + tax, 2) == round(total, 2)\nassert sum(li.qty * li.unit_price for li in line_items) == subtotal\nassert date <= today and date > today - 400_days\nassert invoice_number not in already_seen\nassert supplier in known_suppliers\n```\n\nFive lines of arithmetic catch most of what the model gets wrong on this task, and they cost nothing.\n\n**5. Route**: passes → append the row. Fails → a review queue showing the PDF and the extracted JSON side by side, with the failed assertion named.\n\n**6. Report**: a weekly summary of what went to review and why, so nobody has to watch it to trust it.\n\nCount the model calls. One. Everything else is a workflow. **This is the shape of almost every automation that is still running six months later.**\n\n## Keep a golden set\n\nBefore you change the prompt, the model, or anything else: take thirty real invoices, hand-check the correct answer for each, and store them. Every change re-runs against those thirty. Without this you are not improving the system, you are moving it around and hoping.\n\nThirty is enough to notice a regression. It is not enough to certify anything, and that is fine: you are catching the change that took totals from 94% to 71%, which is the failure that actually happens.\n\n## So when is an agent the right answer?\n\nUse a **script or workflow** when:\n\n- the steps are the same every time, even if there are thirty of them\n- you can write the validation\n- a wrong answer costs money or is hard to undo\n- somebody will eventually ask you to explain why it did what it did\n\nUse an **agent** when all three hold:\n\n- the number and order of steps depends on what it finds partway through\n- you can afford to be wrong sometimes, or a human reviews the output\n- there is a cheap, independent check on the result\n\nThat third condition is the one people skip, and it is what makes agents work at all. \"Independent\" means the check does not come from the same model that produced the answer. Arithmetic that must balance. A record that must exist. Code that must compile and pass tests, which is precisely why coding agents work better than most other agents, and it is not because code is easy.\n\n## The honest closing position\n\nModels keep getting better, and each improvement multiplies through the chain, which is real and worth having. What does not change is the arithmetic: chained steps compound, self-assessment is not measurement, and irreversible actions stay irreversible.\n\nSo build the boring version first. One model call in the middle of a deterministic pipeline, with a validator, a queue, and a golden set. Measure it. Then add autonomy at exactly the points where the boring version demonstrably cannot reach, and no further."},"check":{"question":"On the invoice pipeline, extraction gets totals right 94% of the time. Two proposals: (a) wrap extraction in an agent loop that re-reads the PDF and self-corrects, or (b) add the arithmetic check and route mismatches to a human queue. Why is (b) the better way to catch wrong totals?","options":[{"text":"The arithmetic is an independent test of the answer; the self-correcting loop asks the model that made the error to notice it, and correlated errors go undetected.","correct":true},{"text":"It avoids paying for extra model calls on every invoice, and cost per document is the binding constraint at sixty a month.","correct":false,"tempting":"True but secondary, and the loop would only cost extra when it triggers. Cost is not why self-correction misses errors."},{"text":"Deterministic code reads numbers off a PDF more accurately than a model does.","correct":false,"tempting":"Confuses checking a number with extracting one; the arithmetic check never touches the PDF."},{"text":"(a) would in fact work if the second pass were given the original PDF again, since the first error came from a lossy first read.","correct":false,"tempting":"Assumes the failure is information loss rather than judgement; the model had that same PDF on the first pass and still got it wrong."}]},"prev":{"slug":"the-maintenance-bill","title":"What it costs to keep it alive","url":"https://addaly.com/learn/agents-and-automation/the-maintenance-bill"},"next":{"slug":"who-owns-it-when-it-breaks","title":"Whose is it, at 3am, in eight months","url":"https://addaly.com/learn/agents-and-automation/who-owns-it-when-it-breaks"},"authorship":"Founder and series editor: Dr Mustafa Mun. Written by AI models to a written standard. Exam keys checked blind by a second model: 3,665 of 3,666 agreed. Academic review invited.","review":{"status":"Draft, open for academic review"},"cite":"Addaly (2026) 'Build the boring version first', in Agents and Automation. Edited by M. Mun. Addaly. Available at: https://addaly.com/learn/agents-and-automation/build-one-real-thing (Accessed: 31 October 2026).","licence":{"name":"CC BY-NC-SA 4.0","url":"https://creativecommons.org/licenses/by-nc-sa/4.0/"},"reader":{"schema":1,"title":"Build the boring version first","courseTitle":"Agents and Automation","module":{"index":7,"slug":"shipping-and-operating","title":"Shipping it, and keeping it alive"},"answer":null,"keyIdea":"Most automation that survives is a deterministic pipeline with one model call and a real validator.","hero":{"kind":"plate","image":null,"alt":null,"caption":null},"blocks":[{"t":"h2","id":"a-real-task","runs":[{"text":"A real task"}]},{"t":"p","runs":[{"text":"A building-supplies business in Lagos receives about sixty supplier invoices a month as PDF attachments. Someone opens each one and retypes eight fields into a spreadsheet. It takes four hours a month and the errors are silent until reconciliation."}]},{"t":"p","runs":[{"text":"The tempting design: an agent with an email tool, a PDF tool and a Sheets tool, told “keep the invoice sheet up to date.”"}]},{"t":"p","runs":[{"text":"Don’t. Here is the design that survives."}]},{"t":"h2","id":"the-pipeline","runs":[{"text":"The pipeline"}]},{"t":"p","runs":[{"text":"1. Trigger","b":true},{"text":": new email arriving in a labelled inbox. Deterministic. Gmail push, n8n, Zapier, whatever you already run."}]},{"t":"p","runs":[{"text":"2. Filter","b":true},{"text":": is there a PDF attachment under 10 MB from a known supplier domain? Deterministic. An "},{"text":"if","code":true},{"text":" statement. Everything else goes to a human folder, unopened."}]},{"t":"p","runs":[{"text":"3. Extract","b":true},{"text":": "},{"text":"one model call.","b":true},{"text":" PDF in, strict JSON out:"}]},{"t":"code","lang":"json","label":"JSON","text":"{\n  \"supplier\": \"string\",\n  \"invoice_number\": \"string\",\n  \"date\": \"YYYY-MM-DD\",\n  \"currency\": \"NGN | USD | EUR\",\n  \"subtotal\": 0.00, \"tax\": 0.00, \"total\": 0.00,\n  \"line_items\": [{ \"description\": \"string\", \"qty\": 0, \"unit_price\": 0.00 }]\n}"},{"t":"p","runs":[{"text":"4. Validate","b":true},{"text":": deterministic, and this step is where the reliability actually comes from:"}]},{"t":"code","lang":"python","label":"Python","text":"assert round(subtotal + tax, 2) == round(total, 2)\nassert sum(li.qty * li.unit_price for li in line_items) == subtotal\nassert date <= today and date > today - 400_days\nassert invoice_number not in already_seen\nassert supplier in known_suppliers"},{"t":"p","runs":[{"text":"Five lines of arithmetic catch most of what the model gets wrong on this task, and they cost nothing."}]},{"t":"p","runs":[{"text":"5. Route","b":true},{"text":": passes → append the row. Fails → a review queue showing the PDF and the extracted JSON side by side, with the failed assertion named."}]},{"t":"p","runs":[{"text":"6. Report","b":true},{"text":": a weekly summary of what went to review and why, so nobody has to watch it to trust it."}]},{"t":"p","runs":[{"text":"Count the model calls. One. Everything else is a workflow. "},{"text":"This is the shape of almost every automation that is still running six months later.","b":true}]},{"t":"h2","id":"keep-a-golden-set","runs":[{"text":"Keep a golden set"}]},{"t":"p","runs":[{"text":"Before you change the prompt, the model, or anything else: take thirty real invoices, hand-check the correct answer for each, and store them. Every change re-runs against those thirty. Without this you are not improving the system, you are moving it around and hoping."}]},{"t":"p","runs":[{"text":"Thirty is enough to notice a regression. It is not enough to certify anything, and that is fine: you are catching the change that took totals from 94% to 71%, which is the failure that actually happens."}]},{"t":"h2","id":"so-when-is-an-agent-the-right-answer","runs":[{"text":"So when is an agent the right answer?"}]},{"t":"p","runs":[{"text":"Use a "},{"text":"script or workflow","b":true},{"text":" when:"}]},{"t":"ul","items":[[{"text":"the steps are the same every time, even if there are thirty of them"}],[{"text":"you can write the validation"}],[{"text":"a wrong answer costs money or is hard to undo"}],[{"text":"somebody will eventually ask you to explain why it did what it did"}]]},{"t":"p","runs":[{"text":"Use an "},{"text":"agent","b":true},{"text":" when all three hold:"}]},{"t":"ul","items":[[{"text":"the number and order of steps depends on what it finds partway through"}],[{"text":"you can afford to be wrong sometimes, or a human reviews the output"}],[{"text":"there is a cheap, independent check on the result"}]]},{"t":"p","runs":[{"text":"That third condition is the one people skip, and it is what makes agents work at all. “Independent” means the check does not come from the same model that produced the answer. Arithmetic that must balance. A record that must exist. Code that must compile and pass tests, which is precisely why coding agents work better than most other agents, and it is not because code is easy."}]},{"t":"h2","id":"the-honest-closing-position","runs":[{"text":"The honest closing position"}]},{"t":"p","runs":[{"text":"Models keep getting better, and each improvement multiplies through the chain, which is real and worth having. What does not change is the arithmetic: chained steps compound, self-assessment is not measurement, and irreversible actions stay irreversible."}]},{"t":"p","runs":[{"text":"So build the boring version first. One model call in the middle of a deterministic pipeline, with a validator, a queue, and a golden set. Measure it. Then add autonomy at exactly the points where the boring version demonstrably cannot reach, and no further."}]}],"check":{"question":[{"text":"On the invoice pipeline, extraction gets totals right 94% of the time. Two proposals: (a) wrap extraction in an agent loop that re-reads the PDF and self-corrects, or (b) add the arithmetic check and route mismatches to a human queue. Why is (b) the better way to catch wrong totals?"}],"options":[{"text":[{"text":"The arithmetic is an independent test of the answer; the self-correcting loop asks the model that made the error to notice it, and correlated errors go undetected."}],"correct":true,"tempting":null},{"text":[{"text":"It avoids paying for extra model calls on every invoice, and cost per document is the binding constraint at sixty a month."}],"correct":false,"tempting":[{"text":"True but secondary, and the loop would only cost extra when it triggers. Cost is not why self-correction misses errors."}]},{"text":[{"text":"Deterministic code reads numbers off a PDF more accurately than a model does."}],"correct":false,"tempting":[{"text":"Confuses checking a number with extracting one; the arithmetic check never touches the PDF."}]},{"text":[{"text":"(a) would in fact work if the second pass were given the original PDF again, since the first error came from a lossy first read."}],"correct":false,"tempting":[{"text":"Assumes the failure is information loss rather than judgement; the model had that same PDF on the first pass and still got it wrong."}]}],"order":[1,0,2,3],"why":null},"prev":{"slug":"the-maintenance-bill","title":"What it costs to keep it alive","minutes":8},"next":{"slug":"who-owns-it-when-it-breaks","title":"Whose is it, at 3am, in eight months","minutes":9}}}