All articles

Building an AI Assistant for Your Product's Documentation

Your docs are already the source of truth. Turn them into a conversational AI assistant your users will actually want to use.

D

DocPilot Team

AI documentation research and engineering from Onzira Labs.

7 min read

Why Your Product Docs Deserve Better Than a Search Bar

Most documentation sets are static. Users land on a page, scroll, give up, and open a support ticket. The information they need is almost always in the docs, but the path to finding it is broken. Search returns twenty pages that match a keyword. Navigation assumes the user already knows your mental model. Long pages force readers to Ctrl+F through walls of text.

An AI assistant flips the relationship. Instead of forcing users to find the answer, the answer finds them. They ask in natural language, the assistant replies with the relevant excerpt, the API signature, the configuration snippet, or the troubleshooting step. No new content to write. No migration to a knowledge base. Just a layer on top of what you already have.

The catch: a useful assistant is not a wrapper around a foundation model. It is a retrieval-augmented system that grounds responses in your actual documentation. Build it well, and it pays for itself in reduced support load and faster onboarding. Build it badly, and you ship a hallucinating chatbot that erodes trust.

This guide walks through the architecture, the data pipeline, the prompt design, and the failure modes that matter when you turn your docs into a conversational interface.

The Architecture: RAG Over Your Existing Docs

The pattern is retrieval-augmented generation (RAG). At query time, the system retrieves the most relevant chunks from your documentation and includes them in the prompt to a language model. The model answers using that context, not its training data.

User question
   ↓
Embedding model (query vector)
   ↓
Vector database (top-k similar chunks)
   ↓
Prompt: question + retrieved chunks + system instructions
   ↓
LLM (grounded answer)
   ↓
User-facing response

The four moving parts you need to think about:

  1. Ingestion pipeline: how docs go from your repo to a searchable index.
  2. Embedding model: how text becomes vectors.
  3. Vector store: where vectors live and how similarity is computed.
  4. Generation layer: how the final answer is composed and presented.

Each one has tradeoffs. The rest of this guide works through them in order.

Step 1: Build the Ingestion Pipeline

The goal is to keep your docs index in sync with the canonical source. If you maintain documentation in GitHub alongside the code, treat the repo as the source of truth and rebuild the index on every push.

Chunking Strategy

Documents are too large to embed as a single vector. A typical API reference page might be 4,000 tokens; you want chunks in the 200 to 600 token range. Smaller chunks give finer retrieval but lose context. Larger chunks keep context but dilute relevance.

The practical middle ground:

  • Split on semantic boundaries (headings, code fences, list items), not arbitrary token counts.
  • Keep heading hierarchy attached to each chunk as metadata so the answer can cite the section.
  • Preserve code blocks as a single unit. Splitting inside a function signature breaks retrieval.

Here is a minimal chunker in Python that respects Markdown structure:

from markdown_it import MarkdownIt

def chunk_markdown(text, max_tokens=400):
    md = MarkdownIt()
    tokens = md.parse(text)
    chunks = []
    current = []
    current_len = 0

    for token in tokens:
        if token.type == "heading_open":
            if current:
                chunks.append("".join(current))
                current, current_len = [], 0
        current.append(token.content)
        current_len += len(token.content.split())
        if current_len >= max_tokens:
            chunks.append("".join(current))
            current, current_len = [], 0

    if current:
        chunks.append("".join(current))
    return chunks

Sync Mechanism

A webhook from your Git provider is enough. When main changes, run the ingestion job. Most teams keep the pipeline under ten minutes end-to-end: fetch, chunk, embed, upsert.

If you use a tool like DocPilot, this layer is already handled. Connect the repo, and the platform regenerates READMEs, setup guides, architecture docs, and API references on every commit, then exposes them in a format ready to feed into a vector store. That removes the most painful part of the project: keeping the index fresh.

Step 2: Pick the Embedding Model

The embedding model converts text into a fixed-size vector. Cosine similarity between query and chunk vectors is what retrieval actually measures, so the quality of the embedding model directly determines recall.

A reasonable starting lineup:

ModelDimensionsNotes
OpenAI text-embedding-3-small1536Good default, low cost, multilingual
Cohere embed-english-v3.01024Strong on technical English
Voyage voyage-code-31024Tuned for code and docs
BGE bge-large-en-v1.51024Open weights, self-hostable

For product documentation, technical-language models like Voyage or BGE tend to outperform general-purpose ones on code-heavy chunks. The difference shows up on queries like "how to authenticate with OAuth" or "what does the retry-after header mean" where vocabulary overlap is low.

Evaluate on a held-out set of real support questions. Twenty to fifty questions with the chunk you would manually point a user to. Hit rate at top-5 is the metric that matters.

Step 3: Choose the Vector Store

For teams under a few hundred thousand chunks, any modern vector database works. The differences come down to operational concerns.

StoreBest for
pgvectorExisting Postgres, low ops overhead
PineconeManaged, fast scale, paid
QdrantSelf-hosted, strong filters
WeaviateHybrid keyword + vector search
ChromaPrototyping, in-process

One feature worth caring about: metadata filtering. If your assistant should respect URL paths, product versions, or doc tags, filtering at query time is faster and more reliable than relying on the embedding to encode that signal.

results = index.query(
    vector=embed(question),
    top_k=5,
    filter={"version": "v2", "section": "api"},
)

Step 4: Design the Prompt

Retrieval gets you the right chunks. Prompt design is what keeps the model honest.

A production prompt usually has three parts:

  1. System instructions that constrain behavior.
  2. Retrieved context with clear delimiters.
  3. The user question, unmodified.
You are a documentation assistant for [Product].
Answer the question using only the context below.
If the answer is not in the context, say "I don't have that information"
and point the user to support@example.com.
Quote code snippets verbatim. Cite the section heading in parentheses.

Context:
---
[CHUNK 1]
Section: Authentication > OAuth
[CHUNK 2]
Section: API Reference > Rate Limits
...
---

Question: {user_input}

The "say I don't know" instruction matters more than people expect. Without it, the model will confabulate. With it, you trade a small amount of coverage for a large amount of trust.

Streaming and Citations

Stream the response token by token to keep latency tolerable. Long-context prompts with five chunks can take a few seconds to start generating. Streaming cuts perceived latency roughly in half.

Always include the source citation. Users who can click through to the underlying page trust the assistant more, and your support team gets fewer follow-up questions.

Step 5: Handle the Hard Cases

A working prototype takes a weekend. A working product takes longer, because of the edge cases.

Versioning

Users on v1 should not see v2 answers. Store the version in the chunk metadata and filter at query time. If you cannot determine the user's version from context, ask.

Multi-turn Conversations

Users follow up. "What about retries?" only makes sense if the previous turn was about rate limits. Either rewrite the question using the chat history, or run retrieval on the last user message plus a summary of prior turns.

Code Questions

Embeddings of code and prose are different beasts. For API reference pages, keep code blocks intact and include the surrounding prose in the same chunk. For raw code samples, consider a separate index.

Eval Pipeline

Treat the assistant like any other production system. Maintain a set of fifty to one hundred question-and-expected-answer pairs. Run them nightly. Track:

  • Retrieval hit rate (was the right chunk in top-5?)
  • Answer correctness (did the model produce something right?)
  • Refusal rate (how often did the model say "I don't know"?)
  • Citation accuracy (do the cited sections actually contain the answer?)

A drop in any of these signals a regression in ingestion, embeddings, or the prompt.

Where to Start

If you are starting from zero, the shortest path is:

  1. Pick an embedding model and a vector store you can run locally.
  2. Chunk one section of your docs and embed it.
  3. Wire retrieval into a chat completion call.
  4. Test on real user questions.

You will learn more in a week of running queries against your own docs than in a month of reading papers.

The longer path, the one most teams eventually take, looks like this: keep the docs in Git, automate the ingestion on push, run evals weekly, and version the index alongside the product. None of that requires new content. It requires treating documentation as a system, not a deliverable.


If the part you keep deferring is the documentation itself, DocPilot handles it end to end. Connect your GitHub repo and it generates READMEs, setup guides, architecture overviews, and API references from the code, then keeps them in sync through webhooks on every commit. When you are ready to wire up the assistant described above, the output is already in a format that drops straight into a vector store. Try the free tier at docpilot.io and see what your codebase already explains about itself.

Ship documentation that keeps up

Try DocPilot on your repository — free for one repo.

Connect your first repository in about a minute. No credit card. DocPilot writes and maintains the documentation from your code.

Read next