Building an Agent-Operated Content Pipeline
by Allan Hillman, AI Engineer & Solution Architect
I have written before about the general discipline of shipping AI features: type the contract, put a human on the irreversible step, fail loud. This is the specific thing that discipline turned into on my own site, a script I actually run, so it is worth walking through the parts that matter.
The job is small: a topic or a hand-written spec goes in, and a structured draft comes out as an unpublished entry in Contentful that I review, edit, and publish myself. The agent never touches production. That one constraint shaped every other decision in the script.
Two ways in: a topic or a spec
The script takes either a topic or a finished spec. Give it a topic and it calls a model, with the voice rules and the output contract baked into the system prompt, and gets back a draft. Give it a finished JSON spec instead and it skips the model entirely, because in that mode I have already done the generating, by hand or with an agent, and the script just validates the spec and ingests it. There is a flag that prints the exact schema, so whatever is doing the generating knows the shape it has to hit.
The contract is a few typed blocks
The draft is not markdown. It is a small JSON object: a title, a slug, an excerpt, a few tags, and a body that is an ordered list of blocks, where every block is a paragraph, a heading, a list, or a quote. A short function walks that list and emits the matching Contentful rich-text nodes, so a heading becomes a heading-2 and a list becomes an unordered-list. Because there is no markdown to parse, there are no markdown edge cases to get wrong. The shape is the contract, and a block that is not one of those four types never becomes a node.
The guards run before anything is written
Before the script creates an entry, it checks the spec. The slug has to be clean, lowercase letters, numbers, and dashes only. The body cannot be empty. And the whole spec gets stringified and scanned for one specific character, the em-dash, which is my single most reliable tell for text a human has not touched. If any check fails, the script throws and no entry is created. The rules that can be enforced mechanically are, so a persuasive generation cannot talk its way past them.
Unpublished is the default, not a setting
When the checks pass, the script creates the entry and stops. It does not call publish. A newly created entry in Contentful is a draft by default, so the safe behavior is also the default behavior, and there is no auto-publish flag to get wrong. It links the post to my author entry, prints the id and a direct edit link, and gets out of the way. The publish button stays a human decision, which is the entire point.
The generation is the smallest part of all this. What makes the script worth trusting is everything around it, the typed contract and the hard stop before publish. I would rather run a boring pipeline I trust than a clever one I have to watch.
Let the agent draft. Keep the publish button human.