Полное руководство: Исключение метаданных из эмбеддингов в LlamaIndex
Решение ошибок 'длина метаданных превышает размер чанка' в LlamaIndex 0.14.7. Узнайте, как исключить метаданные из эмбеддингов, сохраняя их для извлечения с примерами кода.
Как исключить метаданные из встраивания в LlamaIndex 0.14.7?
Я использую LlamaIndex 0.14.7 и хочу встраивать текст документа без конкатенации метаданных, так как у меня в метаданных хранится длинный текст. Вот моя текущая реализация:
table_vec_store: SimpleVectorStore = SimpleVectorStore()
pipeline: IngestionPipeline = IngestionPipeline(
transformations=[
SentenceSplitter(chunk_size=300, chunk_overlap=15, include_metadata=False),
embed_model
],
vector_store=table_vec_store
)
pipeline.run(documents=table_documents)
self._table_index = VectorStoreIndex.from_vector_store(table_vec_store)
Несмотря на установку include_metadata=False в SentenceSplitter, я столкнулся с этой ошибкой:
ValueError: Metadata length (348) is longer than chunk size (300). Consider increasing the chunk size or decreasing the size of your metadata to avoid this.
Мне нужно использовать текст документа для индексации, но также требуется длинный текст в метаданных для извлечения. Как можно изменить мой код для решения этой проблемы?
Исключение метаданных из встраивания в LlamaIndex 0.14.7
Чтобы исключить метаданные из встраивания в LlamaIndex 0.14.7, сохранив метаданные для извлечения, необходимо использовать свойство excluded_embed_metadata_keys. Проблема возникает потому, что метаданные все еще учитываются при разбиении на фрагменты (chunking), даже когда установлено include_metadata=False.
Содержание
- Понимание проблемы с метаданными
- Основное решение: Использование excluded_embed_metadata_keys
- Альтернативные решения и обходные пути
- Лучшие практики управления метаданными
- Полный рабочий пример
- Дополнительные ресурсы
Понимание проблемы с метаданными
Ошибка “Длина метаданных (348) превышает размер фрагмента (300)” возникает потому, что LlamaIndex учитывает метаданные в процессе разбиения на фрагменты, независимо от настройки include_metadata=False. Согласно обсуждениям сообщества LlamaIndex, “это происходит потому, что метаданные по умолчанию показываются языковой модели и моделям встраивания”.
Несмотря на то, что вы установили include_metadata=False в SentenceSplitter, длина метаданных все еще рассчитывается и влияет на процесс разбиения на фрагменты. Это известное поведение в LlamaIndex 0.14.7.
Основное решение: Использование excluded_embed_metadata_keys
Наиболее эффективное решение - установить свойство excluded_embed_metadata_keys для ваших документов перед обработкой. Это указывает LlamaIndex исключить определенные ключи метаданных из процесса встраивания, сохраняя их доступными для извлечения.
Вот как изменить ваш код:
from llama_index.core import Document, SimpleVectorStore, IngestionPipeline
from llama_index.core.node_parser import SentenceSplitter
from llama_index.core.node_parser.text import MetadataMode
# Предполагая, что table_documents - это ваш список объектов Document
for doc in table_documents:
# Устанавливаем исключаемые ключи метаданных для встраивания
doc.excluded_embed_metadata_keys = ["your_long_text_field", "other_metadata_fields"]
# Опционально: также исключаем из контекста LLM, если необходимо
doc.excluded_llm_metadata_keys = ["your_long_text_field", "other_metadata_fields"]
# Создаем конвейер обработки
table_vec_store: SimpleVectorStore = SimpleVectorStore()
pipeline: IngestionPipeline = IngestionPipeline(
transformations=[
SentenceSplitter(chunk_size=300, chunk_overlap=15, include_metadata=False),
embed_model
],
vector_store=table_vec_store
)
pipeline.run(documents=table_documents)
self._table_index = VectorStoreIndex.from_vector_store(table_vec_store)
Если вы хотите исключить все метаданные из встраивания, можно использовать:
for doc in table_documents:
# Исключаем все метаданные из встраивания
doc.excluded_embed_metadata_keys = list(doc.metadata.keys()) if doc.metadata else []
Альтернативные решения и обходные пути
Решение 1: Изменение метаданных перед обработкой
Если вам нужно временно удалить метаданные при обработке, но сохранить их для извлечения:
# Создаем копию документов с временным удалением метаданных
temp_documents = []
for doc in table_documents:
# Сохраняем исходные метаданные
original_metadata = doc.metadata.copy()
# Создаем временный документ с минимальными метаданными
temp_doc = Document(
doc_id=doc.doc_id,
text=doc.text,
metadata={"filename": original_metadata.get("filename", "")} # Оставляем только необходимые метаданные
)
temp_documents.append(temp_doc)
# Обрабатываем с временными документами
pipeline.run(documents=temp_documents)
# Восстанавливаем исходные метаданные в обработанных узлах
for i, node in enumerate(temp_documents):
node.metadata = original_metadata
Решение 2: Увеличение размера фрагмента
Если ваши метаданные важны для процесса встраивания, вам может потребоваться увеличить размер фрагмента:
pipeline: IngestionPipeline = IngestionPipeline(
transformations=[
SentenceSplitter(chunk_size=500, chunk_overlap=15, include_metadata=False),
embed_model
],
vector_store=table_vec_store
)
Решение 3: Использование стратегии замены метаданных
Согласно документации LlamaIndex, вы можете использовать стратегии замены метаданных:
from llama_index.core.node_parser import SentenceWindowNodeParser
# Создаем парсер узлов с окном предложений
node_parser = SentenceWindowNodeParser.from_defaults(
window_size=3,
window_metadata_key="window",
original_text_metadata_key="original_text",
)
# Используем это вместо обычного SentenceSplitter
Лучшие практики управления метаданными
1. Разделяйте текст и метаданные
Храните длинный текстовый контент в текстовом поле документа, а не в метаданных:
# Вместо:
doc = Document(text="короткий текст", metadata={"long_content": "здесь очень длинный текст..."})
# Используйте:
doc = Document(text="здесь очень длинный текст...", metadata={"source": "document.pdf"})
2. Используйте метаданные эффективно
Держите метаданные минимальными и сфокусированными на существенной информации:
# Только необходимые метаданные
metadata = {
"source": "document.pdf",
"page": 1,
"author": "Иван Иванов",
"created": "2024-01-01"
}
3. Обрабатывайте метаданные при извлечении
При запросе вы все еще можете получить доступ к метаданным для контекста:
# Пример запроса
query_engine = self._table_index.as_query_engine()
response = query_engine.query("Какова основная тема?")
# Доступ к метаданным из извлеченных узлов
for node in response.source_nodes:
print(f"Источник: {node.metadata.get('source', 'неизвестно')}")
print(f"Длинный контент из метаданных: {node.metadata.get('long_content', '')}")
Полный рабочий пример
Вот полное рабочее решение, которое решает вашу конкретную проблему:
from llama_index.core import Document, SimpleVectorStore, IngestionPipeline
from llama_index.core.node_parser import SentenceSplitter
from llama_index.core.embeddings import resolve_embed_model
# Пример документов с длинными метаданными
table_documents = [
Document(
doc_id="doc1",
text="Это основной контент, который должен быть встроен.",
metadata={
"source": "report.pdf",
"page": 1,
"long_description": "Это очень длинное описание, которое содержит подробную информацию о содержании документа и контексте. Оно может содержать несколько абзацев и обширные детали, которые не подходят для встраивания, но важны для понимания контекста документа."
}
)
]
# Определяем модель встраивания (пример)
embed_model = resolve_embed_model("local:BAAI/bge-small-en-v1.5")
# Устанавливаем исключаемые ключи метаданных перед обработкой
for doc in table_documents:
# Исключаем длинное текстовое поле из встраивания
doc.excluded_embed_metadata_keys = ["long_description"]
# Сохраняем другие метаданные для извлечения
doc.excluded_llm_metadata_keys = [] # Не исключаем из контекста LLM
# Создаем конвейер обработки
table_vec_store: SimpleVectorStore = SimpleVectorStore()
pipeline: IngestionPipeline = IngestionPipeline(
transformations=[
SentenceSplitter(chunk_size=300, chunk_overlap=15, include_metadata=False),
embed_model
],
vector_store=table_vec_store
)
# Запускаем конвейер
pipeline.run(documents=table_documents)
# Создаем индекс
self._table_index = VectorStoreIndex.from_vector_store(table_vec_store)
# Проверяем результат
retriever = self._table_index.as_retriever(similarity_top_k=1)
nodes = retriever.retrieve("Какова основная тема?")
for node in nodes:
print(f"Текст: {node.text}")
print(f"Метаданные: {node.metadata}")
print(f"Длинное описание из метаданных: {node.metadata.get('long_description', '')}")
Дополнительные ресурсы
- Документация LlamaIndex - Парсеры узлов
- Stack Overflow - Исключение метаданных из встраивания в LlamaIndex
- Обсуждение сообщества - Длина метаданных связана с размером фрагмента
- GitHub Issue - Длина метаданных превышает размер фрагмента
Помните, что подход с использованием excluded_embed_metadata_keys является наиболее надежным решением для LlamaIndex 0.14.7, так как он напрямую решает коренную причину проблемы с длиной метаданных при встраивании.
Источники
- Stack Overflow - Исключение метаданных из встраивания в llamaindex
- Community.llamaindex.ai - Почему длина метаданных связана с размером фрагмента
- Medium.com - LlamaIndex: Улучшение контекста с заменой метаданных
- GitHub - Проблема с длиной метаданных превышающей размер фрагмента
- Stack Overflow - Sentence splitter Llama Index ограничен длиной метаданных
- Документация LlamaIndex - API SentenceSplitter
Заключение
Чтобы успешно исключить метаданные из встраивания в LlamaIndex 0.14.7:
-
Используйте
excluded_embed_metadata_keys- Это основное решение, которое исключает определенные поля метаданных из встраивания, сохраняя их доступными для извлечения. -
Устанавливайте ключи метаданных перед обработкой - Применяйте исключаемые ключи к вашим документам перед запуском конвейера обработки.
-
Учитывайте размещение метаданных - По возможности храните длинный контент в текстовом поле документа, а не в метаданных.
-
Обрабатывайте метаданные при извлечении - Помните, что исключенные метаданные все еще доступны для доступа при обработке запросов.
-
Выбирайте правильный размер фрагмента - Если вы должны включить некоторые метаданные, соответственно настройте размер фрагмента.
Ключевое понимание заключается в том, что include_metadata=False в SentenceSplitter не предотвращает влияние метаданных на процесс разбиения на фрагменты - вам необходимо явно исключить ключи метаданных с помощью свойства excluded_embed_metadata_keys документа, чтобы решить ошибку “длина метаданных превышает размер фрагмента”.