Skip to content

T03 — Six photos to a printed catalogue in three languages

Lunardi Antichità, an antiques dealer in Turin, sells to collectors in Italy, Britain and Germany. For the autumn the gallery wants a printed catalogue in each language: the dealer has the records — maker, dating, size, price — and a photo of every piece, but writing six entries three times over, and getting circa, um and c., 11,5 and 11.5, 2.400 € and €2,400 right in each, is an afternoon. This workflow does it in one run, for less than half a credit: three catalogues ready to print, the website’s texts in the three languages, and a list of photos to check — here, a lot filed with the wrong picture.

The workflow in three stages. 1, look at every lot: the dealer’s records with lot, maker, dating, size, price and photo, 7 lots; Look at the photo, AI on kind, material, decoration and condition, 7 readings; The photo is the lot, a Predicate on the kind seen against the kind recorded, 6 match and 1 to check. 2, write once, in three languages: Write the entry, AI with titles and texts in three languages, 6 entries; The words fit the card, a JSON Schema on lengths and no digits, valid; Figures per language, Format as Text and Replace on price, size and circa, 2.400 € and €2,400. 3, lay out and deliver: The lots per language, Generate JSON with one list per catalogue, 3 × 6 lots; Lay out the catalogues, one template and three renders, 3 PDFs of 3 pages; Photos to check, the lot whose photo is not the object, lot 7, a teapot.

The reference run — 0.36 credits:

Delivered What is in it
Catalogues: Italian, English, German three A4 pages each: a cover, then the six lots with photo, title, maker and dating, size, description and price
Texts for the website one row per lot: maker, dating, price, and title and text in Italian, English and German
Photos to check lot 7, Pair of plates: expected plate, the photo shows a teapot

The English catalogue: a dark cover with Lunardi Antichità · Torino, Autumn selection, Delftware, porcelain, glass and furniture, a photo of a gilded armchair, the validity of the prices and the address; then two pages of cards, two by two, each with a photo, the lot number, a title such as Delftware lady’s shoe or Painted porcelain teapot, the maker and dating such as John Bartlam, Charleston · c. 1765, the size, a description and the price such as €9,500.

The same page in the three languages — the words written by the model, the figures by the workflow:

The second page of the catalogue in Italian, English and German side by side. Lot 3 reads Teiera in porcellana dipinta, John Bartlam, Charleston · circa 1765, 12 × 19 cm, 9.500 € in Italian; Painted porcelain teapot, c. 1765, €9,500 in English; Bemalte Porzellanteekanne, um 1765, 9.500 € in German. Lot 1 reads 11,5 × 16 cm in Italian and German and 11.5 × 16 cm in English.

The dealer’s records are a list, one object per lot — the kind of export a stock system gives:

{ "lot": 3, "object": "Teapot", "kind": "teapot", "maker": "John Bartlam, Charleston", "place": "Charleston",
"dating": "c. 1765", "material": "soft-paste porcelain, painted in underglaze blue",
"height_cm": 12, "width_cm": 19, "price_eur": 9500,
"notes": "One of the earliest porcelains made in America.", "photo": "<the photo>" }

Seven lots: a Delftware shoe, a porcelain vase with birds, two teapots, a gilded armchair, a perfume bottle — and a pair of Delft plates whose record points at the photo of a teapot, as happens when a file is attached to the wrong line. The catalogue’s own words — title, subtitle, section heading, the note on sizes and prices, the cover photo — come as a second input, once per language.

The gallery, its records, its prices and its address are fictional. The photos are of objects in The Metropolitan Museum of Art, New York (Open Access, public domain), standing in for the dealer’s stock: “Shoe”, 1697, Delftware; “Vase”, Greenwood Pottery Company, about 1884–93; “Teapot”, John Bartlam, about 1765; “Teapot”, George E. Ohr, about 1897; “Armchair”, Herter Brothers, about 1875; “Perfume bottle”, 17th–18th century, chalcedony glass with a gilded bronze mount.

Input Who provides it Example
The lots the stock system, as JSON, with a photo per lot seven lots
The catalogue’s words the gallery, once per language {"it": {...}, "en": {...}, "de": {...}}
  • The photo is checked against the record. Image Analysis looks at each photo and says what the object is, from a fixed list (shoe, vase, teapot, chair, bottle, plate, …), with its material, decoration and visible condition; a schema checks the answer. A Predicate compares the kind it saw with the kind in the record, and a Filter lets only a matching lot go on. Lot 7 — Pair of plates, with the photo of a teapot — stops there: no entry is written for it, it is not printed, and it is listed in Photos to check. See Reading pictures and Deciding.
  • One step writes three languages. Text Generation receives the record and the reading of the photo and returns, as JSON, a title and a text in Italian, English and German — written as a native catalogue writer would, with each language’s terms (maiolica, Fayence, Irdenware). One call per lot keeps the three versions saying the same thing. See Languages.
  • No figures in the model’s words. The brief forbids numbers, dates, sizes and prices, in figures or in words, and the maker and the town; the schema refuses any digit. Dating, size and price are printed beside the text, from the record.
  • Each language writes its own figures. Format as Text writes the price as 2.400 € in Italian and German and €2,400 in English, and the size as 11,5 × 16 cm or 11.5 × 16 cm; Replace Text turns the record’s c. 1765 into circa 1765 and um 1765. A Text Template adds the lot label — Lotto, Lot, Los. No model touches a figure. See Without a model.
  • One template, three catalogues. Generate JSON collects the lots of each language into one list; the same template, rendered three times, receives the list on lots and the language’s words on data_0. The cards fill a grid that continues on new pages, as in the wine price list.
  • A missing lot stops the print. If an entry fails — a text too long, a reading that breaks the schema — its lot reaches the template without words, and the render stops with DESIGN_PLACEHOLDER_REQUIRED, naming the lot: the catalogue is not printed with a hole in it. A lot the Filter held back is different: it was left out on purpose.

Building it taught four things the brief now says:

  • Ask for words, not characters. Asked for 160 to 250 characters, the model wrote 280 in Italian; asked for 28 to 40 words, it stays within the card.
  • A short text needs no reasoning. With the default Reasoning Effort, counting words cost the model about 0.2 credits an entry and, once, its whole token budget (AI_RESPONSE_INCOMPLETE). With Reasoning Effort none the entry costs 0.024 credits and is as good. See What text costs.
  • Keep the record language-neutral. A maker written as Delft pottery or Unknown workshop appeared in English in the Italian and German catalogues; the record now says Delft, Murano, Herter Brothers, New York.
  • Name the terms. The German text first called Ohr’s earthenware Fayence, which is tin-glazed; the brief now says which word goes with which material. And it says describe the object, never the photograph: an Italian text had begun la fotografia mostra….

The figure above tells the workflow as stages; the exact graph is the workflow (as a diagram), node by node in the table.

Node Type Why
The lots / The catalogue’s words input/json_value ×2 The dealer’s records; the catalogue’s words in each language
Each lot enumerate/json One iteration per lot: number, object, kind, maker, place, dating, material, size, price, notes, and the photo as an image
Look at the photo ai/image_analysis, JSON mode Kind from a fixed list, material, colours, decoration, visible condition
The reading is complete / What the photo shows utility/json_schema_validate fail, utility/extract The reading checked; the kind it saw
The photo is the lot utility/predicate (equals) The kind seen against the kind recorded
The reading as text / The lot as a brief text/format json, text/template The record and the reading, for the writer
Only a lot whose photo matches / A photo to check utility/filter, utility/filter inverted The matching lots go on; the others are listed
Write the entry, in three languages ai/text_generation, JSON mode, Reasoning Effort none Title and text in Italian, English and German
The words fit the card utility/json_schema_validate, fail Every key; lengths for the card; no digits
Title, Text ×3 languages utility/extract ×6 The six texts
Lot number as text; Dating, Maker line, Height, Width, Size, Price, Lot label per language text/format, text/replace, text/template The figures written per language: it-IT, en-GB, de-DE
The lots, it / en / de aggregate/json ×3 One list per catalogue; a lot without words is left out only when the Filter held it back
The catalogue’s words, it / en / de utility/extract ×3 The language’s words for the cover and the headings
Lay out the catalogue, it / en / de design/template_render ×3 The words on data_0, the list on lots
Texts for the website / Photos to check aggregate/csv, aggregate/json The three languages in one row per lot; the lots held back
Outputs output/pdf ×3, output/image, output/csv, output/json ×3 The catalogues, the cover, the website’s texts, the photos to check, the English list and a layout report

Reading a run: Photos to check lists every lot whose photo did not match, with the kind expected and the kind seen; it is empty when all match.

Cost: about 0.03 credits to look at each photo and 0.024 to write each entry in three languages — 0.36 for seven lots. Formatting, checking and laying out are free.

The example installs into your workspace with an API key, through the public API only — see Installing an example. It uploads the six photos and installs the document template too:

Terminal window
node madoo-install-example.mjs t03-antiques-catalogue --api <your Madoo API URL> --key <key.json> --run

--run makes the three catalogues (about 0.4 credits). To make your own, run the workflow with your lots — each with a photo — and your words on the two inputs.

  • Your objects: the list of kinds is in the photo reader’s brief and in its schema; a shop of lamps and mirrors, of bags and shoes, of bottles and labels changes the list. The check works the same.
  • Another language: add a key to the writer’s brief and its schema, a set of figure steps with the locale (fr-FR writes 2 400 €), a list, a render, and the language’s words.
  • A marketplace instead of print: the website CSV already has the three languages; add the columns the marketplace expects.
  • A person in the loop: send Photos to check to whoever keeps the records; the catalogue prints without the lot until its photo is fixed.

manifest · workflow · the complete graph · the template · the lots and words