How it was made
The artist's CV is a list of lines. The archive turns each line into a structured record that can be filtered, searched and opened on its own page. This is the pipeline.
Pipeline
- The raw CV text is read from a plain file.
- Gemini extracts each CV line into a structured JSON record. The request uses a response schema, so the model can only return the fields and type values we define.
- The result is validated again in code. A record with a missing or malformed field stops the run instead of reaching the site.
- Plain code adds what does not need AI: a stable URL id, and a review flag when the AI was not fully certain.
- The AI also proposes a short image search query for each entry. That query goes to the Pexels API, and the first landscape photos found are attached with their credits. If nothing is found, the page shows a generated placeholder.
- A person reads the flagged entries and corrects them in the JSON file.
- The website is built statically from that file, so it needs no API key at runtime.
One line, before and after
Raw CV
2019 — Residency at Villa Medici, Rome 2020 — Group exhibition at Palais de Tokyo, Paris 2021 — Collaboration with XYZ Foundation, London 2022 — Solo exhibition at Gallery ABC, Berlin
Structured record
{
"year": 2019,
"type": "residency",
"title": "Residency at Villa Medici",
"institution": "Villa Medici",
"city": "Rome",
"country": "Italy",
"description": "The artist completed a residency at Villa Medici in Rome in 2019. No further details were provided.",
"tags": [
"residency",
"rome",
"italy",
"villa medici"
],
"imageQuery": "villa medici rome building",
"sourceText": "2019 — Residency at Villa Medici, Rome",
"inferredFields": [
"country",
"description"
],
"confidence": "high",
"id": "2019-villa-medici",
"needsReview": false
}Who does what
AI
- Splits each line into year, type, institution and city
- Infers obvious gaps such as the country of a city
- Writes a short neutral description
- Suggests tags and an image query
Code
- Enforces the schema
- Creates URL ids
- Fetches images and credits
- Flags uncertain records
A person
- Checks flagged entries
- Confirms descriptions
- Replaces stock images with the artist's own documentation
Multi-artist version: database and authentication
This prototype is single-artist and stateless: one committed JSON file, no accounts, no writes at runtime. A real version, many artists, each managing their own archive, needs two things this prototype deliberately doesn't have yet.
Data model
User
|
v
Artist Profile (name, bio, links, one per user)
|
v
Archive (one per artist profile)
|
v
Experiences (year, type, institution, city, country,
description, tags, images, needsReview)Experiences keep the same shape already in use, that part doesn't change. What's new is that they belong to an Archive, which belongs to an Artist Profile, which belongs to a User. A Postgres table per box above, with foreign keys, is enough, no need for anything more exotic.
Auth and publishing flow
Login | v Dashboard (this page, per logged-in artist) | v My Archive (list experiences, edit or delete one) | v Edit / Review (fix a field, clear a "needs review" flag) | v Publish (write to the database, now visible on /archive)
The ingestion pipeline (upload, extract text, group into entries, run through Gemini) stays exactly as it is, it feeds into "Edit / Review" instead of a JSON file. "Publish" is the one genuinely new step: right now nothing this prototype generates is ever saved, that button is what a database is actually for.
Not built here on purpose: this is still the CV-to-archive prototype the brief asked for, adding real accounts and a database is a separate, larger piece of work than what's being evaluated.