RAG Knowledge Assistants
Assistants that answer from your documents, cite their sources, and say "I don't know" instead of inventing an answer.
Most AI work is not a new product. It is a feature inside something you already run: a content tool in your CMS, an assistant in your SaaS app, a chatbot that deflects repetitive support tickets. The difficulty is rarely the API call — it is making an unpredictable, slow, per-token-billed dependency behave inside a codebase built on the assumption that functions return quickly and deterministically.
The features that survive contact with real users are narrow and tied to something the user was already trying to do:
A general "chat with our website" box, by contrast, is the feature most often built and least often used. Narrow beats broad almost every time.
Fourteen years in these platforms means the AI layer arrives as a citizen of the codebase rather than a bolt-on. In practice that means a real module or plugin with configuration in the admin UI, credentials in environment variables rather than the database, calls dispatched to the queue system instead of blocking a page render, results cached so identical requests are not paid for twice, and respect for the existing permission model, revision history and multilingual setup.
The failure mode I am most often called in to fix is an AI feature that calls an API synchronously during page build. It works fine in staging with one editor and falls over the moment real traffic and a slow provider response coincide.
A model can take several seconds to produce a full answer. A spinner for eight seconds feels broken; the same eight seconds with tokens streaming in feels fast. Streaming responses is usually the single highest-impact thing you can do for perceived quality, and it needs support end to end — server-sent events or equivalent, a front end that renders progressively, and infrastructure that will not buffer the whole response before flushing it.
Alongside that: timeouts with a sensible fallback, cancellation when the user navigates away, and never leaving the interface in a state where the user cannot tell whether anything is happening.
Unlike most features, this one has a per-use cost that scales with traffic. Left unmanaged it produces an unpleasant invoice and no way to explain it. What I put in as standard:
Prompts belong in version control, reviewed like any other change, with a test set behind them. Treating them as configuration someone edits in production is how a working feature quietly degrades and nobody can say when or why.
The same applies to model upgrades. A new model version can change output format and break downstream parsing. Pinning versions deliberately, and having a test set to run before switching, turns that from an incident into a routine change.
Providers have outages, rate limits and occasional refusals. A well-built feature degrades: it falls back to a non-AI path where one exists, shows an honest message rather than a raw stack trace, keeps whatever the user had typed, and logs enough context to diagnose the cause. The feature being temporarily unavailable should never take the page down with it.
Usually yes. On Drupal or WordPress it is a module or plugin alongside what you already run; on a custom application it is typically a service the app calls. A rebuild is only warranted where the existing architecture makes background processing or caching impossible, which is rare and would be flagged during discovery rather than discovered mid-build.
It depends on the task, your latency tolerance, your budget and any data residency requirements — and it will change, because the landscape moves quickly. I build behind an abstraction so the model is a configuration choice rather than a rewrite, and I pin versions so a provider update cannot silently change your output format.
Rate limiting per user and per IP, authentication on anything expensive, input length caps, output filtering, and spend budgets that cut off rather than escalate. For public-facing features these are requirements, not enhancements — an unauthenticated LLM endpoint is an open invitation to run up your bill.
It can, if it is grounded in real documentation, honest about its limits, and hands over to a human cleanly. It will not if it is a generic model guessing at your policies — that generates a second wave of tickets from users correcting it. The deciding factor is the quality of the underlying content, which is why this often starts as a documentation exercise.
Realistically: watching cost and usage, re-running the test set when you change prompts or models, refreshing any grounding content as it changes, and reviewing conversations periodically to find where it is falling short. Budget for it as a small ongoing commitment rather than a project that finishes.
Describe the problem in your own words — I will tell you what I would actually build, and what I would not.
Start a conversation