Customer Support AI with Bedrock RAG

Build a RAG-powered support assistant grounded in company docs using Bedrock.

Introduction

30 Second Summary

When you message a company's support chat, you expect answers about their specific products and pricing. But most AI models know nothing about any particular company's internal documentation. They either make things up or admit they cannot help.

In this project, you will build a customer support AI assistant that answers questions grounded in real company documentation using Amazon Bedrock Knowledge Bases and Python.

What You'll Build

Picture asking your assistant "What are CloudNova's pricing tiers?" and getting back an accurate, cited answer pulled directly from the company's docs in seconds.

By the end of this project, you'll have:

  • A RAG-powered assistant that answers company-specific questions with accurate responses and source citations pointing to the exact document the answer came from.
  • A Managed Knowledge Base in Amazon Bedrock that automatically embeds, indexes, and stores your company documents for instant retrieval.
  • A clear before-and-after demonstration showing a foundation model hallucinating without your docs versus answering accurately with RAG.
  • Secret Mission: Add a Bedrock Guardrail with denied topics and content filtering to keep the assistant safe and on-brand.

Are there any prerequisites?

You'll need an AWS account with console access and Python 3 installed on your machine. Basic familiarity with Python syntax is helpful, but you do not need prior experience with AWS AI services.

Before We Start

Before we dive in, let's lock in what you're about to build and why grounding an AI assistant in real company data matters for customer support.

Set Up Your Environment and Enable Bedrock Access

To build a customer support assistant that calls AWS services from Python, you need boto3 installed and configured.

In this step, you will install boto3, enable Amazon Bedrock model access for Amazon Nova Pro, and upload sample company documentation to Amazon S3 so the knowledge base has something to retrieve later.

In this step, get ready to:
  • Install boto3 and confirm your AWS credentials work.
  • Enable Amazon Nova Pro model access in the Bedrock console.
  • Create and upload sample CloudNova company docs to S3.
Install boto3 and verify your AWS credentials

boto3 is the official AWS SDK for Python. It lets your scripts create buckets, call models, and interact with every AWS service.

  • Install boto3 by running this command:
pip install boto3

What does this command do?

pip install boto3 downloads the AWS SDK for Python from PyPI and installs it in your current Python environment.

  • Verify the installation succeeded by running:
python3 -c "import boto3; print(boto3.__version__)"

You should see a version number printed, such as 1.43.69.

Command not found or import error?

  • If pip is not found, try pip3 install boto3 instead.
  • If you see ModuleNotFoundError, make sure you are running the same Python version that pip installed to.
  • help me fix my boto3 installation

Next, confirm that your AWS credentials are configured correctly.

  • Check that the AWS CLI can reach your account by running:
aws sts get-caller-identity

You should see JSON output containing your account ID, user ARN, and user ID.

Why do I need this?

This command proves that your terminal has valid AWS credentials. Every boto3 call you make later relies on these same credentials to authenticate.

Seeing 'Unable to locate credentials'?

  • Run aws configure and enter your Access Key ID, Secret Access Key, default region (us-east-1), and default output format (json).
  • If you are using an IAM Identity Center (SSO) profile, run aws sso login first.
  • help me configure my AWS credentials
  • Set your default region to us-east-1 (where Amazon Nova Pro is available) by running:
aws configure set region us-east-1
Enable Amazon Nova Pro model access

Amazon Bedrock requires you to explicitly request access to each foundation model before you can call it. Amazon models like Nova Pro are typically granted instantly.

  • Open the AWS Console and search for Bedrock in the top search bar.
  • Select Amazon Bedrock to open the service console.
  • In the left navigation panel, scroll down and click Model access.
  • Click Modify model access (or Enable specific models).
  • Find Amazon Nova Pro in the list and check its box.
  • Click Next, then Submit.
  • Wait for the Access status column to show Access granted next to Amazon Nova Pro.

What is Amazon Nova Pro?

Amazon Nova Pro (amazon.nova-pro-v1:0) is an Amazon first-party foundation model available in Bedrock. It supports the Converse API, Knowledge Bases, and Guardrails. Access is typically granted instantly for Amazon models.

Don't see Amazon Nova Pro in the list?

  • Make sure your console region is set to US East (N. Virginia) in the top-right corner. Nova Pro is available in us-east-1.
  • help me find Amazon Nova Pro in the Bedrock console
Create sample company documentation and upload to S3

Your knowledge base needs documents to search. You will create three text files for the fictional company CloudNova, then store them in an S3 bucket where the knowledge base can access them.

  • Create your S3 bucket by running this command (replace the account ID with your own 12-digit AWS account ID from the aws sts get-caller-identity output earlier):
aws s3 mb s3://cloudnova-support-docs-[[AWS_ACCOUNT_ID="your 12-digit AWS account ID"]] --region us-east-1

You should see make_bucket followed by your bucket name.

Why include your account ID in the bucket name?

S3 bucket names must be globally unique across all AWS accounts. Including your account ID guarantees no one else already has the same bucket name.

  • Create a local folder for the company documents by running:
mkdir company_docs

Now you will create the first documentation file.

  • Create a new file called company_docs/product_overview.txt in your text editor and paste the following content:
CloudNova Product Overview

CloudNova is a cloud storage provider designed for small and medium businesses. Our platform offers secure, scalable file storage with automatic backup and team collaboration features.

Key Features:
- Unlimited file versioning: Every change is saved, so you can restore any previous version of a file.
- Team folders: Share folders with your team with granular read/write permissions.
- End-to-end encryption: All files are encrypted at rest (AES-256) and in transit (TLS 1.3).
- API access: Integrate CloudNova storage into your applications with our REST API.
- Mobile apps: Access your files from iOS and Android devices.
- Automatic daily backups: Your data is backed up daily to a geographically separate region.

Supported file types: All file types are supported with no size limit per file. Maximum storage depends on your plan tier.

For pricing information, see our Pricing Guide.
For account issues, see our Troubleshooting FAQ.

What is this file?

This is a fictional product overview for CloudNova. When the knowledge base retrieves chunks from this file, the assistant can answer questions about features, encryption, and supported file types.

  • Save company_docs/product_overview.txt.
  • Confirm the file exists by running:
ls company_docs/

You should see product_overview.txt listed.

  • Create a second file called company_docs/pricing_tiers.txt and paste the following content (the three pricing plans):
CloudNova Pricing Guide

CloudNova offers three pricing tiers to fit businesses of all sizes:

1. Starter Plan - $9.99/month per user
   - 100 GB storage per user
   - Up to 5 team members
   - Email support (response within 24 hours)
   - Basic file versioning (last 30 days)
   - Mobile app access

2. Professional Plan - $24.99/month per user
   - 1 TB storage per user
   - Up to 50 team members
   - Priority email and chat support (response within 4 hours)
   - Unlimited file versioning
   - API access
   - Advanced sharing permissions
   - Admin dashboard with usage analytics

3. Enterprise Plan - Custom pricing
   - Unlimited storage
   - Unlimited team members
   - 24/7 dedicated support with a named account manager
   - Custom integrations and onboarding
   - SSO (SAML 2.0) and advanced security features
   - 99.99% uptime SLA
   - Data residency options
  • Still in the same file, add this content below the Enterprise Plan section:

All plans include:
- End-to-end encryption
- Automatic daily backups
- 99.9% uptime guarantee (99.99% for Enterprise)
- 14-day free trial (no credit card required)

To upgrade your plan, go to Settings > Billing > Change Plan in your CloudNova dashboard.

What is this file?

This pricing guide lets the assistant answer questions about Starter, Professional, and Enterprise plans. When a customer asks about costs or plan features, the knowledge base retrieves chunks from this file.

  • Save company_docs/pricing_tiers.txt.
  • Create a third file called company_docs/troubleshooting_faq.txt and paste the following content:
CloudNova Troubleshooting FAQ

Q: How do I reset my password?
A: Go to the login page and click "Forgot Password." Enter the email address associated with your account. You will receive a reset link within 5 minutes. The link expires after 1 hour. If you do not receive the email, check your spam folder or contact support.

Q: Why are my files not syncing?
A: File sync issues are usually caused by one of the following:
1. Poor internet connection - Check your network status.
2. Storage quota exceeded - Check Settings > Storage to see your usage.
3. File locked by another user - Ask the file owner to release the lock.
4. Outdated desktop client - Update to the latest version from cloudnova.example.com/download.
If none of these solve the issue, restart the CloudNova desktop client and try again.

Q: How do I add a new team member?
A: Navigate to Settings > Team Management > Invite Member. Enter their email address and select their permission level (Viewer, Editor, or Admin). They will receive an invitation email and have 7 days to accept it.

Q: Can I recover a deleted file?
A: Yes. Deleted files are moved to the Trash folder and retained for 30 days (all plans) or 90 days (Professional and Enterprise plans). Go to Trash > select the file > Restore. After the retention period, files are permanently deleted and cannot be recovered.

Q: How do I cancel my subscription?
A: Go to Settings > Billing > Cancel Subscription. Your account will remain active until the end of your current billing period. All your data will be retained for 30 days after cancellation, then permanently deleted. You can reactivate your account within those 30 days without data loss.

Q: Who do I contact for support?
A: 
- Starter plan: Email support@cloudnova.example.com (response within 24 hours)
- Professional plan: Email or live chat via the dashboard (response within 4 hours)
- Enterprise plan: Contact your dedicated account manager or call our 24/7 support line

What is this file?

This FAQ covers common account issues. It gives the assistant grounded answers for questions like password resets, file sync problems, and subscription cancellation.

  • Save company_docs/troubleshooting_faq.txt.
  • Confirm all three files are present by running:
ls company_docs/

You should see pricing_tiers.txt, product_overview.txt, and troubleshooting_faq.txt listed.

  • Upload all three files to your S3 bucket by running:
aws s3 sync company_docs/ s3://cloudnova-support-docs-[[AWS_ACCOUNT_ID="your 12-digit AWS account ID"]]/company_docs/

You should see three upload: lines, one for each file.

Before you verify, what do you expect to see if you list the contents of your bucket? Think about how many files you uploaded and where they should appear.

  • Confirm the files landed in S3 by running:
aws s3 ls s3://cloudnova-support-docs-[[AWS_ACCOUNT_ID="your 12-digit AWS account ID"]]/company_docs/

You should see all three .txt files listed with their sizes and timestamps.

Seeing 'NoSuchBucket' or empty results?

  • Double-check that the bucket name in your aws s3 ls command matches exactly what you used in aws s3 mb (including your account ID).
  • Make sure you included the trailing /company_docs/ prefix in the sync command.
  • help me troubleshoot my S3 upload

Your environment is ready. You have boto3 installed, model access granted, and company documents stored in S3. Next up, you will ask Bedrock a question without RAG to see why grounding matters.

Ask Bedrock Without RAG and See the Problem

You have Amazon Nova Pro ready and your CloudNova company documents sitting in S3. Before building anything sophisticated, let's see what happens when you ask the model a company-specific question with no grounding data.

This step exposes the core problem that RAG exists to solve. A foundation model has no knowledge of your company's internal documentation, so it either invents plausible-sounding answers or admits it cannot help.

In this step, get ready to:
  • Write a Python script that calls the Bedrock Converse API directly without any company context.
  • Observe the model hallucinate or refuse to answer company-specific questions about CloudNova.
Write a Python script that calls the Bedrock Converse API directly

The Converse API is Amazon Bedrock's unified interface for sending messages to any supported foundation model. You send a message, and the model sends a response back.

  • Create a new file called ask_without_rag.py in your project folder and add the following function:
import boto3
import json

def ask_without_rag(question):
    """Ask a question directly to the model without any RAG context."""
    # Create a Bedrock Runtime client for model inference
    client = boto3.client('bedrock-runtime', region_name='us-east-1')

    # Send the question to Amazon Nova Pro using the Converse API
    response = client.converse(
        modelId='amazon.nova-pro-v1:0',
        messages=[
            {
                'role': 'user',
                'content': [{'text': question}]
            }
        ],
        system=[
            {'text': 'You are a customer support assistant for CloudNova, a cloud storage company. Answer customer questions helpfully and accurately.'}
        ],
        inferenceConfig={
            'maxTokens': 1024,
            'temperature': 0.2
        }
    )

    # Extract the text answer from the response
    answer = response['output']['message']['content'][0]['text']
    return answer

What does this code do?

This function sends a question directly to Amazon Nova Pro with a system prompt telling it to act as a CloudNova support assistant.

  • boto3.client('bedrock-runtime') creates a client specifically for model inference (running models).
  • client.converse() sends a message to the model and gets a response, just like a chat exchange.
  • temperature: 0.2 keeps responses focused and less random.
  • Below the function, add a main block that asks two CloudNova-specific questions:
if __name__ == '__main__':
    # Two questions that require company-specific knowledge
    questions = [
        "What are CloudNova's pricing tiers for their cloud storage service?",
        "How do I reset my CloudNova account password?"
    ]

    for question in questions:
        print(f"\nQuestion: {question}")
        print("-" * 50)
        answer = ask_without_rag(question)
        print(f"Answer: {answer}")
        print()

What does this main block do?

The main block loops through two questions about CloudNova and prints each one alongside the model's response. These questions have real answers in your S3 documentation, but the model has no access to those files.

  • Save ask_without_rag.py.

✔️ Awesome, I've got everything!

Great. Make sure you have saved the file before running it.

ⓧ I'd like to double check the full code

Your ask_without_rag.py should contain these two sections in order:

  • The imports (boto3 and json) followed by the ask_without_rag() function definition with the client.converse() call.
  • The if __name__ == '__main__': block with the two questions list and the print loop.

Scroll up and confirm each section is present and matches what you pasted.

Before you run this, what do you think the model will say when asked about CloudNova's pricing tiers? Will it know the correct answer?

  • Run the script to see how Amazon Nova Pro handles company-specific questions:
python3 ask_without_rag.py

Watch the output carefully. The model either invents pricing details that sound plausible but are completely made up, or it admits it has no information about CloudNova.

This is the expected result

The model has no knowledge of CloudNova because CloudNova is a fictional company that does not exist in its training data. Any specific details it provides (dollar amounts, plan names, features) are hallucinations.

Compare what the model says about pricing against your actual pricing_tiers.txt file. The numbers will not match because the model is guessing.

Seeing an AccessDeniedException?

This means your model access for Amazon Nova Pro is not yet active. Return to the Bedrock console, navigate to Model access, and confirm that amazon.nova-pro-v1:0 shows Access granted.

Also verify that your AWS credentials are configured for the us-east-1 region. help me debug my AccessDeniedException when calling the Bedrock Converse API

Confirm the pattern with the second question

Look at the model's response to the second question about resetting a CloudNova password. Your actual documentation has a specific answer: go to the login page, click "Forgot Password," and receive a reset link within 5 minutes.

The model either provides generic password reset advice that does not match your company's actual process, or it acknowledges it cannot help.

Why does this matter for customer support?

A customer support assistant that invents answers is worse than one that admits it does not know. Incorrect pricing information or wrong troubleshooting steps would frustrate real customers and damage trust.

This is exactly why Retrieval-Augmented Generation exists. Instead of relying on what the model memorized during training, RAG feeds it your actual company documents at query time so every answer is grounded in real data.

You have now proven the problem. The model is powerful but blind to your company's specific documentation. Next up, you will build a Knowledge Base that gives the model access to your CloudNova docs so it can answer accurately with citations.

Create a Managed Knowledge Base and Ingest Your Docs

In the previous step, you saw the foundation model fail on company-specific questions. It either invented fake pricing tiers or admitted it had no idea what CloudNova was. That gap is the exact problem RAG solves.

Now you will build the solution. A Managed Knowledge Base in Amazon Bedrock automatically handles embedding, indexing, and retrieval of your documents. You point it at your S3 bucket, sync, and it turns your plain text files into searchable vector data the model can reference.

In this step, get ready to:
  • Create a Managed Knowledge Base in the Amazon Bedrock console.
  • Connect your S3 bucket as a data source.
  • Sync the data source and confirm your documents are ingested.
Create a Managed Knowledge Base in the AWS Console

A Managed Knowledge Base handles the heavy lifting of converting your documents into vector embeddings and storing them in a searchable index. You do not need to provision any database or choose an embedding model. Bedrock manages all of that for you.

  • Return to the AWS Management Console in your browser.
  • In the search bar at the top, type Bedrock and select Amazon Bedrock from the results.
  • In the left sidebar, expand AgentCore.
  • Expand Built-in tools.
  • Click Knowledge Base.
  • Click Create Managed Knowledge Base.

You should now see the Knowledge Base creation form.

  • In the Knowledge Base name field, enter CloudNova-Support-KB.
  • Leave the embedding model set to Managed (the default). Bedrock uses its own service-managed embedding model, so you do not need to select one.
  • Under service role, let Bedrock create a new service role for you (the default option).

Why no embedding model selection?

Managed Knowledge Bases use service-managed embedding and reranking models. You do not need to enable Bedrock model access for embedding or reranking. The only model you need explicit access for is your generation model (Amazon Nova Pro), which you already enabled.

Connect your S3 bucket as a data source

The Knowledge Base needs to know where your documents live. You will point it at the S3 bucket you created in Step 1.

  • Under Data source, select Amazon S3 from the dropdown.
  • Enter the bucket name you created in Step 1 (your cloudnova-support-docs-{account-id} bucket).
  • Leave parsing set to the default (Managed parser).
  • Leave chunking set to the default (Default chunking).
  • Click Create Knowledge Base.

Bedrock will now provision your Knowledge Base. This takes 2-5 minutes. Wait for the status to change before proceeding.

What is chunking?

Chunking splits your documents into smaller pieces that can be individually embedded and retrieved. The default chunking strategy breaks text at logical boundaries (paragraphs, sections) so each chunk is a self-contained piece of information.

When a customer asks a question later, the Knowledge Base retrieves the most relevant chunks rather than entire documents. This keeps the context focused and the answers precise.

Getting a permissions error?

Make sure you selected the option for Bedrock to create a new service role. If you chose an existing role, it may not have the permissions needed to access your S3 bucket.

Also confirm your S3 bucket is in us-east-1. The Knowledge Base and bucket must be in the same region.

Help me troubleshoot my Knowledge Base creation error.

Sync the data source to ingest your documents

Creating the Knowledge Base sets up the infrastructure. Syncing is what actually reads your documents, splits them into chunks, generates embeddings, and indexes everything for retrieval.

  • Once the Knowledge Base status shows as created, select your S3 data source in the list.
  • Click Sync.
  • Wait for the sync to complete. You will see a green success banner when all documents have been ingested.

Before you check the result, what do you expect the sync status to show? How many documents should be ingested?

You should see the green success banner confirming your three documents are ingested and indexed. The Knowledge Base status should show Ready.

  • Note down your Knowledge Base ID from the Knowledge Base details page. You will need this in the next step when writing your retrieval script.

Where to find the Knowledge Base ID

The Knowledge Base ID is displayed at the top of the Knowledge Base detail page, right below the name. It looks like a string of alphanumeric characters (e.g., ABCDE12345). Copy it somewhere handy. You will paste it into your Python script in Step 4.

Your documents are now embedded and indexed, ready to be searched. Next up, you will write a Python script that retrieves relevant chunks from this Knowledge Base and feeds them to Nova Pro for grounded, cited answers.

Build the RAG-Powered Customer Support Assistant

Your Knowledge Base is ready with all three CloudNova documents embedded and indexed. Now it is time to build the assistant that actually uses it.

The Retrieve API fetches relevant document chunks from the Knowledge Base, and the Converse API generates a grounded answer using those chunks as context. By connecting these two APIs, you get answers pulled directly from your documentation instead of hallucinations.

In this step, get ready to:
  • Write a retrieval function that queries the Knowledge Base for relevant document chunks.
  • Build a generation function that feeds retrieved context into the Converse API.
  • Test the full RAG pipeline with multiple questions to confirm grounding works.
Write a script that retrieves relevant document chunks

The retrieval function is the first half of the RAG pattern. It takes a customer question, searches your Knowledge Base for the most relevant text chunks, and returns them alongside source citations.

  • Create a new file called ask_with_rag.py in the same folder as ask_without_rag.py.
  • Add the following imports and configuration constants:
import boto3

KNOWLEDGE_BASE_ID = "[[KB_ID=\"your-knowledge-base-id-here\"]]"
MODEL_ID = "amazon.nova-pro-v1:0"
REGION = "us-east-1"

SYSTEM_PROMPT = """You are a customer support assistant for CloudNova, a cloud storage company.
Answer customer questions based ONLY on the provided context from our documentation.
If the answer is not in the provided context, say: "I don't have information about that in our documentation. Please contact our support team for further assistance."
Do not make up information. Always be helpful and professional.
When answering, reference which document the information comes from."""

What does this code do?

  • KNOWLEDGE_BASE_ID stores the ID of the Managed Knowledge Base you created in the previous step.
  • MODEL_ID points to Amazon Nova Pro, the foundation model that will generate answers.
  • SYSTEM_PROMPT tells the model to only answer from provided context and to refuse if the answer is not there.

Find your Knowledge Base ID

In the Bedrock console, navigate to AgentCore > Built-in tools > Knowledge Base. Select CloudNova-Support-KB and copy the Knowledge Base ID shown at the top of the detail page.

  • Below the constants, add the retrieve_context function that queries the Knowledge Base:
def retrieve_context(question):
    """Retrieve relevant document chunks from the Knowledge Base."""
    client = boto3.client('bedrock-agent-runtime', region_name=REGION)

    response = client.retrieve(
        knowledgeBaseId=KNOWLEDGE_BASE_ID,
        retrievalQuery={
            'text': question
        },
        retrievalConfiguration={
            'managedSearchConfiguration': {
                'numberOfResults': 5
            }
        }
    )

    results = response.get('retrievalResults', [])
    context_chunks = []
    citations = []

    for result in results:
        text = result['content']['text']
        location = result.get('location', {})
        score = result.get('score', 0)

        context_chunks.append(text)

        source_uri = location.get('s3Location', {}).get('uri', 'Unknown source')
        citations.append({
            'source': source_uri,
            'score': score
        })

    return context_chunks, citations

What does this function do?

  • It creates a bedrock-agent-runtime client, which is a different client from the bedrock-runtime client you used for the Converse API.
  • client.retrieve() sends the customer question to the Knowledge Base, which searches the embedded documents for the most relevant chunks.
  • Each result includes the text content, the S3 URI of the source document, and a relevance score.
  • The function returns both the text chunks (for context) and the citations (for source attribution).
  • Save ask_with_rag.py.
  • Verify the module loads without syntax errors by running:
python3 -c "from ask_with_rag import retrieve_context; chunks, cites = retrieve_context('pricing'); print(f'Retrieved {len(chunks)} chunks')"

You should see output like Retrieved 5 chunks, confirming your retrieval function connects to the Knowledge Base successfully.

Seeing an error?

  • If you see ResourceNotFoundException, double-check that the KNOWLEDGE_BASE_ID value matches the ID from your Bedrock console exactly.
  • If you see AccessDeniedException, verify your AWS credentials have permission to call bedrock-agent-runtime in us-east-1.

Still stuck? Help me debug my retrieve_context function

Feed the retrieved context into the Converse API

Now you need the second half of the RAG pipeline. This function takes the retrieved document chunks, formats them into a prompt, and asks Amazon Nova Pro to generate an answer grounded in that context.

  • Below the retrieve_context function in ask_with_rag.py, add the generate_answer function:
def generate_answer(question, context_chunks):
    """Generate a grounded answer using the Converse API with retrieved context."""
    client = boto3.client('bedrock-runtime', region_name=REGION)

    context_text = "\n\n---\n\n".join(context_chunks)
    user_message = f"""Context from company documentation:

{context_text}

---

Customer question: {question}"""

    response = client.converse(
        modelId=MODEL_ID,
        messages=[
            {
                'role': 'user',
                'content': [{'text': user_message}]
            }
        ],
        system=[
            {'text': SYSTEM_PROMPT}
        ],
        inferenceConfig={
            'maxTokens': 1024,
            'temperature': 0.2
        }
    )

    answer = response['output']['message']['content'][0]['text']
    return answer

What does this function do?

  • It joins all the retrieved text chunks into a single context block, separated by dividers.
  • The customer question and the retrieved context are combined into a single user message.
  • The SYSTEM_PROMPT instructs the model to answer only from the provided context. If the answer is not in the context, the model will say so rather than inventing information.
  • A low temperature of 0.2 keeps answers factual and consistent.
Test with multiple questions to confirm grounding works

The final piece ties retrieval and generation together into a single pipeline function. It also adds the main block so you can run the script from the terminal.

  • Below the generate_answer function, add the ask_with_rag pipeline function and the main block:
def ask_with_rag(question):
    """Full RAG pipeline: retrieve context, then generate a grounded answer."""
    print(f"\nQuestion: {question}")
    print("-" * 50)

    context_chunks, citations = retrieve_context(question)

    if not context_chunks:
        print("No relevant documents found in the knowledge base.")
        return

    answer = generate_answer(question, context_chunks)
    print(f"Answer: {answer}")

    print("\nSources:")
    for i, citation in enumerate(citations, 1):
        print(f"  {i}. {citation['source']} (relevance: {citation['score']:.2f})")

    print()


if __name__ == '__main__':
    questions = [
        "What are CloudNova's pricing tiers?",
        "How do I reset my password?",
        "What is CloudNova's policy on cryptocurrency?"
    ]

    for question in questions:
        ask_with_rag(question)

What does this code do?

  • ask_with_rag() is the full pipeline: it retrieves context, generates an answer, and prints sources.
  • The main block tests three different scenarios: a question with a clear answer in the docs, a troubleshooting question, and a question with no answer in the docs.
  • The third question about cryptocurrency is deliberately not covered in your documentation. The model should honestly refuse rather than making something up.
  • Save ask_with_rag.py.

Before you run this, what do you think the assistant will say about CloudNova's cryptocurrency policy?

  • Run the full RAG assistant:
python3 ask_with_rag.py

For the pricing question, you should see accurate details matching your documentation: Starter at $9.99/month, Professional at $24.99/month, and Enterprise with custom pricing. Each answer includes source citations pointing to the S3 URI of the document it came from.

For the cryptocurrency question, the assistant says it does not have that information and suggests contacting support. Compare this to Step 2 where the model either made up a policy or gave a vague non-answer.

Getting incorrect or empty answers?

  • If answers are empty, make sure your Knowledge Base data source sync completed successfully in the Bedrock console.
  • If answers do not match your docs, verify the S3 bucket contains the correct product_overview.txt, pricing_tiers.txt, and troubleshooting_faq.txt files, then re-sync the data source.
  • If you see a throttling error, wait a few seconds and try again. Bedrock has per-account rate limits for API calls.

Still stuck? Help me debug my RAG assistant output

✔️ Awesome, I've got everything!

Great. Double check you have saved ask_with_rag.py and that all three test questions produced meaningful output.

ⓧ I'd like to double check the full code

Your ask_with_rag.py should contain these sections in order. Scroll up and confirm each is present:

  • The import boto3 line and four constants (KNOWLEDGE_BASE_ID, MODEL_ID, REGION, SYSTEM_PROMPT).
  • The retrieve_context(question) function using bedrock-agent-runtime and client.retrieve().
  • The generate_answer(question, context_chunks) function using bedrock-runtime and client.converse().
  • The ask_with_rag(question) pipeline function that retrieves context and generates an answer.
  • The if __name__ == '__main__': block with three test questions.

Your RAG assistant is working. It retrieves real documentation, generates grounded answers with citations, and honestly refuses questions it cannot answer from the docs. Next up, you will add a guardrail to block off-topic questions before they even reach the model.

Secret mission

Add a Guardrail to Keep the Assistant On-Topic

Your RAG assistant answers accurately, but what stops someone from asking it to write poems or reveal its system prompt? In production, a customer support bot needs boundaries. In this secret mission, you will programmatically create an Amazon Bedrock Guardrail that blocks off-topic questions and harmful content before they even reach the model.

Clean Up Your Resources

Clean Up Your Resources

Decide whether to keep your resources running, pause them to come back later, or delete them entirely. The AWS resources in this project are serverless and pay-per-use. You only pay when you query the Knowledge Base or invoke the Guardrail, so total ongoing cost is effectively $0 when not in active use.

Resources you used:

  • Amazon S3 bucket (cloudnova-support-docs-{account-id}).
  • Amazon Bedrock Managed Knowledge Base (CloudNova-Support-KB).
  • Amazon Bedrock Guardrail (CloudNova-Support-Guardrail).
  • IAM service role created automatically by Bedrock.
  • Local project files (ask_without_rag.py, ask_with_rag.py, ask_with_guardrail.py, company_docs/).

Keep everything running

No action needed. Choose this if you plan to keep experimenting with your RAG assistant or want to test additional questions.

  • The Knowledge Base has no idle cost. You only pay when you query it.
  • The Guardrail has no idle cost. You only pay per evaluation (~$0.01 each).
  • The S3 bucket storing a few text files costs fractions of a cent per month.
  • Your local Python files remain on your machine for future reference.

Pause - I'll come back to this later

All resources in this project are serverless and pay-per-use. There is nothing to pause because nothing runs continuously.

  • The Knowledge Base, Guardrail, and S3 bucket all remain available with no ongoing compute cost.
  • When you return, you can run your Python scripts immediately without any re-setup.

Delete - I don't want to use this again

Remove all project resources to stop any potential charges. Follow each step below.

Delete the Managed Knowledge Base:

  • In the AWS Console, navigate to Amazon Bedrock > AgentCore > Built-in tools > Knowledge Base.
  • Select CloudNova-Support-KB.
  • Click Delete and confirm the deletion.

Delete the Guardrail:

  • In the Bedrock console, navigate to Guardrails.
  • Select CloudNova-Support-Guardrail.
  • Click Delete and confirm.

Empty and delete the S3 bucket:

  • Navigate to the S3 console.
  • Select your cloudnova-support-docs-{account-id} bucket.
  • Click Empty to remove all objects. Confirm by typing permanently delete.
  • After emptying, click Delete bucket and confirm by typing the bucket name.

Delete the IAM service role (optional):

  • Navigate to the IAM console and select Roles.
  • Search for the role Bedrock created (it starts with AmazonBedrockExecution).
  • Select the role and click Delete.

Delete local project files:

  • Remove your local Python scripts and documentation by running:
rm ask_without_rag.py ask_with_rag.py ask_with_guardrail.py
rm -rf company_docs/
  • Confirm the files are removed by running:
ls

Nice Work!

Nice Work!

Nice work! You just built a production-quality customer support AI assistant powered by Amazon Bedrock and grounded in real company documentation.

What you learned:

  • Demonstrated the hallucination problem by querying a foundation model without company data, proving why RAG is essential for accurate customer support.
  • Created a Managed Knowledge Base that automatically embeds and indexes documents from S3, handling the entire retrieval pipeline without provisioning any infrastructure.
  • Built a Python script that retrieves relevant document chunks using the Retrieve API and generates grounded, cited answers using the Converse API.
  • Added a Bedrock Guardrail with denied topics and content filtering to block off-topic queries and harmful content before they reach the model (Secret Mission).

Ready to quiz yourself?