Document LlamaIndex, ¿qué son estos objetos en el contexto de los modelos LLM?

Los objetos Document son abstracciones clave dentro de LlamaIndex.

Un Document es un contenedor genérico para cualquier fuente de datos, por ejemplo, un PDF, el output de una API o datos recuperados de una base de datos. Se pueden construir manualmente, o crearse automáticamente via data loaders, también conocidos como Data Connectors (LlamaHub).

Por defecto, un Document almacena texto junto a otros atributos, por ejemplo:

  • metadata – un diccionario con pares clave-valor que se pueden agregar al texto.
  • relationships – un diccionario que contiene las relaciones con otros Document.

Se pueden añadir otras como keywords, summary o questions, todo dependerá del caso de uso que quieras implementar.

En la documentación oficial de LlamaIndex te encontrarás con una nota que dice: we have beta support for allowing Documents to store images, and are actively working on improving its multimodal capabilities.

Por otro lado, un Node representa un «chunk» de un Document, es decir un fragmento del objeto Document, o dicho de otra manera, un Document esta formado por 1 o varios Nodes. Ese fragmento puede ser de texto, una imágen u otra cosa. Al igual que los Document, los Nodes contienen metadatos e información de las relaciones con los otros nodos. Por defecto, cada Node derivado de Document herederá los mismos metadatos de ese Document, por ejemplo, un file_name se propagará a cada Node.

¿Cómo se define un objeto Document en LlamaIndex?

Cómo mencionábamos arriba, los objetos Document se pueden crear automáticamente vía Data Loaders, o construídos manualmente. Por defecto, todos los Data Connectors (incluídos los que puedes encontrar vía LlamaHub) devuelven un objeto Document a través de la función load_data.

from llama_index.core import SimpleDirectoryReader

documents = SimpleDirectoryReader("./data").load_data()
¿Qué es SimpleDirectoryReader?

SimpleDirectoryReader es el conector de datos más utilizado y que Simplemente tienes que pasar un directorio de entrada o una lista de archivos. La clase automáticamente seleccionará el mejor lector de archivos según las extensiones de archivo. Puedes verlo en profundidad aquí.

En este caso, como data voy a utilizar un archivo PDF llamado TSLA-Q1-2024-Update.pdf que he descargado de aquí, pero tu puedes utilizar la fuente de datos que quieras, siempre y cuando tenga una de estas extensiones (.hwp, .pdf, .docx, .pptx, .ppt, .pptm, .jpg, .png, .jpeg, .mp3, .mp4, .csv, .epub, .md, .mbox, .ipynb)

Si imprimimos documentos por pantalla, vermos una lista con diferentes objetos Document, cada uno con un identificador único. Aunque hayas utilizado otra fuente de información, verás algo similar a:

[Document(id_=’b7497362-1117-4f98-b483-08148f09cd5f’, embedding=None, metadata={‘page_label’: ‘1’, ‘file_name’: ‘TSLA-Q1-2024-Update.pdf’, ‘file_path’: ‘c:\\Users\\Usuario\\Downloads\\medium\\data\\TSLA-Q1-2024-Update.pdf’, ‘file_type’: ‘application/pdf’, ‘file_size’: 7458951, ‘creation_date’: ‘2024-06-03’, ‘last_modified_date’: ‘2024-06-03’}, excluded_embed_metadata_keys=[‘file_name’, ‘file_type’, ‘file_size’, ‘creation_date’, ‘last_modified_date’, ‘last_accessed_date’], excluded_llm_metadata_keys=[‘file_name’, ‘file_type’, ‘file_size’, ‘creation_date’, ‘last_modified_date’, ‘last_accessed_date’], relationships={}, text=’Q1 2024 Update\n1′, start_char_idx=None, end_char_idx=None, text_template='{metadata_str}\n\n{content}’, metadata_template='{key}: {value}’, metadata_seperator=’\n’)

Si lo formateamos un poco:

import json

# Importamos la clase Document de LlamaIndex
from llama_index.core import Document

document = Document(id_='b7497362-1117-4f98-b483-08148f09cd5f', embedding=None, metadata={'page_label': '1', 'file_name': 'TSLA-Q1-2024-Update.pdf', 'file_path': 'c:\\Users\\Usuario\\Downloads\\medium\\data\\TSLA-Q1-2024-Update.pdf', 'file_type': 'application/pdf', 'file_size': 7458951, 'creation_date': '2024-06-03', 'last_modified_date': '2024-06-03'}, excluded_embed_metadata_keys=['file_name', 'file_type', 'file_size', 'creation_date', 'last_modified_date', 'last_accessed_date'], excluded_llm_metadata_keys=['file_name', 'file_type', 'file_size', 'creation_date', 'last_modified_date', 'last_accessed_date'], relationships={}, text='Q1 2024 Update\n1', start_char_idx=None, end_char_idx=None, text_template='{metadata_str}\n\n{content}', metadata_template='{key}: {value}', metadata_seperator='\n')

json_document = json.dumps(document.__dict__, indent=4)
print(json_document)

Así se debería ver:

{
    "id_": "b7497362-1117-4f98-b483-08148f09cd5f",
    "embedding": null,
    "metadata": {
        "page_label": "1",
        "file_name": "TSLA-Q1-2024-Update.pdf",
        "file_path": "c:\\Users\\Usuario\\Downloads\\medium\\data\\TSLA-Q1-2024-Update.pdf",
        "file_type": "application/pdf",
        "file_size": 7458951,
        "creation_date": "2024-06-03",
        "last_modified_date": "2024-06-03"
    },
    "excluded_embed_metadata_keys": [
        "file_name",
        "file_type",
        "file_size",
        "creation_date",
        "last_modified_date",
        "last_accessed_date"
    ],
    "excluded_llm_metadata_keys": [
        "file_name",
        "file_type",
        "file_size",
        "creation_date",
        "last_modified_date",
        "last_accessed_date"
    ],
    "relationships": {},
    "text": "Q1 2024 Update\n1",
    "start_char_idx": null,
    "end_char_idx": null,
    "text_template": "{metadata_str}\n\n{content}",
    "metadata_template": "{key}: {value}",
    "metadata_seperator": "\n"
}

A continuación, se explica cada parte de la salida:

  1. "id_": "b7497362-1117-4f98-b483-08148f09cd5f": Este es el identificador único asignado al Document. Sirve para distinguir este Document de otros en el índice.
  2. "embedding": null: Este atributo representa el embedding del Document, que es una representación vectorial del contenido del texto. En este caso, el valor es null, lo que indica que no se ha generado un embedding para este Document.
  3. "metadata": { ... }: Este objeto contiene metadatos adicionales asociados al Document. En este caso, incluye información como el número de página ("page_label": "1"), el nombre del archivo ("file_name": "TSLA-Q1-2024-Update.pdf"), la ruta del archivo ("file_path": "c:\\Users\\Usuario\\Downloads\\medium\\data\\TSLA-Q1-2024-Update.pdf"), el tipo de archivo ("file_type": "application/pdf"), el tamaño del archivo en bytes ("file_size": 7458951), la fecha de creación ("creation_date": "2024-06-03") y la fecha de última modificación ("last_modified_date": "2024-06-03").
  4. "excluded_embed_metadata_keys": [...] y "excluded_llm_metadata_keys": [...]: Estas listas especifican las claves de metadatos que se excluirán al generar embeddings o al interactuar con el modelo de lenguaje (LLM) respectivamente. En este caso, se excluyen las claves relacionadas con el nombre del archivo, tipo de archivo, tamaño del archivo, fecha de creación, fecha de última modificación y fecha de último acceso.
  5. "relationships": {}: Este objeto representa las relaciones del Document con otros documentos o nodos en el índice. En este caso, está vacío, lo que indica que no se han definido relaciones para este Document.
  6. "text": "Q1 2024 Update\n1": Este es el contenido textual del Document. En este caso, contiene el título «Q1 2024 Update» seguido de un salto de línea y el número «1».
  7. "start_char_idx": null y "end_char_idx": null: Estos atributos representan los índices de inicio y fin de los caracteres del contenido textual dentro de un documento más grande. En este caso, ambos valores son null, lo que indica que no se han especificado estos índices.
  8. "text_template": "{metadata_str}\n\n{content}", "metadata_template": "{key}: {value}" y "metadata_seperator": "\n": Estos atributos definen las plantillas y el separador utilizados para formatear la representación textual del Document cuando se combina con los metadatos.

También puedes decidir construir los Document manualmente.
LlamaIndex expone la estructura del objeto:

from llama_index.core import Document

text_list = ["Este es el texto del primer Document", "Este es el texto del segundo Document"]
documents = [Document(text=t) for t in text_list]
documents

[Document(id_=’8af92c72-e268-4a12-ab61-410989ea9951′, embedding=None, metadata={}, excluded_embed_metadata_keys=[], excluded_llm_metadata_keys=[], relationships={}, text=’Este es el texto del primer Document‘, start_char_idx=None, end_char_idx=None, text_template='{metadata_str}\n\n{content}’, metadata_template='{key}: {value}’, metadata_seperator=’\n’)

Para acelerar el prototipado y el desarrollo, puedes crear un document usando un texto por defecto con este método document = Document.example()


Lee estos artículos relacionados:

SimpleDirectoryReader. ¿Cómo cargar datos de archivos locales con LlamaIndex?

CREDITS: https://docs.llamaindex.ai/en/latest/module_guides/loading/documents_and_nodes/usage_documents/