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.

How RAG Makes the Agent Smarter

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 pgvector on the existing Postgres instance and added an article_embeddings table 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_articles embeds the user's question and returns the closest-matching articles by cosine similarity; get_news_article fetches 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.

✓  TL;DR
What we shipped today: real semantic search over our own news articles
The Workshop Assistant can now find and quote InAgentic's own news articles instead of only answering from what it learned in training — ask "do you have anything on CRM automation" and it searches by meaning, not keywords, and can summarise what it finds.
We shipped the simple version first, on purpose. A flat list of titles and excerpts for Claude to scan itself worked fine at a handful of articles — real vector search only replaced it once the scaling ceiling became the actual constraint, not a hypothetical one.
Pinecone's free tier would have covered this easily — we used pgvector instead. Same retrieval quality, but zero new vendors, no new API key to manage, and no new network hop, since Postgres already runs everything else in this app and already supported the extension.
The retrieval source itself needed a rethink. Indexing our own database copy of each article would have gone stale immediately — articles are often edited directly in Ghost's admin, bypassing our dashboard entirely — so search reads straight from Ghost's live API instead.
The trickiest bug wasn't the search — it was a silent one. The chat handler only ever resolved the first tool call per turn, invisible until "search, then fetch the full article" became the first flow to reliably trigger two calls at once. Left unresolved, the second call was producing quietly empty replies.
Added a one-click "Reindex Now" button to the dashboard, so refreshing what the assistant knows after publishing or editing an article doesn't require touching a terminal.
Coming in future chapters
Scheduled reindexing so freshness doesn't depend on remembering to click a button, and extending the same retrieval approach beyond news articles to workshop materials and FAQs.

💡
Today: The Companies House Data Goldmine: How UK Startups Are Using Public Filings to Win Enterprise Clients
https://inagentic.ai/news/the-companies-house-data-goldmine-how-uk-startups-are-using-public-filings-to-win-enterprise-clients/