Gestión de Etiquetas de Documentos y Enriquecimiento de Metadatos en Langchain-Chatchat

Desafíos en la recuperación de información contextual

En la gestión del conocimiento empresarial, extraer respuestas precisas de conjuntos extensos de documetnos privados a menudo falla por la falta de contexto. Cuando un usuario consulta algo como "¿cuál fue el producto con mayor crecimiento en ventas el año pasado?", el sistema puede devolver fragmentos semánticamente similares pero irrelevantes. El problema no radica en la capacidad del modelo de lenguaje, sino en la ausencia de marcos de referencia estructurados.

Langchain-Chatchat, como solución de código abierto para bases de conocimiento local, aborda esto mediante la gestión de etiquetas de documentos y el enriquecimiento de metadatos. Esto permite que la recuperación no solo interprete el significado, sino que también comprenda el contexto, mejorando drásticamente la precisión.

Pipeline de procesamiento de datos con metadatos

El flujo comienza con la carga de documentos, donde se extraen automáticamente propiedades básicas como nombre de archivo, ruta y tipo. Estas se almacenan en el campo metadata del objeto Document y se propagan durante toda la cadena de procesamiento.

from langchain.document_loaders import CargadorPDF

cargador = CargadorPDF("datos/informe_ventas_2023.pdf")
documentos_cargados = cargador.cargar()

# Metadatos base generados automáticamente
print(documentos_cargados[0].metadata)
# Salida ejemplo: {'source': 'datos/informe_ventas_2023.pdf', 'page': 0}

El valor real surge al inyectar etiquetas personalizadas, ya sea manualmente o de forma automatizada. Estas etiquetas se convierten en condiciones de filtrado durante la búsqueda, reduciendo el espacio de búsqueda de manera eficiente.

for doc in documentos_cargados:
    doc.metadata["sector"] = "ventas"
    doc.metadata["ejercicio_fiscal"] = 2023
    doc.metadata["tipo_documento"] = "informe_anual"

Técnicas automatizadas de enriquecimiento

Para escalar la asignación de metadatos, se emplean técnicas de PLN. Un clasificador de cero muestras puede identificar temas automáticamente, mientras que el reconocimiento de entidades con nombre extrae personas u organizaciones mencionadas.

from transformers import pipeline

clasificador = pipeline("zero-shot-classification", model="facebook/bart-large-mnli")

def categorizar_documento(texto):
    etiquetas_candidatas = ["política_rrhh", "plan_proyecto", "reporte_financiero", "especificación_técnica"]
    resultado = clasificador(texto, etiquetas_candidatas)
    return resultado['labels'][0], resultado['scores'][0]

tema, confianza = categorizar_documento(documentos_cargados[0].page_content)
documentos_cargados[0].metadata["tema_principal"] = tema
documentos_cargados[0].metadata["confianza_clasificacion"] = confianza

ner = pipeline("ner", model="dbmdz/bert-large-cased-finetuned-conll03-english", aggregation_strategy="simple")

def extraer_entidades(texto):
    entidades = ner(texto)
    personas = list({e["word"] for e in entidades if e["entity_group"] == "PER"})
    organizaciones = list({e["word"] for e in entidades if e["entity_group"] == "ORG"})
    return personas, organizaciones

personas, orgs = extraer_entidades(documentos_cargados[0].page_content)
if personas:
    documentos_cargados[0].metadata["personas_clave"] = personas
if orgs:
    documentos_cargados[0].metadata["entidades_organizacionales"] = orgs

Expresiones regulares pueden complementarse para extraer patrones específicos, como fechas o identificadores.

import re

def extraer_anios_mencionados(texto):
    patron = r"\b(19|20)\d{2}\b"
    anios_encontrados = re.findall(patron, texto)
    return sorted(set(int(f"{x}{y}") for x, y in anios_encontrados))

anios = extraer_anios_mencionados(documentos_cargados[0].page_content)
if anios:
    documentos_cargados[0].metadata["anios_referenciados"] = anios

Integración modular en Langchain-Chatchat

Estas operaciones pueden encapsularse en un transformador de documentos para integrarse de forma fluida en el pipeline.

class EnriquecedorMetadatos:
    def __init__(self):
        self.clasificador = pipeline("zero-shot-classification", model="facebook/bart-large-mnli")
        self.ner = pipeline("ner", model="dbmdz/bert-large-cased-finetuned-conll03-english", aggregation_strategy="simple")

    def enriquecer_documentos(self, documentos):
        documentos_procesados = []
        for documento in documentos:
            fragmento = documento.page_content[:500]
            
            # Clasificación temática
            resultado_clasificacion = self.clasificador(fragmento, ["financiero", "técnico", "legal", "operativo"], multi_label=True)
            documento.metadata["clasificacion_tema"] = resultado_clasificacion['labels'][0]
            documento.metadata["puntuacion_tema"] = resultado_clasificacion['scores'][0]
            
            # Extracción de entidades
            entidades = self.ner(fragmento)
            menciones_persona = [ent["word"] for ent in entidades if ent["entity_group"] == "PER"]
            if menciones_persona:
                documento.metadata["menciones_persona"] = list(set(menciones_persona))
            
            documentos_procesados.append(documento)
        return documentos_procesados

Este transformador se inserta después de la división en fragmentos y antes de la vectorización.

from langchain.text_splitter import DivisorTextoRecursivo

divisor = DivisorTextoRecursivo(longitud_fragmento=500, superposicion=50)
fragmentos = divisor.split_documents(documentos_cargados)

enriquecedor = EnriquecedorMetadatos()
fragmentos_enriquecidos = enriquecedor.enriquecer_documentos(fragmentos)

from langchain.embeddings import EmbeddingsHuggingFace
modelo_embed = EmbeddingsHuggingFace(modelo_nombre="sentence-transformers/all-MiniLM-L6-v2")

from langchain.vectorstores import Chroma
almacen_vectores = Chroma.from_documents(fragmentos_enriquecidos, embedding=modelo_embed, directorio_persistente="./almacen_chroma")

Aplicaciones y consideraciones de ingeniería

Los metadatos enriquecidos habilitan funcionalidades avanzadas:

  • Control de acceso: Combinar etiquetas de confidencialidad con roles de usuario para una autorización granular.
  • Trazabilidad: Asociar cada respuesta con documentos específicos, páginas y autores para auditorías.
  • Consultas complejas: Permitir filtros compuestos, como "documentos sobre proyectos de IA escritos por Juan Pérez antes de 2024".

Al implementar, se deben considerar aspectos clave:

  • Mantener un número limitado de etiquetas esenciales (5-8) para optimizar el rendimiento de las consultas.
  • Utilizar procesamiento por lotes o caché para operaciones de enriquecimiento costosas, evitando bloqueos.
  • Estandarizar nombres de metadatos (ej: tipo_documento en lugar de tipo, categoría).
  • Seleccionar bases de datos vectoriales con soporte nativo para filtrado por metadatos, como Chroma o Milvus.
  • Diseñar mecanismos para actualizaciones incrementales, evitando reindexaciones completas.

El marco proporcionado por Langchain-Chatchat transforma documentos no estructurados en activos de información gestionables. La evolución hacia modelos especializados y herramientas de etiquetado automático promete enriquecimientos más inteligentes y adaptativos, cerrando el bucle entre la recuperación de información y el contexto dinámico.

Etiquetas: LangChain chatchat metadatos gestion-documentos procesamiento-lenguaje-natural

Publicado el 7-1 04:28