- Format
- Custom AI Tool
- Tools
- Python · Anthropic Claude API · PyMuPDF · ReportLab
- Course
- AI for Architects — ELVTR
Interior architects assemble product selections binders: client-facing PDFs that collect every specified product in a project — vanities, fixtures, tile, furniture — with a page of curated details per product. Building one by hand means opening every manufacturer cut-sheet PDF and product webpage, retyping dimensions, materials, finishes, and care notes into a layout, and keeping a table of contents in sync. It is slow, repetitive work.
This tool automates the whole pipeline. It takes the schedule an architect already has — an Excel export with KEY · DESCRIPTION · PATH columns pointing at each product’s spec sheet or webpage — and produces a finished binder. The intelligence in the middle is Claude, called through the Anthropic API: raw text and images scraped from each source are sent to the model, which returns clean, structured product data. The design is modeled on a real DFH Architects deliverable for a Pacific Palisades rebuild, including the firm’s convention of grouping several schedule keys onto one page when a single document covers them.
Forced Tool-Use
The extraction schema is declared as a tool and the model is compelled to call it, so every response is valid, schema-conforming JSON — no “return JSON only” prompt, no markdown fences, no parsing failures.
Prompt Caching
The long domain system prompt is identical for every product, so it carries a cache breakpoint. The first product writes the cache; every product after reads it at ~10% of the cost.
Vision Input
Product photos and dimensioned drawings extracted from each source are downscaled, encoded, and sent as image blocks alongside the text, grounding the extraction in what the product actually looks like.
How the Pipeline Fits Together
Schedule rows are grouped by shared source; each unique source is extracted, structured by Claude, then rendered into the binder. Two front-ends — a CLI and a notebook form — feed the same pipeline.
Schedule intake
Reads the schedule with case-insensitive header matching; groups rows sharing one PATH into a single product; also writes schedules created in the notebook.
Source extraction
PDF extractor (PyMuPDF text + embedded images with icon filtering), web extractor (requests + BeautifulSoup with keyword and size heuristics for product photos), and a source router for dispatch and path resolution.
Claude API wrapper
Domain system prompt, forced tool-use JSON schema, prompt caching, multimodal image blocks, and layered error handling.
Binder renderer
ReportLab renderer: cover, two-pass table of contents with accurate page numbers, product pages with key badges, and red error blocks for failed products.
Shared pipeline
End-to-end pipeline used by both front-ends, with unique output naming: project__company__timestamp.
Two front-ends
A CLI for batch runs, and a point-and-click Jupyter notebook form (ipywidgets + native file picker) for non-technical users.
The Anthropic API, Deliberately
Rather than hoping for clean JSON in free text, the extraction schema is a tool and tool_choice forces the model to call it. The result is read straight from tool_use.input as a Python dict — no regex, no JSON repair.
ROLE: You are an architectural interior product binder assistant working for an interior architecture firm. You receive raw product information extracted from manufacturer spec sheets (PDF cut sheets) or product webpages, and you extract the information a designer needs when assembling a product selections binder. YOU WILL BE GIVEN: - One or more product KEYS from the project’s finish/fixture schedule (e.g. U-36). Several keys may share one spec sheet when a single document covers multiple scheduled items (a vanity cabinet, its countertop, and its sink). - Short schedule descriptions written by the design team. - The raw text extracted from the source. This text can be noisy: navigation menus, legal boilerplate, unrelated products. Focus only on the product(s) matching the keys and descriptions. - Optionally, images extracted from the source. RULES: - Do not invent information. Only report what is present in the text or clearly visible in the provided images. - If a field is missing from the source, return an empty string. Never guess. - Keep every field concise and professional — a technical reference binder, not marketing copy.
response = client.messages.create(
model="claude-sonnet-4-6",
max_tokens=1500,
system=[{"type": "text", "text": SYSTEM_PROMPT,
"cache_control": {"type": "ephemeral"}}], # prompt caching
tools=[EXTRACT_PRODUCT_INFO_TOOL], # 10-field JSON schema
tool_choice={"type": "tool", "name": "extract_product_info"}, # forced
messages=[{"role": "user", "content": content_blocks}], # text + images
)
info = ProductInfo(**next(b for b in response.content
if b.type == "tool_use").input)Forcing structured output through the tool-use API eliminates an entire class of parsing failures that plague “return JSON only” prompts. Combined with prompt caching on the static system prompt and multimodal image blocks, the integration is production-shaped rather than a minimal single call.
Four Input Classes, All Run Live
The tool ships with four committed example schedules, each stressing a different kind of input — every transcript is unedited terminal output from a live API run.
Multi-key spec sheets
Five schedule rows collapse to three product groups; three vanity keys (cabinet, countertop, sink) share one page and one API call. 3 of 3 succeeded.
Live webpages mixed with PDFs
Correct extraction from ~10,000 characters of noisy page text; a rate-limited image degraded gracefully with a warning rather than a failure. 4 of 4 succeeded.
Broken inputs
A missing file and a dead URL each fail per-product: the run completes with exit code 0 and the failed keys render as red error blocks inside the binder instead of crashing the batch.
Rejected API key
A 401 authentication error is caught as a per-product error — the binder still builds, with the reason documented on the page.
Product keys: U-36, U-37, U-03 Schedule descriptions: VANITY CABINET · COUNTERTOP · MARBLE TOP · BATHROOM SINK · UNDERMOUNT · CERAMIC BASIN Extracted source text: MARLOW 60-in Double Vanity Suite — Model MV-6022, Calderhouse Bath. The MARLOW vanity suite combines a 60-inch solid-birch cabinet, a honed Carrara marble countertop, and two undermount ceramic basins… [+ 1 image block from the PDF]
product_name: Marlow 60-in Double Vanity Suite — Cabinet (Model MV-6022) manufacturer: Calderhouse Bath dimensions: 60 in W × 22 in D × 34 in H materials: Solid birch face frame and doors; plywood box construction finishes: Dove White, Storm Grey, or Natural Birch; Polished Nickel or Matte Black hardware certifications: CARB Phase 2 compliant casework installation_notes: Secure cabinet to wall studs using included mounting cleats. Rough-in drain at 21 in AFF. important_specs: · Model: MV-6022 · Two soft-close doors; six dovetailed drawers · Sold as a coordinated set with countertop and basins (U-37, U-03)
Captured verbatim from the saved interaction log: the model respected the “do not invent” rule — every field traces to the source — and used the schedule keys to select the cabinet as the page’s primary subject while cross-referencing its sibling keys.
Prompt caching was measured from the API usage data across two consecutive extraction calls on a cold cache. The first call wrote the 1,048-token system prompt to the cache; the second read all 1,048 tokens back at roughly a tenth of the normal rate. Across a real binder of 30–50 products, the per-product cost of the domain prompt drops by about 90%.
The Generated Binder
Because the intended users are designers, not developers, the tool also ships a Jupyter notebook: a form with a native file picker, an Add-row button building a live product table, and a Generate button that writes both the schedule and the binder with unique, collision-free names. Both front-ends call the same pipeline.
Reliability Is the Hard Part
Structure beats prompting
Forcing output through the tool-use API removed an entire class of JSON-parsing failures. The schema, not a politely-worded instruction, is what guarantees a usable result every time.
Caching changes the economics
A verbose, careful system prompt would normally be a per-call cost liability. Prompt caching turned it into a one-time expense — the measured 90% drop makes a rich domain prompt essentially free at binder scale.
The real world is messy
Treating every product as an isolated failure domain meant dead links, locked files, rate-limited image hosts, and even a revoked API key all degrade into a documented error block rather than a crashed batch.
The AI is one box in the diagram
The model call was working early; making the tool usable by the people who actually assemble binders — the notebook form, native file picker, self-explanatory output naming — was most of the engineering.