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 Hyperscale and Composite Vector Index

  • Learn how to build a semantic search engine using Couchbase, OpenAI embeddings, and Anthropic using Hyperscale and Composite 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 for embeddings, and Anthropic's Claude as the language model. 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 Couchbase Hyperscale and Composite Vector Index from scratch. Alternatively if you want to perform semantic search using the Search 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 the API Keys

  • Get the OpenAI API key from OpenAI.
  • Get the Anthropic API key from Anthropic.

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 know more, please follow the instructions.

Note: To run this tutorial, you will need Capella with Couchbase Server version 8.0 or above as Hyperscale and Composite Vector Index is supported only from version 8.0

Couchbase Capella Configuration

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

  • Create the database credentials to access the 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.

%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.

import getpass
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)
from couchbase.management.buckets import CreateBucketSettings
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 CouchbaseQueryVectorStore
from langchain_couchbase.vectorstores import DistanceStrategy
from langchain_openai import OpenAIEmbeddings
from langchain_couchbase.vectorstores import IndexType
/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.

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.

load_dotenv(override=True)

# 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', 'query-vector-search-testing') or input('Enter your Couchbase bucket name (default: query-vector-search-testing): ') or 'query-vector-search-testing'
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.

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 10:55:30,308 - 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:

  • 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

        # 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 10:55:30,320 - INFO - Bucket 'vector-search-testing' exists.
2026-02-19 10:55:30,324 - INFO - Collection 'claude' does not exist. Creating it...
2026-02-19 10:55:30,367 - INFO - Collection 'claude' created successfully.
2026-02-19 10:55:32,482 - INFO - All documents cleared from the collection.
2026-02-19 10:55:32,483 - INFO - Bucket 'vector-search-testing' exists.
2026-02-19 10:55:32,484 - INFO - Collection 'cache' already exists. Skipping creation.
2026-02-19 10:55:34,497 - INFO - All documents cleared from the collection.





<couchbase.collection.Collection at 0x1256a06e0>

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. We'll use OpenAI's embedding model to generate these vector representations.

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 10:55:34,667 - INFO - Successfully created OpenAIEmbeddings

Setting Up the Couchbase Query Vector Store

A vector store is where we'll keep our embeddings. The query vector store is specifically designed to handle embeddings and perform similarity searches. When a user inputs a query, the query service 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.

try:
    vector_store = CouchbaseQueryVectorStore(
        cluster=cluster,
        bucket_name=CB_BUCKET_NAME,
        scope_name=SCOPE_NAME,
        collection_name=COLLECTION_NAME,
        embedding = embeddings,
        distance_metric=DistanceStrategy.COSINE
    )
    logging.info("Successfully created vector store")
except Exception as e:
    raise ValueError(f"Failed to create vector store: {str(e)}")
2026-02-19 10:55:36,174 - 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. The dataset is loaded using the Hugging Face datasets library.

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)}")
Warning: You are sending unauthenticated requests to the HF Hub. Please set a HF_TOKEN to enable higher rate limits and faster downloads.
2026-02-19 10:55:36,503 - WARNING - Warning: You are sending unauthenticated requests to the HF Hub. Please set a HF_TOKEN to enable higher rate limits and faster downloads.
2026-02-19 10:55:39,452 - 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:00:06,390 - INFO - Retrying request to /embeddings in 0.438330 seconds
2026-02-19 11:01:31,314 - INFO - Document ingestion completed successfully.

Setting Up a Couchbase Cache

To further optimize our system, we set up a Couchbase-based cache for storing frequently accessed results, reducing response times for repeated queries.

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:01:31,323 - INFO - Successfully created cache

Using the Claude Sonnet 4.6 Language Model (LLM)

We use Anthropic's Claude Sonnet 4.6 as the language model for generating responses based on the retrieved context.

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:01:31,327 - 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.

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)
    baseline_time = time.time() - start_time

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

    # Display search results
    print(f"\nSemantic Search Results (completed in {baseline_time:.2f} seconds):")
    print("-" * 80)  # Add separator line
    for doc, score in search_results:
        print(f"Score: {score:.4f}, Text: {doc.page_content[:200]}...")
        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:01:36,946 - INFO - Semantic search completed in 5.62 seconds



Semantic Search Results (completed in 5.62 seconds):
--------------------------------------------------------------------------------
Score: 0.2501, Text: A map shown during the draw for the 2026 Fifa World Cup has been criticised by Ukraine as an "unacceptable error" after it appeared to exclude Crimea as part of the country. The graphic - showing coun...
--------------------------------------------------------------------------------
Score: 0.5697, 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 s...
--------------------------------------------------------------------------------
Score: 0.5793, 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.5878, 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 ...
--------------------------------------------------------------------------------
Score: 0.5905, 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 cl...
--------------------------------------------------------------------------------
Score: 0.6003, 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 ...
--------------------------------------------------------------------------------
Score: 0.6122, 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 Cu...
--------------------------------------------------------------------------------
Score: 0.6444, 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 fiv...
--------------------------------------------------------------------------------
Score: 0.6465, 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 Monda...
--------------------------------------------------------------------------------
Score: 0.6506, 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 bett...
--------------------------------------------------------------------------------

Optimizing Vector Search with Hyperscale and Composite Vector Index

While the above semantic search using similarity_search_with_score works effectively, we can significantly improve query performance by leveraging Couchbase's query-based vector indexing.

Couchbase offers three types of vector indexes, but for Hyperscale and Composite Vector Index based search we focus on two main types:

Hyperscale Vector Indexes

  • Best for pure vector searches - content discovery, recommendations, semantic search
  • High performance with low memory footprint - designed to scale to billions of vectors
  • Optimized for concurrent operations - supports simultaneous searches and inserts
  • Use when: You primarily perform vector-only queries without complex scalar filtering
  • Ideal for: Large-scale semantic search, recommendation systems, content discovery

Composite Vector Indexes

  • Best for filtered vector searches - combines vector search with scalar value filtering
  • Efficient pre-filtering - scalar attributes reduce the vector comparison scope
  • Use when: Your queries combine vector similarity with scalar filters that eliminate large portions of data
  • Ideal for: Compliance-based filtering, user-specific searches, time-bounded queries

Choosing the Right Index Type

  • Start with Hyperscale Vector Index for pure vector searches and large datasets
  • Use Composite Vector Index when scalar filters significantly reduce your search space
  • Consider your dataset size: Hyperscale scales to billions, Composite works well for tens of millions to billions

For more details, see the Couchbase Vector Index documentation.

Understanding Index Configuration (Couchbase 8.0 Feature)

The index_description parameter controls how Couchbase optimizes vector storage and search performance through centroids and quantization:

Format: 'IVF[<centroids>],{PQ|SQ}<settings>'

Centroids (IVF - Inverted File):

  • Controls how the dataset is subdivided for faster searches
  • More centroids = faster search, slower training
  • Fewer centroids = slower search, faster training
  • If omitted (like IVF,SQ8), Couchbase auto-selects based on dataset size

Quantization Options:

  • SQ (Scalar Quantization): SQ4, SQ6, SQ8 (4, 6, or 8 bits per dimension)
  • PQ (Product Quantization): PQx (e.g., PQ32x8)
  • Higher values = better accuracy, larger index size

Common Examples:

  • IVF,SQ8 - Auto centroids, 8-bit scalar quantization (good default)
  • IVF1000,SQ6 - 1000 centroids, 6-bit scalar quantization
  • IVF,PQ32x8 - Auto centroids, 32 subquantizers with 8 bits

For detailed configuration options, see the Quantization & Centroid Settings.

In the code below, we demonstrate creating a Hyperscale index. This method takes an index type (HYPERSCALE or COMPOSITE) and description parameter for optimization settings. Alternatively, Hyperscale and Composite Vector Indexes can be created manually from the Couchbase UI.

vector_store.create_index(index_type=IndexType.HYPERSCALE, index_name="claude_hyperscale_index", index_description="IVF,SQ8")
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)
    optimized_time = time.time() - start_time

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

    # Display search results
    print(f"\nSemantic Search Results (completed in {optimized_time:.2f} seconds):")
    print("-" * 80)  # Add separator line
    for doc, score in search_results:
        print(f"Score: {score:.4f}, Text: {doc.page_content[:200]}...")
        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:01:49,033 - INFO - Semantic search completed in 0.26 seconds



Semantic Search Results (completed in 0.26 seconds):
--------------------------------------------------------------------------------
Score: 0.6444, 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 fiv...
--------------------------------------------------------------------------------
Score: 0.6506, 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 bett...
--------------------------------------------------------------------------------
Score: 0.6509, 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...
--------------------------------------------------------------------------------
Score: 0.6565, Text: Romania's far right presidential frontrunner vows to end Ukraine aid

Calin Georgescu is leading the presidential race in Romania

Calin Georgescu, the fringe nationalist politician leading the presid...
--------------------------------------------------------------------------------
Score: 0.6583, Text: US gives $20bn to Ukraine funded by seized Russian assets

The US has given $20bn (£15bn) to Ukraine, funded by the profits of seized Russian assets. The economic support forms a significant part of a...
--------------------------------------------------------------------------------
Score: 0.6624, Text: Ukraine's war stamps put humour, patriotism and swearing in the post

The head of Ukraine's postal company, Ukrposhta, says they're "breaking the rules" with their war stamps

Rude gestures are rare o...
--------------------------------------------------------------------------------
Score: 0.6683, Text: How meddling blamed on Russia exploited real grievances in Romania

Calin Georgescu said the annulment of the vote was "an abuse and a crime"

"Romania dodged a bullet," is how a former deputy head of...
--------------------------------------------------------------------------------
Score: 0.6707, Text: Emergency workers have been working to restore the energy plants

Russia has launched a huge attack on Ukraine's energy infrastructure, which authorities said was the 12th large-scale attack on energy...
--------------------------------------------------------------------------------
Score: 0.6763, Text: Lt Gen Igor Kirillov, head of the Nuclear, Biological, Chemical Defence Forces (NBC), was killed early on Tuesday

Appearance and reality: there is a constant battle in Moscow between the two. Despite...
--------------------------------------------------------------------------------
Score: 0.6764, Text: Calin Georgescu cast his vote in the first round of voting last weekend

Romania's constitutional court has annulled the result of the first round of voting in the presidential election just days befo...
--------------------------------------------------------------------------------

Alternative: Composite Index Configuration

If your use case requires complex filtering with scalar attributes, you can create a Composite index instead:

from langchain_couchbase.vectorstores import IndexType
vector_store.create_index(
    index_type=IndexType.COMPOSITE,  # Instead of IndexType.HYPERSCALE
    index_name="claude_composite_index",
    index_description="IVF,SQ8"
)

Choose based on your specific use case and query patterns. For this tutorial's news search scenario, either index type would work, but Hyperscale is more efficient for pure semantic search across news articles.

# Performance Analysis Summary
print("=" * 80)
print("VECTOR SEARCH PERFORMANCE OPTIMIZATION SUMMARY")
print("=" * 80)
print(f"Phase 1 - Baseline Search (No Hyperscale):     {baseline_time:.4f} seconds")
print(f"Phase 2 - Hyperscale-Optimized Search:         {optimized_time:.4f} seconds")

print()
print("-" * 80)
print("VECTOR SEARCH OPTIMIZATION IMPACT:")
print("-" * 80)

if optimized_time < baseline_time:
    speedup = baseline_time / optimized_time
    improvement = ((baseline_time - optimized_time) / baseline_time) * 100
    print(f"Hyperscale Index Benefit:      {speedup:.2f}x faster ({improvement:.1f}% improvement)")
else:
    print(f"Note: Hyperscale index did not show improvement in this run.")
    print(f"This may occur if the index is still training or the dataset is small.")
    print(f"Re-run Phase 2 after the index is fully built for accurate results.")

print()
print("Key Insights for Vector Search Performance:")
print("• Hyperscale indexes provide significant performance improvements for vector similarity search")
print("• Performance gains are most dramatic for large datasets and complex semantic queries")
print("• Hyperscale vector index optimization is particularly effective for high-dimensional embeddings")
print("• Combined with proper quantization (SQ8), Hyperscale delivers production-ready performance")
print("• Consider Composite indexes when you need to combine vector search with scalar filtering")
================================================================================
VECTOR SEARCH PERFORMANCE OPTIMIZATION SUMMARY
================================================================================
Phase 1 - Baseline Search (No Hyperscale):     5.6153 seconds
Phase 2 - Hyperscale-Optimized Search:         0.2611 seconds

--------------------------------------------------------------------------------
VECTOR SEARCH OPTIMIZATION IMPACT:
--------------------------------------------------------------------------------
Hyperscale Index Benefit:      21.51x faster (95.4% improvement)

Key Insights for Vector Search Performance:
• Hyperscale indexes provide significant performance improvements for vector similarity search
• Performance gains are most dramatic for large datasets and complex semantic queries
• Hyperscale vector index optimization is particularly effective for high-dimensional embeddings
• Combined with proper quantization (SQ8), Hyperscale delivers production-ready performance
• Consider Composite indexes when you need to combine vector search with scalar filtering

Retrieval-Augmented Generation (RAG) with Couchbase and LangChain

Couchbase and LangChain can be seamlessly integrated to create RAG chains, enhancing the process of generating contextually relevant responses. In this setup, Couchbase serves as the vector store, and LangChain retrieves the most relevant documents and passes them to the language model for response 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:01:49,045 - 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 the 2026 FIFA World Cup draw or any controversy involving a map related to Ukraine and Crimea at such an event. The context focuses on the Russia-Ukraine war, including missile strikes, casualty figures, Putin's press conference, and Romania's presidential election. I cannot answer your question based on the available information.
RAG response generated in 8.25 seconds

Using Couchbase as a caching mechanism

Couchbase can be used as a caching mechanism for RAG responses, storing precomputed results for repeated queries to significantly reduce response times.

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 notification summarization feature (**Apple Intelligence**) generated a false headline, several significant consequences followed:

### The False Claim
The AI falsely made it appear that **BBC News had reported that 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.

### BBC's Response
- The BBC **formally complained to Apple**, contacting them "to raise this concern and fix the problem"
- The BBC emphasized that it is considered **"the most trusted news media in the world"** and stressed the importance of audiences being able to trust information published in their name

### Broader Context
- This was **not an isolated incident** — Apple Intelligence had also misrepresented a New York Times notification to suggest **"Netanyahu arrested"**, when the actual story was about an ICC arrest *warrant*
- Experts criticized Apple, with Professor Petros Iosifidis calling it **"embarrassing"** and describing the product as **"demonstrably half-baked"**, warning of **"a real danger of spreading disinformation"**

The incident highlighted growing concerns about AI summarization technology being deployed before it is sufficiently reliable, particularly when misrepresenting trusted news sources.
Time taken: 11.43 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 the 2026 FIFA World Cup draw or any controversy involving a map related to Ukraine and Crimea at such an event. The context focuses on the Russia-Ukraine war, including missile strikes, casualty figures, Putin's press conference, and Romania's presidential election. I cannot answer your question based on the available information.
Time taken: 0.30 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 notification summarization feature (**Apple Intelligence**) generated a false headline, several significant consequences followed:

### The False Claim
The AI falsely made it appear that **BBC News had reported that 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.

### BBC's Response
- The BBC **formally complained to Apple**, contacting them "to raise this concern and fix the problem"
- The BBC emphasized that it is considered **"the most trusted news media in the world"** and stressed the importance of audiences being able to trust information published in their name

### Broader Context
- This was **not an isolated incident** — Apple Intelligence had also misrepresented a New York Times notification to suggest **"Netanyahu arrested"**, when the actual story was about an ICC arrest *warrant*
- Experts criticized Apple, with Professor Petros Iosifidis calling it **"embarrassing"** and describing the product as **"demonstrably half-baked"**, warning of **"a real danger of spreading disinformation"**

The incident highlighted growing concerns about AI summarization technology being deployed before it is sufficiently reliable, particularly when misrepresenting trusted news sources.
Time taken: 0.28 seconds

Conclusion

You've built a high-performance semantic search engine using Couchbase Hyperscale/Composite indexes with OpenAI embeddings, Anthropic's Claude, and LangChain. For the Search Vector Index alternative, see the search_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