Notitie
Voor toegang tot deze pagina is autorisatie vereist. U kunt proberen u aan te melden of de directory te wijzigen.
Voor toegang tot deze pagina is autorisatie vereist. U kunt proberen de mappen te wijzigen.
In deze zelfstudie wordt uitgelegd hoe u een RAG-oplossing (Retrieval-Augmented Generation) maakt met behulp van Azure Content Understanding binnen Foundry Tools. Het behandelt de belangrijkste stappen voor het bouwen van een sterk RAG-systeem, biedt tips voor het verbeteren van relevantie en nauwkeurigheid en laat zien hoe u verbinding maakt met andere Azure-services. Aan het einde kunt u Content Understanding gebruiken om multimodale gegevens af te handelen, het ophalen te verbeteren en AI-modellen te helpen nauwkeurige en zinvolle antwoorden te bieden.
Oefeningen die zijn opgenomen in deze zelfstudie
- Analysen maken. Leer hoe u herbruikbare analyses maakt om gestructureerde inhoud te extraheren uit multimodale gegevens met behulp van inhoudextractie.
- Doelmetagegevens genereren met veldextractie. Ontdek hoe u AI gebruikt voor het genereren van verdere metagegevens, zoals samenvattingen of belangrijke onderwerpen, om geëxtraheerde inhoud te verrijken.
- Geëxtraheerde inhoud vooraf verwerken. Verken manieren om geëxtraheerde inhoud te transformeren in vector-insluitingen voor semantische zoekopdrachten en ophalen.
- Ontwerp een uniforme index. Ontwikkel een uniforme Azure AI Search-index die multimodale gegevens integreert en organiseert voor efficiënt ophalen.
- Semantisch segment ophalen. Extraheer contextuele relevante informatie om nauwkeurigere en zinvolle antwoorden op gebruikersquery's te leveren.
- Werken met gegevens met behulp van chatmodellen Gebruik Azure OpenAI-chatmodellen om te communiceren met uw geïndexeerde gegevens, zodat u kunt zoeken in gesprekken, query's uitvoeren en beantwoorden.
Vereiste voorwaarden
U hebt een actief Azure-abonnement nodig om aan de slag te gaan. Als u geen Azure-account hebt, kunt u een gratis abonnement maken.
Zodra u uw Azure-abonnement hebt, maakt u een Microsoft Foundry-resource in Azure Portal.
Deze resource wordt vermeld onder Foundry>Foundry in de portal.
Azure AI Search-resource: Stel een Azure AI Search-resource in om het indexeren en ophalen van multimodale gegevens mogelijk te maken.
Implementatie van Azure OpenAI-chatmodel: Implementeer een Azure OpenAI-chatmodel dat gesprekken mogelijk maakt.
Implementatie van model insluiten: Zorg ervoor dat u een insluitmodel hebt geïmplementeerd om vectorweergaven te genereren voor semantische zoekopdrachten.
API-versie: In deze zelfstudie wordt de nieuwste preview-API-versie gebruikt.
Python-omgeving: Installeer Python 3.11 om de meegeleverde codevoorbeelden en -scripts uit te voeren.
Deze zelfstudie volgt deze voorbeeldcode die u kunt vinden in ons Python-notebook. Volg het LEESMIJ-bestand om essentiële resources te maken, resources de juiste toegangsbeheerrollen (IAM) te verlenen en alle pakketten te installeren die nodig zijn voor deze handleiding.
De multimodale gegevens die in deze zelfstudie worden gebruikt, bestaan uit documenten, afbeeldingen, audio en video. Ze zijn ontworpen om u te begeleiden bij het bouwen van een robuuste RAG-oplossing met Azure Content Understanding in Foundry Tools.
Gegevens extraheren
Retrieval-augmented generation (RAG*) is een methode, die de functionaliteit van Large Language Models (LLM) verbetert door gegevens uit externe kennisbronnen te integreren. Het bouwen van een robuuste multimodale RAG-oplossing begint met het extraheren en structureren van gegevens uit diverse inhoudstypen. Azure Content Understanding biedt drie belangrijke onderdelen om dit proces te vergemakkelijken: extractie van inhoud, veldextractie en analyse. Samen vormen deze onderdelen de basis voor het maken van een uniforme, herbruikbare en verbeterde gegevenspijplijn voor RAG-werkstromen.
Implementatiestappen
Voer de volgende stappen uit om gegevensextractie in Content Understanding te implementeren:
Een analyse maken: Definieer een analyse met behulp van REST API's of onze Python-codevoorbeelden.
Inhoudextractie uitvoeren: Gebruik de analyse om bestanden te verwerken en gestructureerde inhoud te extraheren.
(Optioneel) Verbeteren met veldextractie: Geef desgewenst velden op die door AI zijn gegenereerd om de geëxtraheerde inhoud te verrijken met toegevoegde metagegevens.
Analyzers maken
Analyzers zijn herbruikbare onderdelen in Content Understanding die het proces voor gegevensextractie stroomlijnen. Zodra een analyse is gemaakt, kan deze herhaaldelijk worden gebruikt om bestanden te verwerken en inhoud of velden te extraheren op basis van vooraf gedefinieerde schema's. Een analyse fungeert als een blauwdruk voor hoe gegevens moeten worden verwerkt, waardoor consistentie en efficiëntie in meerdere bestanden en inhoudstypen worden gegarandeerd.
In de volgende codevoorbeelden ziet u hoe u analyses maakt voor elke modaliteit, waarbij u de gestructureerde gegevens opgeeft die moeten worden geëxtraheerd, zoals sleutelvelden, samenvattingen of classificaties. Deze analyses dienen als basis voor het extraheren en verrijken van inhoud in uw RAG-oplossing.
Alle omgevingsvariabelen en benodigde bibliotheken uit Langchain laden
import os
from dotenv import load_dotenv
load_dotenv()
# Load and validate Foundry Tools configs
AZURE_AI_SERVICE_ENDPOINT = os.getenv("AZURE_AI_SERVICE_ENDPOINT")
AZURE_AI_SERVICE_API_VERSION = os.getenv("AZURE_AI_SERVICE_API_VERSION") or "2024-12-01-preview"
AZURE_DOCUMENT_INTELLIGENCE_API_VERSION = os.getenv("AZURE_DOCUMENT_INTELLIGENCE_API_VERSION") or "2024-11-30"
# Load and validate Azure OpenAI configs
AZURE_OPENAI_ENDPOINT = os.getenv("AZURE_OPENAI_ENDPOINT")
AZURE_OPENAI_CHAT_DEPLOYMENT_NAME = os.getenv("AZURE_OPENAI_CHAT_DEPLOYMENT_NAME")
AZURE_OPENAI_CHAT_API_VERSION = os.getenv("AZURE_OPENAI_CHAT_API_VERSION") or "2024-08-01-preview"
AZURE_OPENAI_EMBEDDING_DEPLOYMENT_NAME = os.getenv("AZURE_OPENAI_EMBEDDING_DEPLOYMENT_NAME")
AZURE_OPENAI_EMBEDDING_API_VERSION = os.getenv("AZURE_OPENAI_EMBEDDING_API_VERSION") or "2023-05-15"
# Load and validate Azure Search Services configs
AZURE_SEARCH_ENDPOINT = os.getenv("AZURE_SEARCH_ENDPOINT")
AZURE_SEARCH_INDEX_NAME = os.getenv("AZURE_SEARCH_INDEX_NAME") or "sample-doc-index"
# Import libraries from Langchain
from langchain import hub
from langchain_openai import AzureChatOpenAI
from langchain_openai import AzureOpenAIEmbeddings
from langchain.schema import StrOutputParser
from langchain.schema.runnable import RunnablePassthrough
from langchain.text_splitter import MarkdownHeaderTextSplitter
from langchain.vectorstores.azuresearch import AzureSearch
from langchain_core.prompts import ChatPromptTemplate
from langchain.schema import Document
import requests
import json
import sys
import uuid
from pathlib import Path
from dotenv import find_dotenv, load_dotenv
from azure.identity import DefaultAzureCredential, get_bearer_token_provider
# Add the parent directory to the path to use shared modules
parent_dir = Path(Path.cwd()).parent
sys.path.append(str(parent_dir))
Codevoorbeeld: Analyse maken
from pathlib import Path
from python.content_understanding_client import AzureContentUnderstandingClient
credential = DefaultAzureCredential()
token_provider = get_bearer_token_provider(credential, "https://cognitiveservices.azure.com/.default")
#set analyzer configs
analyzer_configs = [
{
"id": "doc-analyzer" + str(uuid.uuid4()),
"template_path": "../analyzer_templates/content_document.json",
"location": Path("../data/sample_layout.pdf"),
},
{
"id": "image-analyzer" + str(uuid.uuid4()),
"template_path": "../analyzer_templates/image_chart_diagram_understanding.json",
"location": Path("../data/sample_report.pdf"),
},
{
"id": "audio-analyzer" + str(uuid.uuid4()),
"template_path": "../analyzer_templates/call_recording_analytics.json",
"location": Path("../data/callCenterRecording.mp3"),
},
{
"id": "video-analyzer" + str(uuid.uuid4()),
"template_path": "../analyzer_templates/video_content_understanding.json",
"location": Path("../data/FlightSimulator.mp4"),
},
]
# Create Content Understanding client
content_understanding_client = AzureContentUnderstandingClient(
endpoint=AZURE_AI_SERVICE_ENDPOINT,
api_version=AZURE_AI_SERVICE_API_VERSION,
token_provider=token_provider,
x_ms_useragent="azure-ai-content-understanding-python/content_extraction", # This header is used for sample usage telemetry, please comment out this line if you want to opt out.
)
# Iterate through each config and create an analyzer
for analyzer in analyzer_configs:
analyzer_id = analyzer["id"]
template_path = analyzer["template_path"]
try:
# Create the analyzer using the content understanding client
response = content_understanding_client.begin_create_analyzer(
analyzer_id=analyzer_id,
analyzer_template_path=template_path
)
result = content_understanding_client.poll_result(response)
print(f"Successfully created analyzer: {analyzer_id}")
except Exception as e:
print(f"Failed to create analyzer: {analyzer_id}")
print(f"Error: {e}")
Notitie: Schema's voor veldextractie zijn optioneel en niet vereist voor het uitvoeren van inhoudextractie. Als u inhoudsextractie wilt uitvoeren en analyses wilt maken zonder veldschema's te definiëren, geeft u de analyse-id en het bestand op dat moet worden geanalyseerd.
Schema's zijn gebruikt in deze zelfstudie. Hier volgt een voorbeeld van een schemadefinitie
In het volgende voorbeeld definiëren we een schema voor het extraheren van basisinformatie uit een factuurdocument.
{
"description": "Sample invoice analyzer",
"scenario": "document",
"config": {
"returnDetails": true
},
"fieldSchema": {
"fields": {
"VendorName": {
"type": "string",
"method": "extract",
"description": "Vendor issuing the invoice"
},
"Items": {
"type": "array",
"method": "extract",
"items": {
"type": "object",
"properties": {
"Description": {
"type": "string",
"method": "extract",
"description": "Description of the item"
},
"Amount": {
"type": "number",
"method": "extract",
"description": "Amount of the item"
}
}
}
}
}
}
}
Inhoud en veldextractie
Inhoudextractie is de eerste stap in het RAG-implementatieproces. Hiermee worden ruwe multimodale gegevens getransformeerd in gestructureerde, doorzoekbare formaten. Deze basisstap zorgt ervoor dat de inhoud is georganiseerd en gereed is voor indexeren en ophalen. Hoewel inhoudsextractie de basislijn biedt voor het indexeren en ophalen, kan het niet volledig voldoen aan domeinspecifieke behoeften of dieper contextuele inzichten bieden. Meer informatie over mogelijkheden voor het extraheren van inhoud voor elke modaliteit.
Veldextractie bouwt voort op inhoudsextractie met behulp van AI om verdere metagegevens te genereren die de Knowledge Base verrijken. Met deze stap kunt u aangepaste velden definiëren die zijn afgestemd op uw specifieke use-case, waardoor u nauwkeuriger kunt ophalen en de relevantie van de zoekopdracht kunt verbeteren. Veldextractie vormt een aanvulling op inhoudsextractie door diepte en context toe te voegen, waardoor de gegevens beter kunnen worden uitgevoerd voor RAG-scenario's. Meer informatie over mogelijkheden voor veldextractie voor elke modaliteit.
Nu de analyseanalyses zijn gemaakt voor elke modaliteit, kunnen we bestanden verwerken om gestructureerde inhoud en door AI gegenereerde metagegevens te extraheren op basis van de gedefinieerde schema's. In deze sectie wordt gedemonstreerd hoe u de analyses gebruikt om multimodale gegevens te analyseren en een voorbeeld te geven van de resultaten die door de API's worden geretourneerd. Deze resultaten tonen de transformatie van onbewerkte gegevens tot bruikbare inzichten, die de basis vormen voor indexering, ophalen en RAG-werkstromen.
Bestanden analyseren
#Iterate through each analyzer created and analyze content for each modality
analyzer_results =[]
extracted_markdown = []
analyzer_content = []
for analyzer in analyzer_configs:
analyzer_id = analyzer["id"]
template_path = analyzer["template_path"]
file_location = analyzer["location"]
try:
# Analyze content
response = content_understanding_client.begin_analyze(analyzer_id, file_location)
result = content_understanding_client.poll_result(response)
analyzer_results.append({"id":analyzer_id, "result": result["result"]})
analyzer_content.append({"id": analyzer_id, "content": result["result"]["contents"]})
except Exception as e:
print(e)
print("Error in creating analyzer. Please double-check your analysis settings.\nIf there is a conflict, you can delete the analyzer and then recreate it, or move to the next cell and use the existing analyzer.")
print("Analyzer Results:")
for analyzer_result in analyzer_results:
print(f"Analyzer ID: {analyzer_result['id']}")
print(json.dumps(analyzer_result["result"], indent=2))
# Delete the analyzer if it is no longer needed
#content_understanding_client.delete_analyzer(ANALYZER_ID)
Extractieresultaten
In de volgende codevoorbeelden ziet u de uitvoer van inhoud en veldextractie met behulp van Azure Content Understanding. Het JSON-antwoord bevat meerdere velden die elk een specifiek doel dienen om de geëxtraheerde gegevens weer te geven.
Markdown Field: Het
markdownveld biedt een vereenvoudigde, door mensen leesbare weergave van de geëxtraheerde inhoud. Het is vooral handig voor snelle previews of voor het integreren van de geëxtraheerde gegevens in toepassingen waarvoor gestructureerde tekst is vereist, zoals knowledge bases of zoekinterfaces. Met een document kan hetmarkdownveld bijvoorbeeld kopteksten, alinea's en andere structurele elementen bevatten die zijn opgemaakt voor leesbaarheid.JSON-uitvoer: De volledige JSON-uitvoer biedt een uitgebreide weergave van de geëxtraheerde gegevens, inclusief de inhoud en de metagegevens die tijdens het extractieproces worden gegenereerd, inclusief de volgende eigenschappen:
- Velden: Door AI gegenereerde metagegevens, zoals samenvattingen, belangrijke onderwerpen of classificaties, die zijn afgestemd op het specifieke schema dat in de analyse is gedefinieerd.
- Betrouwbaarheidsscores: Indicatoren van de betrouwbaarheid van de geëxtraheerde gegevens.
- Spans: Informatie over de locatie van de geëxtraheerde inhoud in het bronbestand.
- Aanvullende metagegevens: Details zoals paginanummers, dimensies en andere contextuele informatie.
Het resultaat toont de extractie van kopteksten, alinea's, tabellen en andere structurele elementen, terwijl de logische organisatie van de inhoud behouden blijft. Daarnaast toont het de mogelijkheid om belangrijke velden te extraheren, met beknopte extracties van lange materialen.
{
"id": "bcf8c7c7-03ab-4204-b22c-2b34203ef5db",
"status": "Succeeded",
"result": {
"analyzerId": "training_document_analyzer",
"apiVersion": "2024-12-01-preview",
"createdAt": "2024-11-13T07:15:46Z",
"warnings": [],
"contents": [
{
"markdown": "CONTOSO LTD.\n\n\n# Contoso Training Topics\n\nContoso Headquarters...",
"fields": {
"ChapterTitle": {
"type": "string",
"valueString": "Risks and Compliance regulations",
"spans": [ { "offset": 0, "length": 12 } ],
"confidence": 0.941,
"source": "D(1,0.5729,0.6582,2.3353,0.6582,2.3353,0.8957,0.5729,0.8957)"
},
"ChapterAuthor": {
"type": "string",
"valueString": "John Smith",
"spans": [ { "offset": 0, "length": 12 } ],
"confidence": 0.941,
"source": "D(1,0.5729,0.6582,2.3353,0.6582,2.3353,0.8957,0.5729,0.8957)"
},
"ChapterPublishDate": {
"type": "Date",
"valueString": "04-11-2017",
"spans": [ { "offset": 0, "length": 12 } ],
"confidence": 0.941,
"source": "D(1,0.5729,0.6582,2.3353,0.6582,2.3353,0.8957,0.5729,0.8957)"
},
},
"kind": "document",
"startPageNumber": 1,
"endPageNumber": 1,
"unit": "inch",
"pages": [
{
"pageNumber": 1,
"angle": -0.0039,
"width": 8.5,
"height": 11,
"spans": [ { "offset": 0, "length": 1650 } ],
"words": [
{
....
},
],
"lines": [
{
...
},
]
}
],
}
]
}
}
Uitvoer van inhoudsbegrip vooraf verwerken
Zodra de gegevens zijn geëxtraheerd met behulp van Azure Content Understanding, is de volgende stap het voorbereiden van de analyse-uitvoer voor het insluiten van inhoud in een zoeksysteem. Door de uitvoer vooraf te verwerken, zorgt u ervoor dat de geëxtraheerde inhoud wordt omgezet in een indeling die geschikt is voor het indexeren en ophalen van inhoud. Deze stap omvat het converteren van de JSON-uitvoer van de analyses naar gestructureerde tekenreeksen, waarbij zowel de inhoud als metagegevens worden bewaard voor naadloze integratie in downstreamwerkstromen.
In het volgende voorbeeld ziet u hoe u de uitvoergegevens van de analyses voorwerkt, waaronder documenten, afbeeldingen, audio en video. Het proces van het converteren van elke JSON-uitvoer naar een gestructureerde tekenreeks legt het basiswerk voor het insluiten van de gegevens in een vectorzoeksysteem, waardoor u efficiënt RAG-werkstromen kunt ophalen en verbeteren.
def convert_values_to_strings(json_obj):
return [str(value) for value in json_obj]
#process all content and convert to string
def process_allJSON_content(all_content):
# Initialize empty list to store string of all content
output = []
document_splits = [
"This is a json string representing a document with text and metadata for the file located in "+str(analyzer_configs[0]["location"])+" "
+ v
+ "```"
for v in convert_values_to_strings(all_content[0]["content"])
]
docs = [Document(page_content=v) for v in document_splits]
output += docs
#convert image json object to string and append file metadata to the string
image_splits = [
"This is a json string representing an image verbalization and OCR extraction for the file located in "+str(analyzer_configs[1]["location"])+" "
+ v
+ "```"
for v in convert_values_to_strings(all_content[1]["content"])
]
image = [Document(page_content=v) for v in image_splits]
output+=image
#convert audio json object to string and append file metadata to the string
audio_splits = [
"This is a json string representing an audio segment with transcription for the file located in "+str(analyzer_configs[2]["location"])+" "
+ v
+ "```"
for v in convert_values_to_strings(all_content[2]["content"])
]
audio = [Document(page_content=v) for v in audio_splits]
output += audio
#convert video json object to string and append file metadata to the string
video_splits = [
"The following is a json string representing a video segment with scene description and transcript for the file located in "+str(analyzer_configs[3]["location"])+" "
+ v
+ "```"
for v in convert_values_to_strings(all_content[3]["content"])
]
video = [Document(page_content=v) for v in video_splits]
output+=video
return output
all_splits = process_allJSON_content(analyzer_content)
print("There are " + str(len(all_splits)) + " documents.")
# Print the content of all doc splits
for doc in all_splits:
print(f"doc content", doc.page_content)
Geëxtraheerde inhoud insluiten en indexeren
Nadat het vooraf verwerken van de geëxtraheerde gegevens uit Azure Content Understanding is voltooid, is de volgende stap het insluiten en indexeren van de inhoud voor een efficiënte ophaalbewerking. Deze stap omvat het transformeren van de gestructureerde tekenreeksen in vector-insluitingen met behulp van een insluitmodel en het opslaan ervan in een Azure AI Search-systeem. Door de inhoud in te sluiten, schakelt u semantische zoekmogelijkheden in, zodat het systeem de meest relevante informatie kan ophalen op basis van betekenis in plaats van exacte trefwoordovereenkomsten. Deze stap is essentieel voor het bouwen van een robuuste RAG-oplossing, omdat deze ervoor zorgt dat de geëxtraheerde inhoud is geoptimaliseerd voor geavanceerde zoek- en ophaalwerkstromen.
# Embed the splitted documents and insert into Azure Search vector store
def embed_and_index_chunks(docs):
aoai_embeddings = AzureOpenAIEmbeddings(
azure_deployment=AZURE_OPENAI_EMBEDDING_DEPLOYMENT_NAME,
openai_api_version=AZURE_OPENAI_EMBEDDING_API_VERSION, # e.g., "2023-12-01-preview"
azure_endpoint=AZURE_OPENAI_ENDPOINT,
azure_ad_token_provider=token_provider
)
vector_store: AzureSearch = AzureSearch(
azure_search_endpoint=AZURE_SEARCH_ENDPOINT,
azure_search_key=None,
index_name=AZURE_SEARCH_INDEX_NAME,
embedding_function=aoai_embeddings.embed_query
)
vector_store.add_documents(documents=docs)
return vector_store
# embed and index the docs:
vector_store = embed_and_index_chunks(all_splits)
Semantisch segment ophalen
Wanneer de geëxtraheerde inhoud is ingesloten en geïndexeerd, is de volgende stap het gebruik van de kracht van overeenkomsten en vectorzoekopdrachten om de meest relevante segmenten van informatie op te halen. In deze sectie ziet u hoe u zowel overeenkomsten als hybride zoekopdrachten uitvoert, zodat het systeem inhoud kan weergeven op basis van semantische betekenis in plaats van exacte trefwoordovereenkomsten. Door contextafhankelijk relevante segmenten op te halen, kunt u de precisie van uw RAG-werkstromen verbeteren en nauwkeurigere, zinvolle antwoorden bieden op gebruikersquery's.
# Set your query
query = "japan"
# Perform a similarity search
docs = vector_store.similarity_search(
query=query,
k=3,
search_type="similarity",
)
for doc in docs:
print(doc.page_content)
# Perform a hybrid search using the search_type parameter
docs = vector_store.hybrid_search(query=query, k=3)
for doc in docs:
print(doc.page_content)
OpenAI gebruiken om te communiceren met gegevens
Nu de geëxtraheerde inhoud is ingesloten en geïndexeerd, is de laatste stap bij het bouwen van een robuuste RAG-oplossing het inschakelen van gespreksinteracties met behulp van OpenAI-chatmodellen. In deze sectie wordt gedemonstreerd hoe u query's kunt uitvoeren op uw geïndexeerde gegevens en OpenAI-chatmodellen kunt toepassen om beknopte, contextuele uitgebreide antwoorden te bieden. Door conversationele AI te integreren, kunt u uw RAG-oplossing transformeren in een interactief systeem dat zinvolle inzichten biedt en de betrokkenheid van gebruikers verbetert. De volgende voorbeelden helpen u bij het instellen van een uitgebreide gespreksstroom voor het ophalen van gegevens, zodat u naadloos kunt integreren tussen uw gegevens en OpenAI-chatmodellen.
# Setup rag chain
prompt_str = """You are an assistant for question-answering tasks. Use the following pieces of retrieved context to answer the question. If you don't know the answer, just say that you don't know. Use three sentences maximum and keep the answer concise.
Question: {question}
Context: {context}
Answer:"""
def setup_rag_chain(vector_store):
retriever = vector_store.as_retriever(search_type="similarity", k=3)
prompt = ChatPromptTemplate.from_template(prompt_str)
llm = AzureChatOpenAI(
openai_api_version=AZURE_OPENAI_CHAT_API_VERSION,
azure_deployment=AZURE_OPENAI_CHAT_DEPLOYMENT_NAME,
azure_ad_token_provider=token_provider,
temperature=0.7,
)
def format_docs(docs):
return "\n\n".join(doc.page_content for doc in docs)
rag_chain = (
{"context": retriever | format_docs, "question": RunnablePassthrough()}
| prompt
| llm
| StrOutputParser()
)
return rag_chain
# Setup conversational search
def conversational_search(rag_chain, query):
print(rag_chain.invoke(query))
rag_chain = setup_rag_chain(vector_store)
while True:
query = input("Enter your query: ")
if query=="":
break
conversational_search(rag_chain, query)