The problem
Not "generate text with AI". Making a chain of unreliable steps produce something publishable without watching each one by hand.
A history channel lives on rhythm: post whenever you get around to it, and it stops existing. But every piece of content is a long job — researching, deciding which story to tell, writing ten slides, checking the facts hold up, sourcing images, composing them, writing the narration, editing the video. Done by hand, that's half a day per post, which is why almost all of these projects die after three posts.
The temptation is to ask a language model to do all of it in one go. It doesn't work, and not because of writing quality: it fails because a single response can't be checked piece by piece. If the text has a historical error, the image has an anachronism and the narrative structure is flat, there's no way to fix the second one without regenerating the other two as well.
The real problem is architectural: break the work into steps that each produce an inspectable artefact, and put the checks inside the chain — where a mistake can still be fixed at close to zero cost — instead of at the end, where fixing it means redoing everything.
- Historical accuracy is non-negotiable. A history channel loses credibility with a single invented prefect, and never gets it back.
- One operator. Any step that requires sitting in front of the screen waiting is, in practice, a failure of the project.
- Negligible cost per piece. If a carousel costs as much as a coffee, the project stops itself within a month.
- Must be able to fail halfway. APIs error out: starting over after twenty minutes of work is not acceptable.
How it works
An orchestrator, specialized agents, and an artefact on disk at every step.
The dashed boxes in the second view are real files on disk. It's the property everything else depends on: every step checks whether its own file already exists before calling the API, so an interrupted run picks up where it left off, and redoing a single step just means deleting the matching file.
The pipeline is a sequence of agents, each with one job and one output file. The orchestrator knows nothing about Roman history: it only knows in what order to call the agents and where each one writes its result.
-
1 · research
From a topic to a dossier
From a title — "The Battle of Cannae" — the research agent produces a structured dossier: key facts, common misconceptions to debunk, suggested primary sources. It's the first inspectable artefact: if the dossier is wrong, it shows immediately, and fixing it there costs a fraction of what fixing it later would.
-
2 · story
First what to tell, then how to write it
The writing agent works in two separate phases: first it plans the narrative arc — the angle, the tension, the hinges between one slide and the next — and only then writes the ten slides following that plan.
It's the choice I'm proudest of, because it fixes a specific flaw: a model asked to "tell this story in ten slides" produces a list disguised as a story. Ten true facts, lined up, with the fifth one never needing the fourth. Separating planning from writing turns the structure into an artefact of its own, one you can read and correct before a single line of text exists.
-
3 · review
Quality control is an agent, not a final step
An agent re-reads the slides and checks them against the documented history: chronology, correctly attributing actions to the right person, omitted steps. Corrections flow back into writing before a single image is generated — which is the point where redoing things really costs something.
-
4 · images
Generated, but not only
For each slide the system decides whether it needs a generated image or a real, public-domain image from open museum archives: for a map, an artefact or a documented portrait, the real object always beats the reconstruction. Then a second quality check — this one visual — looks at the images produced and rejects anachronisms, modern symbols that slip into frame, and subjects repeated across slides.
-
5 · editing
Composition and video
The final slides are composed to Instagram's dimensions — text, font, watermark — and, in parallel, a separate branch produces the video: synthetic narration, synchronized subtitles and editing. The voice engine is interchangeable, including one that runs locally with no network access.
Every agent checks whether its own file already exists before calling the API. A run interrupted halfway picks up where it left off without spending anything again, and regenerating a single step just means deleting the matching file. It's one line of logic per agent, and it's what makes the system actually usable: without it, any error at the eighth step would cost the previous seven.
The choices, and why
Almost all of them take the same shape: taking decisions away from the model where the model isn't needed.
-
cost
Two models, split by role
The small, fast model handles the structured steps — research, review, prompt preparation — and the large one only the creative steps: narrative plan, slide text, narration. It's not a micro-optimization: it's the difference between a cost per post of a few cents and one that discourages using the system at all. Measured: about 13 cents per carousel, images and video included.
-
determinism
Whatever is deterministic gets written in code
The graphic style descriptor, the negative prompts, the line with the historical source at the bottom of the caption: all things the model could do, and that are instead composed in Python after its response.
The rule is simple: if something has to come out identical every time, asking a language model for it is a design mistake — not because it gets it wrong often, but because when it does, it fails silently, roughly one time in fifty, which is the most expensive kind of failure to catch.
-
robustness
Assume the response is messy
Models return JSON wrapped in code blocks, preceded by a pleasantry or followed by an unrequested explanation. A single shared extractor pulls the first complete JSON object out of any response, and every step uses it. The same goes for network errors: automatic retries with increasing backoff on overload and rate limits, and if retries run out the error is translated into a readable message with the recommended action, instead of a stack trace.
-
control
A dashboard, not a command line
Everything can be driven from the command line, but there's a web interface on top: starting the pipeline with live logs, a slide editor with recomposition, a gallery, an editorial calendar, and the ability to rewrite the instructions for any single step without touching the code. That last one is the feature I use the most: most of the work on a system like this is tuning the instructions, and having to do it by editing source files slows the cycle down enough to discourage experimenting.
What went wrong
The useful part. All real mistakes, where diagnosing cost more than fixing.
-
mistake 1
Prompt vocabulary beats the style instruction
I wanted a fully illustrated carousel instead of a photorealistic one. I took the existing prompts — written for the realistic style, so full of cinematic language: "dramatic light", "cinematic", "golden hour" — and appended the new style descriptor at the end. The result stayed photorealistic on almost every slide.
Diagnosis: the photographic vocabulary already present in the rest of the prompt outweighs a style tag stuck on at the end. It's not a priority problem between instructions, it's that those words are a style instruction, just an implicit one. The fix was rewriting the prompts from scratch in a consistent register, with the descriptor both at the start and at the end.
fig. 3 — why the first attempt failed -
mistake 2
More subtle: it's not the vocabulary, it's the scene
Even with the prompts rewritten, some slides kept coming back photorealistic. Always the same ones: those where the scene described was a dramatic group composition — researchers bent over an artefact in the dim light, senators horrified in front of a threat.
Diagnosis: that kind of composition has too strong a painterly pull for a style tag to override; the scene itself evokes academic painting. The fix wasn't about how it's written, but about what's being asked for: a single subject, one action, flat light, few figures. Simplify the scene, not the language.
-
mistake 3
The same fix needed to be applied in two places
The regeneration path — the one that rewrites an image rejected by visual review — had its own instructions, independent from the main step's. The result: a rejected, regenerated image came back photorealistic even though its metadata recorded the correct style. It happened twice before I connected the two.
Lesson: duplicating a model's instructions is duplicating code, with the added problem that the two branches drift apart silently — no compiler warns you that you only fixed half the system.
-
mistake 4
Finding the error and knowing how to fix it are different skills
Text review was strengthened by checking it against documented history instead of just the starting dossier — because the dossier itself can be incomplete. It worked: it caught a completely invented official and an emperor skipped in a succession, errors the previous check, anchored to the dossier, couldn't see.
But automatic repair didn't work. Asking the model to reinsert a missing emperor by patching the existing text produces inconsistent results: it's a structural change, not a local fix. The operational conclusion is that spotting is reliable, rewriting isn't: for that kind of error it's better to fix the dossier upstream and regenerate the slides.
-
mistake 5
A hypothesis the data disproved, kept anyway — on a leash
I'd guessed that a lighter illustrated style would increase audience pull. Looking at how the history accounts that actually work are made, the hypothesis turned out false: none of the serious ones use it.
I introduced it anyway, experimentally, but allowed on a single slide — the closing fun-fact one — and forced back to the documentary style anywhere the model tries to pick it elsewhere. The cover and the call to action always stay restrained. I wanted to be able to measure it without risking the channel's credibility on the slides that build it.
-
mistake 6
Visual review approved a flag
To close every video I asked for a legionary eagle. The image model drew a red banner with an eagle: the flag of Albania. I noticed it myself, after the video was done, because no step was looking at the images — quality control only existed on the text.
I added the visual check described above. A few days later another eagle on a red banner showed up, and the check had approved it, calling it a "Roman standard." Lesson: where the mistake is costly and recognizable at a glance, a hard rule beats a judgment call. Today any cloth banner with an eagle is rejected, always; and the closing call to action is no longer described by the model at all — it has a fixed image, ruins and laurel, no eagles.
Outcome, and what's missing
The channel is live and publishing. The measured cost is about thirteen cents per carousel, video included, and an interrupted run costs nothing because it picks up where it stopped. The part that has held up best over time isn't any single agent: it's the decision to have each one write a readable file to disk. Every error diagnosed over these months was diagnosed by opening one of those files.
If I had to name the transferable lesson, it's not about the domain: it's that in a multi-agent system the dominant cost isn't generation, it's diagnosis. Almost all the architecture work in here exists to make observable a process that would otherwise be a black box with a video at the end.
The reel became the product, the carousel the raw material. The pipeline was built around the carousel; the video was a presentation of the same slides. Today the reel is its own vertical video, with synchronized subtitles, and the voice is picked from three engines, one of which runs locally.
Publishing is now automatic. A single command uploads the video to temporary storage — the Instagram API won't accept the file directly, it wants a public address to download it from — creates the content, waits for it to be processed, publishes it, deletes the temporary copy and marks the reel as posted. By default it doesn't publish: it shows what it would do and stops.
What's still open. The same API doesn't allow scheduling a reel for a future time, so publishing is immediate and scheduling is still unsolved (see below). And automation solved consistency, not distribution: the reels published so far get anywhere from about twenty to around three hundred views each.
Scheduled publishing is the weak point today: the plan gets calculated, but the actual queuing is still rudimentary. This is the right place for a managed message queue — deferred publishing, retries when the platform rate-limits requests, batch processing — and I'll evaluate it there, where the problem actually exists, not on the site you're reading, where it would be architecture looking for a problem.
See the channel on Instagram ↗ ↑ All projects
The code isn't public. If the project interests you and you'd like to talk, get in touch.