Build a Real-Time RAG Pipeline

Build a Python RAG pipeline that answers questions using live web search.

Introduction

30 Second Summary

When you ask a modern AI assistant about today's news or current prices, it does not just guess from old training data. It searches the web in real time, pulls fresh information, and crafts an answer with sources you can verify.

In this project, you will build a real-time RAG pipeline that searches the live web, retrieves relevant content, and generates cited answers using Cerebras inference and LangChain.

What You'll Build

A command-line Python application that takes any question, searches the web via Tavily, and returns a grounded answer with numbered source citations.

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

  • A Python script that performs real-time web-grounded Q&A from the command line.
  • A LangChain LCEL chain combining Tavily web retrieval and Cerebras generation into a single pipeline.
  • A formatted output with inline citations and source URLs that readers can verify.
  • Secret Mission: Add a query router that intelligently decides whether to search the web or answer directly from the LLM's knowledge.

Are there any prerequisites?

You should have basic Python knowledge and some prior exposure to RAG concepts. No advanced machine learning experience is required.

Set Up Your Environment

Before you can build the RAG pipeline, you need the right tools connected and ready to go. Your pipeline will pull live data from the web and send it to a large language model for answers.

That means you need two things: a search API to fetch web results, and an LLM inference service to generate responses. In this step, you will set up both services and confirm they work.

In this step, get ready to:
  • Install the Python packages for LangChain, Cerebras, Tavily, and dotenv.
  • Create free accounts and get API keys from Cerebras and Tavily.
  • Configure a .env file and verify both API connections.
Create your project folder and install packages

You need a dedicated folder for your project files and a set of Python packages that connect everything together. The LangChain framework orchestrates the pipeline, while separate packages handle Cerebras inference and Tavily search.

  • Open your terminal. Press Cmd+Space (macOS) or the Windows key (Windows) to open your search bar.
  • Type Terminal (macOS) or PowerShell (Windows) and press Enter to open it.
  • Move to your Desktop by running this command:
cd ~/Desktop
  • Create your project folder and move into it by running these commands:
mkdir rag-pipeline
cd rag-pipeline
  • Confirm the folder exists by running:
ls

You should see the rag-pipeline folder listed if you ran ls from the Desktop. If you already moved into it, the folder is empty for now.

Next, check your Python version. The langchain-cerebras package requires Python 3.11 or higher.

  • Check your Python version by running this command:

macOS

python3 --version

Windows

python --version

✔️ I see version 3.11 or higher

Python is ready. Move on to installing packages below.

ⓧ I see an older version

You have Python installed, but you need 3.11 or higher for this project.

On macOS, the system Python is often older than 3.11. Install the latest Python using the official installer at python.org/downloads. After installing, use python3.11 or python3.12 instead of python3 for the rest of this project.

On Windows, download the latest Python from python.org/downloads and run the installer. Check the box that says Add python.exe to PATH during installation.

ⓧ Command not found

Python is not installed on your machine.

  • Download Python 3.11+ from python.org/downloads.
  • Run the installer. On Windows, check Add python.exe to PATH.
  • Close and reopen your terminal, then run the version check again.
  • Install the four packages your project needs by running this command:

macOS

pip3 install langchain-cerebras langchain tavily-python python-dotenv

Windows

pip install langchain-cerebras langchain tavily-python python-dotenv

What do these packages do?

  • langchain-cerebras connects LangChain to Cerebras's inference API.
  • langchain provides the chain orchestration framework and prompt templates.
  • tavily-python is the official SDK for Tavily's web search API.
  • python-dotenv loads environment variables from a .env file so your API keys stay out of your code.

You should see output ending with Successfully installed followed by the package names.

Install command failed?

  • If you see a permission error, try adding --user at the end of the install command.
  • On macOS, if pip3 is not found, try python3 -m pip install instead.
  • Make sure your terminal is still inside the rag-pipeline folder.

Ask for help: Why is my pip install failing?

Get your API keys

Your pipeline needs two API keys: one for Cerebras (the LLM that generates answers) and one for Tavily (the search engine that retrieves web results). Both services offer free tiers with no credit card required.

Start with Cerebras:

  • Open cloud.cerebras.ai in your browser.
  • Click Sign Up to create a free account. You can sign up with Google or email.
  • Once signed in, click API Keys in the left navigation bar.
  • Click Create API Key.
  • Copy the key and save it somewhere safe. You will not be able to see it again after closing the dialog.

What is Cerebras?

Cerebras is an AI inference provider that runs large language models on custom wafer-scale hardware. Their gpt-oss-120b model generates over 500 tokens per second, making it fast enough for interactive applications.

The free tier gives you access to all models with no credit card required.

Now get your Tavily key:

  • Open app.tavily.com in your browser.
  • Click Sign Up to create a free account.
  • After signing in, your API key is displayed on the dashboard.
  • Copy the key and save it alongside your Cerebras key.

What is Tavily?

Tavily is a search API designed for AI agents. It returns clean, structured content from web pages that is ready to feed directly into an LLM prompt.

The free tier gives you 1,000 API credits per month. Each basic search costs 1 credit. No credit card required.

Configure your environment and verify connections

Now you will store your API keys in a .env file so your Python code can load them without hardcoding secrets. Then you will run quick test commands to confirm both services respond.

  • Open the rag-pipeline folder in VS Code. In VS Code, click File in the top menu bar.
  • Click Open Folder.
  • Navigate to your Desktop, select the rag-pipeline folder, and click Open.
  • Create a new file called .env in the root of your project. Press Ctrl+N (Windows) or Cmd+N (macOS) to create a new file, then Ctrl+S or Cmd+S to save it as .env.
  • Paste the following into your .env file, replacing the placeholder values with your actual keys:
CEREBRAS_API_KEY=your-cerebras-api-key-here
TAVILY_API_KEY=your-tavily-api-key-here

Why use a .env file?

A .env file keeps your secrets separate from your code. The python-dotenv package reads this file at runtime and sets the values as environment variables.

This means you can share your code without accidentally exposing your API keys.

  • Save the .env file.

Now verify that both APIs are working. Open the integrated terminal in VS Code by pressing Ctrl+` (Windows) or Cmd+` (macOS).

  • Test your Cerebras connection by running this command:

macOS

python3 -c "from dotenv import load_dotenv; load_dotenv(); import os; from langchain_cerebras import ChatCerebras; llm = ChatCerebras(model='gpt-oss-120b', api_key=os.getenv('CEREBRAS_API_KEY')); print(llm.invoke('Say hello in one word.').content)"

Windows

python -c "from dotenv import load_dotenv; load_dotenv(); import os; from langchain_cerebras import ChatCerebras; llm = ChatCerebras(model='gpt-oss-120b', api_key=os.getenv('CEREBRAS_API_KEY')); print(llm.invoke('Say hello in one word.').content)"

You should see a short response like Hello printed in your terminal. This confirms Cerebras is receiving your requests.

  • Test your Tavily connection by running this command:

macOS

python3 -c "from dotenv import load_dotenv; load_dotenv(); import os; from tavily import TavilyClient; client = TavilyClient(api_key=os.getenv('TAVILY_API_KEY')); results = client.search(query='capital of France', max_results=1); print(results['results'][0]['title'])"

Windows

python -c "from dotenv import load_dotenv; load_dotenv(); import os; from tavily import TavilyClient; client = TavilyClient(api_key=os.getenv('TAVILY_API_KEY')); results = client.search(query='capital of France', max_results=1); print(results['results'][0]['title'])"

You should see a page title printed in your terminal. This confirms Tavily is returning search results.

Seeing an authentication error?

  • Double-check that your .env file is saved in the rag-pipeline folder, not inside a subfolder.
  • Make sure you pasted the actual key values (not the placeholder text) next to each variable name.
  • Ensure there are no spaces around the = sign in your .env file.
  • Confirm your terminal is open in the rag-pipeline directory. Run pwd (macOS) or cd (Windows) to check.

Ask for help: Why am I getting an authentication error when testing my API keys?

✔️ Awesome, I've got everything!

Great. Double check you have saved your .env file.

ⓧ I'd like to double check the full code

Here is what your .env file should look like (with your own keys replacing the placeholders):

CEREBRAS_API_KEY=your-cerebras-api-key-here
TAVILY_API_KEY=your-tavily-api-key-here

Your environment is fully configured. Next up, you will initialize the Cerebras LLM inside a proper Python script and start building the pipeline.

Connect to Cerebras via LangChain

Your environment is ready with both API keys stored securely in a .env file. Now it's time to write the code that actually uses those keys.

In this step, you'll create the main Python file for your RAG pipeline and connect to Cerebras's LLM through LangChain. LangChain gives you a standard interface for talking to any LLM provider. This means if you ever want to swap Cerebras for another model, you change one line instead of rewriting your whole pipeline.

In this step, get ready to:
  • Initialize the ChatCerebras model with LangChain.
  • Send a test message and confirm you get a response.
  • Explore the model parameters (temperature, max_tokens).
Create the pipeline file and import dependencies

Your RAG pipeline lives in a single Python file that imports everything it needs, loads your API keys from the environment, and initializes the LLM.

  • In VS Code, create a new file called rag_pipeline.py in the same folder where your .env file lives.
  • Add the import block and environment variable loader by pasting this code:
import os
from dotenv import load_dotenv
from tavily import TavilyClient
from langchain_cerebras import ChatCerebras
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser

# Load environment variables
load_dotenv()

What does this code do?

These imports bring in the three main tools your pipeline needs.

  • load_dotenv() reads your .env file and makes the API keys available through os.getenv().
  • ChatCerebras is LangChain's interface to the Cerebras inference API.
  • ChatPromptTemplate and StrOutputParser will be used later to build the full RAG chain.
  • Save rag_pipeline.py.

No visible output yet. The next chunk brings the LLM to life.

  • Below the load_dotenv() line, add the initialization code for both Cerebras and Tavily:
# Initialize Cerebras LLM via LangChain
llm = ChatCerebras(
    model="gpt-oss-120b",
    api_key=os.getenv("CEREBRAS_API_KEY"),
    temperature=0.3,
    max_tokens=1024,
)

# Initialize Tavily client
tavily_client = TavilyClient(api_key=os.getenv("TAVILY_API_KEY"))

What does this code do?

  • llm creates a LangChain-compatible chat model pointed at Cerebras's gpt-oss-120b model. This 120-billion parameter model runs at 500+ tokens per second on Cerebras hardware.
  • tavily_client sets up the Tavily search client. You won't use it until the next step, but initializing it now keeps all the setup in one place.
  • Save rag_pipeline.py.
  • Confirm the file runs without errors by running this command in your terminal:
python rag_pipeline.py

You should see no output and no errors. The script loaded your keys and created the client objects silently.

Seeing a ModuleNotFoundError?

  • Make sure you installed all packages in the same Python environment you're running the script from. Run pip install langchain-cerebras tavily-python python-dotenv again if needed.
  • Check that your .env file is in the same directory as rag_pipeline.py. If it's in a different folder, load_dotenv() won't find it.

Still stuck? Help me debug this ModuleNotFoundError when running rag_pipeline.py

Send a test message to Cerebras

The script ran without errors, but that only proves your keys loaded. Let's confirm the LLM actually responds by sending it a quick test message.

  • At the very bottom of rag_pipeline.py, add these temporary test lines:
# Temporary test - remove after verifying
response = llm.invoke("What is retrieval-augmented generation in one sentence?")
print(response.content)
  • Save the file and run it again:
python rag_pipeline.py

You should see a one-sentence definition of RAG printed in your terminal. The exact wording varies, but if you see a coherent response, your Cerebras connection is working.

  • Now remove the three temporary test lines you just added (the comment, the response line, and the print line).
  • Save rag_pipeline.py.

Seeing an authentication error?

  • Open your .env file and check that CEREBRAS_API_KEY has no extra spaces or quotes around the key value.
  • Make sure you copied the full API key from cloud.cerebras.ai. Partial keys will fail silently or throw an authentication error.

Need more help? Help me fix this Cerebras authentication error

Explore the model parameters

The two parameters you set on ChatCerebras control how the model generates text. Understanding them now helps you tune your pipeline later.

What do temperature and max_tokens control?

temperature=0.3 controls randomness. A value of 0.0 makes the model completely deterministic. A value of 1.0 makes it maximally creative. For a RAG pipeline, 0.3 is a good choice because you want factual, consistent answers grounded in your search results.

max_tokens=1024 sets the maximum length of the model's response. The gpt-oss-120b model has a 131,072-token context window, but capping the output at 1024 tokens keeps answers focused and avoids long-winded responses.

Try different temperatures

After the project is complete, try setting temperature to 0.0 and then 0.9 to see how the same question produces different styles of answers. Lower values are better for factual Q&A. Higher values work better for creative writing.

✔️ Awesome, I've got everything!

Great. Double check you've saved your file after removing the test lines.

ⓧ I'd like to double check the full code

Here's what rag_pipeline.py should look like at this point:

import os
from dotenv import load_dotenv
from tavily import TavilyClient
from langchain_cerebras import ChatCerebras
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser

# Load environment variables
load_dotenv()

# Initialize Cerebras LLM via LangChain
llm = ChatCerebras(
    model="gpt-oss-120b",
    api_key=os.getenv("CEREBRAS_API_KEY"),
    temperature=0.3,
    max_tokens=1024,
)

# Initialize Tavily client
tavily_client = TavilyClient(api_key=os.getenv("TAVILY_API_KEY"))

Your LLM is connected and responding. Next up, you'll wire in Tavily web search so your pipeline can pull live information from the internet before generating answers.

Integrate Tavily Web Search

In the previous step, you connected to Cerebras and confirmed your LLM is responding. Now it is time to give your pipeline access to fresh, real-time information from the web.

Your tavily_client is already initialized, but you have not used it yet. In this step, you will run a test search, explore what Tavily returns, and build a helper function that formats search results into clean context for your LLM.

In this step, get ready to:
  • Run a test search and explore the response structure.
  • Build a helper function that formats search results into LLM-ready context.
Run a test search and explore the response

Before writing any permanent code, you should see what a raw Tavily search response looks like. This tells you exactly which fields you can use when formatting context for the LLM.

  • Load your script into Python's interactive mode by running this command:
python -i rag_pipeline.py

What does the -i flag do?

The -i flag runs your script and then drops you into an interactive Python prompt. All the variables from your script (like llm and tavily_client) are available for you to use directly.

  • At the >>> prompt, run a test search by typing this:
response = tavily_client.search(query="latest AI news", max_results=5)
  • Inspect the top-level keys of the response:
print(response.keys())
  • Now look at the fields available in one search result:
print(response["results"][0].keys())

You should see keys including title, url, content, and score. These are the fields your helper function will extract.

  • View a full result to see the actual content returned:
print(response["results"][0]["title"])
print(response["results"][0]["content"][:200])

You should see a real article title and a snippet of its content. This is the raw material your RAG pipeline will use to generate grounded answers.

  • Exit the interactive session by typing:
exit()

What did you just learn about the response?

Each Tavily search returns a dictionary with a results list. Each result contains a title (the page headline), a url (the source link), content (extracted text from the page), and a score (relevance ranking).

Your helper function needs to combine these fields into a format the LLM can read and cite.

Getting a connection or API key error?

Make sure your .env file contains your TAVILY_API_KEY and that the key is valid. You can double-check by logging into tavily.com and copying a fresh key.

help me debug a Tavily API connection error in my Python script

Build the search_web helper function

Now that you know what Tavily returns, you can write a function that searches the web and formats results into numbered source blocks. The LLM will use these blocks to cite its answers.

  • In rag_pipeline.py, find the line that says tavily_client = TavilyClient(api_key=os.getenv("TAVILY_API_KEY")). Add the following function below it, with two blank lines separating them:
def search_web(query: str) -> str:
    """Search the web using Tavily and format results as context."""
    response = tavily_client.search(query=query, max_results=5)

    if not response["results"]:
        return "No search results found."

    context_parts = []
    for i, result in enumerate(response["results"], 1):
        context_parts.append(
            f"[Source {i}] {result['title']}\n"
            f"URL: {result['url']}\n"
            f"Content: {result['content']}\n"
        )

    return "\n".join(context_parts)

What does this code do?

  • tavily_client.search(query=query, max_results=5) sends your question to Tavily and retrieves the top 5 results.
  • The if not response["results"] check handles cases where no results come back, so the LLM gets a clear message instead of crashing.
  • The loop formats each result as a numbered [Source N] block with title, URL, and content. This structure lets the LLM reference specific sources when generating answers.
  • Save rag_pipeline.py.
  • Test your new function by running this command:
python -i rag_pipeline.py
  • At the >>> prompt, call your function:
print(search_web("what is retrieval augmented generation"))

You should see 5 numbered source blocks, each with a title, URL, and content snippet. This formatted output is exactly what your LLM will receive as context in the next step.

  • Exit the interactive session:
exit()

Seeing 'No search results found' or an empty output?

Check that your TAVILY_API_KEY in .env is correct and has not expired. Tavily's free tier gives you 1,000 credits per month. Each search costs 1 credit.

Also confirm there are no typos in the function name or the response["results"] key. The dictionary key must be the exact string results.

help me troubleshoot my search_web function that returns no results

✔️ Awesome, I've got everything!

Great. Make sure you have saved rag_pipeline.py before moving on.

ⓧ I'd like to double check the full code

Here is the complete rag_pipeline.py file at this point:

import os
from dotenv import load_dotenv
from tavily import TavilyClient
from langchain_cerebras import ChatCerebras
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser

# Load environment variables
load_dotenv()

# Initialize Cerebras LLM via LangChain
llm = ChatCerebras(
    model="gpt-oss-120b",
    api_key=os.getenv("CEREBRAS_API_KEY"),
    temperature=0.3,
    max_tokens=1024,
)

# Initialize Tavily client
tavily_client = TavilyClient(api_key=os.getenv("TAVILY_API_KEY"))


def search_web(query: str) -> str:
    """Search the web using Tavily and format results as context."""
    response = tavily_client.search(query=query, max_results=5)

    if not response["results"]:
        return "No search results found."

    context_parts = []
    for i, result in enumerate(response["results"], 1):
        context_parts.append(
            f"[Source {i}] {result['title']}\n"
            f"URL: {result['url']}\n"
            f"Content: {result['content']}\n"
        )

    return "\n".join(context_parts)

Your pipeline can now pull fresh information from the web and structure it for an LLM to read. Next up, you will build the full RAG chain that combines this search context with Cerebras to generate cited answers.

Build the RAG Chain

You now have two powerful components working independently: Cerebras for fast LLM inference and Tavily for live web search. The missing piece is a bridge that connects them into a single pipeline.

In this step, you'll create a prompt template that tells the LLM exactly how to use search results, then wire everything together using LangChain Expression Language (LCEL). The result is a single function that takes a question, searches the web, and returns a cited answer.

In this step, get ready to:
  • Create a prompt template that instructs the LLM to cite sources.
  • Connect retrieval and generation into a single LCEL chain.
  • Test the full pipeline with a real question.
Create the RAG prompt template

The prompt template is what controls how the LLM behaves when it receives search results. It needs to instruct the model to base its answer on the provided context, cite sources using [Source N] notation, and be honest when the results don't contain enough information.

  • In rag_pipeline.py, scroll to the bottom of the file (below the search_web function).
  • Add the RAG prompt template by pasting this code:
# Define the RAG prompt template
rag_prompt = ChatPromptTemplate.from_messages([
    ("system", (
        "You are a helpful research assistant. Answer the user's question "
        "based on the provided web search results. Always cite your sources "
        "using [Source N] notation. If the search results don't contain "
        "relevant information, say so clearly."
    )),
    ("human", (
        "Web Search Results:\n{context}\n\n"
        "Question: {question}\n\n"
        "Provide a comprehensive answer with citations:"
    )),
])

What does this code do?

The system message defines the LLM's role as a research assistant and sets three rules: use the search results, cite with [Source N] notation, and admit when information is missing.

The human message template has two placeholders: {context} (where search results go) and {question} (the user's original question). At runtime, LangChain fills these in automatically when you invoke the chain.

  • Save rag_pipeline.py.

You won't see a visible change yet. The next chunk uses this template to build the full chain.

Combine retrieval and generation into an LCEL chain

LCEL uses Python's pipe (|) operator to chain components together. Data flows left to right: the prompt formats the input, the LLM generates a response, and the output parser extracts the text.

  • Below the rag_prompt you just added, paste this code to build the chain and the function that orchestrates the full pipeline:
# Build the RAG chain using LCEL
output_parser = StrOutputParser()
rag_chain = rag_prompt | llm | output_parser


def ask(question: str) -> str:
    """Run the full RAG pipeline: search, then generate a cited answer."""
    print(f"\nSearching the web for: {question}")
    context = search_web(question)
    
    print("Generating answer...\n")
    answer = rag_chain.invoke({
        "context": context,
        "question": question,
    })
    
    return answer

What does this code do?

rag_prompt | llm | output_parser creates the LCEL chain. When invoked, the prompt fills in {context} and {question}, passes the formatted messages to Cerebras, and the StrOutputParser extracts the response as a plain string.

The ask function ties everything together: it calls search_web to get live context from Tavily, then invokes the chain with both the context and the original question. Status messages print along the way so you can see the pipeline working.

  • Save rag_pipeline.py.

You won't see a change yet. The next substep tests the full pipeline end to end.

✔️ Awesome, I've got everything!

Great. Double check you've saved your file before moving on.

ⓧ I'd like to double check the full code

import os
from dotenv import load_dotenv
from tavily import TavilyClient
from langchain_cerebras import ChatCerebras
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser

# Load environment variables
load_dotenv()

# Initialize Cerebras LLM via LangChain
llm = ChatCerebras(
    model="gpt-oss-120b",
    api_key=os.getenv("CEREBRAS_API_KEY"),
    temperature=0.3,
    max_tokens=1024,
)

# Initialize Tavily client
tavily_client = TavilyClient(api_key=os.getenv("TAVILY_API_KEY"))


def search_web(query: str) -> str:
    """Search the web using Tavily and format results as context."""
    response = tavily_client.search(query=query, max_results=5)
    
    if not response["results"]:
        return "No search results found."
    
    context_parts = []
    for i, result in enumerate(response["results"], 1):
        context_parts.append(
            f"[Source {i}] {result['title']}\n"
            f"URL: {result['url']}\n"
            f"Content: {result['content']}\n"
        )
    
    return "\n".join(context_parts)


# Define the RAG prompt template
rag_prompt = ChatPromptTemplate.from_messages([
    ("system", (
        "You are a helpful research assistant. Answer the user's question "
        "based on the provided web search results. Always cite your sources "
        "using [Source N] notation. If the search results don't contain "
        "relevant information, say so clearly."
    )),
    ("human", (
        "Web Search Results:\n{context}\n\n"
        "Question: {question}\n\n"
        "Provide a comprehensive answer with citations:"
    )),
])

# Build the RAG chain using LCEL
output_parser = StrOutputParser()
rag_chain = rag_prompt | llm | output_parser


def ask(question: str) -> str:
    """Run the full RAG pipeline: search, then generate a cited answer."""
    print(f"\nSearching the web for: {question}")
    context = search_web(question)
    
    print("Generating answer...\n")
    answer = rag_chain.invoke({
        "context": context,
        "question": question,
    })
    
    return answer
Test the full pipeline

Time to see the entire pipeline in action. You'll call the ask function with a real question and watch it search the web, then generate a cited answer.

  • Test your pipeline by running this command in your terminal:
python -c "from rag_pipeline import ask; print(ask('What are the latest developments in AI'))"

What should I see?

You should see two status messages ("Searching the web for..." and "Generating answer...") followed by a multi-paragraph response. The answer references [Source 1], [Source 2], etc. throughout the text.

The content will be current because Tavily searched the live web just moments ago. Cerebras then synthesized those results into a coherent answer with citations pointing back to the original sources.

Not seeing any output or getting an error?

  • If you see ModuleNotFoundError, make sure you're running the command from the same folder that contains rag_pipeline.py.
  • If you see an authentication error, double check your .env file has valid API keys for both CEREBRAS_API_KEY and TAVILY_API_KEY.
  • On Windows CMD, if you get a quote-related error, try running the command in PowerShell instead.

Help me debug my RAG pipeline test command

Your RAG pipeline is working end to end. Next up, you'll add polish: formatted output with numbered source URLs, error handling, and an interactive loop for asking multiple questions.

Add Citations and Polish

Your RAG chain already generates cited answers from live web data. The missing piece is a polished user experience.

In this step, you'll wrap everything in an interactive loop that accepts questions, displays formatted answers, and handles errors gracefully. This transforms your script from a single-shot function into a reusable command-line tool.

In this step, get ready to:
  • Understand how citations flow through the pipeline.
  • Create an interactive loop with error handling.
  • Run the finished pipeline and ask real questions.
Review how citations flow through the pipeline

Before adding the interactive loop, take a moment to trace how citations work end-to-end. The system is already wired up from previous steps.

  • The search_web function labels each result with [Source 1], [Source 2], etc., along with the URL and extracted content.
  • The rag_prompt tells the LLM to cite sources using [Source N] notation, matching the labels in the context.
  • The LLM weaves these citations into its answer naturally, referencing the numbered sources you provided.

Why does this approach work?

By labeling search results with numbered source tags BEFORE they reach the LLM, you give the model a consistent vocabulary to reference. The model sees [Source 1] in the context and naturally uses the same tag in its answer.

This is more reliable than asking the LLM to generate URLs from memory, which often leads to hallucinated links.

Add the interactive main function

Right now, calling the pipeline requires manually invoking ask() in a Python shell. A main() function turns the script into a proper command-line tool that accepts questions in a loop.

  • In rag_pipeline.py, scroll to the bottom of the file (below the ask function) and add the following code:

def main():
    """Interactive loop for asking questions."""
    print("=" * 60)
    print("Real-Time RAG Pipeline")
    print("Ask any question and get a cited answer from the live web!")
    print("Type 'quit' to exit.")
    print("=" * 60)

    while True:
        question = input("\nYour question: ").strip()

        if question.lower() in ("quit", "exit", "q"):
            print("Goodbye!")
            break

        if not question:
            print("Please enter a question.")
            continue

        try:
            answer = ask(question)
            print(f"\nAnswer:\n{answer}")
        except Exception as e:
            print(f"\nError: {e}")
            print("Please check your API keys and try again.")


if __name__ == "__main__":
    main()

What does this code do?

  • The header banner (print("=" * 60)) gives the tool a clean startup message so the user knows what to do.
  • The while True loop keeps accepting questions until the user types quit, exit, or q.
  • The try/except block catches any API errors (expired keys, rate limits, network issues) and prints a helpful message instead of crashing.
  • The if __name__ == "__main__": guard means main() only runs when you execute the file directly, not when it's imported as a module.
  • Save rag_pipeline.py.
  • Run the script by entering this command in your terminal:
python rag_pipeline.py
  • You should see the header banner appear, followed by a Your question: prompt.
  • Type a question like What is the latest news about AI regulation? and press **Enter**.
  • You should see a formatted answer with [Source 1], [Source 2] citations woven into the response.
  • Type quit to exit the loop.

Not seeing an answer?

  • If you see an error about API keys, double-check your .env file has the correct values for CEREBRAS_API_KEY and TAVILY_API_KEY.
  • If the script exits immediately without prompting, make sure you added if __name__ == "__main__": main() at the very bottom of the file.
  • If you see a ModuleNotFoundError, make sure your virtual environment is activated and all packages are installed.

My RAG pipeline isn't returning results when I run it. Can you help me debug?

Test with different question types

Try a few different questions to see how the pipeline handles various topics. Current events, technical questions, and comparison queries all produce different citation patterns.

  • Run the script again and try asking 2-3 different questions.
  • Notice how each answer references different sources depending on the topic.
  • Try pressing **Enter** without typing anything to confirm the empty-input handling works.

✔️ Awesome, I've got everything!

Great. Double check you've saved your file before moving on.

ⓧ I'd like to double check the full code

Here's the complete rag_pipeline.py file:

import os
from dotenv import load_dotenv
from tavily import TavilyClient
from langchain_cerebras import ChatCerebras
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser

# Load environment variables
load_dotenv()

# Initialize Cerebras LLM via LangChain
llm = ChatCerebras(
    model="gpt-oss-120b",
    api_key=os.getenv("CEREBRAS_API_KEY"),
    temperature=0.3,
    max_tokens=1024,
)

# Initialize Tavily client
tavily_client = TavilyClient(api_key=os.getenv("TAVILY_API_KEY"))


def search_web(query: str) -> str:
    """Search the web using Tavily and format results as context."""
    response = tavily_client.search(query=query, max_results=5)

    if not response["results"]:
        return "No search results found."

    context_parts = []
    for i, result in enumerate(response["results"], 1):
        context_parts.append(
            f"[Source {i}] {result['title']}\n"
            f"URL: {result['url']}\n"
            f"Content: {result['content']}\n"
        )

    return "\n".join(context_parts)


# Define the RAG prompt template
rag_prompt = ChatPromptTemplate.from_messages([
    ("system", (
        "You are a helpful research assistant. Answer the user's question "
        "based on the provided web search results. Always cite your sources "
        "using [Source N] notation. If the search results don't contain "
        "relevant information, say so clearly."
    )),
    ("human", (
        "Web Search Results:\n{context}\n\n"
        "Question: {question}\n\n"
        "Provide a comprehensive answer with citations:"
    )),
])

# Build the RAG chain using LCEL
output_parser = StrOutputParser()
rag_chain = rag_prompt | llm | output_parser


def ask(question: str) -> str:
    """Run the full RAG pipeline: search, then generate a cited answer."""
    print(f"\nSearching the web for: {question}")
    context = search_web(question)

    print("Generating answer...\n")
    answer = rag_chain.invoke({
        "context": context,
        "question": question,
    })

    return answer


def main():
    """Interactive loop for asking questions."""
    print("=" * 60)
    print("Real-Time RAG Pipeline")
    print("Ask any question and get a cited answer from the live web!")
    print("Type 'quit' to exit.")
    print("=" * 60)

    while True:
        question = input("\nYour question: ").strip()

        if question.lower() in ("quit", "exit", "q"):
            print("Goodbye!")
            break

        if not question:
            print("Please enter a question.")
            continue

        try:
            answer = ask(question)
            print(f"\nAnswer:\n{answer}")
        except Exception as e:
            print(f"\nError: {e}")
            print("Please check your API keys and try again.")


if __name__ == "__main__":
    main()

Your RAG pipeline is complete. You now have a command-line tool that searches the live web, generates grounded answers, and cites its sources. Next up, the Secret Mission challenges you to make it even smarter with intelligent query routing.

Secret mission

Add a Query Router

Your pipeline searches the web for every question, even ones like "what is photosynthesis" that don't need fresh data. Can you build an intelligent router that decides when to search and when to answer directly?

Clean Up Your Resources

Clean Up Your Resources

This project runs entirely locally and uses free-tier APIs from both Cerebras and Tavily. There are no ongoing costs to worry about.

Decide whether to keep your resources running, pause them to come back later, or delete them entirely.

Resources you used:

  • Project folder on your Desktop containing rag_pipeline.py and .env.
  • Python packages: langchain-cerebras, tavily-python, python-dotenv, and langchain.
  • Cerebras API key (free tier, stored in your .env file).
  • Tavily API key (free tier, stored in your .env file).

Keep everything running

No action needed. Choose this if you plan to keep experimenting with your RAG pipeline or extend it further.

  • Both Cerebras and Tavily free tiers remain active indefinitely with no charges.
  • Tavily gives you 1,000 credits per month that reset automatically.
  • Your project files stay on your Desktop ready for future use.

Pause - I'll come back to this later

Free up disk space while keeping your accounts ready for when you return.

  • No pausing action needed for Cerebras or Tavily. Both free tiers have no ongoing cost and will be ready when you return.
  • You can optionally close VS Code and any terminal windows running the script.

Delete - I don't want to use this again

Remove all project files and packages from your machine.

  • Delete your project folder by running this command:

macOS

rm -rf ~/Desktop/rag-pipeline

Windows

Remove-Item -Recurse -Force ~\Desktop\rag-pipeline
  • Uninstall the Python packages by running:
pip uninstall langchain-cerebras tavily-python python-dotenv langchain langchain-core -y
  • Your Cerebras and Tavily accounts remain free and do not need to be deleted. If you prefer, you can revoke your API keys from each provider's dashboard settings page.

That's a Wrap!

That's a Wrap!

Nice work! You just built a real-time RAG pipeline that searches the live web and generates grounded, cited answers using Cerebras and Tavily.

You've learned how to:

  • Build a real-time RAG pipeline that retrieves live web data via Tavily and generates answers with Cerebras inference.
  • Compose a LangChain LCEL chain that connects prompt templates, LLM calls, and output parsers into a single pipeline.
  • Engineer prompts that produce cited, verifiable answers with numbered source references and URLs.
  • Secret Mission: Added intelligent query routing that classifies questions and skips unnecessary web searches.

Ready to quiz yourself?