Capstone: a mini-builder “brief, structure, copy, code”

Why:One big request breaks where you can’t see it. Afterwards you’ll assemble a pipeline of four stages with checks between them, understand how a call to a model through an API works (a request, a key, tokens, errors), decide which fields of the brief go to a model and when to pick a model in another region or one you run yourself, and write down the design of your own mini-builder.

35 minutes

Read by speech synthesis (Qwen3-TTS); the voice is not a person’s.

A “brief, structure, copy, code” pipeline is a chain of model requests where ordinary code checks each stage’s output before the next stage uses it.

One request or a pipeline

In the earlier lessons you gave a model a task and checked the result. Now put it together into a pipeline: a chain of small requests in which the result of each is checked before it becomes the input of the next. That’s a mini-builder, what is inside any AI builder from the first lesson, only now you see all the details.

Why split it up. One big request “make a site” breaks where you can’t see it: it isn’t clear at which step an invented fact appeared and what exactly to redo. In a pipeline the stages are shorter and more exact, a program can check each output, a mistake is visible where it arose, and repeating one stage costs less than repeating everything. The authors of guides to building agents call this technique prompt chaining and advise putting programmatic checks between the steps.

Four stages

  1. The brief. You choose which fields go to the model. Only public ones: who you are, for whom, the main action, the sections, the offers, the proof, the tone. The phone number, the email and other people’s personal data stay with you. The output: JSON of the chosen fields. The check: that it has no contacts.
  2. The structure. The model receives the JSON and returns JSON with the structure: a list of sections and the label of the main button. The format is strict, because a program reads it. The check: the schema (does the JSON parse) and a comparison with the brief (the section names exactly the same, no extras, the button’s label matches).
  3. The copy. One section per request, only from the brief’s facts and in your tone (by examples). The fact check: every number and name in the text is found in the brief, and there are no empty formulas (“a personal approach”).
  4. The code. The model receives the structure and the copy and writes HTML and CSS. The code check: one h1, lang, alt on images, labels on fields, no outside scripts and no div “buttons”. These are the same checks you did by hand in the last lesson, now done by a program.

The checks between the stages are ordinary code, not another model: it applies an exact rule, doesn’t tire and doesn’t confidently “approve” a mistake. A reviewer model is fine as an addition but not as the only gate.

Play with the diagram. It’s a teaching mode: no model runs, and each stage assembles its output by a rule from your brief, so the diagram works offline and for free. But the design is the same: what goes in, what comes out, and how a check refuses. Switch on a “model mistake” at each stage and see what the check says.

The pipeline “brief, structure, copy, code” with checks

No model runs here. Each stage assembles its output by a written rule from your brief, so the page works offline and costs nothing. What is real here is the pipeline’s design: what goes into a stage, what comes out and in what format, and how a check (ordinary code, not another model) refuses when an output has broken the contract.

1. The brief: what goes to the model
{
 "who": "What the project does",
 "forWhom": "For whom",
 "mainAction": "Get in touch",
 "sections": [
  "About",
  "Services",
  "Contact"
 ],
 "items": [
  "Service"
 ],
 "proofs": []
}

The check passed. The prompt holds only what will become public: no personal contacts. About 47 tokens go in.

2. The structure (JSON)
{
 "sections": [
  {
   "title": "About"
  },
  {
   "title": "Services"
  },
  {
   "title": "Contact"
  }
 ],
 "cta": "Get in touch"
}

The check passed. The JSON parsed, and the sections and the button’s label match the brief exactly.

3. The copy, section by section
  • About. What the project does. For whom: For whom.
  • Services. Service: What it is.
  • Contact. To “Get in touch”, use the form below.

The check passed. Every number is in the brief, and there are no empty formulas.

4. The code
<!doctype html>
<html lang="en">
<head><meta charset="utf-8"><title>Project</title></head>
<body>
  <header><h1>Project</h1></header>
  <main>
    <section>
      <h2>About</h2>
      <p>What the project does. For whom: For whom.</p>
    </section>
    <section>
      <h2>Services</h2>
      <p>Service: What it is.</p>
    </section>
    <section>
      <h2>Contact</h2>
      <p>To “Get in touch”, use the form below.</p>
    </section>
    <form action="https://example.com/leads" method="post">
      <p><label for="n">Name</label> <input id="n" name="name" required></p>
      <p><label for="p">Phone</label> <input id="p" name="phone" type="tel" required></p>
      <p><label><input type="checkbox" required> I agree to the processing of my data, <a href="/privacy/">privacy notice</a></label></p>
      <button>Get in touch</button>
    </form>
  </main>
</body>
</html>

In a real run there is a network call between the stages: cost, time and random failures. The teaching mode has none of those, but the shape is the same.

Check yourself: why check the structure at the second stage, and not only the finished code at the last?

A mistake caught at the second stage (an extra section) is cheap: one small answer is redone. The same mistake in finished code means redoing the copy and the code.

What a call to a model through an API is

When you type in a builder’s window, an ordinary HTTP request happens behind the scenes, the kind you know from the part on servers (you had REST and fetch). Only it’s a model that answers, not a page.

  • The request goes by the POST method to the provider’s address (Anthropic, OpenAI, OpenRouter and others each have their own addresses and formats).
  • The body of the request is JSON: the model’s name, the messages (an instruction, system, that sets the behavior, and an assignment, user, with the data) and a limit on the length of the answer.
  • In the header is the key: your pass. By it the provider knows whom to bill.
  • The answer is JSON too: inside is the text, and beside it the number of tokens used.

Tokens are the pieces of text a model cuts words into; in English, about four characters, or three quarters of a word, to a token. You pay for tokens in and out separately, and the output ones usually cost more, so long answers and repeated “fix it” cost noticeable money. The textbook shows an estimate of cost before each question, and you should do the same in your own pipeline.

There are three kinds of error: 401 or 403: the key didn’t fit; 429: too many requests, wait; 5xx: a failure on the provider’s side, try again later. A pipeline should be able to wait and retry, not break silently.

A key is a secret. It belongs in your settings or in environment variables on a server. Not in the page’s code, not in a repository, not in the site’s archive that you download and send out into the world. If a key ever gets somewhere public, it’s revoked and a new one is issued. In the textbook your key is kept only in your browser and goes only to the provider you chose, and only when you ask a question. And on a published site, requests to a model go through your server code or a function where the key is in the settings.

Check yourself: why can’t you call a model straight from a site’s page with your key inside?

All the page’s code is visible to visitors, so everyone will see the key and be able to spend your money. A request to a model is made from a server or a function, and the page asks that.

Choosing a model, and where the data goes

You have a choice of providers (Anthropic, OpenAI, Google, OpenRouter, which routes to many) and of models you can run yourself (open-weight models you host on your own machine or server). They differ in quality on your task, in price, and in where your data goes. Ask a provider where data is processed, whether it’s used for training and how to delete it, and compare quality on your own texts: it differs between models and between tasks.

The practical choice is this. If a request holds only public material (and in a good pipeline it should), any model will do. If personal data is unavoidable (answering clients’ letters, say), then the transfer rules from the last lesson of the part on infrastructure apply to the model’s provider like any other processor: for people in the EU you need a provider that states where it processes data and signs a data processing agreement and, where data leaves the EU, a transfer mechanism, or a model you run yourself. General information, not legal advice.

A note on the Russian market, as one comparison: there the law asks for citizens’ personal data to be recorded first in Russia, so Russian projects look at Russian models such as GigaChat from Sber and YandexGPT in Yandex AI Studio, with their own APIs and documentation. The idea carries over anywhere: a provider in the right legal region makes a legal question simpler, and doesn’t take away your duties or the need to read the contract.

For those who want to run it for real

The textbook shows the pipeline’s design in a teaching mode. If you want to try it with a real model, here is a minimal script for Node.js with an Anthropic model. It’s an example to run on your own machine (not in a browser and not on a site): the key and the model’s name come from environment variables, and a program checks the structure and the copy. The format of API answers may change, so check the provider’s documentation (links in the sources).

import { readFile, writeFile } from 'node:fs/promises';

const key = process.env.ANTHROPIC_API_KEY;
const model = process.env.MODEL;
if (!key || !model) throw new Error('Set the variables ANTHROPIC_API_KEY and MODEL');

async function ask(system, user) {
  const res = await fetch('https://api.anthropic.com/v1/messages', {
    method: 'POST',
    headers: { 'content-type': 'application/json', 'x-api-key': key, 'anthropic-version': '2023-06-01' },
    body: JSON.stringify({ model, max_tokens: 4000, system, messages: [{ role: 'user', content: user }] })
  });
  if (!res.ok) throw new Error(`Error ${res.status}`);
  const data = await res.json();
  return data.content.filter((b) => b.type === 'text').map((b) => b.text).join('');
}

const brief = JSON.parse(await readFile(process.argv[2], 'utf8'));

// Stage 1. Only public fields go to the model: no phone, no email.
const { who, forWhom, mainAction, sections, items, proofs, tone } = brief;
const input = JSON.stringify({ who, forWhom, mainAction, sections, items, proofs, tone });

// Stage 2. The structure: JSON that a program checks.
const structure = JSON.parse(await ask('Answer with JSON only, of the form {"sections":[{"title":""}]}, no explanations.', `Brief: ${input}`));
const titles = structure.sections.map((s) => s.title);
if (JSON.stringify(titles) !== JSON.stringify(sections)) throw new Error('The structure does not match the brief');

// Stage 3. Copy, one section at a time, from the brief's facts only.
const copy = [];
for (const title of titles) {
  const text = await ask('Write in English, briefly, from the brief’s facts only. Do not invent numbers or achievements.', `Brief: ${input}\nWrite the text of the section “${title}”.`);
  for (const n of text.match(/d+/g) ?? []) if (!input.includes(n)) throw new Error(`The number ${n} in the section “${title}” is not from the brief`);
  copy.push({ title, text });
}

// Stage 4. The code, and a program checks again.
const html = await ask('Return one HTML file: semantic markup, one h1, lang="en", alt on images, no external scripts. Code only.', JSON.stringify({ copy, action: mainAction }));
if (/<script[^>]+src=/i.test(html)) throw new Error('The code has an external script');
if (/<img(?![^>]*alt=)/i.test(html)) throw new Error('An image has no alt');
await writeFile('index.html', html);
console.log('Done: index.html. Check it by eye and against the list from the last lesson.');

To run it: ANTHROPIC_API_KEY=… MODEL=… node pipeline.mjs brief.json, where brief.json is your brief without contacts. It’s an example, not a finished product: a real pipeline would need retries on 429 and 5xx errors, counting tokens and a more forgiving parse of the answer (a model may wrap the JSON in explanations). But the builders from the first lesson are built on this pattern.

Practice

Build the design of your mini-builder: a table of four stages, a structure JSON for your sections and the decisions about data. For each stage, name the input (from the previous stage’s output), what the model does, the output with its format and the check. Then settle three questions: which facts are allowed in the copy, which fields of the brief you’ll send to a model and which you won’t, where the key is kept, and when you would choose another region or a model of your own.

It’s a design, not a run: the textbook doesn’t call a model from your practice, so as not to send your data without your decision. The check reads your table and makes sure the stages are linked, the checks are in place, and there’s no key or contact in the plan. You can make a real run by the example above.

What was wrong with 2025

In 2025 a builder was a black box: you wrote a request and got a site without seeing the steps between. Today the opposite is valued: visible stages, outputs you can check and data you yourself decided to send. Someone who understands the pipeline doesn’t depend on a particular product: they can build the same from any parts, and that’s the real measure of understanding.

Practice

Preparing your page…

Test

Question 1 of 5

Put the stages of the pipeline in order

Put them in order with the “up” and “down” buttons.

Cards

These cards go into your review once you finish the lesson.

  • Why a pipeline instead of one request?

    Answer

    The stages are shorter and more exact, a program checks each output, a mistake shows where it arose, and a repeat costs less. It is the technique of prompt chaining, with checks between the steps.

  • What is a call to a model through an API made of?

    Answer

    An HTTPS request with JSON to the provider’s address: the model’s name, the messages (an instruction and an assignment), a limit on the answer and a key in the header. The answer is JSON too, with text. You pay for tokens in and out, counted separately.

  • Where may you keep an API key?

    Answer

    In your own protected settings, and on a server in environment variables. Not in the page’s code, not in a repository, not in the site’s archive. If it got somewhere public: revoke it and issue a new one.

  • What do you send to a model?

    Answer

    Only what will become public anyway: who you are, for whom, the sections, the offers, the proof, the tone. Contacts and other people’s personal data are not sent; for personal data covered by a transfer rule, weigh a provider in the right region or a model you run yourself.

Sources

Next

You now know: working with a model’s API and your own key, the builder pipeline, models beyond the big US labs.