Intégration de Google Drive avec langchain

OCTO propose, en Open Source, une nouvelle intégration de langchain avec Google Drive, pour permettre d’exploiter simplement la recherche, tous les types de fichiers, avec conversions, etc.

Pour utiliser les différents modèles de Large Language Model (LLM), de nombreux frameworks sont proposés. L’un des plus utilisés est probablement langchain, proposé par Lance Martin. Ce framework permet de combiner plusieurs appels à un LLM dans une chaîne d’appel, pour obtenir des résultats plus pertinents.

Le framework langchain propose une intégration simple avec Google Drive. Chez OCTO, utilisant essentiellement Google Drive pour nos données internes, nous avons besoin d’une évolution majeure de cette intégration.

Pourquoi nous ne sommes pas satisfaits de l’implémentation proposée ?

  • Elle ne permet pas d’exploiter la recherche de document dans GDrive
  • Elle est limitée à une seule page de résultat
  • Seuls les documents GDoc et GSheet sont partiellement convertis en texte
  • Elle n’est pas compatible avec tous les paramètres de l’API de Google Drive pour gérer les fichiers effacés, les références de documents, les documents partagées
  • Etc.

Nous avons alors décidé de proposer une version plus évoluée. Cette dernière expose un Loader, un Retriever et un Tools, permettant au LLM d’interroger GDrive s’il le juge nécessaire.

Elle est complètement compatible avec l’API de Google Drive:

  • Gère les fichiers dans la corbeille (pour ne pas les renvoyer à l’application)
  • Gère les raccourcis/liens (avec détection des doublons, pour ne pas retrouver plusieurs fois le même document)
  • Gère la description des fichiers, pour éviter le chargement d’un document en entier, alors qu’un résumé dans la description est suffisant
  • N’est pas limité en nombre de fichiers retourné
  • Est capable de gérer tout type de fichiers que langchain sait convertir en texte
  • Et y ajoute la conversion des GDoc, GSlide et GSheet
    • En markdown
    • Avec gestion des liens
    • Des bullets
    • Et des tableaux
  • Utilise une stratégie paresseuse pour n’avoir en mémoire qu’un document à la fois
  • Utilise une variable d’environnement pour l’authentification, afin d’être conforme aux Twelve-Factor App.

Par défaut, voici les différents types mime que nous traitons :

  • text/text
  • text/plain
  • text/html
  • text/csv
  • text/markdown
  • image/png
  • image/jpeg
  • application/epub+zip
  • application/pdf
  • application/rtf
  • application/vnd.google-apps.document (GDoc)
  • application/vnd.google-apps.presentation (GSlide)
  • application/vnd.google-apps.spreadsheet (GSheet)
  • application/vnd.google.colaboratory (Notebook colab)
  • application/vnd.openxmlformats-officedocument.presentationml.presentation (PPTX)
  • application/vnd.openxmlformats-officedocument.wordprocessingml.document (DOCX)

Voici quelques exemples d’utilisations:

from langchain_googledrive.document_loaders import GoogleDriveLoader
loader = GoogleDriveLoader(
                gdrive_api_file=os.environ["GOOGLE_ACCOUNT_FILE"],
                num_results=2,
                template="gdrive-query",
                query='machine learning',
                supportsAllDrives=False,
                )
for doc in loader.load():
    print("---")
    print(doc.page_content.strip()[:60]+"...")

Utilisation via un retriever, avec un template de recherche sur mesure.

from langchain import PromptTemplate
from langchain_googledrive.retrievers import GoogleDriveRetriever
retriever = GoogleDriveRetriever(
    template=PromptTemplate(input_variables=['query'],
      template="(fullText contains '{query}') "
        "And mimeType='application/vnd.google-apps.document' "
        "and modifiedTime > '2000-01-01T00:00:00' "
        "and trashed=false"),
    num_results=2,
    includeItemsFromAllDrives=False,
    supportsAllDrives=False,
)
for doc in retriever.get_relevant_documents("machine learning"):
    print(f"{doc.metadata['name']}:")
    print("---")
    print(doc.page_content.strip()[:60]+"...")

Utilisation en tant que tools

from langchain_googledrive.utilities.google_drive import GoogleDriveAPIWrapper
from langchain_googledrive.tools.google_drive.tool import GoogleDriveSearchTool

folder_id='root'
# By default, search only in the filename.
tool = GoogleDriveSearchTool(
    api_wrapper=GoogleDriveAPIWrapper(
        folder_id=folder_id,
        num_results=2,
        template="gdrive-query-in-folder", # Search in the body of documents
    )
)
from langchain import OpenAI
from langchain.agents import initialize_agent, AgentType
llm = OpenAI(temperature=0)
agent = initialize_agent(
    tools=[tool],
    llm=llm,
    agent=AgentType.STRUCTURED_CHAT_ZERO_SHOT_REACT_DESCRIPTION,
)
agent.run(
    "Search in google drive, who is 'Yann LeCun' ?"
)

Tous les paramètres sont compatibles avec l’API list() de Google et peuvent être valorisés.

Modifier le pattern de recherche

Comme vous pouvez le constater dans les exemples, il est possible d’utiliser un template de recherche. Pour spécifier de nouveaux patterns lors de la recherche, vous devez utiliser un PromptTemplate(). Les variables du prompt peuvent être valorisés via des paramètres supplémentaires type kwargs, dans le constructeur, ou lors de l’interrogation.

Il existe une liste de templates pré-paramétré qu’il suffit d’indiquer par leurs noms. Certains nécessitent des paramètres complémentaires ({query}, {folder_id} et/ou {mime_type}).

templatedescription
gdrive-all-in-folderRetourne tous les documents compatibles depuis un folder_id
gdrive-queryFait une recherche GDrive sur le contenu des documents (query)
gdrive-by-nameFait une recherche sur les noms des fichiers (query)
gdrive-query-in-folderFait une recherche de document, dans un répertoire et éventuellement ses sous-répertoires (si recursive=true)
gdrive-mime-typeRecherche un mime_type spécifique
gdrive-mime-type-in-folderRecherche un mime_type spécifique dans un folder_id
gdrive-query-with-mime-typeFaire une recherche (query) en filtrant sur un mime_type
gdrive-query-with-mime-type-and-folderFait une recherche (query) avec un mime_type dans un folder_id
loader = GoogleDriveLoader(
    folder_id=folder_id,
    recursive=False,
    template="gdrive-query",  # Default template to use
    query="machine learning",
    num_results=2,            # Maximum number of file to load
    supportsAllDrives=False,  # GDrive `list()` parameter
)
for doc in loader.load():
    print("---")
    print(doc.page_content.strip()[:60]+"...")

Mais vous être libre d’enrichir tout cela. Par exemple, pour faire une recherche combinée: nom de fichier et contenu.

from langchain.prompts.prompt import PromptTemplate
loader = GoogleDriveLoader(
    folder_id=folder_id,
    recursive=False,
    template=PromptTemplate(
        input_variables=["query", "query_name"],
        template="fullText contains '{query}' and name contains '{query_name}' and trashed=false",
        ),
    query="machine learning",
    query_name="ML",    
    num_results=2,  # Maximum number of file to load
)
for doc in loader.load():
    print("---")
    print(doc.page_content.strip()[:60]+"...")

Modes for GSlide and GSheet

Le paramètre mode accepte différentes valeurs:

  • document : retourne le contenu en texte de chaque document
  • snippets: retourne la valeur du champ description de chaque fichier Le paramètre gslide_mode accepte également plusieurs valeurs:
  • single: un document avec tous les slides, séparé de <PAGE BREAK>.
  • slide: Un document par slide
  • elements: un document pour chaque bloc de texte
loader = GoogleDriveLoader(
    template="gdrive-mime-type",
    mime_type="application/vnd.google-apps.presentation",
    gslide_mode="slide",
    num_results=2,  # Maximum number of file to load
)
for doc in loader.load():
    print("---")
    print(doc.page_content.strip()[:60]+"...")

Le paramètre gsheet_mode accepte également différentes valeurs:

Pull request ?

Nous avons bien essayé de proposer un pull-request, mais le code est trop important pour trouver une âme courageuse pour le relire (+2000 lignes).

Finalement, nous avons opté pour un composant autonome, dont la doc de langchain fait mention.

Nous espérons que cette contribution vous sera utile. Au vu des statistiques de download, cela semble le cas.