How RAG Makes the Agent Smarter
RAG (Retrieval-Augmented Generation) grounds an LLM's answers by retrieving relevant info from an external source—like a vector database—and feeding it into the prompt before generating a response. This reduces hallucination and lets the model use your own data without retraining.
Ask most chatbots "do you have anything on X?" and you get a shrug dressed up as confidence — an answer stitched together from whatever the model happened to learn during training, with no idea what your company actually published last week.
The Workshop Assistant on inagentic.ai workshop page had exactly that problem: it knew everything about our workshops, and nothing about our own news articles at inagentic.ai.
Today we fixed that with retrieval-augmented generation RAG and it's worth walking through both the concept and the build, because the interesting decisions weren't about the AI at all. They were about where the data lives and who owns it.
Background: what RAG actually is
A language model's knowledge is frozen at training time and baked into its weights — ask it about something written after that cutoff, or something that was never public in the first place (like an internal blog), and it either says so or, worse, guesses. RAG sidesteps this by not asking the model to know the answer — it asks the model to look it up, the same way a person would search before answering.
Mechanically, that means three pieces working together:
- Embeddings — a model that turns text into a vector of numbers positioned so that semantically similar text ends up close together in that space. "CRM automation for small businesses" and "using AI to manage customer relationships" land near each other even though they don't share a single word.
- A vector store — a database that can answer "which stored vectors are closest to this one?" quickly, even across thousands of documents.
- Retrieval as a tool call — the chatbot doesn't get the whole knowledge base stuffed into its prompt. It gets a tool it can call — "search these articles" — and only the handful of results that come back get added to its context before it answers.
The result: a query like "show me the article about CRM" doesn't require the word "CRM" to appear anywhere in the article. It requires the meaning to be close.
What we considered
Flat list vs. real search. The simplest possible version — and the one we shipped first — was to hand the model a plain list of every published article's title and a short excerpt, and let Claude itself scan the list and pick. This works, and for a handful of articles it's arguably fine: no embeddings, no vector database, nothing to index. But it doesn't scale — every chat turn pays to ship the entire article list, whether or not the question has anything to do with news, and it can't search inside a long article, only match on the whole thing. We built this version first, confirmed it worked, and then replaced it once the scaling ceiling became the actual constraint rather than a hypothetical one.
Pinecone vs. pgvector. The obvious next step is a dedicated vector database, and Pinecone is the best-known name in that space. We priced it out: at InAgentic's current scale — a few dozen articles, a modest number of chatbot queries a month — Pinecone's free Starter tier (2GB storage, 1–2M read/write units/month) would cover this without ever hitting a paid tier. But "free" isn't the same as "free of cost" — it's still a new vendor, a new API key to manage and rotate, and a new network hop out to Pinecone's infrastructure on every search. We already run PostgreSQL on AWS RDS for everything else in this app, and it turned out to already support pgvector (version 0.8.1, just not yet enabled) — an extension that adds a native vector type and approximate-nearest-neighbour search directly to Postgres. Same retrieval quality, zero new vendors, one CREATE EXTENSION away. We went with pgvector.
Where the source-of-truth actually lives. This was the least obvious decision and the one that mattered most. Our dashboard keeps its own copy of every article in a content_drafts table, and publishing pushes that copy to Ghost. The natural thing to index would have been that copy — it's already in the same database. But articles here are often edited directly in Ghost's own admin interface, bypassing the dashboard entirely, and those edits never flow back into our copy. Indexing content_drafts would have meant the assistant confidently quoting outdated text for exactly the articles someone bothered to touch up after publishing. So retrieval reads from Ghost's Content API instead — the actual live site — regardless of which door an edit walked through.
What we built today
- Enabled
pgvectoron the existing Postgres instance and added anarticle_embeddingstable with an HNSW cosine-similarity index — no new database, no new server. - Titan Embeddings v2 via Bedrock for turning article text into vectors. Anthropic doesn't offer an embeddings model, so this uses the same AWS Bedrock account as every Claude call in this app, just a different model. Turning this on surfaced a real gap: the IAM identity this app uses for Bedrock was scoped to exactly one model ARN — Claude — and Titan wasn't in the allow-list. A one-line policy update fixed it, but it's the kind of thing that only shows up once you actually try the new call.
- A read-only Ghost Content API client, separate from the existing Admin API client used for publishing — deliberately hitting the public, live version of the site rather than our own database mirror, for the reason above.
- Two new tools on the Workshop Assistant:
search_news_articlesembeds the user's question and returns the closest-matching articles by cosine similarity;get_news_articlefetches one article's full text by slug so the assistant can quote or summarise it accurately rather than guessing from a snippet. - A "Reindex Now" button on the dashboard's Chatbot tab, so refreshing what the assistant knows after publishing or editing an article doesn't require a terminal.
- A bug fix that the new tools smoked out: the chat handler only ever resolved the first tool call the model made in a turn. That was invisible with one tool, but the natural "search, then fetch the full article" sequence was the first flow to reliably trigger two tool calls in the same turn — and the second one being left unresolved was quietly producing empty replies. Every tool call in a turn now gets resolved before the conversation continues.
We tested the whole path against the live Ghost site and real Bedrock calls before calling it done — indexed the three articles actually published today, ran real queries like "agents that pay for themselves," and confirmed the right article came back first every time.
Article we published today with the InAgentic Dashboard
https://inagentic.ai/news/the-companies-house-data-goldmine-how-uk-startups-are-using-public-filings-to-win-enterprise-clients/