The most useful AI assistant for a business is not a general chatbot — it is one that knows your company. It should answer questions about your products, policies, processes and data, and it should do so with sources you can check. That is exactly what Retrieval-Augmented Generation (RAG) is for: it grounds a large language model in your own documents so the answers are accurate, current and traceable.
How RAG works
Instead of asking a model to answer from memory — where it can confidently make things up — RAG first retrieves the most relevant passages from your knowledge base, then asks the model to answer using only those passages. The model becomes a careful reader of your documents rather than a free improviser.
A working RAG pipeline in code
Below is a compact, production-shaped RAG pipeline. It chunks your documents, stores them as embeddings in a vector database, retrieves the most relevant chunks for a question, and asks the model to answer using only those chunks — returning the sources so a human can verify.
import os
from langchain_community.document_loaders import DirectoryLoader
from langchain_text_splitters import RecursiveCharacterTextSplitter
from langchain_community.vectorstores import Chroma
from langchain_openai import OpenAIEmbeddings, ChatOpenAI
# 1. Load and chunk your company documents
loader = DirectoryLoader("./knowledge_base", glob="**/*")
docs = loader.load()
splitter = RecursiveCharacterTextSplitter(chunk_size=1200, chunk_overlap=150)
chunks = splitter.split_documents(docs)
# 2. Store embeddings in a persistent vector database
embeddings = OpenAIEmbeddings(model="text-embedding-3-small")
vectorstore = Chroma.from_documents(chunks, embeddings, persist_directory="./chroma")
# 3. Retrieve the most relevant passages for a question
retriever = vectorstore.as_retriever(search_kwargs={"k": 4})
# 4. Ask the model to answer using ONLY the retrieved context
llm = ChatOpenAI(model="gpt-4o-mini", temperature=0)
def ask(question: str):
context = retriever.invoke(question)
prompt = (
"Answer using ONLY the context below. If it is not there, say so.\n\n"
"Context:\n" + "\n\n".join(d.page_content for d in context)
+ "\n\nQuestion: " + question
)
answer = llm.invoke(prompt)
sources = [f"{d.metadata.get('source', 'doc')}" for d in context]
return answer.content, sources
print("Q: What is our refund window?")
answer, sources = ask("What is our refund window?")
print("A:", answer)
print("Sources:", sources) # traceable, verifiable answers
The guardrails that make it safe for business
A RAG assistant is only as trustworthy as the controls around it. These are the guardrails we consider non-negotiable before anything like this touches real users or real decisions.
- Grounding & citations — the model answers only from retrieved context and returns the source documents, so every answer can be checked.
- "I don't know" behaviour — if the answer is not in your documents, the assistant says so instead of guessing. This single rule removes most hallucination risk.
- Access control — different teams see different documents. Retrieval is scoped to what the user is allowed to read.
- Freshness & ownership — documents have owners and review dates, so the knowledge base does not silently rot.
- Evaluation — a small set of known questions with expected answers is run on every change, so you can measure accuracy over time.
A business AI assistant is not a model — it is a model plus a knowledge base, access control, citations and an evaluation harness. The model is the smallest part.
If you want an assistant your team will actually trust, the hard part is not the model — it is the knowledge base, the access rules and the evaluation. That is exactly the kind of thing we build with clients, from the first document to the first measured win.