Back to the Couchbase homepageCouchbase logo
Couchbase Developer

  • Docs

    • Integrations
    • SDKs
    • Mobile SDKs

    • AI Developer
    • Backend
    • Full-stack
    • Mobile
    • Ops / DBA

    • Data Modeling
    • Scalability

  • Tutorials

    • Developer Community
    • Ambassador Program
  • Sign In
  • Try Free

RAG with Claude using Couchbase Search Vector Index

  • Learn how to build a semantic search engine using Couchbase, OpenAI embeddings, and Anthropic using Search Vector Index.
  • This tutorial demonstrates how to integrate Couchbase's vector search capabilities with OpenAI embeddings and use Anthropic as the language model.
  • You'll understand how to perform Retrieval-Augmented Generation (RAG) using LangChain and Couchbase.

View Source

Introduction

In this guide, we will walk you through building a powerful semantic search engine using Couchbase as the backend database, OpenAI as the AI-powered embedding and Anthropic as the language model provider. Semantic search goes beyond simple keyword matching by understanding the context and meaning behind the words in a query, making it an essential tool for applications that require intelligent information retrieval. This tutorial is designed to be beginner-friendly, with clear, step-by-step instructions that will equip you with the knowledge to create a fully functional semantic search system using the Search Vector Index from scratch. Alternatively if you want to perform semantic search using the Hyperscale or Composite Vector Index, please take a look at this.

How to run this tutorial

This tutorial is available as a Jupyter Notebook (.ipynb file) that you can run interactively. You can access the original notebook here.

You can either download the notebook file and run it on Google Colab or run it on your system by setting up the Python environment.

Before you start

Get Credentials for OpenAI and Anthropic

  • Please follow the instructions to generate the OpenAI credentials.
  • Please follow the instructions to generate the Anthropic credentials.

Create and Deploy Your Free Tier Operational cluster on Capella

To get started with Couchbase Capella, create an account and use it to deploy a forever free tier operational cluster. This account provides you with an environment where you can explore and learn about Capella with no time constraint.

To learn more, please follow the instructions.

Couchbase Capella Configuration

When running Couchbase using Capella, the following prerequisites need to be met.

  • Create the database credentials to access the required bucket (Read and Write) used in the application.

  • Allow access to the Cluster from the IP on which the application is running.

Setting the Stage: Installing Necessary Libraries

To build our semantic search engine, we need a robust set of tools. The libraries we install handle everything from connecting to databases to performing complex machine learning tasks. Each library has a specific role: Couchbase libraries manage database operations, LangChain handles AI model integrations, and OpenAI provides advanced AI models for generating embeddings and Claude(by Anthropic) for understanding natural language. By setting up these libraries, we ensure our environment is equipped to handle the data-intensive and computationally complex tasks required for semantic search.

%pip install --no-user --quiet datasets==4.5.0 langchain-couchbase==1.0.1 langchain-anthropic==1.3.2 langchain-openai==1.1.8 python-dotenv==1.2.1
[notice] A new release of pip is available: 25.0.1 -> 26.0.1
[notice] To update, run: pip install --upgrade pip
Note: you may need to restart the kernel to use updated packages.

Importing Necessary Libraries

The script starts by importing a series of libraries required for various tasks, including handling JSON, logging, time tracking, Couchbase connections, embedding generation, and dataset loading. These libraries provide essential functions for working with data, managing database connections, and processing machine learning models.

import getpass
import json
import logging
import os
import time
from datetime import timedelta
from multiprocessing import AuthenticationError

from couchbase.auth import PasswordAuthenticator
from couchbase.cluster import Cluster
from couchbase.exceptions import (CouchbaseException,
                                  InternalServerFailureException,
                                  ServiceUnavailableException)
from couchbase.management.buckets import CreateBucketSettings
from couchbase.management.search import SearchIndex
from couchbase.options import ClusterOptions
from datasets import load_dataset
from dotenv import load_dotenv
from langchain_anthropic import ChatAnthropic
from langchain_core.globals import set_llm_cache
from langchain_core.prompts.chat import (ChatPromptTemplate,
                                         HumanMessagePromptTemplate,
                                         SystemMessagePromptTemplate)
from langchain_core.runnables import RunnablePassthrough
from langchain_couchbase.cache import CouchbaseCache
from langchain_couchbase.vectorstores import CouchbaseSearchVectorStore
from langchain_openai import OpenAIEmbeddings
/Users/kaustavghosh/Desktop/vector-search-cookbook/.venv/lib/python3.12/site-packages/tqdm/auto.py:21: TqdmWarning: IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html
  from .autonotebook import tqdm as notebook_tqdm

Setup Logging

Logging is configured to track the progress of the script and capture any errors or warnings. This is crucial for debugging and understanding the flow of execution. The logging output includes timestamps, log levels (e.g., INFO, ERROR), and messages that describe what is happening in the script.

logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s', force=True)

# Disable all logging except critical to prevent OpenAI API request logs
logging.getLogger("httpx").setLevel(logging.CRITICAL)

Loading Sensitive Information

In this section, we prompt the user to input essential configuration settings needed. These settings include sensitive information like API keys, database credentials, and specific configuration names. Instead of hardcoding these details into the script, we request the user to provide them at runtime, ensuring flexibility and security.

The project includes an .env.sample file that lists all the environment variables. To get started:

  1. Create a .env file in the same directory as this notebook
  2. Copy the contents from .env.sample to your .env file
  3. Fill in the required credentials

The script also validates that all required inputs are provided, raising an error if any crucial information is missing. This approach ensures that your integration is both secure and correctly configured without hardcoding sensitive information, enhancing the overall security and maintainability of your code.

load_dotenv()

# Load from environment variables or prompt for input in one-liners
ANTHROPIC_API_KEY = os.getenv('ANTHROPIC_API_KEY') or getpass.getpass('Enter your Anthropic API key: ')
OPENAI_API_KEY = os.getenv('OPENAI_API_KEY') or getpass.getpass('Enter your OpenAI API key: ')
CB_HOST = os.getenv('CB_HOST', 'couchbase://localhost') or input('Enter your Couchbase host (default: couchbase://localhost): ') or 'couchbase://localhost'
CB_USERNAME = os.getenv('CB_USERNAME', 'Administrator') or input('Enter your Couchbase username (default: Administrator): ') or 'Administrator'
CB_PASSWORD = os.getenv('CB_PASSWORD', 'password') or getpass.getpass('Enter your Couchbase password (default: password): ') or 'password'
CB_BUCKET_NAME = os.getenv('CB_BUCKET_NAME', 'vector-search-testing') or input('Enter your Couchbase bucket name (default: vector-search-testing): ') or 'vector-search-testing'
INDEX_NAME = os.getenv('INDEX_NAME', 'vector_search_claude') or input('Enter your index name (default: vector_search_claude): ') or 'vector_search_claude'
SCOPE_NAME = os.getenv('SCOPE_NAME', 'shared') or input('Enter your scope name (default: shared): ') or 'shared'
COLLECTION_NAME = os.getenv('COLLECTION_NAME', 'claude') or input('Enter your collection name (default: claude): ') or 'claude'
CACHE_COLLECTION = os.getenv('CACHE_COLLECTION', 'cache') or input('Enter your cache collection name (default: cache): ') or 'cache'
# Check if the variables are correctly loaded
if not ANTHROPIC_API_KEY:
    raise ValueError("ANTHROPIC_API_KEY is not set in the environment.")
if not OPENAI_API_KEY:
    raise ValueError("OPENAI_API_KEY is not set in the environment.")

Connecting to the Couchbase Cluster

Connecting to a Couchbase cluster is the foundation of our project. Couchbase will serve as our primary data store, handling all the storage and retrieval operations required for our semantic search engine. By establishing this connection, we enable our application to interact with the database, allowing us to perform operations such as storing embeddings, querying data, and managing collections. This connection is the gateway through which all data will flow, so ensuring it's set up correctly is paramount.

try:
    auth = PasswordAuthenticator(CB_USERNAME, CB_PASSWORD)
    options = ClusterOptions(auth)
    cluster = Cluster(CB_HOST, options)
    cluster.wait_until_ready(timedelta(seconds=5))
    logging.info("Successfully connected to Couchbase")
except Exception as e:
    raise ConnectionError(f"Failed to connect to Couchbase: {str(e)}")
2026-02-19 11:02:28,572 - INFO - Successfully connected to Couchbase

Setting Up Collections in Couchbase

The setup_collection() function handles creating and configuring the hierarchical data organization in Couchbase:

  1. Bucket Creation:

    • Checks if specified bucket exists, creates it if not
    • Sets bucket properties like RAM quota (1024MB) and replication (disabled)
    • Note: You will not be able to create a bucket on Capella
  2. Scope Management:

    • Verifies if requested scope exists within bucket
    • Creates new scope if needed (unless it's the default "_default" scope)
  3. Collection Setup:

    • Checks for collection existence within scope
    • Creates collection if it doesn't exist
    • Waits 2 seconds for collection to be ready

Additional Tasks:

  • Creates primary index on collection for query performance
  • Clears any existing documents for clean state
  • Implements comprehensive error handling and logging

The function is called twice to set up:

  1. Main collection for vector embeddings
  2. Cache collection for storing results
def setup_collection(cluster, bucket_name, scope_name, collection_name):
    try:
        # Check if bucket exists, create if it doesn't
        try:
            bucket = cluster.bucket(bucket_name)
            logging.info(f"Bucket '{bucket_name}' exists.")
        except Exception as e:
            logging.info(f"Bucket '{bucket_name}' does not exist. Creating it...")
            bucket_settings = CreateBucketSettings(
                name=bucket_name,
                bucket_type='couchbase',
                ram_quota_mb=1024,
                flush_enabled=True,
                num_replicas=0
            )
            cluster.buckets().create_bucket(bucket_settings)
            time.sleep(2)  # Wait for bucket creation to complete and become available
            bucket = cluster.bucket(bucket_name)
            logging.info(f"Bucket '{bucket_name}' created successfully.")

        bucket_manager = bucket.collections()

        # Check if scope exists, create if it doesn't
        scopes = bucket_manager.get_all_scopes()
        scope_exists = any(scope.name == scope_name for scope in scopes)
        
        if not scope_exists and scope_name != "_default":
            logging.info(f"Scope '{scope_name}' does not exist. Creating it...")
            bucket_manager.create_scope(scope_name)
            logging.info(f"Scope '{scope_name}' created successfully.")

        # Check if collection exists, create if it doesn't
        collection_exists = any(
            scope.name == scope_name and collection_name in [col.name for col in scope.collections]
            for scope in scopes
        )

        if not collection_exists:
            logging.info(f"Collection '{collection_name}' does not exist. Creating it...")
            bucket_manager.create_collection(scope_name, collection_name)
            logging.info(f"Collection '{collection_name}' created successfully.")
        else:
            logging.info(f"Collection '{collection_name}' already exists. Skipping creation.")

        # Wait for collection to be ready
        collection = bucket.scope(scope_name).collection(collection_name)
        time.sleep(2)  # Give the collection time to be ready for queries

        # Ensure primary index exists
        try:
            cluster.query(f"CREATE PRIMARY INDEX IF NOT EXISTS ON `{bucket_name}`.`{scope_name}`.`{collection_name}`").execute()
            logging.info("Primary index present or created successfully.")
        except Exception as e:
            logging.warning(f"Error creating primary index: {str(e)}")

        # Clear all documents in the collection
        try:
            query = f"DELETE FROM `{bucket_name}`.`{scope_name}`.`{collection_name}`"
            cluster.query(query).execute()
            logging.info("All documents cleared from the collection.")
        except Exception as e:
            logging.warning(f"Error while clearing documents: {str(e)}. The collection might be empty.")

        return collection
    except Exception as e:
        raise RuntimeError(f"Error setting up collection: {str(e)}")
    
setup_collection(cluster, CB_BUCKET_NAME, SCOPE_NAME, COLLECTION_NAME)
setup_collection(cluster, CB_BUCKET_NAME, SCOPE_NAME, CACHE_COLLECTION)
2026-02-19 11:02:28,583 - INFO - Bucket 'vector-search-testing' exists.
2026-02-19 11:02:28,588 - INFO - Collection 'claude' already exists. Skipping creation.
2026-02-19 11:02:30,594 - INFO - Primary index present or created successfully.
2026-02-19 11:02:30,669 - INFO - All documents cleared from the collection.
2026-02-19 11:02:30,670 - INFO - Bucket 'vector-search-testing' exists.
2026-02-19 11:02:30,671 - INFO - Collection 'cache' already exists. Skipping creation.
2026-02-19 11:02:32,675 - INFO - Primary index present or created successfully.
2026-02-19 11:02:32,678 - INFO - All documents cleared from the collection.





<couchbase.collection.Collection at 0x1371a0890>

Loading Couchbase Vector Search Index

Semantic search requires an efficient way to retrieve relevant documents based on a user's query. This is where the Couchbase Vector Search Index comes into play. In this step, we load the Vector Search Index definition from a JSON file, which specifies how the index should be structured. This includes the fields to be indexed, the dimensions of the vectors, and other parameters that determine how the search engine processes queries based on vector similarity.

For more information on creating a vector search index, please follow the instructions.

Note: Index creation will not fail if used with the concerned bucket(vector-search-testing) instead of travel-sample

# If you are running this script locally (not in Google Colab), uncomment the following line
# and provide the path to your index definition file.

# index_definition_path = '/path_to_your_index_file/claude_index.json'  # Local setup: specify your file path here

# # Version for Google Colab
# def load_index_definition_colab():
#     from google.colab import files
#     print("Upload your index definition file")
#     uploaded = files.upload()
#     index_definition_path = list(uploaded.keys())[0]

#     try:
#         with open(index_definition_path, 'r') as file:
#             index_definition = json.load(file)
#         return index_definition
#     except Exception as e:
#         raise ValueError(f"Error loading index definition from {index_definition_path}: {str(e)}")

# Version for Local Environment
def load_index_definition_local(index_definition_path):
    try:
        with open(index_definition_path, 'r') as file:
            index_definition = json.load(file)
        return index_definition
    except Exception as e:
        raise ValueError(f"Error loading index definition from {index_definition_path}: {str(e)}")

# Usage
# Uncomment the appropriate line based on your environment
# index_definition = load_index_definition_colab()
index_definition = load_index_definition_local('claude_index.json')

Creating or Updating Search Indexes

With the index definition loaded, the next step is to create or update the Vector Search Index in Couchbase. This step is crucial because it optimizes our database for vector similarity search operations, allowing us to perform searches based on the semantic content of documents rather than just keywords. By creating or updating a Vector Search Index, we enable our search engine to handle complex queries that involve finding semantically similar documents using vector embeddings, which is essential for a robust semantic search engine.

try:
    scope_index_manager = cluster.bucket(CB_BUCKET_NAME).scope(SCOPE_NAME).search_indexes()
    search_index_name = index_definition["name"]

    # Create SearchIndex object from JSON definition
    search_index = SearchIndex.from_json(index_definition)

    # Get UUID from existing index if it exists (required for scope-level upsert)
    try:
        existing_index = scope_index_manager.get_index(search_index_name)
        search_index.uuid = existing_index.uuid
        logging.info(f"Found existing search index '{search_index_name}', updating...")
    except Exception:
        logging.info(f"Search index '{search_index_name}' does not exist, creating...")

    # Upsert the index (create if not exists, update if exists)
    try:
        scope_index_manager.upsert_index(search_index)
        logging.info(f"Search index '{search_index_name}' upserted successfully.")
    except Exception as e:
        raise RuntimeError(f"Failed to upsert search index '{search_index_name}': {str(e)}")

except ServiceUnavailableException:
    raise RuntimeError("Search service is not available. Please ensure the Search service is enabled in your Couchbase cluster.")
except InternalServerFailureException as e:
    logging.error(f"Internal server error: {str(e)}")
    raise
2026-02-19 11:02:32,693 - INFO - Search index 'vector_search_claude' does not exist, creating...
2026-02-19 11:02:32,718 - INFO - Search index 'vector_search_claude' upserted successfully.

Creating OpenAI Embeddings

Embeddings are at the heart of semantic search. They are numerical representations of text that capture the semantic meaning of the words and phrases. Unlike traditional keyword-based search, which looks for exact matches, embeddings allow our search engine to understand the context and nuances of language, enabling it to retrieve documents that are semantically similar to the query, even if they don't contain the exact keywords. By creating embeddings using OpenAI, we equip our search engine with the ability to understand and process natural language in a way that's much closer to how humans understand language. This step transforms our raw text data into a format that the search engine can use to find and rank relevant documents.

try:
    embeddings = OpenAIEmbeddings(openai_api_key=OPENAI_API_KEY, model='text-embedding-3-small')
    logging.info("Successfully created OpenAIEmbeddings")
except Exception as e:
    raise ValueError(f"Error creating OpenAIEmbeddings: {str(e)}")
2026-02-19 11:02:32,896 - INFO - Successfully created OpenAIEmbeddings

Setting Up the Couchbase Vector Store

A vector store is where we'll keep our embeddings. The vector store is specifically designed to handle embeddings and perform similarity searches. When a user inputs a query, the search engine converts the query into an embedding and compares it against the embeddings stored in the vector store. This allows the engine to find documents that are semantically similar to the query, even if they don't contain the exact same words. By setting up the vector store in Couchbase, we create a powerful tool that enables our search engine to understand and retrieve information based on the meaning and context of the query, rather than just the specific words used.

try:
    vector_store = CouchbaseSearchVectorStore(
        cluster=cluster,
        bucket_name=CB_BUCKET_NAME,
        scope_name=SCOPE_NAME,
        collection_name=COLLECTION_NAME,
        embedding=embeddings,
        index_name=INDEX_NAME,
    )
    logging.info("Successfully created vector store")
except Exception as e:
    raise ValueError(f"Failed to create vector store: {str(e)}")
2026-02-19 11:02:32,906 - INFO - Successfully created vector store

Load the BBC News Dataset

To build a search engine, we need data to search through. We use the BBC News dataset from RealTimeData, which provides real-world news articles. This dataset contains news articles from BBC covering various topics and time periods. Loading the dataset is a crucial step because it provides the raw material that our search engine will work with. The quality and diversity of the news articles make it an excellent choice for testing and refining our search engine, ensuring it can handle real-world news content effectively.

The BBC News dataset allows us to work with authentic news articles, enabling us to build and test a search engine that can effectively process and retrieve relevant news content. The dataset is loaded using the Hugging Face datasets library, specifically accessing the "RealTimeData/bbc_news_alltime" dataset with the "2024-12" version.

try:
    news_dataset = load_dataset(
        "RealTimeData/bbc_news_alltime", "2024-12", split="train"
    )
    print(f"Loaded the BBC News dataset with {len(news_dataset)} rows")
    logging.info(f"Successfully loaded the BBC News dataset with {len(news_dataset)} rows.")
except Exception as e:
    raise ValueError(f"Error loading the BBC News dataset: {str(e)}")
2026-02-19 11:02:36,901 - INFO - Successfully loaded the BBC News dataset with 2687 rows.


Loaded the BBC News dataset with 2687 rows

Cleaning up the Data

We will use the content of the news articles for our RAG system.

The dataset contains a few duplicate records. We are removing them to avoid duplicate results in the retrieval stage of our RAG system.

news_articles = news_dataset["content"]
unique_articles = set()
for article in news_articles:
    if article:
        unique_articles.add(article)
unique_news_articles = list(unique_articles)
print(f"We have {len(unique_news_articles)} unique articles in our database.")
We have 1749 unique articles in our database.

Saving Data to the Vector Store

To efficiently handle the large number of articles, we process them in batches of articles at a time. This batch processing approach helps manage memory usage and provides better control over the ingestion process.

We first filter out any articles that exceed 50,000 characters to avoid potential issues with token limits. Then, using the vector store's add_texts method, we add the filtered articles to our vector database. The batch_size parameter controls how many articles are processed in each iteration.

This approach offers several benefits:

  1. Memory Efficiency: Processing in smaller batches prevents memory overload
  2. Progress Tracking: Easier to monitor and track the ingestion progress
  3. Resource Management: Better control over CPU and network resource utilization

We use a conservative batch size of 100 to ensure reliable operation. The optimal batch size depends on many factors including:

  • Document sizes being inserted
  • Available system resources
  • Network conditions
  • Concurrent workload

Consider measuring performance with your specific workload before adjusting.

batch_size = 100

# Automatic Batch Processing
articles = [article for article in unique_news_articles if article and len(article) <= 50000]

try:
    vector_store.add_texts(
        texts=articles,
        batch_size=batch_size
    )
    logging.info("Document ingestion completed successfully.")
except Exception as e:
    raise ValueError(f"Failed to save documents to vector store: {str(e)}")
2026-02-19 11:03:09,187 - INFO - Document ingestion completed successfully.

Setting Up a Couchbase Cache

To further optimize our system, we set up a Couchbase-based cache. A cache is a temporary storage layer that holds data that is frequently accessed, speeding up operations by reducing the need to repeatedly retrieve the same information from the database. In our setup, the cache will help us accelerate repetitive tasks, such as looking up similar documents. By implementing a cache, we enhance the overall performance of our search engine, ensuring that it can handle high query volumes and deliver results quickly.

Caching is particularly valuable in scenarios where users may submit similar queries multiple times or where certain pieces of information are frequently requested. By storing these in a cache, we can significantly reduce the time it takes to respond to these queries, improving the user experience.

try:
    cache = CouchbaseCache(
        cluster=cluster,
        bucket_name=CB_BUCKET_NAME,
        scope_name=SCOPE_NAME,
        collection_name=CACHE_COLLECTION,
    )
    logging.info("Successfully created cache")
    set_llm_cache(cache)
except Exception as e:
    raise ValueError(f"Failed to create cache: {str(e)}")
2026-02-19 11:03:09,197 - INFO - Successfully created cache

Using the Claude Sonnet 4.6 Language Model (LLM)

Language models are AI systems that are trained to understand and generate human language. We'll be using the Claude Sonnet 4.6 language model to process user queries and generate meaningful responses. This model is a key component of our semantic search engine, allowing it to go beyond simple keyword matching and truly understand the intent behind a query. By creating this language model, we equip our search engine with the ability to interpret complex queries, understand the nuances of language, and provide more accurate and contextually relevant responses.

The language model's ability to understand context and generate coherent responses is what makes our search engine truly intelligent. It can not only find the right information but also present it in a way that is useful and understandable to the user.

try:
    llm = ChatAnthropic(temperature=0.1, anthropic_api_key=ANTHROPIC_API_KEY, model_name='claude-sonnet-4-6') 
    logging.info("Successfully created ChatAnthropic")
except Exception as e:
    logging.error(f"Error creating ChatAnthropic: {str(e)}. Please check your API key and network connection.")
    raise
2026-02-19 11:03:09,201 - INFO - Successfully created ChatAnthropic

Perform Semantic Search

Semantic search in Couchbase involves converting queries and documents into vector representations using an embeddings model. These vectors capture the semantic meaning of the text and are stored directly in Couchbase. When a query is made, Couchbase performs a similarity search by comparing the query vector against the stored document vectors. The similarity metric used for this comparison is configurable, allowing flexibility in how the relevance of documents is determined.

In the provided code, the search process begins by recording the start time, followed by executing the similarity_search_with_score method of the CouchbaseSearchVectorStore. This method searches Couchbase for the most relevant documents based on the vector similarity to the query. The search results include the document content and a similarity score that reflects how closely each document aligns with the query in the defined semantic space. The time taken to perform this search is then calculated and logged, and the results are displayed, showing the most relevant documents along with their similarity scores. This approach leverages Couchbase as both a storage and retrieval engine for vector data, enabling efficient and scalable semantic searches. The integration of vector storage and search capabilities within Couchbase allows for sophisticated semantic search operations without relying on external services for vector storage or comparison.

query = "What happened with the map shown during the 2026 FIFA World Cup draw regarding Ukraine and Crimea? What was the controversy?"

try:
    # Perform the semantic search
    start_time = time.time()
    search_results = vector_store.similarity_search_with_score(query, k=10)
    search_elapsed_time = time.time() - start_time

    logging.info(f"Semantic search completed in {search_elapsed_time:.2f} seconds")

    # Display search results
    print(f"\nSemantic Search Results (completed in {search_elapsed_time:.2f} seconds):")
    print("-" * 80)  # Add separator line
    for doc, score in search_results:
        print(f"Score: {score:.4f}, Text: {doc.page_content}")
        print("-" * 80)  # Add separator between results

except CouchbaseException as e:
    raise RuntimeError(f"Error performing semantic search: {str(e)}")
except Exception as e:
    raise RuntimeError(f"Unexpected error: {str(e)}")
2026-02-19 11:03:09,454 - INFO - Semantic search completed in 0.25 seconds



Semantic Search Results (completed in 0.25 seconds):
--------------------------------------------------------------------------------
Score: 0.4302, Text: Defending champions Manchester City will face Juventus in the group stage of the Fifa Club World Cup next summer, while Chelsea meet Brazilian side Flamengo. Pep Guardiola's City, who beat Brazilian side Fluminense to win the tournament for the first time in 2023, begin their title defence against Morocco's Wydad and also play Al Ain of the United Arab Emirates in Group G. Chelsea, winners of the 2021 final, were also drawn alongside Mexico's Club Leon and Tunisian side Esperance Sportive de Tunisie in Group D. The revamped Fifa Club World Cup, which has been expanded to 32 teams, will take place in the United States between 15 June and 13 July next year.

A complex and lengthy draw ceremony was held across two separate Miami locations and lasted more than 90 minutes, during which a new Club World Cup trophy was revealed. There was also a video message from incoming US president Donald Trump, whose daughter Ivanka drew the first team. Lionel Messi's Inter Miami will take on Egyptian side Al Ahly at the Hard Rock Stadium in the opening match, staged in Miami. Elsewhere, Paris St-Germain were drawn against Atletico Madrid in Group B, while Bayern Munich meet Benfica in another all-European group-stage match-up. Teams will play each other once in the group phase and the top two will progress to the knockout stage.

This video can not be played To play this video you need to enable JavaScript in your browser. What is the Club World Cup?

Teams from each of the six international football confederations will be represented at next summer's tournament, including 12 European clubs - the highest quota of any confederation. The European places were decided by clubs' Champions League performances over the past four seasons, with recent winners Chelsea, Manchester City and Real Madrid guaranteed places. Al Ain, the most successful club in the UAE with 14 league titles, are owned by the country's president Sheikh Mohamed bin Zayed Al Nahyan - the older brother of City owner Sheikh Mansour. Real, who lifted the Fifa Club World Cup trophy for a record-extending fifth time in 2022, will open up against Saudi Pro League champions Al-Hilal, who currently have Neymar in their ranks. One place was reserved for a club from the host nation, which Fifa controversially awarded to Inter Miami, who will contest the tournament curtain-raiser. Messi's side were winners of the regular-season MLS Supporters' Shield but beaten in the MLS play-offs, meaning they are not this season's champions.
• None How does the new Club World Cup work & why is it so controversial?

Matches will be played across 12 venues in the US which, alongside Canada and Mexico, also host the 2026 World Cup. Fifa is facing legal action from player unions and leagues about the scheduling of the event, which begins two weeks after the Champions League final at the end of the 2024-25 European calendar and ends five weeks before the first Premier League match of the 2025-2026 season. But football's world governing body believes the dates allow sufficient rest time before the start of the domestic campaigns. The Club World Cup will now take place once every four years, when it was previously held annually and involved just seven teams. Streaming platform DAZN has secured exclusive rights to broadcast next summer's tournament, during which 63 matches will take place over 29 days.
--------------------------------------------------------------------------------
Score: 0.4207, Text: After Fifa awards Saudi Arabia the hosting rights for the men's 2034 World Cup, BBC analysis editor Ros Atkins looks at how we got here and the controversies surrounding the decision.
--------------------------------------------------------------------------------
Score: 0.4123, Text: FA still to decide on endorsing Saudi World Cup bid

The King Abdullah Sports City Stadium in Jeddah would be refurbished before the tournament

The Football Association is still to decide whether it will officially endorse Saudi Arabia's 2034 men's World Cup bid at next week's virtual Fifa Congress. Insiders have told the BBC that discussions involving the FA's board remain ongoing. It has been suggested that rather than a traditional vote, ratification might instead be confirmed by acclamation - with federations in favour asked to show their support by applauding. However, Fifa is yet to confirm this, and FA officials have sought clarification on the process. On Friday, the DFB - Germany's national football association - announced it would vote in favour of both the Saudi Arabia bid and a multi-nation bid for the 2030 event. The BBC has learned that FA officials are mindful of concerns over Saudi Arabia's human rights record. However, some are also wary of suggestions of hypocrisy if the body declines to endorse the tournament, but then participates in it. Saudi Arabia is the sole bidder for the 2034 event, while the 2030 World Cup is set to be awarded to unopposed co-hosts Spain, Morocco and Portugal, with early matches also being played in Uruguay, Argentina and Paraguay. The ratification process has been combined so the 2030 and 2034 hosts will be decided jointly. It would appear, therefore, that if federations oppose one bid, they would have to support neither, with no separate acclamation for each of the potential hosts.

In recent years, Saudi Arabia has hosted many major sports events, including Formula One, tennis, boxing and golf. Yet the kingdom's human rights record, restrictions on women's rights and the criminalisation of the LGBTQ+ community has prompted controversy over its ambition to host the men's World Cup in 10 years time. Last week, Fifa released its evaluation report for Saudi Arabia's bid, awarding it an average score of 4.2 out of 5 - the highest ever - with a conclusion that the tournament posed a 'medium' human rights risk. The assessment sparked condemnation from human rights groups.

This video can not be played To play this video you need to enable JavaScript in your browser. Dec 2023: Saudi sports minister tells BBC sports editor Dan Roan that 'all are welcome'

The DFB held a committee meeting on Friday to decide its stance, and unanimous approval was given, external to support the 2030 and 2034 bids. Its president, Bernd Neuendor, explained: "We did not make the decision lightly and carefully examined the application for the 2034 World Cup. "There was an exchange with many interest groups and experts, including human rights organisations and fans, on the basis of which a well-founded decision was made. "We take the criticism of the applicant country seriously and will continue to engage in dialogue. Our goal is to work together with Fifa to improve the situation in the coming years." In 2022, England and Germany were among a group of European teams that abandoned plans to promote diversity and inclusion by wearing 'OneLove' armbands at the Qatar World Cup after Fifa threatened sporting sanctions. The FA had spoken out about human rights in the country.

It is a sign of the complexity of this decision that the FA's hierarchy are still to come down one way or another, with just five days to go. While Saudi Arabia has become a fairly regular host of top-level sport, this would be another level entirely. The German FA's statement essentially acknowledges it will be criticised, and makes clear it did not take the decision lightly. It seems likely, therefore, that whatever decision the English FA takes, it will divide opinion. Any support of the event comes amid strong criticism from human rights and environmental campaigners, and just six weeks after more than one hundred professional women's footballers wrote to Fifa urging it to drop the Saudi oil giant Aramco as a sponsor. They called such a deal a "punch in the stomach" to the sport, so awarding the biggest football tournament in the world to the kingdom would likely provoke similar if not stronger criticism. However, there is a school of thought that sport can be a force for positive change, and that putting a spotlight on Saudi Arabia, if it was to host the World Cup, could help accelerate and enhance reforms.
--------------------------------------------------------------------------------
Score: 0.4095, Text: Uefa 'not worried' at Euro 2025 clash with Club World Cup

England won Euro 2022 on home soil beating Germany in the final at Wembley

Uefa is "not worried" that some Women's Euro 2025 matches will clash with the Fifa men's Club World Cup, says managing director Nadine Kessler. Fifa's new expanded men's tournament takes place in the United States from 15 June to 13 July and involves 32 teams, including Chelsea and Manchester City. Switzerland is hosting Euro 2025 from 2-27 July, meaning five or six matches are likely to be played at the same time as some in the men's competition. "Overall, I'm not really worried. We're talking about two tournaments, in two different countries, with two different timezones," said Kessler.

"There are also broadcasting agreements in place that differs from ours. Again, I'm not worried. "As much as we always try to get full exclusivity for our women's football tournaments, in the times we live in, with our men's football tournaments having so many in a year, to get that privilege of full exclusivity is not easy any more. "We must also stick to our plan because I think it's important we get a professional, respected international calendar in place for women's football. We have to co-exist." In May, Fifa rejected claims that Fifpro and the World Leagues Association were not consulted over its plans for the Club World Cup. BBC Sport asked Fifa to justify the scheduling clash with Euro 2025 this month. A Fifa spokesperson told BBC Sport: "The international match calendar for 2025-2030 was approved by the Fifa Council in 2023, which is made up of members from each of the six confederations, including Uefa. "While Fifa accepts that both the men's and women's international match calendars are constrained by obvious limitations, this was deemed to be the most balanced solution."

Uefa is aiming to sell out all matches at Euro 2025 and make it the most watched women's European football tournament. It has a target for a total attendance of more than 700,000. A portion of tickets went on sale on 1 October and over 200,000 have been sold. St Jakob Park in Basel will host the final with a capacity of 34,050. Uefa's events chief executive Martin Kallen said the tournament is getting "bigger and bigger" and this may be the last chance for such a small nation to host the Women's Euros. "It's already at the edge for Switzerland to be able to do this Euros. I think they waited for the right moment to ask for it," said Kallen. "They got it because in the future I think the stadiums and infrastructure in Switzerland is too small." The draw takes place on Monday, 16 December, when defending champions England and tournament debutants Wales will find out their group-stage opponents. Among Uefa's key objectives is an aim to meet "men's Euros standards" in terms of team facilities, football technology and analysis. Video assistant referee, goalline technology and semi-automated offsides will all be included. Artificial pitches in Bern and Thun will be overlaid with natural grass in June for the duration of the tournament in order to ensure conditions are the same across all venues. These pitches will be retained until September.
--------------------------------------------------------------------------------
Score: 0.3997, Text: The King Abdullah Sports City Stadium is one of 15 stadiums in line to host games in 2034

The Football Association says it supported Saudi Arabia's bid to host the 2034 World Cup after being assured all fans would be safe and welcome. As expected, the governing body for English football backed the Saudi bid and a multi-nation hosting of the 2030 World Cup at Wednesday afternoon's online Fifa Congress, where the tournament hosts were officially confirmed. The FA released a statement after the meeting, which said: "Our focus is on ensuring that all our fans can attend and enjoy tournaments. "The FA board met the Saudi Arabian Football Federation last month to discuss their bid in more detail. "We asked them to commit to ensuring all fans would be safe and welcome in Saudi Arabia in 2034 - including LGBTQ+ fans. They assured us that they are fully committed to providing a safe and welcome environment for all fans." Some senior FA officials are known to have been wary of accusations of hypocrisy if it were not to support Saudi Arabia but then wants England to participate. The FA will also be mindful of having caved in to Fifa's threats of sporting sanctions at the Qatar World Cup, when it and some other associations abandoned plans for players to wear 'OneLove' armbands intended as an anti-discrimination protest. But with a potential joint bid for the 2031 Women's World Cup, the British football federations may have been keen to avoid a rift with Fifa. And the FA will also have been aware of Saudi Arabia's importance to the UK Government as a key ally in the Middle East, with Prime Minister Sir Keir Starmer visiting the country's Crown Prince this week in a bid to strengthen economic ties between the two countries. Last year, Jake Daniels, the UK's only openly gay active male professional footballer, told the BBC he "wouldn't feel safe" at a Saudi World Cup. When the country's sports minister Prince Abdulaziz bin Turki Al Faisal was asked by BBC Sport last year what he would say to female and gay fans worrying whether they would be safe to attend, he said that "everyone is welcome". Saudi Arabia was the sole bidder for the 2034 event, while the 2030 World Cup was awarded to unopposed co-hosts Spain, Morocco and Portugal, with early matches also being played in Uruguay, Argentina and Paraguay. Rather than a traditional vote, the ratification process was confirmed by acclamation - with federations in favour asked to show their support by applauding for each bid in turn.

This video can not be played To play this video you need to enable JavaScript in your browser.

Norway's football federation abstained from the vote, arguing the bidding process "undermines Fifa's own reforms for good governance". On Friday, the DFB - Germany's national football association - announced it would vote in favour of both bids. "We did not make the decision lightly and carefully examined the application for the 2034 World Cup," DFB president Bernd Neuendorf said. "We take the criticism of the applicant country seriously and will continue to engage in dialogue. Our goal is to work together with Fifa to improve the situation in the coming years."
--------------------------------------------------------------------------------
Score: 0.3878, Text: BBC and ITV agree World Cup deal for 2026 and 2030

Argentina, led by Lionel Messi, won the 2022 men's World Cup in Qatar

BBC Sport has agreed a deal to share live coverage of the men's Fifa World Cup in 2026 and 2030 with ITV and will broadcast the tournament across TV, audio and digital platforms. The 2026 World Cup in the United States, Canada and Mexico will be the biggest yet, with 48 teams playing 104 matches over 39 days - beginning in Mexico City on 11 June and ending in East Rutherford, New Jersey on 19 July. The two broadcasters will share the rights equally, splitting matches between them, including a shared final, ensuring continued free-to-air coverage of the Fifa World Cup. Alongside live TV coverage and highlights across the BBC TV channels and iPlayer, live audio commentary will be broadcast on BBC Radio 5 Live and 5 Sports Extra. Fans will be able to listen to 5 Live coverage on BBC Sounds and follow all the action on the BBC Sport website and app.

ITV will deliver free-to-air coverage of live fixtures across ITV1, ITV4 and ITVX, plus highlights and exclusive content on ITV Sport social accounts. The 2030 tournament will be held across three continents and six countries. Spain, Portugal, and Morocco are co-hosting, but to mark 100 years since Uruguay staged the first World Cup there will be three matches played in South America - Argentina, Paraguay and Uruguay hosting one each - to open the tournament. Alex Kay-Jelski, BBC director of sport, said: "Securing these iconic tournaments means BBC Sport is once again bringing people together for the biggest sporting moments. "The World Cup is magical, something the whole planet stops to experience, and we can't wait to show it to audiences across all platforms." BBC Sport's rights portfolio also includes the Olympic Games, the FA Cup, men's Euro 2028 and women's Euro 2025, the Women's Super League, the Wimbledon Championships, the Women's Rugby World Cup 2025 and Match of the Day.
--------------------------------------------------------------------------------
Score: 0.3556, Text: Five killed in strike on Russia's Kursk after deadly missile attack on Kyiv

Ukrainian officials said at least one person was killed and nine others were injured in the attack on Kyiv

Russia says five people have been killed in a Ukrainian strike in the western Kursk region. Ukrainian officials reported earlier that Moscow had launched a fresh missile attack on Kyiv, damaging a building hosting several embassies. In Russia, the acting governor of the Kursk region said in addition to those killed, nine had been taken to hospital following the attack on the town of Rylsk. Alexander Khinshtein said a cultural centre, a fitness complex, a school and homes had been damaged in the strike which took place at 15:30 local time (12:30 GMT) on Friday.

Russian officials earlier reported six killed, including a child, in Rylsk, about 25km (16 miles) from the Ukrainian border. But in an audio message on Telegram on Saturday morning, Khinshtein gave the latest update, saying there were five fatalities. "There were no children among those [killed]," he said. Ukrainian troops still hold parts of the Kursk region after launching a surprise cross-border offensive in early August. Ukraine's foreign ministry said Russia's strike on Kyiv had affected the diplomatic missions of Albania, Argentina, North Macedonia, Palestine, Portugal and Montenegro. It is unclear whether the building housing them was directly targeted in the Ukrainian capital. At least one person was killed and nine others were injured in the strike which damaged a number of buildings in the city, Ukraine's military said. It is not thought that any of the embassy diplomats were injured. In a verified video filmed in the Pecherskyi District, Kyiv's second oldest Roman Catholic church, St Nicholas Cathedral, is shown with windows shattered following a nearby blast. Ukraine's military said Russia had launched 65 drones and missiles across the country overnight, with most shot down. One man in Kyiv, who said he was the owner of a restaurant that suffered extensive damage following the attack, was filmed cursing the Russians as "beasts" as he surveyed the charred shell of a building in front of him. The video was widely shared on social media.

Oksana, another resident, sent the BBC photos of her destroyed apartment, with the windows blown in and glass and brickwork strewn across the floors. "I don't understand how I survived," she said. "My balcony flew away, half my walls are gone. My neighbour is in such shock she can't even speak. I have no words for the people who did this." A local journalist at the scene told the BBC that one of the buildings nearby had been used by the Ukrainian Security Service, the SBU, and was likely to have been the target of the strikes, although much of the damage seen by the BBC had affected residential buildings. In a statement confirming the attack, the Russian defence ministry said missiles had been launched at an SBU "command post" in response to a strike on a chemical plant in Russia's Rostov Region two days ago. But there is also speculation in Kyiv that Friday's attack could be linked to the killing of a Russian general, Lt-Gen Igor Kirillov, in Moscow on Tuesday. Friday's attack come one day after Vladimir Putin's end-of-year press conference and phone-in show, in which he threatened to launch more ballistic missiles at the Ukrainian capital. There is concern in Ukraine that Russia could use a so-called Oreshnik intermediate-range ballistic missile to hit Kyiv. Moscow test-fired the missile on the central city of Dnipro earlier this month. Earlier on Friday morning, the Ukrainian authorities issued an air alert linked to the possible launch of an Oreshnik missile, and urged people in Kyiv to urgently seek shelter. It turned out to be a false alarm.
--------------------------------------------------------------------------------
Score: 0.3535, Text: Watch five iconic England goals from Euro 2022 including Georgia Stanway's screamer against Spain and Alessia Russo's cheeky backheel versus Sweden.

Watch Women’s Euro 2025 draw at 16:55 GMT on Monday, 16 December, on BBC Two and the BBC Sport website & app

Available to UK users only.
--------------------------------------------------------------------------------
Score: 0.3494, Text: I should have invaded Ukraine earlier, Putin tells Russians in TV marathon

Russian President Vladimir Putin has said Russia should have launched a full-scale invasion of Ukraine earlier and been better prepared for the war. In his end-of-year press conference on Thursday, Putin said, with hindsight, there should have been "systemic preparation" for the 2022 invasion, which he refers to as a "special military operation". Russia seized Crimea from Ukraine in 2014 and pro-Russian forces began a conflict in eastern Ukraine, but it was eight years later that Putin tried to seize Kyiv. During his four-hour long appearance, Putin also talked about Syria's deposed leader, Russia's more aggressive nuclear doctrine as well as domestic issues, like the price of butter.

Billed as "Results of the Year with Vladimir Putin", the event was broadcast live across the main state TV channels on Thursday. Putin appeared in front of a large blue screen emblazoned with a map of the Russian Federation, complete with annexed parts of Ukraine. He took questions from members of the public, foreign journalists and pensioners - but it was a highly choreographed and tightly controlled affair. When asked by the BBC's Russia editor Steve Rosenberg whether he felt the country was in a better state than where his predecessor, Boris Yeltsin, had left it 25 years ago, Putin said Russia had regained its "sovereignty". "With everything that was happening to Russia before that, we were heading towards a complete, total loss of our sovereignty."

This video can not be played To play this video you need to enable JavaScript in your browser.

Asked about the fall of the Assad regime in Syria, Putin insisted it was not a defeat for the Kremlin - which supported President Bashar al-Assad militarily for years - but he admitted the situation was "complicated". He said he had not yet spoken to ousted Syrian leader, who fled to Moscow as rebel forces closed in on Damascus earlier this month, but planned to do so soon. He added that Russia was in talks with Syria's new rulers to retain two strategically important military bases on the Mediterranean coast and that Moscow would consider using them for humanitarian purposes.

Russia holds some airbases in Syria, including this one at Hmeimim military base in Latakia province

On US President-elect Donald Trump, Putin said the pair had not spoken in four years, but he was ready to meet him "if he wants it". When put to him he was in a weak position compared to Trump, who is set to take office in January, Putin quoted American writer Mark Twain: "The rumours of my death are much exaggerated," prompting a smattering of laughs in the conference hall. Moving on to China, Putin said Russia's relations with its eastern neighbour had reached an all-time high and the two countries were coordinating actions on the world stage. "In the last decade, the level and quality of our [Russia-China] relations have reached a point that has never existed throughout our entire history, " he said. A lengthy portion of the session was focused on the war in Ukraine, with Putin saying he was "open to compromises" to end the war - although it was unclear what such compromises could entail. Russian forces are making progress on the frontlines "everyday", he said, describing his troops as "heroes". At one point, he produced a signed flag he said was given to him by Russian marines who were "fighting for the motherland" in the Kursk region, and ushered two observers to hold it behind him for the cameras.

Putin produced a flag he said was given to him by Russian marines fighting in Kursk

He also talked up Russia's construction projects in areas it has seized from Ukraine, claiming the standard of roads in the Ukrainian region of Luhansk had greatly improved since it was seized by Russia-backed forces in 2014. When asked by an audience member if the West had "received the message" on Russia's change to its nuclear doctrine, which Putin pushed through in November, he said "you'll have to ask them." The new nuclear doctrine allows Russia to conduct a nuclear strike on any country, if it is backed by a nuclear power. That means if Ukraine were to launch a large attack on Russia with conventional missiles, drones or aircraft, that could meet the criteria for a nuclear response, as could an attack on Belarus or any critical threat to Russia's sovereignty. Putin also emphasised the capabilities of Russia's new intermediate-range ballistic missile, Oreshnik, which was used in a strike on Ukraine in November. In order to test its power, he suggested Russia should fire the Oreshnik towards Ukraine, and Ukrainian air defence - using US-supplied systems - should try to bring it down. As for the name "Oreshnik"? "Honestly," Putin said with a smirk, "No idea. No clue." A dominant theme throughout the event was "Russian sovereignty", with Putin claiming that less reliance on international partners - partly a result of Western sanctions - was one of the key achievements of his invasion of Ukraine. He said the economy was "stable", pointing to higher growth than countries like Germany, but admitted inflation of 9.1% was "alarming". In fact, the economy is overheating and highly reliant on military production - sometimes termed the "military industrial complex". Throughout the address, Putin also answered questions on domestic issues - from telephone scammers to young people's struggles with getting a mortgage.
--------------------------------------------------------------------------------
Score: 0.3490, Text: 43,000 troops killed in war with Russia, Zelensky says

Some 43,000 Ukrainian soldiers have been killed since Russia's full-scale invasion began, Volodymyr Zelensky has said in a rare admission of the extent of the nation's casualties. In a post on social media, the Ukrainian president said 370,000 injuries had been reported, though this figure included soldiers who had been hurt more than once and some of the injuries were said to be minor. He also claimed that 198,000 Russian soldiers had been killed and a further 550,000 wounded. The BBC has not been able to verify either side's figures. While both Kyiv and Moscow have regularly published estimates of the other side's losses, they have been reluctant to detail their own.

The new figure marks a significant increase in Ukrainian deaths since the start of the year. The last time Zelensky gave an update on Ukraine's casualties was in February, when he put deaths at 31,000. The Ukrainian president is thought to have been compelled to make the admission after incoming US President-elect Donald Trump wrote on social media that Ukraine had "ridiculously lost" 400,000 soldiers, while close to 600,000 Russians had been killed or wounded. Trump did not state where these figures were from. The incoming president, who has long made clear he wants to bring an end to the war, said too many lives had been "needlessly wasted". Zelensky's estimates of Russian losses are similar to those provided by senior Western officials, who estimate Russia has suffered around 800,000 casualties, both killed and injured. The UK's defence ministry says Russia suffered 45,680 casualties in November alone - more than during any month since its full-scale invasion began in February 2022. According to the latest UK Defence Intelligence estimates, an average of 1,523 Russian soldiers are being killed and wounded every day. On 28 November, it says, Russia lost more than 2,000 men in a single day, the first time this has happened. Moscow disputes those figures. In a statement, the Kremlin claimed that Ukrainian losses were "many times higher" than Russian ones. Outside of Russia, the consensus is that Russian casualty figures are far higher than Ukraine's due to their "meat grinder" tactics. Recent developments in the war have only added to the number of dead. Russian forces continue to make incremental advances along the eastern front line, capturing and retaking about 2,350 sq km of territory (907 sq miles) in eastern Ukraine and in Russia's western Kursk region since the start of the year. Ukrainian forces maintain control over a small amount of Russian territory which was captured during a surprise offensive into Russia in August. The Russian defence ministry says more than 38,000 Ukrainian soldiers have been killed or wounded in Kursk alone - a number that cannot be verified.

Russia annexed the Crimean peninsula in 2014. Eight years later, it launched a full-scale invasion of Ukraine and has occupied territory in the country's south and east. Zelensky mentioned Ukraine's war dead in a broader post about the prospects for an eventual end to the war. It follows talks in Paris on Saturday with French President Emmanuel Macron and Trump, who has sought to capitalise on views held by around a quarter of Americans that the US is providing too much support to Ukraine. During the campaign, Trump repeatedly said he could end the war between Russia and Ukraine "in a day" - but has yet to specify how he intends to do so. In his post, Zelensky stressed that any peace deal had to be backed by effective international guarantees for his country's security. He said he told Macron and Trump that Kyiv needs an "enduring peace" which Moscow would not "destroy in a few years". Responding to Trump's call for an immediate ceasefire, the Kremlin said it was open to negotiations, but the conditions for a cessation of hostilities had been set by Russian President Vladimir Putin in June. His demands included Ukraine giving up more of its territory and abandoning ambitions to join Nato, which Kyiv has rejected.
--------------------------------------------------------------------------------

Retrieval-Augmented Generation (RAG) with Couchbase and LangChain

Couchbase and LangChain can be seamlessly integrated to create RAG (Retrieval-Augmented Generation) chains, enhancing the process of generating contextually relevant responses. In this setup, Couchbase serves as the vector store, where embeddings of documents are stored. When a query is made, LangChain retrieves the most relevant documents from Couchbase by comparing the query’s embedding with the stored document embeddings. These documents, which provide contextual information, are then passed to a generative language model within LangChain.

The language model, equipped with the context from the retrieved documents, generates a response that is both informed and contextually accurate. This integration allows the RAG chain to leverage Couchbase’s efficient storage and retrieval capabilities, while LangChain handles the generation of responses based on the context provided by the retrieved documents. Together, they create a powerful system that can deliver highly relevant and accurate answers by combining the strengths of both retrieval and generation.

system_template = "You are a helpful assistant that answers questions based on the provided context."
system_message_prompt = SystemMessagePromptTemplate.from_template(system_template)

human_template = "Context: {context}\n\nQuestion: {question}"
human_message_prompt = HumanMessagePromptTemplate.from_template(human_template)

chat_prompt = ChatPromptTemplate.from_messages([
    system_message_prompt,
    human_message_prompt
])

def format_docs(docs):
    return "\n\n".join(doc.page_content for doc in docs)

rag_chain = (
    {"context": lambda x: format_docs(vector_store.similarity_search(x)), "question": RunnablePassthrough()}
    | chat_prompt
    | llm
)
logging.info("Successfully created RAG chain")
2026-02-19 11:03:09,461 - INFO - Successfully created RAG chain
try:
    start_time = time.time()
    rag_response = rag_chain.invoke(query)
    rag_elapsed_time = time.time() - start_time

    print(f"RAG Response: {rag_response.content}")
    print(f"RAG response generated in {rag_elapsed_time:.2f} seconds")
except AuthenticationError as e:
    print(f"Authentication error: {str(e)}")
except InternalServerFailureException as e:
    if "query request rejected" in str(e):
        print("Error: Search request was rejected due to rate limiting. Please try again later.")
    else:
        print(f"Internal server error occurred: {str(e)}")
except Exception as e:
    print(f"Unexpected error occurred: {str(e)}")
RAG Response: The provided context does not contain any information about a map shown during the 2026 FIFA World Cup draw or any controversy regarding Ukraine and Crimea. The context focuses on the FIFA Club World Cup draw, the 2034 World Cup bid by Saudi Arabia, and the Women's Euro 2025, but does not mention this topic at all. Therefore, I cannot answer this question based on the available information.
RAG response generated in 3.28 seconds

Using Couchbase as a caching mechanism

Couchbase can be effectively used as a caching mechanism for RAG (Retrieval-Augmented Generation) responses by storing and retrieving precomputed results for specific queries. This approach enhances the system's efficiency and speed, particularly when dealing with repeated or similar queries. When a query is first processed, the RAG chain retrieves relevant documents, generates a response using the language model, and then stores this response in Couchbase, with the query serving as the key.

For subsequent requests with the same query, the system checks Couchbase first. If a cached response is found, it is retrieved directly from Couchbase, bypassing the need to re-run the entire RAG process. This significantly reduces response time because the computationally expensive steps of document retrieval and response generation are skipped. Couchbase's role in this setup is to provide a fast and scalable storage solution for caching these responses, ensuring that frequently asked queries can be answered more quickly and efficiently.

try:
    queries = [
        "What happened when Apple's AI feature generated a false BBC headline about a murder case in New York?",
        "What happened with the map shown during the 2026 FIFA World Cup draw regarding Ukraine and Crimea? What was the controversy?", # Repeated query
        "What happened when Apple's AI feature generated a false BBC headline about a murder case in New York?", # Repeated query
    ]

    for i, query in enumerate(queries, 1):
        print(f"\nQuery {i}: {query}")
        start_time = time.time()

        response = rag_chain.invoke(query)
        elapsed_time = time.time() - start_time
        print(f"Response: {response.content}")
        print(f"Time taken: {elapsed_time:.2f} seconds")
except AuthenticationError as e:
    print(f"Authentication error: {str(e)}")
except InternalServerFailureException as e:
    if "query request rejected" in str(e):
        print("Error: Search request was rejected due to rate limiting. Please try again later.")
    else:
        print(f"Internal server error occurred: {str(e)}")
except Exception as e:
    print(f"Unexpected error occurred: {str(e)}")
Query 1: What happened when Apple's AI feature generated a false BBC headline about a murder case in New York?
Response: ## Apple Intelligence Generates False BBC Headline

When Apple's AI feature, **Apple Intelligence**, generated a false BBC headline, it falsely made it appear that **BBC News had published an article claiming Luigi Mangione — the man arrested for the murder of healthcare insurance CEO Brian Thompson in New York — had shot himself**, when in fact he had not.

### Key Consequences and Responses:

- **The BBC complained directly to Apple**, contacting them "to raise this concern and fix the problem"
- The BBC emphasized that as *"the most trusted news media in the world"*, it is essential that audiences can trust any information published in their name
- **Professor Petros Iosifidis** of City University London called the mistake *"embarrassing"*, criticizing Apple for releasing a *"demonstrably half-baked product"* and warning of *"a real danger of spreading disinformation"*

### Context:
This was not an isolated incident, as a similar issue occurred with the **New York Times**, where an AI summary falsely suggested *"Netanyahu arrested"* rather than accurately conveying that an **arrest warrant** had been issued by the International Criminal Court.

Users can report concerns about notification summaries on their devices, though Apple has not disclosed how many reports it has received.
Time taken: 10.04 seconds

Query 2: What happened with the map shown during the 2026 FIFA World Cup draw regarding Ukraine and Crimea? What was the controversy?
Response: The provided context does not contain any information about a map shown during the 2026 FIFA World Cup draw or any controversy regarding Ukraine and Crimea. The context focuses on the FIFA Club World Cup draw, the 2034 World Cup bid by Saudi Arabia, and the Women's Euro 2025, but does not mention this topic at all. Therefore, I cannot answer this question based on the available information.
Time taken: 0.24 seconds

Query 3: What happened when Apple's AI feature generated a false BBC headline about a murder case in New York?
Response: ## Apple Intelligence Generates False BBC Headline

When Apple's AI feature, **Apple Intelligence**, generated a false BBC headline, it falsely made it appear that **BBC News had published an article claiming Luigi Mangione — the man arrested for the murder of healthcare insurance CEO Brian Thompson in New York — had shot himself**, when in fact he had not.

### Key Consequences and Responses:

- **The BBC complained directly to Apple**, contacting them "to raise this concern and fix the problem"
- The BBC emphasized that as *"the most trusted news media in the world"*, it is essential that audiences can trust any information published in their name
- **Professor Petros Iosifidis** of City University London called the mistake *"embarrassing"*, criticizing Apple for releasing a *"demonstrably half-baked product"* and warning of *"a real danger of spreading disinformation"*

### Context:
This was not an isolated incident, as a similar issue occurred with the **New York Times**, where an AI summary falsely suggested *"Netanyahu arrested"* rather than accurately conveying that an **arrest warrant** had been issued by the International Criminal Court.

Users can report concerns about notification summaries on their devices, though Apple has not disclosed how many reports it has received.
Time taken: 0.20 seconds

Conclusion

You've built a semantic search engine using Couchbase Search Vector Index with OpenAI embeddings, Anthropic's Claude, and LangChain. For the Hyperscale/Composite Vector Index alternative, see the query_based tutorial.


This tutorial is part of a Couchbase Learning Path:
Contents
Couchbase home page link

3155 Olsen Drive
Suite 150, San Jose
CA 95117, United States

  • Company
  • About
  • Leadership
  • News & Press
  • Careers
  • Events
  • Legal
  • Contact us
  • Support
  • Developer Portal
  • Documentation
  • Forums
  • Professional Services
  • Support Login
  • Support Policy
  • Training
  • Quicklinks
  • Blog
  • Downloads
  • Online Training
  • Resources
  • Why NoSQL
  • Pricing
  • Follow us
  • Social Media Link for TwitterTwitter
  • Social Media Link for LinkedInLinkedIn
  • Social Media Link for YoutubeYouTube
  • Social Media Link for FacebookFacebook
  • Social Media Link for GitHubGitHub
  • Social Media Link for Stack OverflowStack Overflow
  • Social Media Link for DiscordDiscord

© 2026 Couchbase, Inc. Couchbase and the Couchbase logo are registered trademarks of Couchbase, Inc. All third party trademarks (including logos and icons) referenced by Couchbase, Inc. remain the property of their respective owners.

  • Terms of Use
  • Privacy Policy
  • Cookie Policy
  • Support Policy
  • Do Not Sell My Personal Information
  • Marketing Preference Center
  • Trust Center