1. Введение
Что такое RAG?
Метод расширенной генерации с учетом поиска (Retrieval Augmented Generation, RAG) — это техника, которая сочетает в себе возможности больших языковых моделей (LLM) со способностью извлекать релевантную информацию из внешних источников знаний. Это означает, что LLM не только полагается на свои внутренние обучающие данные, но и может получать доступ к актуальной, специфической информации и включать ее в свои ответы.

Популярность RAG растет по нескольким причинам:
- Повышенная точность и релевантность: RAG позволяет магистрам права давать более точные и релевантные ответы, основываясь на фактической информации, полученной из внешних источников. Это особенно полезно в ситуациях, когда актуальные знания имеют решающее значение, например, при ответе на вопросы о текущих событиях или предоставлении информации по конкретным темам.
- Снижение галлюцинаций: LLM-ы иногда могут генерировать ответы, которые кажутся правдоподобными, но на самом деле неверны или бессмысленны. RAG помогает смягчить эту проблему, проверяя полученную информацию по внешним источникам.
- Повышенная адаптивность: RAG делает программы магистратуры по прикладным наукам (LLM) более адаптируемыми к различным областям и задачам. Благодаря использованию различных источников знаний, программу LLM можно легко настроить для предоставления информации по широкому кругу тем.
- Улучшение пользовательского опыта: RAG может улучшить общий пользовательский опыт, предоставляя более информативные, надежные и релевантные ответы.
Почему мультимодальный
В современном мире, насыщенном данными, документы часто сочетают текст и изображения для всесторонней передачи информации. Однако большинство систем поиска с дополненной реальностью (RAG) упускают из виду ценные сведения, скрытые в изображениях. По мере роста популярности многомодальных больших языковых моделей (LLM) крайне важно изучить, как мы можем использовать визуальный контент наряду с текстом в системах RAG, чтобы глубже понять информационный ландшафт.
Два варианта для многомодальной RAG
- Мультимодальные встраивания — модель мультимодальных встраиваний генерирует 1408-мерные векторы* на основе предоставленных вами входных данных, которые могут включать комбинацию изображений, текста и видео. Вектор встраивания изображения и вектор встраивания текста находятся в одном семантическом пространстве и имеют одинаковую размерность. Следовательно, эти векторы можно использовать взаимозаменяемо для таких задач, как поиск изображения по тексту или поиск видео по изображению. Посмотрите эту демонстрацию .
- Используйте мультимодальное встраивание для встраивания текста и изображений.
- Найдите оба варианта, используя поиск по сходству.
- Передайте как полученные исходные изображения, так и фрагменты текста в многомодальную LLM-систему для синтеза ответа.
- Встраивание текста -
- Используйте многомодальный LLM для создания текстовых резюме изображений.
- Встраивание и извлечение текста
- Передайте фрагменты текста в LLM для синтеза ответов.
Что такое многовекторный ретривер?
Многовекторный поиск использует резюме разделов документа для извлечения исходного контента с целью синтеза ответов. Он повышает качество RAG, особенно для задач, требующих большого количества таблиц, графиков, диаграмм и т. д. Подробнее можно узнать в блоге Langchain .
Что вы построите
Пример использования: Разработка системы ответов на вопросы с использованием Gemini Pro.
Представьте, что у вас есть документы, содержащие сложные графики или диаграммы, насыщенные информацией. Вы хотите извлечь эти данные, чтобы ответить на вопросы или запросы.
В этом практическом задании вы выполните следующие действия:
- Загрузка данных с использованием
document_loadersиз LangChain. - Создавайте текстовые резюме, используя модель
gemini-proот Google. - Создавайте краткие описания изображений, используя модель
gemini-pro-visionот Google. - Создайте многовекторный поиск, используя модель
textembedding-geckoот Google и Croma Db в качестве векторного хранилища. - Разработать многомодальную цепочку RAG для ответов на вопросы.
2. Прежде чем начать
- В консоли Google Cloud на странице выбора проекта выберите или создайте проект Google Cloud.
- Убедитесь, что для вашего проекта Google Cloud включена функция выставления счетов. Узнайте, как проверить, включена ли функция выставления счетов для проекта .
- Включите все рекомендуемые API на панели управления Vertex AI.
- Откройте блокнот Colab и войдите в ту же учетную запись, что и ваша текущая активная учетная запись Google Cloud.
3. Создание многомодальной RAG
В этом практическом занятии используется Vertex AI SDK для Python и Langchain , чтобы продемонстрировать, как реализовать описанный здесь «Вариант 2» с помощью Google Cloud.
Полный код можно найти в файле Multi-modal RAG with Google Cloud в указанном репозитории .
4. Шаг 1: Установка и импорт зависимостей
!pip install -U --quiet langchain langchain_community chromadb langchain-google-vertexai
!pip install --quiet "unstructured[all-docs]" pypdf pillow pydantic lxml pillow matplotlib chromadb tiktoken
Введите идентификатор вашего проекта и завершите аутентификацию.
#TODO : ENter project and location
PROJECT_ID = ""
REGION = "us-central1"
from google.colab import auth
auth.authenticate_user()
Инициализация платформы Vertex AI
import vertexai
vertexai.init(project = PROJECT_ID , location = REGION)
5. Шаг 2: Подготовка и загрузка данных
Мы используем ZIP-архив с подмножеством извлеченных изображений и PDF-файлов из этой статьи в блоге. Если вы хотите проследить весь процесс, пожалуйста, используйте оригинальный пример .
Сначала загрузите данные.
import logging
import zipfile
import requests
logging.basicConfig(level=logging.INFO)
data_url = "https://storage.googleapis.com/benchmarks-artifacts/langchain-docs-benchmarking/cj.zip"
result = requests.get(data_url)
filename = "cj.zip"
with open(filename, "wb") as file:
file.write(result.content)
with zipfile.ZipFile(filename, "r") as zip_ref:
zip_ref.extractall()
Загрузите текстовое содержимое из документа.
from langchain_community.document_loaders import PyPDFLoader
loader = PyPDFLoader("./cj/cj.pdf")
docs = loader.load()
tables = []
texts = [d.page_content for d in docs]
Проверьте содержимое первой страницы.
texts[0]
Вы должны увидеть результат.

Общее количество страниц в документе
len(texts)
Ожидаемый результат:

6. Шаг 3: Создание текстовых резюме
Сначала импортируйте необходимые библиотеки.
from langchain_google_vertexai import VertexAI , ChatVertexAI , VertexAIEmbeddings
from langchain.prompts import PromptTemplate
from langchain_core.messages import AIMessage
from langchain_core.output_parsers import StrOutputParser
from langchain_core.runnables import RunnableLambda
Получить текстовые краткие обзоры
# Generate summaries of text elements
def generate_text_summaries(texts, tables, summarize_texts=False):
"""
Summarize text elements
texts: List of str
tables: List of str
summarize_texts: Bool to summarize texts
"""
# Prompt
prompt_text = """You are an assistant tasked with summarizing tables and text for retrieval. \
These summaries will be embedded and used to retrieve the raw text or table elements. \
Give a concise summary of the table or text that is well optimized for retrieval. Table or text: {element} """
prompt = PromptTemplate.from_template(prompt_text)
empty_response = RunnableLambda(
lambda x: AIMessage(content="Error processing document")
)
# Text summary chain
model = VertexAI(
temperature=0, model_name="gemini-pro", max_output_tokens=1024
).with_fallbacks([empty_response])
summarize_chain = {"element": lambda x: x} | prompt | model | StrOutputParser()
# Initialize empty summaries
text_summaries = []
table_summaries = []
# Apply to text if texts are provided and summarization is requested
if texts and summarize_texts:
text_summaries = summarize_chain.batch(texts, {"max_concurrency": 1})
elif texts:
text_summaries = texts
# Apply to tables if tables are provided
if tables:
table_summaries = summarize_chain.batch(tables, {"max_concurrency": 1})
return text_summaries, table_summaries
# Get text summaries
text_summaries, table_summaries = generate_text_summaries(
texts, tables, summarize_texts=True
)
text_summaries[0]
Ожидаемый результат:

7. Шаг 4: Создание сводных данных по изображениям
Сначала импортируйте необходимые библиотеки.
import base64
import os
from langchain_core.messages import HumanMessage
Создание сводных изображений
def encode_image(image_path):
"""Getting the base64 string"""
with open(image_path, "rb") as image_file:
return base64.b64encode(image_file.read()).decode("utf-8")
def image_summarize(img_base64, prompt):
"""Make image summary"""
model = ChatVertexAI(model_name="gemini-pro-vision", max_output_tokens=1024)
msg = model(
[
HumanMessage(
content=[
{"type": "text", "text": prompt},
{
"type": "image_url",
"image_url": {"url": f"data:image/jpeg;base64,{img_base64}"},
},
]
)
]
)
return msg.content
def generate_img_summaries(path):
"""
Generate summaries and base64 encoded strings for images
path: Path to list of .jpg files extracted by Unstructured
"""
# Store base64 encoded images
img_base64_list = []
# Store image summaries
image_summaries = []
# Prompt
prompt = """You are an assistant tasked with summarizing images for retrieval. \
These summaries will be embedded and used to retrieve the raw image. \
Give a concise summary of the image that is well optimized for retrieval."""
# Apply to images
for img_file in sorted(os.listdir(path)):
if img_file.endswith(".jpg"):
img_path = os.path.join(path, img_file)
base64_image = encode_image(img_path)
img_base64_list.append(base64_image)
image_summaries.append(image_summarize(base64_image, prompt))
return img_base64_list, image_summaries
# Image summaries
img_base64_list, image_summaries = generate_img_summaries("./cj")
len(img_base64_list)
len(image_summaries)
image_summaries[0]
Вы должны увидеть примерно такой вывод. 
8. Шаг 5: Создание многовекторного поиска
Давайте сгенерируем текстовые и графические краткие описания и сохраним их в векторном хранилище ChromaDB.
Импортируйте необходимые библиотеки.
import uuid
from langchain.retrievers.multi_vector import MultiVectorRetriever
from langchain.storage import InMemoryStore
from langchain_community.vectorstores import Chroma
from langchain_core.documents import Document
Создание многовекторного поиска
def create_multi_vector_retriever(
vectorstore, text_summaries, texts, table_summaries, tables, image_summaries, images
):
"""
Create retriever that indexes summaries, but returns raw images or texts
"""
# Initialize the storage layer
store = InMemoryStore()
id_key = "doc_id"
# Create the multi-vector retriever
retriever = MultiVectorRetriever(
vectorstore=vectorstore,
docstore=store,
id_key=id_key,
)
# Helper function to add documents to the vectorstore and docstore
def add_documents(retriever, doc_summaries, doc_contents):
doc_ids = [str(uuid.uuid4()) for _ in doc_contents]
summary_docs = [
Document(page_content=s, metadata={id_key: doc_ids[i]})
for i, s in enumerate(doc_summaries)
]
retriever.vectorstore.add_documents(summary_docs)
retriever.docstore.mset(list(zip(doc_ids, doc_contents)))
# Add texts, tables, and images
# Check that text_summaries is not empty before adding
if text_summaries:
add_documents(retriever, text_summaries, texts)
# Check that table_summaries is not empty before adding
if table_summaries:
add_documents(retriever, table_summaries, tables)
# Check that image_summaries is not empty before adding
if image_summaries:
add_documents(retriever, image_summaries, images)
return retriever
# The vectorstore to use to index the summaries
vectorstore = Chroma(
collection_name="mm_rag_cj_blog",
embedding_function=VertexAIEmbeddings(model_name="textembedding-gecko@latest"),
)
# Create retriever
retriever_multi_vector_img = create_multi_vector_retriever(
vectorstore,
text_summaries,
texts,
table_summaries,
tables,
image_summaries,
img_base64_list,
)
9. Шаг 6: Создание многомодальной RAG
- Определить вспомогательные функции
import io
import re
from IPython.display import HTML, display
from langchain_core.runnables import RunnableLambda, RunnablePassthrough
from PIL import Image
def plt_img_base64(img_base64):
"""Disply base64 encoded string as image"""
# Create an HTML img tag with the base64 string as the source
image_html = f'<img src="data:image/jpeg;base64,{img_base64}" />'
# Display the image by rendering the HTML
display(HTML(image_html))
def looks_like_base64(sb):
"""Check if the string looks like base64"""
return re.match("^[A-Za-z0-9+/]+[=]{0,2}$", sb) is not None
def is_image_data(b64data):
"""
Check if the base64 data is an image by looking at the start of the data
"""
image_signatures = {
b"\xFF\xD8\xFF": "jpg",
b"\x89\x50\x4E\x47\x0D\x0A\x1A\x0A": "png",
b"\x47\x49\x46\x38": "gif",
b"\x52\x49\x46\x46": "webp",
}
try:
header = base64.b64decode(b64data)[:8] # Decode and get the first 8 bytes
for sig, format in image_signatures.items():
if header.startswith(sig):
return True
return False
except Exception:
return False
def resize_base64_image(base64_string, size=(128, 128)):
"""
Resize an image encoded as a Base64 string
"""
# Decode the Base64 string
img_data = base64.b64decode(base64_string)
img = Image.open(io.BytesIO(img_data))
# Resize the image
resized_img = img.resize(size, Image.LANCZOS)
# Save the resized image to a bytes buffer
buffered = io.BytesIO()
resized_img.save(buffered, format=img.format)
# Encode the resized image to Base64
return base64.b64encode(buffered.getvalue()).decode("utf-8")
def split_image_text_types(docs):
"""
Split base64-encoded images and texts
"""
b64_images = []
texts = []
for doc in docs:
# Check if the document is of type Document and extract page_content if so
if isinstance(doc, Document):
doc = doc.page_content
if looks_like_base64(doc) and is_image_data(doc):
doc = resize_base64_image(doc, size=(1300, 600))
b64_images.append(doc)
else:
texts.append(doc)
if len(b64_images) > 0:
return {"images": b64_images[:1], "texts": []}
return {"images": b64_images, "texts": texts}
- Определите запрос на отображение изображения, специфичный для данной области.
def img_prompt_func(data_dict):
"""
Join the context into a single string
"""
formatted_texts = "\n".join(data_dict["context"]["texts"])
messages = []
# Adding the text for analysis
text_message = {
"type": "text",
"text": (
"You are financial analyst tasking with providing investment advice.\n"
"You will be given a mixed of text, tables, and image(s) usually of charts or graphs.\n"
"Use this information to provide investment advice related to the user question. \n"
f"User-provided question: {data_dict['question']}\n\n"
"Text and / or tables:\n"
f"{formatted_texts}"
),
}
messages.append(text_message)
# Adding image(s) to the messages if present
if data_dict["context"]["images"]:
for image in data_dict["context"]["images"]:
image_message = {
"type": "image_url",
"image_url": {"url": f"data:image/jpeg;base64,{image}"},
}
messages.append(image_message)
return [HumanMessage(content=messages)]
- Определение многомодальной цепочки RAG
def multi_modal_rag_chain(retriever):
"""
Multi-modal RAG chain
"""
# Multi-modal LLM
model = ChatVertexAI(
temperature=0, model_name="gemini-pro-vision", max_output_tokens=1024
)
# RAG pipeline
chain = (
{
"context": retriever | RunnableLambda(split_image_text_types),
"question": RunnablePassthrough(),
}
| RunnableLambda(img_prompt_func)
| model
| StrOutputParser()
)
return chain
# Create RAG chain
chain_multimodal_rag = multi_modal_rag_chain(retriever_multi_vector_img)
10. Шаг 7: Проверьте свои запросы
- Найдите соответствующие документы.
query = "What are the EV / NTM and NTM rev growth for MongoDB, Cloudflare, and Datadog?"
docs = retriever_multi_vector_img.get_relevant_documents(query, limit=1)
# We get relevant docs
len(docs)
docs
You may get similar output

plt_img_base64(docs[3])

- Запустите наш RAG на том же запросе.
result = chain_multimodal_rag.invoke(query)
from IPython.display import Markdown as md
md(result)
Пример выходных данных (может отличаться при выполнении кода)

11. Уборка
Чтобы избежать списания средств с вашего аккаунта Google Cloud за ресурсы, использованные в этом практическом задании, выполните следующие действия:
- В консоли Google Cloud перейдите на страницу «Управление ресурсами» .
- В списке проектов выберите проект, который хотите удалить, и нажмите кнопку «Удалить» .
- В диалоговом окне введите идентификатор проекта, а затем нажмите «Завершить» , чтобы удалить проект.
12. Поздравляем!
Поздравляем! Вы успешно разработали многомодальную RAG с использованием Gemini.