Don't ask the model to keep two things in sync
An editor used to retype eight fields for every article and hope the table of contents still pointed at the right headings. I built the pipeline that replaced that work, and the most useful decision in it was what I refused to hand to the model.
On this page
Preparing an article for publication at Fibre2Fashion meant an editor retyping eight derived fields by hand: a headline, key takeaways, a table of contents, SEO metadata, FAQs and more. Then came the part nobody enjoyed, which was checking that every link in the table of contents still jumped to the heading it was supposed to. Sometimes it did not, and a broken page went out.
It was slow, it was identical every time, and it was exactly the kind of work a language model looks perfect for. I built the system that does it now, AI Article Gen, on my own and it is in production. The most useful decision in it was a list of things I did not let the model do.
Parsing has a right answer, so code does it
The input is a Word document. The tempting design is to hand the file to a model and ask for clean HTML.
I did not, because converting a .docx to HTML is not a judgement call. A heading is a heading. So intake is deterministic: a converter turns the document into semantic HTML, with no LLM anywhere in that path.
That one choice moved a whole class of bugs. When formatting comes out wrong, it is a code problem I can reproduce and write a test for, not a prompt problem that fails differently on Tuesday.
Three calls, each with one job
The model does the work that needs judgement, in three JSON-mode calls for a short article: one for structure, one for the body, one for metadata. Each call is retried on failure and counted against a token budget.
The body pass is chunked by a character budget. A long research paper would otherwise fill the context window and fail at the worst possible moment, on the longest and most valuable documents.
The anchors match because they cannot not match
This was the recurring source of broken pages, so it got the strictest treatment.
The obvious approach is to ask the model for a table of contents and for headings with ids, and tell it to keep them consistent. That works most of the time, which for links on a published page is the same as not working.
Instead the contents list and the heading anchors both come from the same walk over the document. There are not two outputs to keep in agreement, because there is one source and two views of it. The model is never asked to keep them in sync.
Contract tests pin that parity on the HTML and on the .docx bookmarks, and it is re-derived on every save, so an editor's later change cannot quietly break it.
Guidelines live in the database
Editorial rules change more often than code should. So they are SQL rows, resolved in order: an exact match for the article type, then a default for any type, then a built-in prompt.
If the database is unreachable, the pipeline falls back and carries on. An editor can change house style without waiting for me to deploy, and a database fault costs nothing worse than default wording.
A missing PDF is not a failed article
The pipeline writes three files: DOCX, PDF and HTM. The PDF is rendered in layers. The first choice is a true Word export of the generated .docx, so the two cannot disagree. Where Word is not available it falls back to a pure-Python renderer. If both fail, the article is still saved, because an article can be published without its PDF.
Failing open like that is only safe if somebody finds out. Metrics label every render by file type and result, so a PDF that silently degraded shows up in the numbers instead of in a complaint.
It lives where the editors already work
None of this is a new tool to log in to. It is a page inside the company's existing ASP.NET portal, with the Python service hosted behind it. The editor's job went from retyping eight fields to three steps: upload, review, save.
The rule I took from it
Give the model the parts that need judgement and nothing else. Anything with a right answer, such as parsing, linking, file rendering and which rule applies, belongs in code where it can be tested. A model asked to keep two things consistent will do it nearly every time, and "nearly" is the word that ships broken pages.
The same idea runs through the news-discovery agent I built at the same company. If you have a process like this one, repetitive, rule-bound and done by hand, tell me about it.
Frequently asked questions
- What does the document-to-article pipeline do?
- It takes a raw Word document and produces a publish-ready article package: headline, key takeaways, table of contents, body, SEO metadata and FAQs, plus the DOCX, PDF and HTM files the public site serves. The editor uploads, reviews and saves.
- Why not let the LLM parse the Word document?
- Because parsing has a right answer. A converter turns the .docx into semantic HTML with no model involved, so formatting fidelity is a code problem that can be tested, not a prompt problem that fails differently each time.
- How do you stop a table of contents drifting from its headings?
- By never generating them separately. The contents list and the heading anchors are both derived from one walk over the document, so they match by construction, and contract tests pin that on the HTML and on the .docx bookmarks.
- How do editorial guidelines change without a deploy?
- They are rows in SQL, not text in the code. The pipeline looks for an exact match on article type, then a default for any type, then falls back to a built-in prompt, and it keeps working if the database is unreachable.
One vector search across a mixed corpus returns confident nonsense, because semantic closeness is not topical relevance. Filtering on metadata first, then searching, then reranking fixed it. Here is what each stage actually earns.
ReadFibre2Fashion's news desk spent every morning searching dozens of sources by hand, judging what mattered, confirming it, then writing it up. A two-phase agent now does the finding and the drafting, with an editor choosing what gets pursued — cutting 80–90% of that routine and running the same desk with fewer people on it.
Read