Gérer un long texte
Lorsque vous travaillez avec des fichiers, tels que des PDF, il est probable que vous rencontriez du texte qui dépasse la fenêtre contextuelle de votre modèle linguistique. Pour traiter ce texte, envisagez ces stratégies:
- Changer de LLM Choisissez un LLM différent qui prend en charge une fenêtre contextuelle plus large.
- Force brute Découpez le document et extrayez le contenu de chaque morceau.
- RAG Découpez le document, indexez les morceaux et extrayez uniquement le contenu d'un sous-ensemble de morceaux qui semblent "pertinents".
Gardez à l'esprit que ces stratégies présentent des compromis différents et que la meilleure stratégie dépend probablement de l'application que vous concevez!
Configuration
Nous avons besoin de données d'exemple! Téléchargeons un article sur les voitures de Wikipedia et chargeons-le en tant que document 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
Définir le schéma
Ici, nous allons définir un schéma pour extraire les principaux développements du texte.
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):
"""Informations sur un développement dans l'histoire des voitures."""
year: int = Field(
..., description="L'année où un développement historique important a eu lieu."
)
description: str = Field(
..., description="Qu'est-il arrivé cette année-là? Quel a été le développement?"
)
evidence: str = Field(
...,
description="Répétez textuellement la phrase ou les phrases à partir desquelles les informations sur l'année et la description ont été extraites",
)
class ExtractionData(BaseModel):
"""Informations extraites sur les principaux développements dans l'histoire des voitures."""
key_developments: List[KeyDevelopment]
prompt = ChatPromptTemplate.from_messages(
[
(
"system",
"Vous êtes un expert dans l'identification des principaux développements historiques dans un texte. "
"N'extraire que les développements historiques importants. N'extraire rien s'il n'y a pas d'informations importantes dans le texte.",
),
("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,
)
/home/eugene/.pyenv/versions/3.11.2/envs/langchain_3_11/lib/python3.11/site-packages/langchain_core/_api/beta_decorator.py:86: LangChainBetaWarning: The function `with_structured_output` is in beta. It is actively being worked on, so the API may change.
warn_beta(
Approche de la force brute
Divisez les documents en morceaux de manière à ce que chaque morceau s'insère dans la fenêtre contextuelle des LLM.
from langchain_text_splitters import TokenTextSplitter
text_splitter = TokenTextSplitter(
chunk_size=2000,
chunk_overlap=20,
)
texts = text_splitter.split_text(document.page_content)
Utilisez la fonctionnalité .batch
pour exécuter l'extraction en parallèle sur chaque morceau!
Astuce:
Vous pouvez souvent utiliser .batch() pour paralléliser les extractions! batch
utilise un pool de threads en interne pour vous aider à paralléliser les charges de travail.
Si votre modèle est exposé via une API, cela accélérera probablement votre flux d'extraction!
first_few = texts[:3]
extractions = extractor.batch(
[{"text": text} for text in first_few],
{"max_concurrency": 5}, # limitez la concurrence en spécifiant la concurrence maximale!
)
Fusion des résultats
Après avoir extrait des données des différentes tranches, nous voudrons fusionner les extractions ensemble.
developpements_cles = []
for extraction in extractions:
developpements_cles.extend(extraction.developpements_cles)
developpements_cles[: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.')]
If you have any technical material that needs to be translated from English to French, feel free to share it with me. I'll make sure to provide a high-quality translation while maintaining the original format and technical accuracy.
Approche basée sur RAG
Une autre idée simple est de découper le texte en morceaux, mais au lieu d'extraire des informations de chaque morceau, concentrez-vous uniquement sur les morceaux les plus pertinents.
Attention
Il peut être difficile d'identifier quels morceaux sont pertinents.
Par exemple, dans l'article sur les voitures
que nous utilisons ici, la plupart de l'article contient des informations clés sur le développement. Ainsi, en utilisant RAG, nous risquons de rejeter beaucoup d'informations pertinentes.
Nous vous suggérons d'expérimenter avec votre cas d'utilisation et de déterminer si cette approche fonctionne ou non.
Voici un exemple simple qui repose sur le FAISS
vectorstore.
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}
) # Extraire uniquement à partir du premier document
Dans ce cas, l'extracteur RAG ne regarde que le document supérieur.
rag_extractor = {
"text": retriever | (lambda docs: docs[0].page_content) # récupérer le contenu du document supérieur
} | extractor
results = rag_extractor.invoke("Évolutions clés associées aux voitures")
for key_development in results.key_developments:
print(key_development)
année=1924 description="La première voiture fabriquée en série en Allemagne, l'Opel 4PS Laubfrosch, a été produite, faisant d'Opel le premier constructeur automobile en Allemagne avec 37,5% du marché." preuve="La première voiture fabriquée en série en Allemagne, l'Opel 4PS Laubfrosch (Grenouille arboricole), est sortie de la chaîne de production à Rüsselsheim en 1924, faisant bientôt d'Opel le premier constructeur automobile en Allemagne, avec 37,5 % du marché."
année=1925 description="Morris détenait 41% de la production totale de voitures britanniques, dominant ainsi le marché." preuve="en 1925, Morris détenait 41 % de la production totale de voitures britanniques."
année=1925 description="Citroën, Renault et Peugeot ont produit 550 000 voitures en France, dominant le marché." preuve="Citroën en a fait autant en France, arrivant sur le marché des voitures en 1919 ; entre elles et d'autres voitures bon marché en réponse telles que la Renault 10CV et la Peugeot 5CV, elles ont produit 550 000 voitures en 1925."
année=2017 description="La production de voitures à essence a atteint son apogée." preuve="La production de voitures à essence a atteint son apogée en 2017."
Problèmes courants
Différentes méthodes ont leurs propres avantages et inconvénients liés au coût, à la vitesse et à la précision.
Méfiez-vous de ces problèmes :
- Le découpage du contenu signifie que le LLM peut échouer à extraire des informations si celles-ci sont réparties sur plusieurs morceaux.
- Un chevauchement important entre les morceaux peut entraîner l'extraction deux fois des mêmes informations, donc soyez prêt à effectuer une déduplication !
- Les LLM peuvent inventer des données. Si vous cherchez un seul fait sur un long texte et utilisez une approche de force brute, vous risquez d'obtenir plus de données inventées.