Systems Library / AI Capabilities / How to Create a Client-Facing Knowledge Base with RAG
AI Capabilities rag knowledge

How to Create a Client-Facing Knowledge Base with RAG

Build a customer-facing knowledge base powered by RAG for accurate answers.

Jay Banlasan

Jay Banlasan

The AI Systems Guy

A client facing knowledge base with rag and ai search delivers accurate, conversational answers instead of forcing customers to browse through article lists. I build these as the public-facing version of internal RAG systems. Same technology, different audience. The key difference is tone, simplicity, and making sure the AI never exposes internal information.

Customers ask questions in plain language and get the answer they need in one response.

What You Need Before Starting

Step 1: Curate External Content

Only index content approved for customer access:

def prepare_external_content(articles):
    external = []
    for article in articles:
        if article.get("visibility") != "public":
            continue
        if contains_internal_info(article["content"]):
            flag_for_review(article["id"])
            continue
        external.append(article)
    return external

def contains_internal_info(text):
    internal_markers = ["INTERNAL ONLY", "CONFIDENTIAL", "internal use", "@company.slack.com"]
    return any(marker.lower() in text.lower() for marker in internal_markers)

Step 2: Index with Customer-Friendly Metadata

from sentence_transformers import SentenceTransformer
import chromadb

model = SentenceTransformer("all-MiniLM-L6-v2")
chroma = chromadb.PersistentClient(path="./client_kb")
collection = chroma.get_or_create_collection("help_center")

def index_help_articles(articles):
    for article in articles:
        embedding = model.encode(f"{article['title']} {article['content']}").tolist()
        collection.add(
            ids=[article["id"]],
            embeddings=[embedding],
            documents=[article["content"]],
            metadatas=[{
                "title": article["title"],
                "url": article["url"],
                "category": article["category"],
                "last_updated": article["updated_at"]
            }]
        )

Step 3: Build the Customer-Facing Q&A

import anthropic

client = anthropic.Anthropic()

CUSTOMER_PROMPT = """You are a helpful assistant for [Product Name].
Answer customer questions using only the provided help articles.

Rules:
- Be friendly and clear. Use simple language.
- Always link to the full article for more details
- Never mention internal processes, employee names, or internal tools
- If you cannot answer from the provided articles, say: "I could not find a specific answer. You can reach our support team at [email protected]."
- Never speculate or make up features"""

@app.route("/api/help/ask", methods=["POST"])
def customer_ask():
    question = request.json["question"]
    query_embedding = model.encode(question).tolist()
    results = collection.query(query_embeddings=[query_embedding], n_results=3)

    context = "\n\n".join([
        f"Article: {results['metadatas'][0][i]['title']}\nURL: {results['metadatas'][0][i]['url']}\n{results['documents'][0][i]}"
        for i in range(len(results["ids"][0]))
    ])

    response = client.messages.create(
        model="claude-sonnet-4-20250514",
        max_tokens=400,
        system=CUSTOMER_PROMPT,
        messages=[{"role": "user", "content": f"Help articles:\n{context}\n\nCustomer question: {question}"}]
    )

    return jsonify({
        "answer": response.content[0].text,
        "related_articles": [{"title": results["metadatas"][0][i]["title"], "url": results["metadatas"][0][i]["url"]} for i in range(len(results["ids"][0]))]
    })

Step 4: Add Safety Filters

Prevent the AI from leaking anything it should not:

def filter_response(response_text):
    blocked_patterns = ["internal", "employee", "slack channel", "jira", "confluence"]
    for pattern in blocked_patterns:
        if pattern.lower() in response_text.lower():
            return "I could not generate a safe response. Please contact [email protected]."
    return response_text

Step 5: Track Customer Search Behavior

def log_customer_search(question, answer, helpful=None):
    conn = sqlite3.connect("help_analytics.db")
    conn.execute("""
        INSERT INTO searches (question, answer, helpful, searched_at)
        VALUES (?, ?, ?, datetime('now'))
    """, (question, answer, helpful))
    conn.commit()

def get_content_gaps():
    conn = sqlite3.connect("help_analytics.db")
    return conn.execute("""
        SELECT question, COUNT(*) as freq FROM searches
        WHERE answer LIKE '%could not find%'
        GROUP BY question ORDER BY freq DESC LIMIT 20
    """).fetchall()

What to Build Next

Add personalized answers based on the customer's plan tier or product version. A free-tier customer asking about a premium feature should get a different response than an enterprise customer asking the same question.

Related Reading

Want this system built for your business?

Get a free assessment. We will map every system your business needs and show you the ROI.

Get Your Free Assessment

Related Systems