Création d’une application GenAI de bout en bout avec Ruby et Milvus
L’introduction de frameworks GenAI spécialisés comme LangChain nous a permis de créer rapidement et facilement des applications d’IA sophistiquées en exploitant de puissants grands modèles de langage (LLMs) tels que ChatGPT et LLaMA. LangChain, par exemple, nous permet de créer une puissante application de génération augmentée par récupération (RAG) en seulement quelques lignes de code, sans nécessiter de connaissances théoriques approfondies en IA.
Cette tendance signifie qu’aujourd’hui, les data scientists et les ingénieurs en machine learning ne sont plus les seuls capables de créer des applications GenAI. Les ingénieurs full-stack ou les développeurs logiciels peuvent désormais créer des applications GenAI à l’aide de LangChain.
Cependant, ces frameworks GenAI sont généralement écrits en Python, et nous savons que certains ingénieurs full-stack et développeurs logiciels utilisent rarement Python dans leurs projets. Il existe donc un besoin d’extensions de ces frameworks GenAI dans d’autres langages de programmation afin que ces ingénieurs full-stack puissent exploiter de puissants LLMs pour créer des applications GenAI dans leurs projets logiciels.
Lors d’une conférence récente, Andrei Bondarev, architecte de solutions chez Source Labs LLC, a présenté une extension Ruby de LangChain appelée LangChain.rb afin de faciliter la création d’applications GenAI par les ingénieurs full-stack dans leurs projets logiciels.
Regardez cette conférence sur Youtube
Mais avant d’aborder la manière de créer une application GenAI avec Ruby, explorons brièvement le fonctionnement interne de la génération augmentée par récupération (RAG), un cas d’usage populaire de la GenAI.
Comment fonctionne RAG
Ce n’est un secret pour personne que les données sont la mine d’or de toute application GenAI. Elles servent de source d’information utilisée par la GenAI pour générer des réponses factuelles et précises. Parmi toutes les données disponibles actuellement, 80 % peuvent être classées comme données non structurées.
Les données non structurées désignent des données qui ne se conforment pas à un format de données prédéfini. Ce type de données comprend les images, le texte, le son et les vidéos. Pour que les machines puissent donner du sens à ces types de données non structurées, nous devons les transformer en un format numérique appelé vector embeddings.
Concepts fondamentaux des vector embeddings
Un embedding se compose d’un vecteur à n dimensions, où n désigne la dimensionnalité de l’embedding. La dimensionnalité dépend du modèle de deep learning qui transforme les données en embedding. Un embedding porte la signification sémantique des données qu’il représente.
Nous pouvons utiliser des modèles de deep learning pour transformer diverses modalités de données en embeddings. Par exemple, si nous avons des données textuelles, nous pouvons utiliser des modèles OpenAI ou Sentence Transformer pour transformer ces données textuelles en embedding. Si nous avons des données d’image, nous pouvons utiliser des modèles pré-entraînés spécialisés capables d’extraire des caractéristiques d’image, comme Vision Transformer, en tant que modèle d’embedding.
Étant donné qu’un embedding porte la signification sémantique des données qu’il représente, nous pouvons calculer la similarité de cet embedding avec d’autres embeddings dans ce que l’on appelle l’espace vectoriel. Les embeddings ayant des significations sémantiques similaires seront placés près les uns des autres dans l’espace vectoriel, comme vous pouvez le voir dans la visualisation ci-dessous :
Vector embeddings in a vector space.png
Embedding de mots apparentés dans l’espace vectoriel
Comme le montre l’image ci-dessus, les embeddings de "queen" et "king" sont placés à proximité l’un de l’autre, tout comme ceux de "woman" et "man". La distance euclidienne entre "queen-king" et "woman-man" serait également approximativement la même, puisqu’ils portent des significations similaires.
Ce concept constitue la base d’une opération de recherche vectorielle, où nous calculons la similarité entre un embedding et plusieurs embeddings.
Le rôle de la base de données vectorielle dans la recherche vectorielle et les applications RAG
Mettre en œuvre une recherche vectorielle est simple si nous ne traitons qu’une petite quantité d’embeddings. Cependant, dans les cas réels, nous traitons couramment des milliers, des millions, voire des milliards d’embeddings. Par conséquent, nous avons besoin d’une solution pour stocker efficacement les embeddings et effectuer rapidement des recherches vectorielles dessus.
C’est là qu’une base de données vectorielle comme Milvus entre en jeu. Milvus est une base de données vectorielle open source dans laquelle vous pouvez stocker d’énormes quantités d’embeddings et effectuer des recherches vectorielles sur ces embeddings en une fraction de seconde.
Le flux de travail de transformation de données non structurées en embeddings et de leur stockage dans Milvus
Le flux de travail de transformation de données non structurées en embeddings et de leur stockage dans Milvus
Les bases de données vectorielles jouent également un rôle crucial dans les applications GenAI populaires comme RAG. Comme vous le savez peut-être déjà, l’objectif principal de RAG est d’améliorer la précision des réponses générées par des LLM comme ChatGPT et LLaMA en leur fournissant un contexte pouvant être utile pour répondre à la requête de l’utilisateur.
Dans une application RAG, une fois la requête de l’utilisateur reçue, elle est transformée en embedding à l’aide d’un modèle d’embedding. Ensuite, une recherche vectorielle est effectuée, où l’embedding de la requête est comparé aux embeddings de contexte stockés dans la base de données vectorielle comme Milvus. Les données de contexte les plus similaires sont ensuite récupérées et transmises avec la requête au LLM. Le LLM peut alors utiliser les informations du contexte pour générer une réponse contextualisée afin de répondre à la requête de l’utilisateur.
RAG
Flux de travail RAG
LangChain en tant que framework GenAI populaire
LangChain est un framework qui facilite la création et le développement d’applications GenAI à l’aide de modèles LLM de pointe. Il s’intègre facilement aux fournisseurs de LLM populaires comme OpenAI, Anthropic et Google, ainsi qu’aux fournisseurs de bases de données vectorielles comme Zilliz.
LangChain offre également des abstractions flexibles pour développer des applications d’IA alimentées par des LLM, ce qui permet aux data scientists et aux développeurs logiciels de créer simplement des systèmes sophistiqués comme RAG avec seulement quelques lignes de code.
Par exemple, disons que nous voulons résumer le contenu de cet article de blog à l’aide de GPT-4. Nous pouvons accomplir cette tâche avec le code suivant :
import os
from langchain.chains.summarize import load_summarize_chain
from langchain_community.document_loaders import WebBaseLoader
from langchain_openai import ChatOpenAI
os.environ["LANGCHAIN_TRACING_V2"] = "True"
loader = WebBaseLoader("https://lilianweng.github.io/posts/2023-06-23-agent/")
docs = loader.load()
llm = ChatOpenAI(temperature=0, model_name="gpt-3.5-turbo-1106")
chain = load_summarize_chain(llm, chain_type="stuff")
result = chain.invoke(docs)
"""
Sortie : L’article traite du concept d’agents autonomes propulsés par des LLM, en mettant l’accent sur les composants de planification, de mémoire et d’utilisation d’outils. Il inclut des études de cas et des exemples de preuve de concept, ainsi que des défis et des références à des recherches connexes. L’auteur souligne le potentiel des LLM pour créer de puissants agents de résolution de problèmes, tout en mettant également en évidence des limites telles que la longueur finie du contexte et la fiabilité des interfaces en langage naturel.
"""
Comme vous pouvez le voir, avec environ seulement 10 lignes de code, nous pouvons exploiter le modèle GPT-4 pour résumer avec précision un long article de blog.
Avec LangChain, vous pouvez également effectuer des tâches plus complexes. Par exemple, vous pouvez diviser un long texte provenant d’un document PDF en segments, transformer chaque segment en embedding à l’aide d’un modèle d’embedding de votre choix, stocker l’embedding de ces segments dans une base de données vectorielle, puis effectuer du RAG ensuite.
import getpass
import os
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain.vectorstores.milvus import Milvus
from langchain_core.runnables import RunnablePassthrough
from langchain_core.output_parsers import StrOutputParser
from langchain.embeddings import SentenceTransformerEmbeddings
from langchain_openai import ChatOpenAI
# Set the API key
os.environ["OPENAI_API_KEY"] = getpass.getpass("Enter your OpenAI API key: ")
llm = ChatOpenAI(model="gpt-4")
# Text to be processed
texts = "This is a very long text.....:"
# Split text into chunks
text_splitter = RecursiveCharacterTextSplitter(chunk_size=1000, chunk_overlap=100) # Example chunk size and overlap
chunk_texts = text_splitter.split_text(texts)
# Instantiate embedding model
embeddings = SentenceTransformerEmbeddings(model_name="all-MiniLM-L6-v2")
# Store embeddings of chunks inside Milvus DB
vector_db = Milvus.from_texts(texts=chunk_texts, embedding=embeddings, collection_name="rag_milvus")
retriever = vector_db.as_retriever()
# Define function to format documents
def format_docs(docs):
return "\\n\\n".join(doc.page_content for doc in docs)
# Perform RAG (Retrieval Augmented Generation)
rag_chain = (
{"context": retriever | format_docs, "question": RunnablePassthrough()}
| llm
| StrOutputParser()
)
# Example question
question = "What is the main idea of the text?"
# Execute RAG chain and print the result
for chunk in rag_chain.stream(question):
print(chunk, end="", flush=True)
En dehors des démonstrations fournies ci-dessus, LangChain offre un large éventail de fonctionnalités. Par exemple, il intègre des LLM et des API provenant de sources externes telles que des applications météo, des calculatrices ou Google Search. Cette approche permet aux LLM d’utiliser les informations issues de ces sources pour générer des réponses plus précises et contextuelles. Nous verrons l’implémentation détaillée de cette approche dans les sections à venir.
Vous pouvez également explorer toutes les fonctionnalités de LangChain sur leur page de documentation.
Développement d’applications GenAI avec Ruby et Milvus
Python est devenu le langage de programmation de facto pour les frameworks de recherche et de développement en IA, y compris LangChain. Pendant ce temps, Ruby reste populaire pour le développement rapide de logiciels et d’applications web.
Cependant, comme vous l’avez vu dans la section précédente, l’introduction de LangChain ouvre des possibilités aux développeurs de logiciels pour intégrer la puissance des LLM dans leurs applications web sans connaître les théories détaillées des LLM et de l’IA en général.
Cette capacité a créé une demande croissante pour étendre ces frameworks de développement GenAI à d’autres langages plus familiers aux développeurs full-stack, tels que Ruby. Pour répondre à cette demande, Andrei Bondarev a introduit LangChain.rb, qui est l’extension Ruby du framework LangChain original.
LangChain.rb permet aux développeurs Ruby full-stack de créer des applications web propulsées par des LLM sans avoir à intégrer plusieurs langages de programmation dans leurs projets. Avec lui, vous pouvez facilement intégrer des bases de données vectorielles populaires, des LLM et des ressources externes dans vos applications web LLM.
LangChain.rb possède les mêmes fonctionnalités générales que le LangChain original, telles que :
Gestion des prompts : créer, charger et enregistrer des modèles de prompts pour les LLM de votre choix
Validation de la longueur du contexte : valider la longueur du contexte des entrées en fonction de la longueur du contexte des LLM et des modèles d’embedding de votre choix
Découpage des données : diviser les données en morceaux selon des règles prédéfinies avant de les ingérer dans les bases de données vectorielles de votre choix
Mémoire de conversation : persister une conversation avec un LLM dans une mémoire
Dans les sections suivantes, nous démontrerons le développement d’applications simples propulsées par des LLM avec l’aide de LangChain.rb.
Applications RAG générales avec LangChain.rb
Dans ce premier exemple, nous allons créer une application RAG simple et rapide à l’aide de LangChain.rb. Avant de pouvoir utiliser LangChain.rb pour votre projet Ruby, assurez-vous d’installer la gem en exécutant la commande suivante :
gem install langchainrb
Dans ce projet, nous utiliserons Milvus comme base de données vectorielle et les modèles d’OpenAI à la fois comme LLM et comme modèles d’embedding. Pour lancer Milvus, nous devons installer Milvus dans Docker et démarrer le conteneur avec la commande suivante :
# Télécharger le script d’installation
curl -sfL https://raw.githubusercontent.com/milvus-io/milvus/master/scripts/standalone_embed.sh -o standalone_embed.sh
# Démarrer le conteneur Docker
bash standalone_embed.sh start
Maintenant que nous avons démarré le conteneur Docker, instancions Milvus et les modèles que nous utiliserons pour notre application RAG.
require 'langchain'
milvus = Langchain::Vectorsearch::Milvus.new(
url: ENV["MILVUS_URL"],
index_name: "Documents",
llm: Langchain::LLM::OpenAI.new(api_key: ENV["OPENAI_API_KEY"])
)
La première chose que nous devons faire est de créer un schéma dans la base de données vectorielle Milvus ainsi que la méthode d’indexation correspondante. Ensuite, nous devons charger ce schéma avant de pouvoir effectuer une recherche vectorielle avec celui-ci.
# Créer le schéma par défaut
milvus.create_default_schema
# Créer l’index par défaut
milvus.create_default_index
# Charger le schéma par défaut
milvus.load_default_schema
Nous pouvons maintenant ingérer certaines données dans notre schéma. Supposons que nous ayons un PDF dans notre répertoire local contenant des informations sur les avantages sociaux des employés. Si nous voulons stocker tous les textes de ce PDF dans la base de données Milvus, nous pouvons le faire en exécutant les commandes suivantes :
pdf = Langchain.root.join("path/to/my.pdf")
# Ajouter le PDF dans Milvus
milvus.add_data(path: pdf)
Une fois que vous exécutez les commandes ci-dessus, LangChain fera tout le prétraitement en coulisses. Il analysera le texte à l’intérieur du fichier PDF, le divisera en plusieurs morceaux, transformera chaque morceau en embeddings, puis stockera les embeddings dans la base de données vectorielle Milvus.
Après avoir stocké nos données dans la base de données vectorielle Milvus, nous pouvons commencer à poser des questions liées à notre document PDF. Supposons que nous voulions demander : « Quelle est la politique de l’entreprise en matière de congés ? Combien de temps puis-je prendre ? » ; nous pouvons alors interroger nos LLM dans un système RAG en exécutant simplement cette seule ligne de code :
response = milvus.ask(question: "What’s the company’s vacation policy? How much can I take off?")
puts response
"""
Response:
=> The company's vacation policy allows employees to take any reasonable amount of time off with pay,
as long as they consult with their manager in advance and get their work done.
"""
Et voilà ! En plus de créer une application RAG générale, nous pouvons également créer une application RAG agentique avec LangChain.rb, ce dont nous discuterons dans la section suivante.
Utiliser des agents pour interagir avec des outils tiers
La principale limite de nombreux LLM est leur date limite de connaissances. GPT-4, par exemple, a une date limite fixée à avril 2023. Cela signifie que si nous voulons poser des questions sur des événements généraux ou factuels postérieurs à avril 2023, nous n’obtiendrons pas de réponse précise de la part du LLM.
Pour résoudre ce problème, LangChain.rb nous permet de créer une application RAG agentique. Ce type d’application RAG ajoute une autre couche d’intelligence, contenant un « agent » qui agit comme un décideur. L’agent analyse la requête de l’utilisateur, puis décide des outils tiers les plus efficaces pouvant fournir le contexte le plus approprié pour répondre à la requête.
Supposons que nous voulions interroger notre LLM sur la météo actuelle à New York. Avec un système RAG général, le LLM ne peut pas connaître la météo en temps réel à New York. Il commencera très probablement à halluciner et nous donnera des prévisions météo aléatoires.
Le workflow RAG agentique (1).png
Workflow d’un RAG agentique
Le RAG agentique résout ce problème en nous permettant d’utiliser des outils ou des API, comme l’API OpenWeather, dans le système afin d’obtenir la météo en temps réel à New York. L’agent traitera d’abord la requête de l’utilisateur, puis décidera des outils capables de fournir un contexte pertinent pour répondre à la requête avant de synthétiser ce contexte en une réponse précise.
La démo suivante utilisera des outils tiers dans notre système RAG, comme une calculatrice, l’application OpenWeather et Google Search.
weather = Langchain::Tool::Weather.new(api_key: ENV["OPEN_WEATHER_API_KEY"])
google_search = Langchain::Tool::GoogleSearch.new(api_key: ENV["SERPAPI_API_KEY"])
calculator = Langchain::Tool::Calculator.new
Ensuite, nous devons ajouter ces trois outils dans notre système RAG avec la commande suivante :
openai = Langchain::LLM::OpenAI.new(api_key: ENV["OPENAI_API_KEY"])
agent = Langchain::Agent::ReActAgent.new(
llm: openai,
tools: [weather, google_search, calculator]
)
Maintenant, nous pouvons commencer à poser des questions à notre LLM. Supposons que nous voulions poser la question suivante : « Find current weather in Boston, MA, and Washington, D.C., and take an average. »
response = agent.run(question: "Find current weather in Boston, MA and Washington, D.C. and take an average")
Sortie du RAG avec intégration d’outils
Sortie du RAG avec intégration d’outils
Comme vous pouvez le voir dans la capture d’écran ci-dessus, notre système RAG agentique a pu répondre précisément à la requête. Décomposons le workflow de ce système RAG :
La requête a d’abord été envoyée au LLM d’OpenAI.
L’agent a reconnu la nécessité d’utiliser l’API OpenWeather pour obtenir la météo actuelle à Boston et à Washington D.C.
Après avoir récupéré les données météorologiques, l’agent a vu que la requête nécessitait de faire la moyenne de la météo des deux villes.
L’agent a ensuite invoqué un outil de calculatrice pour calculer la moyenne météorologique.
Enfin, le LLM a synthétisé les résultats en une réponse cohérente et l’a renvoyée à l’utilisateur.
Cet exemple démontre la puissance de l’approche RAG agentique. En incorporant des outils et API externes, le système surmonte les limites de la date limite de connaissances du LLM et fournit une réponse précise et à jour à la requête de l’utilisateur.
Utiliser des agents pour interagir avec une base de données interne
Nous pouvons également utiliser le RAG agentique pour interagir avec nos bases de données internes. C’est très utile, car nous pouvons demander des insights sur nos données en utilisant un langage proche du langage humain au lieu de nous appuyer sur des requêtes SQL traditionnelles.
Supposons que nous ayons une boutique en ligne et des données utilisateur stockées dans une base de données. Normalement, nous devrions écrire des requêtes SQL pour extraire des insights de ces données. Avec le RAG agentique, il nous suffit de demander au LLM l’insight que nous voulons, et la réponse sera renvoyée immédiatement.
Par exemple, supposons que nous voulions savoir combien d’enregistrements utilisateur sont stockés dans la base de données. Nous pouvons simplement demander : "Combien y a-t-il d’utilisateurs ?" et exécuter les commandes suivantes :
require 'langchain'
# Instancier la connexion à la base de données
database = Langchain::Tool::Database.new(connection_string: "postgres://localhost:5432/my_database")
# Créer une instance OpenAI LLM
openai = Langchain::LLM::OpenAI.new(api_key: ENV["OPENAI_API_KEY"])
# Créer SQLAgent avec le LLM et la connexion à la base de données
agent = Langchain::Agent::SQLAgent.new(
llm: openai,
db: database
)
# Poser une question à l’agent
response = agent.run("Combien y a-t-il d’utilisateurs ?")
Voici l’exemple de sortie de la commande :
Sortie de RAG avec intégration SQL
Sortie de RAG avec intégration SQL
Comme vous pouvez le voir, notre système RAG agentique a pu répondre précisément à une question spécifique liée aux données de notre base de données. Le workflow de l’agent est similaire à l’exemple précédent :
La requête a été envoyée au LLM OpenAI.
L’agent a analysé la requête et a déterminé qu’une recherche dans la base de données était nécessaire pour compter le nombre d’utilisateurs.
Le LLM a généré une requête SQL appropriée à partir du schéma des tables de la base de données.
La requête SQL a été exécutée sur la base de données et a renvoyé le résultat.
La sortie de la base de données a été renvoyée au LLM.
Le LLM a synthétisé le résultat de la base de données en une réponse cohérente et lisible par un humain, puis l’a fournie comme réponse finale.
Conclusion
L’introduction de LangChain rend les LLM accessibles aux professionnels qui n’ont peut-être pas de connaissances approfondies des théories de l’IA et de la science des données. Nous pouvons créer une puissante application RAG à l’aide de LangChain avec seulement quelques lignes de code.
Cette accessibilité est la raison pour laquelle Andrei Bondarev a introduit LangChain.rb, une extension de LangChain pour Ruby. Ce framework permet aux développeurs full-stack d’intégrer les performances puissantes des LLM dans leurs applications web sans avoir besoin d’une expertise approfondie en IA. De plus, LangChain.rb élimine les tracas liés au passage des développeurs full-stack à un autre langage de programmation lorsqu’ils souhaitent exploiter les LLM dans leurs applications web.
Continuer à lire

From Vector Database to Vector Lakebase
Zilliz offers a fully managed Vector Lakebase powered by Milvus, unifying real-time vector search, lake-scale discovery, and Al data operations.

A Developer's Guide to Exploring Milvus 2.6 Features on Zilliz Cloud
Milvus 2.6 marks a shift from “vector search + glue code” to a more advanced retrieval engine, and it is now Generally Available (GA) on Zilliz Cloud (a managed Milvus service).

Bringing AI to Legal Tech: The Role of Vector Databases in Enhancing LLM Guardrails
Discover how vector databases enhance AI reliability in legal tech, ensuring accurate, compliant, and trustworthy AI-powered legal solutions.


