Dans cette application, l'ingestion de tous les documents de l'exemple est déclenchée par la commande flask create-index. L'implémentation de cette commande se trouve dans le fichier app.py du répertoire api et importe simplement le module index_data.py du répertoire data et appelle sa fonction main(), qui effectue une importation complète de tous les documents stockés dans le fichier data.json.

Structure du document

La structure de chaque document est la suivante :

  • namele titre du document
  • urlun URL vers le document hébergé sur un site externe
  • summaryun bref résumé du contenu du document
  • contentle corps du document
  • created_onDate de création
  • updated_atdate de mise à jour (peut être absente si le document n'a jamais été mis à jour)
  • categoryla catégorie du document, qui peut être github, sharepoint ou teams
  • rolePermissionsune liste des autorisations de rôle

Cet exemple d'application utilise le champ content comme texte à indexer et ajoute name, summary, url, category et updated_at comme métadonnées associées.

L'extrait de code Python suivant montre comment les documents sont importés :

Ici, le module json de la bibliothèque standard Python est utilisé pour lire le fichier de données, puis pour chaque document inclus, un objet Document de Langchain est créé. Les documents possèdent un attribut page_content qui définit le contenu à convertir en vecteurs et à rechercher, ainsi qu'un certain nombre de champs supplémentaires qui sont stockés en tant que métadonnées. Le site metadata_keys détermine quels champs du contenu source doivent être stockés en tant que métadonnées du document.

En fonction de vos besoins d'ingestion, la méthode peut être affinée ou modifiée. Le projet Langchain fournit une large sélection de chargeurs de documents qui peuvent être utilisés en fonction du format du contenu source.

Le modèle ELSER (Elastic Learned Sparse EncodeR)

L'index Elasticsearch utilisé dans cette application est configuré pour créer automatiquement des encastrements de vecteurs épars pour tous les documents qui sont insérés. La fonction install_elser() dans index_data.py s'assure que le modèle ELSER est installé et déployé sur l'instance Elasticsearch que vous utilisez.

Fractionnement du texte

Le champ content dans ces documents est long, ce qui signifie qu'un seul encodage ne pourra pas le représenter complètement. La solution standard lorsque l'on travaille avec de grandes quantités de texte consiste à diviser le texte en passages plus courts, puis d'obtenir des embeddings pour les passages individuels, qui sont tous stockés et indexés.

Dans cette application, la classe RecursiveCharacterTextSplitter de la bibliothèque Langchain est utilisée, associée à l'encodeur tiktoken d'OpenAI, qui compte les longueurs des passages en tokens, les mêmes unités que celles utilisées par les LLM.

Prenons l'exemple suivant, qui montre comment fonctionne le fractionnement du texte dans l'application :

En définissant l'argument chunk_size du séparateur de texte, il est possible de contrôler la longueur des passages obtenus. Le site chunk_overlap permet un certain chevauchement entre les passages, ce qui permet souvent d'obtenir de meilleures intégrations.

Dans l'application réelle, le séparateur est initialisé avec les arguments suivants :

Vous pouvez modifier ces valeurs et voir comment les changements affectent la qualité du chatbot. Chaque fois que vous modifiez la configuration du séparateur, vous devez générer à nouveau l'index en exécutant la commande flask create-index.

Notez qu'il existe d'autres considérations sur le fractionnement du texte en combinaison avec le modèle ELSER que vous devez connaître. Pour les cas d'utilisation en production, il se peut que vous deviez choisir une autre méthode de tokenisation comme dans ce tutoriel.

Magasin de documents

Les documents sont stockés dans un index Elasticsearch. Le nom de l'index est contrôlé par la variable d'environnement ES_INDEX, définie dans le fichier .env fichier. Par défaut, le nom de cet index est workplace-app-docs.

L'application utilise la classe ElasticsearchStore, qui fait partie de l'intégration d'Elasticsearch dans Langchain, et utilise la bibliothèque client Elasticsearch officielle pour Python.

La logique complète qui traite de l'index Elasticsearch est présentée ci-dessous :

La méthode ElasticsearchStore.from_documents() importe toutes les instances Document stockées dans workplace_docs, en les écrivant dans l'index donné dans l'argument index_name. Toutes les opérations sont effectuées par l'intermédiaire du client indiqué dans l'argument es_connection.

L'argument strategy définit la manière dont cet index sera utilisé. Pour cette application, la classe SparseVectorRetrievalStrategy indique qu'il faut maintenir des encastrements vectoriels épars pour chaque document. Cela ajoutera un pipeline à l'index qui générera des embeddings à l'aide du modèle demandé (ELSER version 2 dans ce cas).

L'intégration d'Elasticsearch avec Langchain offre d'autres stratégies qui peuvent être utilisées en fonction du cas d'utilisation. En particulier, la stratégie ApproxRetrievalStrategy peut être utilisée en cas d'intégration de vecteurs denses.

Précédemment

Implémentation

Prêt à créer des expériences de recherche d'exception ?

Une recherche suffisamment avancée ne se fait pas avec les efforts d'une seule personne. Elasticsearch est alimenté par des data scientists, des ML ops, des ingénieurs et bien d'autres qui sont tout aussi passionnés par la recherche que vous. Mettons-nous en relation et travaillons ensemble pour construire l'expérience de recherche magique qui vous permettra d'obtenir les résultats que vous souhaitez.

Jugez-en par vous-même