⚡ Création d'applications basées sur LLM dans Ruby ⚡
Pour une intégration approfondie de Rails, voir : langchainrb_rails gem.
Disponible pour des missions de conseil rémunérées ! Envoyez-moi un email.
Installez la gem et ajoutez-la au Gemfile de l'application en exécutant :
bundle add langchainrb
Si le bundler n'est pas utilisé pour gérer les dépendances, installez la gem en exécutant :
gem install langchainrb
Des gemmes supplémentaires peuvent être nécessaires. Ils ne sont pas inclus par défaut, vous pouvez donc inclure uniquement ce dont vous avez besoin.
require "langchain"
Le module Langchain::LLM
fournit une interface unifiée pour interagir avec divers fournisseurs de Large Language Model (LLM). Cette abstraction vous permet de basculer facilement entre différents backends LLM sans modifier le code de votre application.
Toutes les classes LLM héritent de Langchain::LLM::Base
et fournissent une interface cohérente pour les opérations courantes :
La plupart des classes LLM peuvent être initialisées avec une clé API et des options facultatives par défaut :
llm = Langchain :: LLM :: OpenAI . new (
api_key : ENV [ "OPENAI_API_KEY" ] ,
default_options : { temperature : 0.7 , chat_model : "gpt-4o" }
)
Utilisez la méthode embed
pour générer des intégrations pour un texte donné :
response = llm . embed ( text : "Hello, world!" )
embedding = response . embedding
embed()
text
: (Obligatoire) Le texte d’entrée à intégrer.model
: (Facultatif) Le nom du modèle à utiliser ou le modèle d'intégration par défaut sera utilisé. Utilisez la méthode complete
pour générer des complétions pour une invite donnée :
response = llm . complete ( prompt : "Once upon a time" )
completion = response . completion
complete()
prompt
: (Obligatoire) L'invite de saisie pour l'achèvement.max_tokens
: (Facultatif) Le nombre maximum de jetons à générer.temperature
: (Facultatif) Contrôle le caractère aléatoire de la génération. Des valeurs plus élevées (par exemple 0,8) rendent la sortie plus aléatoire, tandis que des valeurs plus faibles (par exemple 0,2) la rendent plus déterministe.top_p
: (Facultatif) Une alternative à la température, contrôle la diversité des jetons générés.n
: (Facultatif) Nombre de complétions à générer pour chaque invite.stop
: (Facultatif) Séquences dans lesquelles l'API cessera de générer d'autres jetons.presence_penalty
: (Facultatif) Pénalise les nouveaux jetons en fonction de leur présence dans le texte jusqu'à présent.frequency_penalty
: (Facultatif) Pénalise les nouveaux jetons en fonction de leur fréquence dans le texte jusqu'à présent. Utilisez la méthode chat
pour générer des terminaisons de chat :
messages = [
{ role : "system" , content : "You are a helpful assistant." } ,
{ role : "user" , content : "What's the weather like today?" }
# Google Gemini and Google VertexAI expect messages in a different format:
# { role: "user", parts: [{ text: "why is the sky blue?" }]}
]
response = llm . chat ( messages : messages )
chat_completion = response . chat_completion
chat()
messages
: (Obligatoire) Un tableau d’objets de message représentant l’historique de la conversation.model
: (Facultatif) Le modèle de discussion spécifique à utiliser.temperature
: (Facultatif) Contrôle le caractère aléatoire de la génération.top_p
: (Facultatif) Une alternative à la température, contrôle la diversité des jetons générés.n
: (Facultatif) Nombre de choix de complétion de discussion à générer.max_tokens
: (Facultatif) Le nombre maximum de jetons à générer lors de la fin du chat.stop
: (Facultatif) Séquences dans lesquelles l'API cessera de générer d'autres jetons.presence_penalty
: (Facultatif) Pénalise les nouveaux jetons en fonction de leur présence dans le texte jusqu'à présent.frequency_penalty
: (Facultatif) Pénalise les nouveaux jetons en fonction de leur fréquence dans le texte jusqu'à présent.logit_bias
: (Facultatif) Modifie la probabilité que les jetons spécifiés apparaissent dans la complétion.user
: (Facultatif) Un identifiant unique représentant votre utilisateur final.tools
: (Facultatif) Une liste d’outils que le modèle peut appeler.tool_choice
: (Facultatif) Contrôle la façon dont le modèle appelle les fonctions. Grâce à l'interface unifiée, vous pouvez facilement basculer entre différents fournisseurs LLM en changeant la classe que vous instanciez :
# Using Anthropic
anthropic_llm = Langchain :: LLM :: Anthropic . new ( api_key : ENV [ "ANTHROPIC_API_KEY" ] )
# Using Google Gemini
gemini_llm = Langchain :: LLM :: GoogleGemini . new ( api_key : ENV [ "GOOGLE_GEMINI_API_KEY" ] )
# Using OpenAI
openai_llm = Langchain :: LLM :: OpenAI . new ( api_key : ENV [ "OPENAI_API_KEY" ] )
Chaque méthode LLM renvoie un objet de réponse qui fournit une interface cohérente pour accéder aux résultats :
embedding
: renvoie le vecteur d'intégrationcompletion
: renvoie la complétion du texte généréchat_completion
: renvoie la complétion du chat généréetool_calls
: Renvoie les appels d'outils effectués par le LLMprompt_tokens
: renvoie le nombre de jetons dans l'invitecompletion_tokens
: renvoie le nombre de jetons dans la complétiontotal_tokens
: renvoie le nombre total de jetons utilisés Note
Bien que l'interface principale soit cohérente entre les fournisseurs, certains LLM peuvent offrir des fonctionnalités ou des paramètres supplémentaires. Consultez la documentation de chaque classe LLM pour en savoir plus sur les capacités et options spécifiques au fournisseur.
Créez une invite avec des variables d'entrée :
prompt = Langchain :: Prompt :: PromptTemplate . new ( template : "Tell me a {adjective} joke about {content}." , input_variables : [ "adjective" , "content" ] )
prompt . format ( adjective : "funny" , content : "chickens" ) # "Tell me a funny joke about chickens."
Création d'un PromptTemplate en utilisant simplement une invite et aucune variable d'entrée :
prompt = Langchain :: Prompt :: PromptTemplate . from_template ( "Tell me a funny joke about chickens." )
prompt . input_variables # []
prompt . format # "Tell me a funny joke about chickens."
Enregistrez le modèle d'invite dans le fichier JSON :
prompt . save ( file_path : "spec/fixtures/prompt/prompt_template.json" )
Chargement d'un nouveau modèle d'invite à l'aide d'un fichier JSON :
prompt = Langchain :: Prompt . load_from_path ( file_path : "spec/fixtures/prompt/prompt_template.json" )
prompt . input_variables # ["adjective", "content"]
Créez une invite avec quelques exemples de prises de vue :
prompt = Langchain :: Prompt :: FewShotPromptTemplate . new (
prefix : "Write antonyms for the following words." ,
suffix : "Input: {adjective} n Output:" ,
example_prompt : Langchain :: Prompt :: PromptTemplate . new (
input_variables : [ "input" , "output" ] ,
template : "Input: {input} n Output: {output}"
) ,
examples : [
{ "input" : "happy" , "output" : "sad" } ,
{ "input" : "tall" , "output" : "short" }
] ,
input_variables : [ "adjective" ]
)
prompt . format ( adjective : "good" )
# Write antonyms for the following words.
#
# Input: happy
# Output: sad
#
# Input: tall
# Output: short
#
# Input: good
# Output:
Enregistrez le modèle d'invite dans le fichier JSON :
prompt . save ( file_path : "spec/fixtures/prompt/few_shot_prompt_template.json" )
Chargement d'un nouveau modèle d'invite à l'aide d'un fichier JSON :
prompt = Langchain :: Prompt . load_from_path ( file_path : "spec/fixtures/prompt/few_shot_prompt_template.json" )
prompt . prefix # "Write antonyms for the following words."
Chargement d'un nouveau modèle d'invite à l'aide d'un fichier YAML :
prompt = Langchain :: Prompt . load_from_path ( file_path : "spec/fixtures/prompt/prompt_template.yaml" )
prompt . input_variables #=> ["adjective", "content"]
Analysez les réponses textuelles LLM dans une sortie structurée, telle que JSON.
Vous pouvez utiliser StructuredOutputParser
pour générer une invite qui demande au LLM de fournir une réponse JSON adhérant à un schéma JSON spécifique :
json_schema = {
type : "object" ,
properties : {
name : {
type : "string" ,
description : "Persons name"
} ,
age : {
type : "number" ,
description : "Persons age"
} ,
interests : {
type : "array" ,
items : {
type : "object" ,
properties : {
interest : {
type : "string" ,
description : "A topic of interest"
} ,
levelOfInterest : {
type : "number" ,
description : "A value between 0 and 100 of how interested the person is in this interest"
}
} ,
required : [ "interest" , "levelOfInterest" ] ,
additionalProperties : false
} ,
minItems : 1 ,
maxItems : 3 ,
description : "A list of the person's interests"
}
} ,
required : [ "name" , "age" , "interests" ] ,
additionalProperties : false
}
parser = Langchain :: OutputParsers :: StructuredOutputParser . from_json_schema ( json_schema )
prompt = Langchain :: Prompt :: PromptTemplate . new ( template : "Generate details of a fictional character. n {format_instructions} n Character description: {description}" , input_variables : [ "description" , "format_instructions" ] )
prompt_text = prompt . format ( description : "Korean chemistry student" , format_instructions : parser . get_format_instructions )
# Generate details of a fictional character.
# You must format your output as a JSON value that adheres to a given "JSON Schema" instance.
# ...
Analysez ensuite la réponse llm :
llm = Langchain :: LLM :: OpenAI . new ( api_key : ENV [ "OPENAI_API_KEY" ] )
llm_response = llm . chat ( messages : [ { role : "user" , content : prompt_text } ] ) . completion
parser . parse ( llm_response )
# {
# "name" => "Kim Ji-hyun",
# "age" => 22,
# "interests" => [
# {
# "interest" => "Organic Chemistry",
# "levelOfInterest" => 85
# },
# ...
# ]
# }
Si l'analyseur ne parvient pas à analyser la réponse LLM, vous pouvez utiliser OutputFixingParser
. Il envoie un message d'erreur, une sortie préalable et le texte d'invite d'origine au LLM, demandant une réponse « corrigée » :
begin
parser . parse ( llm_response )
rescue Langchain :: OutputParsers :: OutputParserException => e
fix_parser = Langchain :: OutputParsers :: OutputFixingParser . from_llm (
llm : llm ,
parser : parser
)
fix_parser . parse ( llm_response )
end
Alternativement, si vous n'avez pas besoin de gérer OutputParserException
, vous pouvez simplifier le code :
# we already have the `OutputFixingParser`:
# parser = Langchain::OutputParsers::StructuredOutputParser.from_json_schema(json_schema)
fix_parser = Langchain :: OutputParsers :: OutputFixingParser . from_llm (
llm : llm ,
parser : parser
)
fix_parser . parse ( llm_response )
Voir ici pour un exemple concret
RAG est une méthodologie qui aide les LLM à générer des informations précises et à jour. Un flux de travail RAG typique suit les 3 étapes ci-dessous :
Langchain.rb fournit une interface unifiée pratique en plus des bases de données de recherche vectorielle prises en charge qui facilitent la configuration de votre index, l'ajout de données, l'interrogation et la récupération.
Base de données | Source ouverte | Offre cloud |
---|---|---|
Chroma | ✅ | ✅ |
Épsilla | ✅ | ✅ |
Hnswlib | ✅ | |
Milvus | ✅ | ✅ Nuage Zilliz |
Pomme de pin | ✅ | |
Pgvecteur | ✅ | ✅ |
Qdrant | ✅ | ✅ |
Tisser | ✅ | ✅ |
Recherche élastique | ✅ | ✅ |
Choisissez la base de données de recherche de vecteurs que vous utiliserez, ajoutez la dépendance gem et instanciez le client :
gem "weaviate-ruby" , "~> 0.8.9"
Choisissez et instanciez le fournisseur LLM que vous utiliserez pour générer des intégrations
llm = Langchain :: LLM :: OpenAI . new ( api_key : ENV [ "OPENAI_API_KEY" ] )
client = Langchain :: Vectorsearch :: Weaviate . new (
url : ENV [ "WEAVIATE_URL" ] ,
api_key : ENV [ "WEAVIATE_API_KEY" ] ,
index_name : "Documents" ,
llm : llm
)
Vous pouvez instancier n'importe quelle autre base de données de recherche de vecteurs prise en charge :
client = Langchain :: Vectorsearch :: Chroma . new ( ... ) # `gem "chroma-db", "~> 0.6.0"`
client = Langchain :: Vectorsearch :: Epsilla . new ( ... ) # `gem "epsilla-ruby", "~> 0.0.3"`
client = Langchain :: Vectorsearch :: Hnswlib . new ( ... ) # `gem "hnswlib", "~> 0.8.1"`
client = Langchain :: Vectorsearch :: Milvus . new ( ... ) # `gem "milvus", "~> 0.9.3"`
client = Langchain :: Vectorsearch :: Pinecone . new ( ... ) # `gem "pinecone", "~> 0.1.6"`
client = Langchain :: Vectorsearch :: Pgvector . new ( ... ) # `gem "pgvector", "~> 0.2"`
client = Langchain :: Vectorsearch :: Qdrant . new ( ... ) # `gem "qdrant-ruby", "~> 0.9.3"`
client = Langchain :: Vectorsearch :: Elasticsearch . new ( ... ) # `gem "elasticsearch", "~> 8.2.0"`
Créez le schéma par défaut :
client . create_default_schema
Ajoutez des données en texte brut à votre base de données de recherche de vecteurs :
client . add_texts (
texts : [
"Begin by preheating your oven to 375°F (190°C). Prepare four boneless, skinless chicken breasts by cutting a pocket into the side of each breast, being careful not to cut all the way through. Season the chicken with salt and pepper to taste. In a large skillet, melt 2 tablespoons of unsalted butter over medium heat. Add 1 small diced onion and 2 minced garlic cloves, and cook until softened, about 3-4 minutes. Add 8 ounces of fresh spinach and cook until wilted, about 3 minutes. Remove the skillet from heat and let the mixture cool slightly." ,
"In a bowl, combine the spinach mixture with 4 ounces of softened cream cheese, 1/4 cup of grated Parmesan cheese, 1/4 cup of shredded mozzarella cheese, and 1/4 teaspoon of red pepper flakes. Mix until well combined. Stuff each chicken breast pocket with an equal amount of the spinach mixture. Seal the pocket with a toothpick if necessary. In the same skillet, heat 1 tablespoon of olive oil over medium-high heat. Add the stuffed chicken breasts and sear on each side for 3-4 minutes, or until golden brown."
]
)
Ou utilisez les analyseurs de fichiers pour charger, analyser et indexer des données dans votre base de données :
my_pdf = Langchain . root . join ( "path/to/my.pdf" )
my_text = Langchain . root . join ( "path/to/my.txt" )
my_docx = Langchain . root . join ( "path/to/my.docx" )
client . add_data ( paths : [ my_pdf , my_text , my_docx ] )
Formats de fichiers pris en charge : docx, html, pdf, texte, json, jsonl, csv, xlsx, eml, pptx.
Récupérez des documents similaires en fonction de la chaîne de requête transmise :
client . similarity_search (
query : ,
k : # number of results to be retrieved
)
Récupérez des documents similaires en fonction de la chaîne de requête transmise via la technique HyDE :
client . similarity_search_with_hyde ( )
Récupérez des documents similaires en fonction de l'intégration transmise :
client . similarity_search_by_vector (
embedding : ,
k : # number of results to be retrieved
)
Requête basée sur RAG
client . ask ( question : "..." )
Langchain::Assistant
est une classe puissante et flexible qui combine des modèles linguistiques étendus (LLM), des outils et une gestion des conversations pour créer des assistants intelligents et interactifs. Il est conçu pour gérer des conversations complexes, exécuter des outils et fournir des réponses cohérentes basées sur le contexte de l'interaction.
llm = Langchain :: LLM :: OpenAI . new ( api_key : ENV [ "OPENAI_API_KEY" ] )
assistant = Langchain :: Assistant . new (
llm : llm ,
instructions : "You're a helpful AI assistant" ,
tools : [ Langchain :: Tool :: NewsRetriever . new ( api_key : ENV [ "NEWS_API_KEY" ] ) ]
)
# Add a user message and run the assistant
assistant . add_message_and_run! ( content : "What's the latest news about AI?" )
# Supply an image to the assistant
assistant . add_message_and_run! (
content : "Show me a picture of a cat" ,
image_url : "https://upload.wikimedia.org/wikipedia/commons/thumb/d/dd/Gfp-wisconsin-madison-the-nature-boardwalk.jpg/2560px-Gfp-wisconsin-madison-the-nature-boardwalk.jpg"
)
# Access the conversation thread
messages = assistant . messages
# Run the assistant with automatic tool execution
assistant . run ( auto_tool_execution : true )
# If you want to stream the response, you can add a response handler
assistant = Langchain :: Assistant . new (
llm : llm ,
instructions : "You're a helpful AI assistant" ,
tools : [ Langchain :: Tool :: NewsRetriever . new ( api_key : ENV [ "NEWS_API_KEY" ] ) ]
) do | response_chunk |
# ...handle the response stream
# print(response_chunk.inspect)
end
assistant . add_message ( content : "Hello" )
assistant . run ( auto_tool_execution : true )
Notez que le streaming n'est actuellement pas pris en charge pour tous les LLM.
llm
: L'instance LLM à utiliser (obligatoire)tools
: Un tableau d’instances d’outils (facultatif)instructions
: Instructions système pour l'assistant (facultatif)tool_choice
: Spécifie comment les outils doivent être sélectionnés. Par défaut : "auto". Un nom de fonction d'outil spécifique peut être transmis. Cela obligera l'Assistant à toujours utiliser cette fonction.parallel_tool_calls
: s'il faut effectuer plusieurs appels d'outils parallèles. Par défaut : vraiadd_message_callback
: Une fonction de rappel (proc, lambda) qui est appelée lorsqu'un message est ajouté à la conversation (facultatif) assistant . add_message_callback = -> ( message ) { puts "New message: #{ message } " }
tool_execution_callback
: Une fonction de rappel (proc, lambda) qui est appelée juste avant l'exécution d'un outil (facultatif) assistant . tool_execution_callback = -> ( tool_call_id , tool_name , method_name , tool_arguments ) { puts "Executing tool_call_id: #{ tool_call_id } , tool_name: #{ tool_name } , method_name: #{ method_name } , tool_arguments: #{ tool_arguments } " }
add_message
: Ajoute un message utilisateur au tableau des messagesrun!
: Traite la conversation et génère des réponsesadd_message_and_run!
: Combine l'ajout d'un message et l'exécution de l'assistantsubmit_tool_output
: Soumettre manuellement la sortie à un appel d'outilmessages
: renvoie une liste des messages en coursLangchain::Tool::Calculator
: Utile pour évaluer les expressions mathématiques. Nécessite gem "eqn"
.Langchain::Tool::Database
: Connectez votre base de données SQL. Nécessite gem "sequel"
.Langchain::Tool::FileSystem
: Interagissez avec le système de fichiers (lecture et écriture).Langchain::Tool::RubyCodeInterpreter
: Utile pour évaluer le code Ruby généré. Nécessite gem "safe_ruby"
(besoin d'une meilleure solution).Langchain::Tool::NewsRetriever
: Un wrapper autour de NewsApi.org pour récupérer des articles d'actualité.Langchain::Tool::Tavily
: Un wrapper autour de Tavily AI.Langchain::Tool::Weather
: Appelle l'API Open Weather pour récupérer la météo actuelle.Langchain::Tool::Wikipedia
: Appelle l'API Wikipedia. Langchain :: Assistant peut être facilement étendu avec des outils personnalisés en créant des classes qui extend Langchain::ToolDefinition
et implémentent les méthodes requises.
class MovieInfoTool
extend Langchain :: ToolDefinition
define_function :search_movie , description : "MovieInfoTool: Search for a movie by title" do
property :query , type : "string" , description : "The movie title to search for" , required : true
end
define_function :get_movie_details , description : "MovieInfoTool: Get detailed information about a specific movie" do
property :movie_id , type : "integer" , description : "The TMDb ID of the movie" , required : true
end
def initialize ( api_key : )
@api_key = api_key
end
def search_movie ( query : )
...
end
def get_movie_details ( movie_id : )
...
end
end
movie_tool = MovieInfoTool . new ( api_key : "..." )
assistant = Langchain :: Assistant . new (
llm : llm ,
instructions : "You're a helpful AI assistant that can provide movie information" ,
tools : [ movie_tool ]
)
assistant . add_message_and_run ( content : "Can you tell me about the movie 'Inception'?" )
# Check the response in the last message in the conversation
assistant . messages . last
L'assistant inclut la gestion des erreurs pour les entrées non valides, les types LLM non pris en charge et les échecs d'exécution des outils. Il utilise une machine à états pour gérer le flux de conversation et gérer différents scénarios avec élégance.
Le module Évaluations est un ensemble d'outils qui peuvent être utilisés pour évaluer et suivre les performances des produits de sortie par LLM et vos pipelines RAG (Retrieval Augmented Generation).
Ragas vous aide à évaluer vos pipelines de récupération augmentée (RAG). L'implémentation est basée sur cet article et le dépôt Python original. Ragas suit les 3 métriques suivantes et attribue les scores de 0,0 à 1,0 :
# We recommend using Langchain::LLM::OpenAI as your llm for Ragas
ragas = Langchain :: Evals :: Ragas :: Main . new ( llm : llm )
# The answer that the LLM generated
# The question (or the original prompt) that was asked
# The context that was retrieved (usually from a vectorsearch database)
ragas . score ( answer : "" , question : "" , context : "" )
# =>
# {
# ragas_score: 0.6601257446503674,
# answer_relevance_score: 0.9573145866787608,
# context_relevance_score: 0.6666666666666666,
# faithfulness_score: 0.5
# }
Exemples supplémentaires disponibles : /exemples
Langchain.rb utilise le mécanisme Ruby Logger standard et utilise par défaut la même valeur level
(actuellement Logger::DEBUG
).
Pour afficher tous les messages du journal :
Langchain . logger . level = Logger :: DEBUG
L'enregistreur se connecte par défaut à STDOUT
. Afin de configurer la destination du journal (c'est-à-dire se connecter à un fichier), procédez :
Langchain . logger = Logger . new ( "path/to/file" , ** Langchain :: LOGGER_OPTIONS )
Si vous rencontrez des problèmes pour installer la gemme unicode
requise par pragmatic_segmenter
, essayez d'exécuter :
gem install unicode -- --with-cflags= " -Wno-incompatible-function-pointer-types "
git clone https://github.com/andreibondarev/langchainrb.git
cp .env.example .env
, puis remplissez les variables d'environnement dans .env
bundle exec rake
pour garantir la réussite des tests et exécuter standardrbbin/console
pour charger la gem dans une session REPL. N'hésitez pas à ajouter vos propres instances de LLM, d'outils, d'agents, etc. et à les expérimenter.gem install lefthook && lefthook install -f
Rejoignez-nous sur le serveur Discord Langchain.rb.
Les rapports de bogues et les demandes d'extraction sont les bienvenus sur GitHub à l'adresse https://github.com/andreibondarev/langchainrb.
La gemme est disponible en open source selon les termes de la licence MIT.