The Cache Key Is the Whole Design
A derived text — a summary, a translation, a tidied body — is expensive once and free thereafter, but only if you can recognize the request the second time. So the key has to name everything that would change the output: which article, which kind of derivation, and which target language. Miss one dimension and you either return the wrong artifact or pay again for one you already have.
Keying on the article alone is the classic mistake, and it fails in the more embarrassing direction: ask for a summary, then ask for a translation, and the second request finds a cached entry and returns the summary. A composite key costs nothing and makes the wrong answer unrepresentable.
Derive From the Processed Form, Not the Raw One
A derivation should have exactly one input, and it should be the cleaned text rather than the raw extraction. Summarizing raw output means summarizing navigation menus and cookie notices along with the article, which produces a worse result at the same price.
The consequence is a dependency worth making explicit: derivations refuse politely on an article that has not been processed yet, rather than falling back to the raw form. A cached derivation built from the wrong input is worse than no derivation, because nothing downstream can tell which input produced it.
Not Everything Needs a Model
One detail worth borrowing: deciding which language an article is in does not require a turn. Counting characters by script answers it deterministically, instantly, and for free. The rule generalizes — before spending a turn on a classification, ask whether the answer is computable. Language detection, length, whether something is a list, whether a field is empty: all of these get proposed as model tasks and none of them should be.
Composting Is Allowed, and That Is the Point
When the voice specification for derivations changed, the previously cached texts had been generated under the old one. They were deleted rather than migrated, and the next tap re-derived them.
That is only defensible because of what a derivation is: working set, reproducible from an input that still exists. Nothing was lost, because nothing there was ground truth. Being able to say "delete it, it will come back" is the property you get for keeping derived data strictly derived — and the moment a derivation acquires an edit, an annotation, or a manual correction, that property is gone and you have a migration problem forever.