Criando uma aplicação GenAI de ponta a ponta com Ruby e Milvus
A introdução de frameworks especializados de GenAI como LangChain nos permitiu criar aplicações de IA sofisticadas de forma rápida e fácil, aproveitando poderosos modelos de linguagem de grande porte (LLMs) como ChatGPT e LLaMA. O LangChain, por exemplo, nos permite criar uma aplicação poderosa de Geração Aumentada por Recuperação (RAG) em apenas algumas linhas de código, sem exigir conhecimento teórico profundo de IA.
Essa tendência significa que, hoje em dia, cientistas de dados e engenheiros de machine learning não são mais os únicos capazes de criar aplicações de GenAI. Engenheiros full-stack ou desenvolvedores de software agora podem criar apps de GenAI usando LangChain.
No entanto, esses frameworks de GenAI são normalmente escritos em Python, e sabemos que alguns engenheiros full-stack e desenvolvedores de software raramente usam Python em seus projetos. Portanto, há uma necessidade de extensões desses frameworks de GenAI em outras linguagens de programação para que esses engenheiros full-stack possam aproveitar LLMs poderosos para criar aplicações de GenAI em seus projetos de software.
Em uma palestra recente, Andrei Bondarev, um Arquiteto de Soluções na Source Labs LLC, apresentou uma extensão Ruby do LangChain chamada LangChain.rb para facilitar que engenheiros full-stack criem aplicações de GenAI em seus projetos de software.
Assista a esta palestra no Youtube
Mas antes de discutirmos como criar uma aplicação de GenAI com Ruby, vamos explorar brevemente o funcionamento interno da Geração Aumentada por Recuperação (RAG), um caso de uso popular de GenAI.
Como a RAG Funciona
Não é segredo que os dados são a mina de ouro de qualquer app de GenAI. Eles servem como a fonte de informações usada pela GenAI para gerar respostas factuais e precisas. De todos os dados disponíveis atualmente, 80% podem ser classificados como dados não estruturados.
Dados não estruturados referem-se a dados que não se conformam a um formato de dados predefinido. Esse tipo de dado inclui imagens, texto, som e vídeos. Para que as máquinas façam sentido desses tipos de dados não estruturados, precisamos transformá-los em um formato numérico chamado embeddings vetoriais.
Conceitos Fundamentais de Embeddings Vetoriais
Um embedding consiste em um vetor n-dimensional, em que n se refere à dimensionalidade do embedding. A dimensionalidade depende do modelo de deep learning que transforma os dados em um embedding. Um embedding carrega o significado semântico dos dados que representa.
Podemos usar modelos de deep learning para transformar várias modalidades de dados em embeddings. Por exemplo, se temos dados de texto, podemos usar modelos da OpenAI ou Sentence Transformer para transformar esses dados de texto em um embedding. Se temos dados de imagem, podemos usar modelos pré-treinados especializados capazes de extrair características de imagens, como Vision Transformer, como um modelo de embedding.
Como um embedding carrega o significado semântico dos dados que representa, podemos calcular a similaridade desse embedding com outros embeddings no chamado espaço vetorial. Embeddings com significados semânticos semelhantes serão colocados próximos uns dos outros no espaço vetorial, como você pode ver na visualização abaixo:
Embeddings vetoriais em um espaço vetorial.png
Embedding de palavras relacionadas no espaço vetorial
Como mostrado na imagem acima, os embeddings de "rainha" e "rei" são colocados próximos um do outro, assim como "mulher" e "homem". A Distância Euclidiana entre "rainha-rei" e "mulher-homem" também seria aproximadamente a mesma, pois eles carregam significados semelhantes.
Esse conceito é a base de uma operação de busca vetorial, na qual calculamos a similaridade entre um embedding e vários embeddings.
O Papel do Banco de Dados Vetorial na Busca Vetorial e em Aplicações RAG
Implementar uma busca vetorial é simples se lidarmos apenas com uma pequena quantidade de embeddings. No entanto, comumente lidamos com milhares, milhões ou até bilhões de embeddings em casos reais Portanto, precisamos de uma solução para armazenar os embeddings de forma eficiente e realizar buscas vetoriais rápidas neles.
É aqui que um banco de dados vetorial como o Milvus entra em ação. O Milvus é um banco de dados vetorial de código aberto no qual você pode armazenar enormes quantidades de embeddings e realizar buscas vetoriais nesses embeddings em uma fração de segundo.
O fluxo de trabalho de transformar dados não estruturados em embeddings e armazená-los no Milvus
O fluxo de trabalho de transformar dados não estruturados em embeddings e armazená-los no Milvus
Bancos de dados vetoriais também desempenham um papel crucial em apps populares de GenAI como RAG. Como você talvez já saiba, o principal objetivo do RAG é melhorar a precisão das respostas geradas por LLMs como ChatGPT e LLaMA, fornecendo a eles contexto que pode ser útil para responder à consulta do usuário.
Em uma aplicação RAG, assim que a consulta do usuário é recebida, ela é transformada em um embedding usando um modelo de embedding. Em seguida, uma busca vetorial é realizada, na qual o embedding da consulta é comparado com embeddings de contexto armazenados dentro do banco de dados vetorial, como o Milvus. Os dados de contexto mais semelhantes são então recuperados e passados junto com a consulta para o LLM. O LLM pode então usar as informações do contexto para gerar uma resposta contextualizada para responder à consulta do usuário.
RAG
Fluxo de trabalho RAG
LangChain como um Framework GenAI Popular
LangChain é um framework que facilita a criação e o desenvolvimento de apps de GenAI usando modelos LLM de última geração. Ele se integra facilmente a provedores populares de LLM como OpenAI, Anthropic e Google, bem como a provedores de bancos de dados vetoriais como Zilliz.
O LangChain também oferece abstrações flexíveis para desenvolver aplicações de IA baseadas em LLM, tornando simples para cientistas de dados e desenvolvedores de software criar sistemas sofisticados como RAG com apenas algumas linhas de código.
Por exemplo, digamos que queremos resumir o conteúdo de esta postagem de blog usando GPT-4. Podemos concluir essa tarefa com o seguinte código:
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)
"""
Saída: O artigo discute o conceito de agentes autônomos impulsionados por LLM, com foco nos componentes de planejamento, memória e uso de ferramentas. Ele inclui estudos de caso e exemplos de prova de conceito, bem como desafios e referências a pesquisas relacionadas. A autora enfatiza o potencial dos LLMs na criação de agentes poderosos para resolução de problemas, ao mesmo tempo em que destaca limitações como o comprimento finito do contexto e a confiabilidade das interfaces de linguagem natural.
"""
Como você pode ver, com aproximadamente apenas 10 linhas de código, podemos aproveitar o modelo GPT-4 para resumir com precisão uma longa publicação de blog.
Com LangChain, você também pode executar tarefas mais complexas. Por exemplo, você pode dividir um texto longo de um documento PDF em partes, transformar cada parte em um embedding usando um modelo de embedding de sua escolha, armazenar o embedding dessas partes dentro de um banco de dados vetorial e realizar RAG posteriormente.
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)
Além das demos fornecidas acima, LangChain oferece uma ampla variedade de funcionalidades. Por exemplo, ele integra LLMs e APIs de fontes externas, como aplicativos de clima, calculadoras ou Google Search. Essa abordagem permite que LLMs utilizem informações dessas fontes para gerar respostas mais precisas e contextuais. Veremos a implementação detalhada dessa abordagem nas próximas seções.
Você também pode explorar todas as funcionalidades do LangChain na página de documentação deles.
Desenvolvimento de Apps GenAI com Ruby e Milvus
Python se tornou a linguagem de programação de facto para frameworks de pesquisa e desenvolvimento em IA, incluindo LangChain. Enquanto isso, Ruby continua popular para o desenvolvimento rápido de software e aplicações web.
No entanto, como você viu na seção anterior, a introdução do LangChain abre possibilidades para desenvolvedores de software integrarem o poder dos LLMs em seus web apps sem conhecer as teorias detalhadas dos LLMs e da IA em geral.
Essa capacidade criou uma demanda crescente para estender esses frameworks de desenvolvimento GenAI a outras linguagens que são mais familiares para desenvolvedores full-stack, como Ruby. Para atender a essa demanda, Andrei Bondarev apresentou LangChain.rb, que é a extensão em Ruby do framework LangChain original.
LangChain.rb permite que desenvolvedores Ruby full-stack criem aplicativos web alimentados por LLM sem o incômodo de incorporar várias linguagens de programação em seus projetos. Com ele, você pode integrar facilmente bancos de dados vetoriais populares, LLMs e recursos externos aos seus aplicativos web com LLM.
LangChain.rb tem as mesmas funcionalidades gerais do LangChain original, como:
Gerenciamento de prompts: crie, carregue e salve modelos de prompt para LLMs de sua escolha
Validação do comprimento do contexto: valide o comprimento do contexto das entradas de acordo com o comprimento do contexto dos LLMs e modelos de embedding de sua escolha
Fragmentação de dados: divida dados em fragmentos com regras predefinidas antes de ingeri-los nos bancos de dados vetoriais de sua escolha
Memória de Conversa: persistência de um chat com um LLM em uma memória
Nas seções a seguir, demonstraremos o desenvolvimento de aplicativos simples alimentados por LLM com a ajuda do LangChain.rb.
Aplicativos RAG Gerais com LangChain.rb
Neste primeiro exemplo, construiremos um aplicativo RAG simples e rápido usando LangChain.rb. Antes de poder usar LangChain.rb no seu projeto Ruby, certifique-se de instalar a gem executando o seguinte comando:
gem install langchainrb
Neste projeto, usaremos Milvus como banco de dados vetorial e modelos da OpenAI tanto como LLM quanto como modelos de embedding. Para iniciar o Milvus, precisamos instalar o Milvus no Docker e iniciar o contêiner com o seguinte comando:
# Download the installation script
curl -sfL https://raw.githubusercontent.com/milvus-io/milvus/master/scripts/standalone_embed.sh -o standalone_embed.sh
# Start the Docker container
bash standalone_embed.sh start
Agora que iniciamos o contêiner Docker, vamos instanciar o Milvus e os modelos que usaremos para nossa aplicação 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"])
)
A primeira coisa que precisamos fazer é criar um schema dentro do banco de dados vetorial Milvus e o método de indexação correspondente. Em seguida, precisamos carregar esse schema antes de podermos realizar uma busca vetorial com ele.
# Create default schema
milvus.create_default_schema
# Create default index
milvus.create_default_index
# Load default schema
milvus.load_default_schema
Agora podemos ingerir alguns dados em nosso schema. Digamos que temos um PDF em nosso diretório local contendo informações sobre benefícios dos funcionários. Se quisermos armazenar todos os textos desse PDF dentro do banco de dados Milvus, podemos fazer isso executando os seguintes comandos:
pdf = Langchain.root.join("path/to/my.pdf")
# Add PDF inside of Milvus
milvus.add_data(path: pdf)
Depois que você executar os comandos acima, o LangChain fará todo o pré-processamento nos bastidores. Ele analisará o texto dentro do arquivo PDF, o dividirá em vários fragmentos, transformará cada fragmento em embeddings e, então, armazenará os embeddings dentro do banco de dados vetorial Milvus.
Depois de armazenar nossos dados dentro do banco de dados vetorial Milvus, podemos começar a fazer perguntas relacionadas ao nosso documento PDF. Digamos que queremos perguntar: “Qual é a política de férias da empresa? Quanto tempo posso tirar?” então podemos perguntar aos nossos LLMs em um sistema RAG simplesmente executando esta única linha de código:
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.
"""
E é isso! Além de criar uma aplicação RAG geral, também podemos criar uma aplicação RAG agente com LangChain.rb, que discutiremos na próxima seção.
Utilizando Agentes para Interagir com Ferramentas de Terceiros
A principal limitação de muitos LLMs é sua data de corte de conhecimento. O GPT-4, por exemplo, tem uma data de corte em abril de 2023. Isso significa que, se quisermos perguntar sobre eventos gerais ou factuais após abril de 2023, não obteremos uma resposta precisa do LLM.
Para resolver esse problema, LangChain.rb nos permite construir uma aplicação RAG agêntica. Esse tipo de aplicação RAG adiciona outra camada de inteligência, contendo um "agente" que atua como tomador de decisões. O agente analisa a consulta do usuário e então decide quais ferramentas de terceiros mais eficazes podem fornecer o contexto mais adequado para responder à consulta.
Digamos que queiramos perguntar ao nosso LLM sobre o clima atual em Nova York. Com um sistema RAG geral, o LLM não consegue saber o clima em tempo real em Nova York. Muito provavelmente, ele começará a alucinar e nos dará algumas previsões climáticas aleatórias.
O fluxo de trabalho do RAG agêntico (1).png
Fluxo de trabalho de um RAG agêntico
O RAG agêntico resolve esse problema permitindo que usemos ferramentas ou APIs, como a OpenWeather API, no sistema para obter o clima em tempo real em Nova York. O agente primeiro processará a consulta do usuário e então decidirá quais ferramentas podem fornecer contexto relevante para responder à consulta antes de sintetizar o contexto em uma resposta precisa.
A demonstração a seguir usará ferramentas de terceiros dentro do nosso sistema RAG, como uma calculadora, o aplicativo OpenWeather e o 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
Em seguida, precisamos adicionar essas três ferramentas dentro do nosso sistema RAG com o seguinte comando:
openai = Langchain::LLM::OpenAI.new(api_key: ENV["OPENAI_API_KEY"])
agent = Langchain::Agent::ReActAgent.new(
llm: openai,
tools: [weather, google_search, calculator]
)
Agora, podemos começar a fazer perguntas ao nosso LLM. Digamos que queiramos fazer a seguinte pergunta: “Encontre o clima atual em Boston, MA, e Washington, D.C., e calcule uma média.”
response = agent.run(question: "Find current weather in Boston, MA and Washington, D.C. and take an average")
Saída do RAG com integração de ferramentas
Saída do RAG com integração de ferramentas
Como você pode ver na captura de tela acima, nosso sistema RAG agêntico conseguiu responder à consulta com precisão. Vamos detalhar o fluxo de trabalho deste sistema RAG:
A consulta foi enviada primeiro ao LLM da OpenAI.
O agente reconheceu a necessidade de utilizar a OpenWeather API para obter o clima atual em Boston e Washington D.C.
Depois de recuperar os dados meteorológicos, o agente viu que a consulta exigia calcular a média do clima das duas cidades.
O agente então invocou uma ferramenta de calculadora para calcular a média do clima.
Por fim, o LLM sintetizou os resultados em uma resposta coerente e a retornou ao usuário.
Este exemplo demonstra o poder da abordagem RAG agêntica. Ao incorporar ferramentas e APIs externas, o sistema superou as limitações da data de corte de conhecimento do LLM e forneceu uma resposta precisa e atualizada à consulta do usuário.
Utilizando agentes para interagir com banco de dados interno
Também podemos usar RAG agêntico para interagir com nossos bancos de dados internos. Isso é muito útil porque podemos pedir insights sobre nossos dados usando linguagem semelhante à humana em vez de depender de consultas SQL tradicionais.
Digamos que tenhamos uma loja online e dados de usuários armazenados em um banco de dados. Normalmente, precisaríamos escrever consultas SQL para extrair insights desses dados. Com RAG agêntico, tudo o que precisamos fazer é perguntar ao LLM sobre o insight que queremos, e a resposta será retornada imediatamente.
Por exemplo, digamos que queiramos saber quantos registros de usuários estão armazenados no banco de dados. Podemos simplesmente perguntar: "Quantos usuários há?" e executar os seguintes comandos:
require 'langchain'
# Instantiate the database connection
database = Langchain::Tool::Database.new(connection_string: "postgres://localhost:5432/my_database")
# Create OpenAI LLM instance
openai = Langchain::LLM::OpenAI.new(api_key: ENV["OPENAI_API_KEY"])
# Create SQLAgent with the LLM and database connection
agent = Langchain::Agent::SQLAgent.new(
llm: openai,
db: database
)
# Ask a question to the agent
response = agent.run("How many users are there?")
Abaixo está o exemplo de saída do comando:
Saída do RAG com integração SQL
Saída do RAG com integração SQL
Como você pode ver, nosso sistema RAG agêntico conseguiu responder com precisão a uma pergunta específica relacionada aos dados do nosso banco de dados. O fluxo de trabalho do agente é semelhante ao exemplo anterior:
A consulta foi enviada para o LLM da OpenAI.
O agente analisou a consulta e determinou que uma busca no banco de dados é necessária para contar o número de usuários.
O LLM gerou uma consulta SQL apropriada com base no esquema da tabela do banco de dados.
A consulta SQL foi executada no banco de dados e retornou o resultado.
A saída do banco de dados foi enviada de volta ao LLM.
O LLM sintetizou o resultado do banco de dados em uma resposta coerente e legível por humanos e a forneceu como resposta final.
Conclusão
A introdução do LangChain torna os LLMs acessíveis a profissionais que talvez não tenham conhecimento profundo de teorias de IA e ciência de dados. Podemos criar uma aplicação RAG poderosa usando o LangChain com apenas algumas linhas de código.
Essa acessibilidade é o motivo pelo qual Andrei Bondarev introduziu o LangChain.rb, uma extensão do LangChain para Ruby. Esse framework permite que desenvolvedores full-stack incorporem o desempenho poderoso dos LLMs em suas aplicações web sem precisar de ampla expertise em IA. Além disso, o LangChain.rb elimina o incômodo de desenvolvedores full-stack terem que mudar para outra linguagem de programação quando desejam aproveitar os LLMs em suas aplicações web.
Continue lendo

Introducing Functions and Model Inference on Zilliz Cloud: Automatic Embedding and Reranking with Hosted Models
Zilliz Cloud Functions auto-generate embeddings via OpenAI, Voyage AI, Cohere, or Zilliz Hosted Models. Built-in reranking — just insert text and search.

Announcing VDBBench 1.0: Open-Source VectorDB Benchmarking with Your Real-World Production Workloads
Discover VDBBench 1.0, an open-source tool for benchmarking vector databases with real-world production data, streaming ingestion, and concurrent workloads.

AI Integration in Video Surveillance Tools: Transforming the Industry with Vector Databases
Discover how AI and vector databases are revolutionizing video surveillance with real-time analysis, faster threat detection, and intelligent search capabilities for enhanced security.


