⚡ Construindo aplicativos com tecnologia LLM em Ruby ⚡
Para integração profunda do Rails, consulte: langchainrb_rails gem.
Disponível para contratos de consultoria pagos! Envie-me um e-mail.
Instale a gem e adicione-a ao Gemfile da aplicação executando:
bundle add langchainrb
Se o bundler não estiver sendo usado para gerenciar dependências, instale a gem executando:
gem install langchainrb
Gemas adicionais podem ser necessárias. Eles não estão incluídos por padrão, então você pode incluir apenas o que precisa.
require "langchain"
O módulo Langchain::LLM
fornece uma interface unificada para interagir com vários provedores de Large Language Model (LLM). Essa abstração permite alternar facilmente entre diferentes back-ends LLM sem alterar o código do aplicativo.
Todas as classes LLM herdam de Langchain::LLM::Base
e fornecem uma interface consistente para operações comuns:
A maioria das classes LLM pode ser inicializada com uma chave API e opções padrão opcionais:
llm = Langchain :: LLM :: OpenAI . new (
api_key : ENV [ "OPENAI_API_KEY" ] ,
default_options : { temperature : 0.7 , chat_model : "gpt-4o" }
)
Use o método embed
para gerar embeddings para determinado texto:
response = llm . embed ( text : "Hello, world!" )
embedding = response . embedding
embed()
text
: (Obrigatório) O texto de entrada a ser incorporado.model
: (Opcional) O nome do modelo a ser usado ou o modelo de incorporação padrão será usado. Use o método complete
para gerar conclusões para um determinado prompt:
response = llm . complete ( prompt : "Once upon a time" )
completion = response . completion
complete()
prompt
: (Obrigatório) O prompt de entrada para conclusão.max_tokens
: (opcional) o número máximo de tokens a serem gerados.temperature
: (Opcional) Controla a aleatoriedade na geração. Valores mais altos (por exemplo, 0,8) tornam a saída mais aleatória, enquanto valores mais baixos (por exemplo, 0,2) a tornam mais determinística.top_p
: (Opcional) Uma alternativa à temperatura, controla a diversidade dos tokens gerados.n
: (Opcional) Número de conclusões a serem geradas para cada prompt.stop
: (Opcional) Sequências onde a API irá parar de gerar mais tokens.presence_penalty
: (Opcional) Penaliza novos tokens com base em sua presença no texto até o momento.frequency_penalty
: (Opcional) Penaliza novos tokens com base em sua frequência no texto até o momento. Use o método chat
para gerar conclusões 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
: (obrigatório) uma matriz de objetos de mensagem que representa o histórico da conversa.model
: (opcional) o modelo de chat específico a ser usado.temperature
: (Opcional) Controla a aleatoriedade na geração.top_p
: (Opcional) Uma alternativa à temperatura, controla a diversidade dos tokens gerados.n
: (Opcional) Número de opções de conclusão de bate-papo a serem geradas.max_tokens
: (Opcional) O número máximo de tokens a serem gerados na conclusão do chat.stop
: (Opcional) Sequências onde a API irá parar de gerar mais tokens.presence_penalty
: (Opcional) Penaliza novos tokens com base em sua presença no texto até o momento.frequency_penalty
: (Opcional) Penaliza novos tokens com base em sua frequência no texto até o momento.logit_bias
: (opcional) modifica a probabilidade de tokens especificados aparecerem na conclusão.user
: (opcional) um identificador exclusivo que representa seu usuário final.tools
: (opcional) uma lista de ferramentas que o modelo pode chamar.tool_choice
: (opcional) controla como o modelo chama funções. Graças à interface unificada, você pode alternar facilmente entre diferentes provedores de LLM alterando a classe que você instancia:
# 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" ] )
Cada método LLM retorna um objeto de resposta que fornece uma interface consistente para acessar os resultados:
embedding
: Retorna o vetor de incorporaçãocompletion
: Retorna a conclusão do texto geradochat_completion
: Retorna a conclusão do chat geradotool_calls
: Retorna chamadas de ferramenta feitas pelo LLMprompt_tokens
: Retorna o número de tokens no promptcompletion_tokens
: Retorna o número de tokens na conclusãototal_tokens
: Retorna o número total de tokens usados Observação
Embora a interface principal seja consistente entre os provedores, alguns LLMs podem oferecer recursos ou parâmetros adicionais. Consulte a documentação de cada classe LLM para aprender sobre recursos e opções específicas do provedor.
Crie um prompt com variáveis de entrada:
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."
Criando um PromptTemplate usando apenas um prompt e sem input_variables:
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."
Salve o modelo de prompt no arquivo JSON:
prompt . save ( file_path : "spec/fixtures/prompt/prompt_template.json" )
Carregando um novo modelo de prompt usando um arquivo JSON:
prompt = Langchain :: Prompt . load_from_path ( file_path : "spec/fixtures/prompt/prompt_template.json" )
prompt . input_variables # ["adjective", "content"]
Crie um prompt com alguns exemplos de fotos:
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:
Salve o modelo de prompt no arquivo JSON:
prompt . save ( file_path : "spec/fixtures/prompt/few_shot_prompt_template.json" )
Carregando um novo modelo de prompt usando um arquivo 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."
Carregando um novo modelo de prompt usando um arquivo YAML:
prompt = Langchain :: Prompt . load_from_path ( file_path : "spec/fixtures/prompt/prompt_template.yaml" )
prompt . input_variables #=> ["adjective", "content"]
Analise as respostas de texto do LLM em saída estruturada, como JSON.
Você pode usar o StructuredOutputParser
para gerar um prompt que instrui o LLM a fornecer uma resposta JSON aderindo a um esquema JSON específico:
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.
# ...
Em seguida, analise a resposta do 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
# },
# ...
# ]
# }
Se o analisador não conseguir analisar a resposta LLM, você poderá usar OutputFixingParser
. Ele envia uma mensagem de erro, uma saída anterior e o texto do prompt original para o LLM, solicitando uma resposta "fixa":
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
Como alternativa, se você não precisar lidar com OutputParserException
, poderá simplificar o código:
# 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 )
Veja aqui um exemplo concreto
RAG é uma metodologia que auxilia os LLMs a gerar informações precisas e atualizadas. Um fluxo de trabalho RAG típico segue as 3 etapas abaixo:
Langchain.rb fornece uma interface unificada conveniente sobre bancos de dados de pesquisa vetorial suportados que facilitam a configuração de seu índice, adição de dados, consulta e recuperação dele.
Banco de dados | Código aberto | Oferta de nuvem |
---|---|---|
Croma | ✅ | ✅ |
Epsila | ✅ | ✅ |
Hnswlib | ✅ | |
Milvus | ✅ | ✅ Nuvem Zilliz |
Pinha | ✅ | |
Pgvector | ✅ | ✅ |
Qdrant | ✅ | ✅ |
Tecer | ✅ | ✅ |
Elasticsearch | ✅ | ✅ |
Escolha o banco de dados de pesquisa vetorial que você usará, adicione a dependência gem e instancie o cliente:
gem "weaviate-ruby" , "~> 0.8.9"
Escolha e instancie o provedor LLM que você usará para gerar embeddings
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
)
Você pode instanciar qualquer outro banco de dados de pesquisa vetorial compatível:
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"`
Crie o esquema padrão:
client . create_default_schema
Adicione dados de texto simples ao seu banco de dados de pesquisa vetorial:
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 use os analisadores de arquivos para carregar, analisar e indexar dados em seu banco de dados:
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 ] )
Formatos de arquivo suportados: docx, html, pdf, texto, json, jsonl, csv, xlsx, eml, pptx.
Recuperar documentos semelhantes com base na string de consulta passada:
client . similarity_search (
query : ,
k : # number of results to be retrieved
)
Recupere documentos semelhantes com base na string de consulta transmitida por meio da técnica HyDE:
client . similarity_search_with_hyde ( )
Recuperar documentos semelhantes com base na incorporação passada em:
client . similarity_search_by_vector (
embedding : ,
k : # number of results to be retrieved
)
Consulta baseada em RAG
client . ask ( question : "..." )
Langchain::Assistant
é uma classe poderosa e flexível que combina Large Language Models (LLMs), ferramentas e gerenciamento de conversas para criar assistentes inteligentes e interativos. Ele foi projetado para lidar com conversas complexas, executar ferramentas e fornecer respostas coerentes com base no contexto da interação.
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 )
Observe que o streaming não é atualmente compatível com todos os LLMs.
llm
: A instância LLM a ser usada (obrigatório)tools
: uma matriz de instâncias de ferramentas (opcional)instructions
: instruções do sistema para o assistente (opcional)tool_choice
: especifica como as ferramentas devem ser selecionadas. Padrão: "automático". Um nome de função de ferramenta específico pode ser passado. Isso forçará o Assistente a sempre usar esta função.parallel_tool_calls
: Se deve fazer múltiplas chamadas de ferramentas paralelas. Padrão: verdadeiroadd_message_callback
: uma função de retorno de chamada (proc, lambda) que é chamada quando qualquer mensagem é adicionada à conversa (opcional) assistant . add_message_callback = -> ( message ) { puts "New message: #{ message } " }
tool_execution_callback
: uma função de retorno de chamada (proc, lambda) que é chamada logo antes de uma ferramenta ser executada (opcional) 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
: Adiciona uma mensagem do usuário ao array de mensagensrun!
: Processa a conversa e gera respostasadd_message_and_run!
: Combina a adição de uma mensagem e a execução do assistentesubmit_tool_output
: envia manualmente a saída para uma chamada de ferramentamessages
: Retorna uma lista de mensagens em andamentoLangchain::Tool::Calculator
: Útil para avaliar expressões matemáticas. Requer gem "eqn"
.Langchain::Tool::Database
: Conecte seu banco de dados SQL. Requer gem "sequel"
.Langchain::Tool::FileSystem
: Interage com o sistema de arquivos (leitura e gravação).Langchain::Tool::RubyCodeInterpreter
: Útil para avaliar o código Ruby gerado. Requer gem "safe_ruby"
(precisa de uma solução melhor).Langchain::Tool::NewsRetriever
: Um wrapper em torno do NewsApi.org para buscar artigos de notícias.Langchain::Tool::Tavily
: Um wrapper em torno da Tavily AI.Langchain::Tool::Weather
: chama a API Open Weather para recuperar o clima atual.Langchain::Tool::Wikipedia
: chama a API da Wikipedia. O Langchain::Assistant pode ser facilmente estendido com ferramentas personalizadas criando classes que extend Langchain::ToolDefinition
e implementam os métodos necessários.
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
O assistente inclui tratamento de erros para entradas inválidas, tipos de LLM não suportados e falhas de execução de ferramentas. Ele usa uma máquina de estado para gerenciar o fluxo da conversa e lidar com diferentes cenários de maneira elegante.
O módulo Avaliações é uma coleção de ferramentas que podem ser usadas para avaliar e rastrear o desempenho dos produtos de saída pelo LLM e seus pipelines RAG (Retrieval Augmented Generation).
Ragas ajuda você a avaliar seus pipelines de geração aumentada de recuperação (RAG). A implementação é baseada neste artigo e no repositório Python original. Ragas rastreia as três métricas a seguir e atribui pontuações de 0,0 a 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
# }
Exemplos adicionais disponíveis: /examples
Langchain.rb usa o mecanismo Ruby Logger padrão e o padrão é o mesmo valor level
(atualmente Logger::DEBUG
).
Para mostrar todas as mensagens de log:
Langchain . logger . level = Logger :: DEBUG
O criador de logs registra em STDOUT
por padrão. Para configurar o destino do log (ou seja, registrar em um arquivo), faça:
Langchain . logger = Logger . new ( "path/to/file" , ** Langchain :: LOGGER_OPTIONS )
Se você estiver tendo problemas para instalar a gem unicode
exigida por pragmatic_segmenter
, tente executar:
gem install unicode -- --with-cflags= " -Wno-incompatible-function-pointer-types "
git clone https://github.com/andreibondarev/langchainrb.git
cp .env.example .env
e preencha as variáveis de ambiente em .env
bundle exec rake
para garantir que os testes sejam aprovados e para executar o standardrbbin/console
para carregar a gema em uma sessão REPL. Sinta-se à vontade para adicionar suas próprias instâncias de LLMs, Ferramentas, Agentes, etc. e experimentá-las.gem install lefthook && lefthook install -f
Junte-se a nós no servidor Langchain.rb Discord.
Relatórios de bugs e solicitações pull são bem-vindos no GitHub em https://github.com/andreibondarev/langchainrb.
A gema está disponível como código aberto sob os termos da licença MIT.