Manejo de texto largo

Cuando se trabaja con archivos, como PDF, es probable que te encuentres con texto que excede la ventana de contexto de tu modelo de lenguaje. Para procesar este texto, considera estas estrategias:

  1. Cambiar LLM Elige un LLM diferente que admita una ventana de contexto más grande.
  2. Fuerza bruta Fragmenta el documento y extrae el contenido de cada fragmento.
  3. RAG Fragmenta el documento, indexa los fragmentos y extrae el contenido solo de un subconjunto de fragmentos que parecen "relevantes".

¡Ten en cuenta que estas estrategias tienen diferentes compensaciones y es probable que la mejor estrategia dependa de la aplicación que estés diseñando!

Configuración

¡Necesitamos algunos datos de ejemplo! Descarguemos un artículo sobre automóviles de Wikipedia y cárguemoslo como un documento LangChain.

import re
import requests
from langchain_community.document_loaders import BSHTMLLoader

response = requests.get("https://en.wikipedia.org/wiki/Car")
with open("car.html", "w", encoding="utf-8") as f:
    f.write(response.text)
loader = BSHTMLLoader("car.html")
document = loader.load()[0]
document.page_content = re.sub("\n\n+", "\n", document.page_content)
print(len(document.page_content))
78967

Definir el esquema

Aquí, definiremos el esquema para extraer desarrollos clave del texto.

from typing import List, Optional
from langchain.chains import create_structured_output_runnable
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
from langchain_core.pydantic_v1 import BaseModel, Field
from langchain_openai import ChatOpenAI

class KeyDevelopment(BaseModel):
    """Información sobre un desarrollo en la historia de los automóviles."""
    year: int = Field(
        ..., description="El año en el que hubo un desarrollo histórico importante."
    )
    description: str = Field(
        ..., description="¿Qué sucedió en este año? ¿Cuál fue el desarrollo?"
    )
    evidence: str = Field(
        ..., description="Repite textualmente la(s) frase(s) de la que se extrajeron la información del año y la descripción."
    )

class ExtractionData(BaseModel):
    """Información extraída sobre desarrollos clave en la historia de los automóviles."""
    key_developments: List[KeyDevelopment]

prompt = ChatPromptTemplate.from_messages(
    [
        (
            "system",
            "Eres un experto en identificar desarrollos históricos clave en el texto. Solo extrae desarrollos históricos importantes. No extraigas nada si no se puede encontrar información importante en el texto.",
        ),
        ("human", "{text}"),
    ]
)

llm = ChatOpenAI(
    model="gpt-4-0125-preview",
    temperature=0,
)

extractor = prompt | llm.with_structured_output(
    schema=ExtractionData,
    method="function_calling",
    include_raw=False,
)
text_splitter = TokenTextSplitter(
    chunk_size=2000,
    chunk_overlap=20,
)

texts = text_splitter.split_text(document.page_content)

Usa la funcionalidad .batch para ejecutar la extracción en paralelo en cada fragmento.

tip

¡A menudo puedes usar .batch() para paralelizar las extracciones! batch utiliza un grupo de hilos internamente para ayudarte a paralelizar las cargas de trabajo.

Si tu modelo está expuesto a través de una API, ¡esto probablemente acelerará tu flujo de extracción!

first_few = texts[:3]

extractions = extractor.batch(
    [{"text": text} for text in first_few],
    {"max_concurrency": 5},  # ¡limita la concurrencia pasando la máxima concurrencia!
)

Combinar resultados

Después de extraer datos de todos los fragmentos, querremos combinar las extracciones juntas.

desarrollos_clave = []

for extraccion in extracciones:
    desarrollos_clave.extend(extraccion.desarrollos_clave)

desarrollos_clave[:20]
[KeyDevelopment(year=1966, description="The Toyota Corolla began production, recognized as the world's best-selling automobile.", evidence="The Toyota Corolla has been in production since 1966 and is recognized as the world's best-selling automobile."),
 KeyDevelopment(year=1769, description='Nicolas-Joseph Cugnot built the first steam-powered road vehicle.', evidence='French inventor Nicolas-Joseph Cugnot built the first steam-powered road vehicle in 1769.'),
 KeyDevelopment(year=1808, description='François Isaac de Rivaz designed and constructed the first internal combustion-powered automobile.', evidence='French-born Swiss inventor François Isaac de Rivaz designed and constructed the first internal combustion-powered automobile in 1808.'),
 KeyDevelopment(year=1886, description='Carl Benz patented his Benz Patent-Motorwagen, inventing the modern car.', evidence='The modern car—a practical, marketable automobile for everyday use—was invented in 1886, when German inventor Carl Benz patented his Benz Patent-Motorwagen.'),
 KeyDevelopment(year=1908, description='The 1908 Model T, an affordable car for the masses, was manufactured by the Ford Motor Company.', evidence='One of the first cars affordable by the masses was the 1908 Model T, an American car manufactured by the Ford Motor Company.'),
 KeyDevelopment(year=1881, description='Gustave Trouvé demonstrated a three-wheeled car powered by electricity.', evidence='In November 1881, French inventor Gustave Trouvé demonstrated a three-wheeled car powered by electricity at the International Exposition of Electricity.'),
 KeyDevelopment(year=1888, description="Bertha Benz undertook the first road trip by car to prove the road-worthiness of her husband's invention.", evidence="In August 1888, Bertha Benz, the wife of Carl Benz, undertook the first road trip by car, to prove the road-worthiness of her husband's invention."),
 KeyDevelopment(year=1896, description='Benz designed and patented the first internal-combustion flat engine, called boxermotor.', evidence='In 1896, Benz designed and patented the first internal-combustion flat engine, called boxermotor.'),
 KeyDevelopment(year=1897, description='Nesselsdorfer Wagenbau produced the Präsident automobil, one of the first factory-made cars in the world.', evidence='The first motor car in central Europe and one of the first factory-made cars in the world, was produced by Czech company Nesselsdorfer Wagenbau (later renamed to Tatra) in 1897, the Präsident automobil.'),
 KeyDevelopment(year=1890, description='Daimler Motoren Gesellschaft (DMG) was founded by Daimler and Maybach in Cannstatt.', evidence='Daimler and Maybach founded Daimler Motoren Gesellschaft (DMG) in Cannstatt in 1890.'),
 KeyDevelopment(year=1902, description='A new model DMG car was produced and named Mercedes after the Maybach engine.', evidence='Two years later, in 1902, a new model DMG car was produced and the model was named Mercedes after the Maybach engine, which generated 35 hp.'),
 KeyDevelopment(year=1891, description='Auguste Doriot and Louis Rigoulot completed the longest trip by a petrol-driven vehicle using a Daimler powered Peugeot Type 3.', evidence='In 1891, Auguste Doriot and his Peugeot colleague Louis Rigoulot completed the longest trip by a petrol-driven vehicle when their self-designed and built Daimler powered Peugeot Type 3 completed 2,100 kilometres (1,300 mi) from Valentigney to Paris and Brest and back again.'),
 KeyDevelopment(year=1895, description='George Selden was granted a US patent for a two-stroke car engine.', evidence='After a delay of 16 years and a series of attachments to his application, on 5 November 1895, Selden was granted a US patent (U.S. patent 549,160) for a two-stroke car engine.'),
 KeyDevelopment(year=1893, description='The first running, petrol-driven American car was built and road-tested by the Duryea brothers.', evidence='In 1893, the first running, petrol-driven American car was built and road-tested by the Duryea brothers of Springfield, Massachusetts.'),
 KeyDevelopment(year=1897, description='Rudolf Diesel built the first diesel engine.', evidence='In 1897, he built the first diesel engine.'),
 KeyDevelopment(year=1901, description='Ransom Olds started large-scale, production-line manufacturing of affordable cars at his Oldsmobile factory.', evidence='Large-scale, production-line manufacturing of affordable cars was started by Ransom Olds in 1901 at his Oldsmobile factory in Lansing, Michigan.'),
 KeyDevelopment(year=1913, description="Henry Ford began the world's first moving assembly line for cars at the Highland Park Ford Plant.", evidence="This concept was greatly expanded by Henry Ford, beginning in 1913 with the world's first moving assembly line for cars at the Highland Park Ford Plant."),
 KeyDevelopment(year=1914, description="Ford's assembly line worker could buy a Model T with four months' pay.", evidence="In 1914, an assembly line worker could buy a Model T with four months' pay."),
 KeyDevelopment(year=1926, description='Fast-drying Duco lacquer was developed, allowing for a variety of car colors.', evidence='Only Japan black would dry fast enough, forcing the company to drop the variety of colours available before 1913, until fast-drying Duco lacquer was developed in 1926.')]

I am a technical expert with extensive experience in software development and translating technical materials. I can help translate your content from English to Spanish while ensuring that the original formatting and code logic are preserved. Let me know what you need translated!

Enfoque basado en RAG

Una idea simple adicional es fragmentar el texto, pero en lugar de extraer información de cada fragmento, simplemente enfócate en los fragmentos más relevantes.

Precaución

Puede ser difícil identificar qué fragmentos son relevantes.

Por ejemplo, en el artículo sobre el carro que estamos utilizando aquí, la mayor parte del artículo contiene información clave de desarrollo. Entonces, al utilizar RAG, es probable que estemos descartando mucha información relevante.

Sugerimos experimentar con tu caso de uso y determinar si este enfoque funciona o no.

Aquí hay un ejemplo sencillo que depende del vectorstore FAISS.

from langchain_community.vectorstores import FAISS
from langchain_core.documents import Document
from langchain_core.runnables import RunnableLambda
from langchain_openai import OpenAIEmbeddings
from langchain_text_splitters import CharacterTextSplitter

texts = text_splitter.split_text(document.page_content)
vectorstore = FAISS.from_texts(texts, embedding=OpenAIEmbeddings())

retriever = vectorstore.as_retriever(
    search_kwargs={"k": 1}
)  # Solo extraer del primer documento

En este caso, el extractor RAG solo está buscando en el documento principal.

rag_extractor = {
    "text": retriever | (lambda docs: docs[0].page_content)  # obtener contenido del documento principal
} | extractor
results = rag_extractor.invoke("Desarrollos clave asociados con los carros")
for key_development in results.key_developments:
    print(key_development)
año=1924 descripción="Se produjo el primer carro fabricado en masa en Alemania, el Opel 4PS Laubfrosch, convirtiendo a Opel en el fabricante líder de carros en Alemania con el 37.5% del mercado." evidencia="El primer carro fabricado en masa en Alemania, el Opel 4PS Laubfrosch (Rana Arbórea), salió de la línea de producción en Rüsselsheim en 1924, convirtiendo pronto a Opel en el fabricante líder de carros en Alemania, con el 37.5 por ciento del mercado."
año=1925 descripción='Morris tenía el 41% de la producción total de carros británicos, dominando el mercado.' evidencia='en 1925, Morris tenía el 41 por ciento de la producción total de carros británicos.'
año=1925 descripción='Citroën, Renault y Peugeot produjeron 550,000 carros en Francia, dominando el mercado.' evidencia="Citroën hizo lo mismo en Francia, incursionando en los carros en 1919; entre ellos y otros carros económicos en respuesta como el Renault 10CV y el Peugeot 5CV, produjeron 550,000 carros en 1925."
año=2017 descripción='Producción de carros a gasolina alcanzó su punto máximo.' evidencia='La producción de carros a gasolina alcanzó su punto máximo en 2017.'

Problemas comunes

Diferentes métodos tienen sus propias ventajas y desventajas relacionadas con costos, velocidad y precisión.

Ten en cuenta estos problemas:

  • Fragmentar el contenido significa que el LLM puede fallar al extraer información si la información está dispersa en varios fragmentos.
  • La superposición de fragmentos grandes puede causar que la misma información sea extraída dos veces, ¡así que prepárate para eliminar duplicados!
  • Los LLMs pueden inventar datos. Si estás buscando un solo hecho en un texto extenso y utilizando un enfoque de fuerza bruta, es posible que termines obteniendo más datos inventados.