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.
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.
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
When running Couchbase using Capella, the following prerequisites need to be met.
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.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_tqdmLogging 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)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:
.env file in the same directory as this notebook.env.sample to your .env fileThe 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 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 CouchbaseThe setup_collection() function handles creating and configuring the hierarchical data organization in Couchbase:
Bucket Creation:
Scope Management:
Collection Setup:
Additional Tasks:
The function is called twice to set up:
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>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 OpenAIEmbeddingsA 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 storeTo 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 rowsWe 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.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:
We use a conservative batch size of 100 to ensure reliable operation. The optimal batch size depends on many factors including:
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.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 cacheWe 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.")
raise2026-02-19 11:01:31,327 - INFO - Successfully created ChatAnthropicSemantic 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...
--------------------------------------------------------------------------------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
Composite Vector Indexes
Choosing the Right Index Type
For more details, see the Couchbase Vector Index documentation.
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):
Quantization Options:
Common Examples:
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...
--------------------------------------------------------------------------------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 filteringCouchbase 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 chaintry:
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 secondsCouchbase 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 secondsYou'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.