On-demand AI generation
$9,000/month → $700/month
Moving personalized outreach from precomputation to user demand reduced model calls by 96% and removed an obsolete batch pipeline.
4 min readBy Abdullah Raheel
Precomputation multiplied at product scale
A property search returned 50 to 500 buyers. The old path generated outreach for every result before the user chose anyone to contact.
That made cost scale with search results. It should have scaled with the much smaller number of people who actually received a message.
Four agents ran for every buyer
SendFlow created the first draft with up to three retries. A Review Gate accepted or rejected it, a Content Rewriter adjusted tone and detail, and a Message Humanizer produced two or three variants. Eight worker threads ran this chain in parallel and stored the output in the database before anyone clicked Send.
Search result
-> SendFlow (up to three retries)
-> Review Gate
-> Content Rewriter
-> Message Humanizer (2-3 variants)
-> store every resultThe chain made four to five model calls per buyer. For a search returning 500 buyers, that meant roughly 2,500 model calls before the user selected a recipient.
Eighty percent of the work was discarded
Users contacted about 100 buyers from that 500-result example. Approximately 80% of the precomputed messages were never consumed, leaving about 2,000 calls with no user-facing outcome. Prompt tuning could make each call cheaper, but it could not justify running the unused calls.
Move generation to the moment of intent
The revised search returned buyers without generating outreach. One explicit Regenerate with AI action invoked one specialized agent for the selected contact. The request context included location, phone, buyer and sender first names, market, buyer activity, formatted conversation history, and buyer ID; the result remained editable before sending.
Search 500 buyers -> generate 0 messages
User selects a buyer -> assemble contact and conversation context
Regenerate with AI -> 1 model call
ProgressReporter -> WebSocket update every two seconds
Editable result -> user decides whether to sendThe implementation evolved. One repository snapshot used a direct REST request when the selected contact panel opened; the later article-source flow moved the action behind explicit user intent and streamed progress over WebSocket. Those are stages of the product, not one simultaneous code path.
Make on-demand work feel immediate
The product had more than 25 specialized agent configurations on gpt-4.1-nano. One configuration was selected for each request; this was not a chain of 25 agents. ProgressReporter sent a WebSocket update every two seconds while the message was generated.
Moving generation behind a click made the wait visible. I kept the response editable, showed progress, and generated only for the recipient the user had selected.
Delete work instead of tuning waste
In the documented 500-buyer example, calls fell from about 2,500 to about 100, a 96% reduction. Monthly model spend moved from roughly $9,000 to roughly $700. Those rounded monthly figures imply roughly $100,000 in annualized spend reduction.
The change also removed 1,218 lines, including the 640-line batch processor. Deleting that path removed its retries, workers, persistence, and multi-agent coordination as well.
- Measure how much generated output users actually consume.
- Put expensive work behind the product event that needs it.
- Use visible progress when on-demand work cannot finish immediately.
- Delete the batch architecture after the product no longer depends on it.

