{"course":{"slug":"prompting","title":"Getting Real Work out of a Model","url":"https://addaly.com/learn/prompting","level":"beginner","track":"start"},"lesson":{"slug":"machine-readable-output","title":"Output a spreadsheet or a script can read","url":"https://addaly.com/learn/prompting/machine-readable-output","position":31,"of":72,"minutes":9,"answer":"Ask for CSV with explicit rules for separators, dates and missing values, keep the reasoning in prose before the structured block, and remember that valid output is not correct output.","keyIdea":"Ask for CSV with explicit rules for separators, dates and missing values, keep the reasoning in prose before the structured block, and remember that valid output is not correct output."},"body":{"format":"markdown","text":"## You do not need to be a programmer for this\n\nThe moment you want to do the same extraction over forty documents, you want output that goes into a spreadsheet without hand-editing. That means one of two formats, and both are easy to ask for.\n\n**CSV** (one line per row, fields separated by commas) opens directly in Excel, LibreOffice Calc or Google Sheets.\n\n**JSON** (nested key-value structure) is what a script or an app expects.\n\nFor most non-programmers, CSV is the right answer and JSON is a distraction. Ask for CSV.\n\n## Asking for CSV without the classic failures\n\n```\nOutput CSV only. No prose before or after, no code fence, no header row.\nColumns, in this order: invoice_number, date (YYYY-MM-DD), supplier,\namount, currency.\n- If a field is absent, write NOT_STATED. Do not guess.\n- Never use a comma inside a field; replace any comma with a semicolon.\n- Wrap the supplier name in double quotes.\n- One line per invoice, no blank lines.\n```\n\nFive rules, and each one prevents a failure that will otherwise waste your evening:\n\n**Commas inside fields** break every column to the right. \"Adeyemi Ltd, Nairobi\" becomes two columns. This is the single most common CSV failure and one line prevents it.\n\n**Prose around the data** means you cannot paste straight into a sheet. \"Output CSV only\" plus \"no code fence\" handles it.\n\n**Inconsistent date formats**: 3/4/26 could be March or April, and will differ by row. Specify `YYYY-MM-DD` and the ambiguity disappears.\n\n**Silent invention.** Without `NOT_STATED`, a missing amount becomes a plausible amount. A blank is obvious; a wrong number is not.\n\n**Header rows** are useful once and a nuisance when you paste in batches. Decide, and say which.\n\n## If you do need JSON\n\nGive the exact structure, using an example rather than a description:\n\n```\nReturn JSON matching this shape exactly, and nothing else:\n\n{\"invoices\": [\n  {\"number\": \"INV-1042\", \"date\": \"2026-03-14\",\n   \"supplier\": \"Adeyemi Ltd\", \"amount\": 45200.00,\n   \"currency\": \"KES\", \"vat_included\": null}\n]}\n\nUse null where the document does not state a value.\nAmounts as numbers, not strings. No trailing commas.\n```\n\nThe example settles a dozen questions a description leaves open. Some APIs also offer *structured output* or *JSON mode*, which constrains generation so the result is valid JSON by construction. Use it if you have it: it removes parse errors entirely.\n\n> Figure: Valid output is not correct output. A two by two grid, rows Parses cleanly and Fails to parse, columns Right value and Wrong value: Parses cleanly and Right value: What you wanted; Parses cleanly and Wrong value: The failure a schema cannot see; Fails to parse and Right value: Caught instantly by the parser; Fails to parse and Wrong value: Caught instantly by the parser. Structured output and JSON mode remove the bottom row entirely. The top-right cell, the order date sitting in the invoice-date field, passes validation perfectly, and it is the cell every extraction failure you care about lives in.\n\n## The thing structured output does not buy you\n\nValid JSON is not correct JSON. A schema guarantees that `amount` is a number. It guarantees nothing about whether it is the right number.\n\nThis is worth saying plainly because the guarantee feels stronger than it is. Every extraction failure you care about (the wrong total picked off an invoice, the date of the purchase order instead of the invoice) passes schema validation perfectly.\n\n## The reasoning problem, and its fix\n\nAsk for bare JSON with a `risk_score` from 1 to 10 and nothing else, and the scores will look arbitrary, because they are. The text is where the work happens, and you asked for the conclusion with the working removed.\n\nThe fix is to make room, then tidy:\n\n> For each contract, write one sentence naming the specific clause that concerns you most. Then, after all the sentences, output the JSON array of `{id, risk_score}` only.\n\nReasoning first, structure last, in one response. You get both, and the sentences let you spot the contract where the reasoning was wrong even though the JSON parsed.\n\nThe same applies to classification with confidence scores. A number produced alongside a reason means something; a number produced alone is decoration.\n\n## Checking a batch before you trust it\n\nThree checks, each takes a minute:\n\n1. **Count the rows.** Forty documents should give forty rows. A short count means something was silently dropped.\n2. **Open it in a spreadsheet.** Broken commas show up instantly as ragged columns.\n3. **Spot-check three rows against the source.** Not the first three: pick one from the middle and one from the end, since the end of a long generation is where quality drifts.\n\nIf you can run code, a five-line Python script that reads the CSV and prints the row count and any row with the wrong number of fields will catch most of this automatically. That is free, offline, and worth learning if this becomes routine."},"check":{"question":"An accounts clerk uses JSON mode to extract totals from 200 invoices. Every response is valid JSON with a numeric total. What has this guaranteed?","options":[{"text":"That every total was found in the document, since a missing value would break the schema.","correct":false,"tempting":"A schema can be satisfied by an invented number as easily as a found one, unless nulls are required and enforced."},{"text":"Only that each response parses and each total is a number: nothing about whether it is the right number from the right field.","correct":true},{"text":"That the extraction is reliable enough to post to the ledger without review.","correct":false,"tempting":"Conflates structural validity with factual accuracy, which is exactly the error this lesson is about."},{"text":"That the model read each invoice in full, since partial reading produces malformed output.","correct":false,"tempting":"Malformed output and partial reading are unrelated; a partially read document yields well-formed wrong answers."}]},"prev":{"slug":"choosing-the-container","title":"Choosing the container","url":"https://addaly.com/learn/prompting/choosing-the-container"},"next":{"slug":"templates-and-skeletons","title":"Hand it the skeleton","url":"https://addaly.com/learn/prompting/templates-and-skeletons"},"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) 'Output a spreadsheet or a script can read', in Getting Real Work out of a Model. Edited by M. Mun. Addaly. Available at: https://addaly.com/learn/prompting/machine-readable-output (Accessed: 13 October 2026).","licence":{"name":"CC BY-NC-SA 4.0","url":"https://creativecommons.org/licenses/by-nc-sa/4.0/"},"reader":{"schema":1,"title":"Output a spreadsheet or a script can read","courseTitle":"Getting Real Work out of a Model","module":{"index":4,"slug":"the-shape-of-the-answer","title":"The shape of the answer"},"answer":null,"keyIdea":"Ask for CSV with explicit rules for separators, dates and missing values, keep the reasoning in prose before the structured block, and remember that valid output is not correct output.","hero":{"kind":"figure","figure":{"t":"figure","label":"Figure","figure":{"cols":["Right value","Wrong value"],"rows":["Parses cleanly","Fails to parse"],"type":"matrix","cells":[["What you wanted","The failure a schema cannot see"],["Caught instantly by the parser","Caught instantly by the parser"]],"title":"Valid output is not correct output","caption":"Structured output and JSON mode remove the bottom row entirely. The top-right cell, the order date sitting in the invoice-date field, passes validation perfectly, and it is the cell every extraction failure you care about lives in.","cellLabels":[["valid and correct","valid and wrong"],["invalid","invalid"]]},"alt":"Figure: Valid output is not correct output. A two by two grid, rows Parses cleanly and Fails to parse, columns Right value and Wrong value: Parses cleanly and Right value: What you wanted; Parses cleanly and Wrong value: The failure a schema cannot see; Fails to parse and Right value: Caught instantly by the parser; Fails to parse and Wrong value: Caught instantly by the parser. Structured output and JSON mode remove the bottom row entirely. The top-right cell, the order date sitting in the invoice-date field, passes validation perfectly, and it is the cell every extraction failure you care about lives in."}},"blocks":[{"t":"h2","id":"you-do-not-need-to-be-a-programmer-for-this","runs":[{"text":"You do not need to be a programmer for this"}]},{"t":"p","runs":[{"text":"The moment you want to do the same extraction over forty documents, you want output that goes into a spreadsheet without hand-editing. That means one of two formats, and both are easy to ask for."}]},{"t":"p","runs":[{"text":"CSV","b":true},{"text":" (one line per row, fields separated by commas) opens directly in Excel, LibreOffice Calc or Google Sheets."}]},{"t":"p","runs":[{"text":"JSON","b":true},{"text":" (nested key-value structure) is what a script or an app expects."}]},{"t":"p","runs":[{"text":"For most non-programmers, CSV is the right answer and JSON is a distraction. Ask for CSV."}]},{"t":"h2","id":"asking-for-csv-without-the-classic-failures","runs":[{"text":"Asking for CSV without the classic failures"}]},{"t":"code","lang":"","label":"Text","text":"Output CSV only. No prose before or after, no code fence, no header row.\nColumns, in this order: invoice_number, date (YYYY-MM-DD), supplier,\namount, currency.\n- If a field is absent, write NOT_STATED. Do not guess.\n- Never use a comma inside a field; replace any comma with a semicolon.\n- Wrap the supplier name in double quotes.\n- One line per invoice, no blank lines."},{"t":"p","runs":[{"text":"Five rules, and each one prevents a failure that will otherwise waste your evening:"}]},{"t":"p","runs":[{"text":"Commas inside fields","b":true},{"text":" break every column to the right. “Adeyemi Ltd, Nairobi” becomes two columns. This is the single most common CSV failure and one line prevents it."}]},{"t":"p","runs":[{"text":"Prose around the data","b":true},{"text":" means you cannot paste straight into a sheet. “Output CSV only” plus “no code fence” handles it."}]},{"t":"p","runs":[{"text":"Inconsistent date formats","b":true},{"text":": 3/4/26 could be March or April, and will differ by row. Specify "},{"text":"YYYY-MM-DD","code":true},{"text":" and the ambiguity disappears."}]},{"t":"p","runs":[{"text":"Silent invention.","b":true},{"text":" Without "},{"text":"NOT_STATED","code":true},{"text":", a missing amount becomes a plausible amount. A blank is obvious; a wrong number is not."}]},{"t":"p","runs":[{"text":"Header rows","b":true},{"text":" are useful once and a nuisance when you paste in batches. Decide, and say which."}]},{"t":"h2","id":"if-you-do-need-json","runs":[{"text":"If you do need JSON"}]},{"t":"p","runs":[{"text":"Give the exact structure, using an example rather than a description:"}]},{"t":"code","lang":"","label":"Text","text":"Return JSON matching this shape exactly, and nothing else:\n\n{\"invoices\": [\n  {\"number\": \"INV-1042\", \"date\": \"2026-03-14\",\n   \"supplier\": \"Adeyemi Ltd\", \"amount\": 45200.00,\n   \"currency\": \"KES\", \"vat_included\": null}\n]}\n\nUse null where the document does not state a value.\nAmounts as numbers, not strings. No trailing commas."},{"t":"p","runs":[{"text":"The example settles a dozen questions a description leaves open. Some APIs also offer "},{"text":"structured output","i":true},{"text":" or "},{"text":"JSON mode","i":true},{"text":", which constrains generation so the result is valid JSON by construction. Use it if you have it: it removes parse errors entirely."}]},{"t":"h2","id":"the-thing-structured-output-does-not-buy-you","runs":[{"text":"The thing structured output does not buy you"}]},{"t":"p","runs":[{"text":"Valid JSON is not correct JSON. A schema guarantees that "},{"text":"amount","code":true},{"text":" is a number. It guarantees nothing about whether it is the right number."}]},{"t":"p","runs":[{"text":"This is worth saying plainly because the guarantee feels stronger than it is. Every extraction failure you care about (the wrong total picked off an invoice, the date of the purchase order instead of the invoice) passes schema validation perfectly."}]},{"t":"h2","id":"the-reasoning-problem-and-its-fix","runs":[{"text":"The reasoning problem, and its fix"}]},{"t":"p","runs":[{"text":"Ask for bare JSON with a "},{"text":"risk_score","code":true},{"text":" from 1 to 10 and nothing else, and the scores will look arbitrary, because they are. The text is where the work happens, and you asked for the conclusion with the working removed."}]},{"t":"p","runs":[{"text":"The fix is to make room, then tidy:"}]},{"t":"quote","runs":[{"text":"For each contract, write one sentence naming the specific clause that concerns you most. Then, after all the sentences, output the JSON array of "},{"text":"{id, risk_score}","code":true},{"text":" only."}]},{"t":"p","runs":[{"text":"Reasoning first, structure last, in one response. You get both, and the sentences let you spot the contract where the reasoning was wrong even though the JSON parsed."}]},{"t":"p","runs":[{"text":"The same applies to classification with confidence scores. A number produced alongside a reason means something; a number produced alone is decoration."}]},{"t":"h2","id":"checking-a-batch-before-you-trust-it","runs":[{"text":"Checking a batch before you trust it"}]},{"t":"p","runs":[{"text":"Three checks, each takes a minute:"}]},{"t":"ol","start":1,"items":[[{"text":"Count the rows.","b":true},{"text":" Forty documents should give forty rows. A short count means something was silently dropped."}],[{"text":"Open it in a spreadsheet.","b":true},{"text":" Broken commas show up instantly as ragged columns."}],[{"text":"Spot-check three rows against the source.","b":true},{"text":" Not the first three: pick one from the middle and one from the end, since the end of a long generation is where quality drifts."}]]},{"t":"p","runs":[{"text":"If you can run code, a five-line Python script that reads the CSV and prints the row count and any row with the wrong number of fields will catch most of this automatically. That is free, offline, and worth learning if this becomes routine."}]}],"check":{"question":[{"text":"An accounts clerk uses JSON mode to extract totals from 200 invoices. Every response is valid JSON with a numeric total. What has this guaranteed?"}],"options":[{"text":[{"text":"That every total was found in the document, since a missing value would break the schema."}],"correct":false,"tempting":[{"text":"A schema can be satisfied by an invented number as easily as a found one, unless nulls are required and enforced."}]},{"text":[{"text":"Only that each response parses and each total is a number: nothing about whether it is the right number from the right field."}],"correct":true,"tempting":null},{"text":[{"text":"That the extraction is reliable enough to post to the ledger without review."}],"correct":false,"tempting":[{"text":"Conflates structural validity with factual accuracy, which is exactly the error this lesson is about."}]},{"text":[{"text":"That the model read each invoice in full, since partial reading produces malformed output."}],"correct":false,"tempting":[{"text":"Malformed output and partial reading are unrelated; a partially read document yields well-formed wrong answers."}]}],"order":[0,1,2,3],"why":null},"prev":{"slug":"choosing-the-container","title":"Choosing the container","minutes":8},"next":{"slug":"templates-and-skeletons","title":"Hand it the skeleton","minutes":8}}}