Utilisation de Cohere pour RAG et Rerank dans Elasticsearch
Apprenez à créer un système RAG à l’aide de l’API Cohere Inference et d’Elasticsearch avec des représentations vectorielles, une recherche hybride et des performances de reclassement avancées. Ce notebook utilise le client Python Elasticsearch pour effectuer ces opérations.
Ouvrir à ColabTélécharger le carnet de notesThis tutorial shows you how to compute embeddings with Cohere using the inference API and store them for efficient vector or hybrid search in Elasticsearch. This tutorial uses the Python Elasticsearch client to perform the operations.
You'll learn how to:
- create an inference endpoint for text embedding using the Cohere service,
- create the necessary index mapping for the Elasticsearch index,
- build an inference pipeline to ingest documents into the index together with the embeddings,
- perform hybrid search on the data,
- rerank search results by using Cohere's rerank model,
- design a RAG system with Cohere's Chat API.
The tutorial uses the SciFact data set.
Refer to Cohere's tutorial for an example using a different data set.
🧰 Requirements
For this example, you will need:
-
An Elastic deployment with minimum 4GB machine learning node
- We'll be using Elastic Cloud for this example (available with a free trial)
-
A paid Cohere account is required to use the Inference API with the Cohere service as the Cohere free trial API usage is limited.
-
Python 3.7 or later.
Install and import required packages
Install Elasticsearch and Cohere:
!pip install elasticsearch
!pip install cohereImport the required packages:
from elasticsearch import Elasticsearch, helpers
import cohere
import json
import requests
from getpass import getpassCreate an Elasticsearch client
Now you can instantiate the Python Elasticsearch client.
First provide your password and Cloud ID.
Then create a client object that instantiates an instance of the Elasticsearch class.
# https://www.elastic.co/search-labs/tutorials/install-elasticsearch/elastic-cloud#finding-your-cloud-id
ELASTIC_CLOUD_ID = getpass("Elastic Cloud ID: ")
# https://www.elastic.co/search-labs/tutorials/install-elasticsearch/elastic-cloud#creating-an-api-key
ELASTIC_API_KEY = getpass("Elastic API key: ")
# Create the client instance
client = Elasticsearch(
# For local development
# hosts=["http://localhost:9200"]
cloud_id=ELASTIC_CLOUD_ID,
api_key=ELASTIC_API_KEY,
)
# Confirm the client has connected
print(client.info())Create the inference endpoint
Create the inference endpoint first. In this example, the inference endpoint
uses Cohere's embed-english-v3.0 model and the embedding_type is set to
byte.
COHERE_API_KEY = getpass("Cohere API key: ")
co = cohere.Client(api_key=COHERE_API_KEY)client.inference.put_model(
task_type="text_embedding",
inference_id="cohere_embeddings",
body={
"service": "cohere",
"service_settings": {
"api_key": COHERE_API_KEY,
"model_id": "embed-english-v3.0",
"embedding_type": "byte",
},
},
)You can find your API keys in your Cohere dashboard under the API keys section.
Create the index mapping
Create the index mapping for the index that will contain the embeddings.
client.indices.create(
index="cohere-embeddings",
settings={"index": {"default_pipeline": "cohere_embeddings"}},
mappings={
"properties": {
"text_embedding": {
"type": "dense_vector",
"dims": 1024,
"element_type": "byte",
},
"text": {"type": "text"},
"id": {"type": "integer"},
"title": {"type": "text"},
}
},
)Create the inference pipeline
Now you have an inference endpoint and an index ready to store embeddings. The next step is to create an ingest pipeline that creates the embeddings using the inference endpoint and stores them in the index.
client.ingest.put_pipeline(
id="cohere_embeddings",
description="Ingest pipeline for Cohere inference.",
processors=[
{
"inference": {
"model_id": "cohere_embeddings",
"input_output": {
"input_field": "text",
"output_field": "text_embedding",
},
}
}
],
)Prepare data and insert documents
This example uses the SciFact data set that you can find on HuggingFace.
url = "https://huggingface.co/datasets/mteb/scifact/raw/main/corpus.jsonl"
# Fetch the JSONL data from the URL
response = requests.get(url)
response.raise_for_status() # Ensure we notice bad responses
# Split the content by new lines and parse each line as JSON
data = [json.loads(line) for line in response.text.strip().split("\n") if line]
# Change `_id` key to `id` as `_id` is a reserved key in Elasticsearch.
for item in data:
if "_id" in item:
item["id"] = item.pop("_id")
# Prepare the documents to be indexed
documents = []
for line in data:
data_dict = line
documents.append(
{
"_index": "cohere-embeddings",
"_source": data_dict,
}
)
# Use the bulk endpoint to index
helpers.bulk(client, documents)
print("Data ingestion completed, text embeddings generated!")Your index is populated with the SciFact data and text embeddings for the text field.
Hybrid search
Let's start querying the index!
The code below performs a hybrid search. The kNN query computes the relevance
of search results based on vector similarity using the text_embedding field.
The lexical search query uses BM25 retrieval to compute keyword similarity on
the title and text fields.
query = "What is biosimilarity?"
response = client.search(
index="cohere-embeddings",
size=100,
knn={
"field": "text_embedding",
"query_vector_builder": {
"text_embedding": {
"model_id": "cohere_embeddings",
"model_text": query,
}
},
"k": 10,
"num_candidates": 50,
},
query={"multi_match": {"query": query, "fields": ["text", "title"]}},
)
raw_documents = response["hits"]["hits"]
# Display the first 10 results
for document in raw_documents[0:10]:
print(
f'Title: {document["_source"]["title"]}\nText: {document["_source"]["text"]}\n'
)
# Format the documents for ranking
documents = []
for hit in response["hits"]["hits"]:
documents.append(hit["_source"]["text"])Rerank search results
To combine the results more effectively, use Cohere's Rerank v3 model through the inference API to provide a more precise semantic reranking of the results.
Create an inference endpoint with your Cohere API key and the used model name as
the model_id (rerank-english-v3.0 in this example).
client.inference.put_model(
task_type="rerank",
inference_id="cohere_rerank",
body={
"service": "cohere",
"service_settings": {
"api_key": COHERE_API_KEY,
"model_id": "rerank-english-v3.0",
},
"task_settings": {
"top_n": 10,
},
},
)Rerank the results using the new inference endpoint.
response = client.inference.inference(
inference_id="cohere_rerank",
body={
"query": query,
"input": documents,
"task_settings": {"return_documents": False},
},
)
# Reconstruct the input documents based on the index provided in the rereank response
ranked_documents = []
for document in response.body["rerank"]:
ranked_documents.append(
{
"title": raw_documents[int(document["index"])]["_source"]["title"],
"text": raw_documents[int(document["index"])]["_source"]["text"],
}
)
# Print the top 10 results
for document in ranked_documents[0:10]:
print(f"Title: {document['title']}\nText: {document['text']}\n")Retrieval Augmented Generation (RAG) with Cohere and Elasticsearch
RAG is a method for generating text using additional information fetched from an external data source. With the ranked results, you can build a RAG system on top of what you created with Cohere's Chat API.
Pass in the retrieved documents and the query to receive a grounded response using Cohere's newest generative model Command R+.
Then pass in the query and the documents to the Chat API, and print out the response.
response = co.chat(message=query, documents=ranked_documents, model="command-r-plus")
source_documents = []
for citation in response.citations:
for document_id in citation.document_ids:
if document_id not in source_documents:
source_documents.append(document_id)
print(f"Query: {query}")
print(f"Response: {response.text}")
print("Sources:")
for document in response.documents:
if document["id"] in source_documents:
print(f"{document['title']}: {document['text']}")