Simplificando a utilização da API do ChatGPT com LangChain
Bom, o buzz sobre o #ChatGPT está instalado, só se fala nisso e todos querem saber como utilizá-lo para melhorar suas aplicações. O problema é como gerenciar as múltiplas possibilidades de interação e processar as respostas para que a aplicação tome ações.
Esta é a ideia do #LangChain. Já que muitas empresas estão adaptando suas aplicações para interagir com os LLM e realizam basicamente as mesmas ações, o LangChain foi criado para padronizar e simplificar as interações frequentes com o modelo de LLM. Ou seja, é um framework open-source que integra aplicações ao modelo.
Obs: O GPT é um Large Language Model, ou LLM, criado pela OpenAI. Há outros, mas neste texto sempre que eu mencionar o modelo, será o da OpenAI.
Aplicações e ChatGPT 101
Ao usar o ChatGPT nos deparamos com um prompt, onde escrevemos o que queremos e, a partir desta informação, o modelo de IA vai processar e gerar a resposta ao nosso comando. Porém, as aplicações não funcionam deste jeito, Ou melhor, até funcionam, mas para integrar os apps com o GPT, modelo de linguagem por trás do ChatGPT, precisamos então que as aplicações escrevam seus comandos, submetam à API da OpenAI e processem suas saídas para gerar as ações adequadas.
Como tudo isso se dá através de texto, é necessário converter os objetivos em comandos, e depois converter as saídas em uma estrutura de dados que a aplicação entenda.
É aí que entra o LangChain!
LangChain
A ideia inicial do LangChain é criar templates de entrada e processadores de saída, com suporte a variáveis e transformações, sequências e memória para que todo o fluxo de interação com a API seja padronizada e convertida.
Prompts
Suponha que você queira submeter ao GPT uma solicitação de tradução de um determinado texto dado por um usuário. A partir da resposta, além da tradução em si, você deseja extrair também alguma informação de contexto, como, por exemplo, se o texto é uma reclamação, elogio ou pedido de informação.
O LangChain nos permitiria então definir:
- Modelos: Padrões de mensagem, ou prompts, que, além do texto em si, também especifica o formato que o texto será passado (Ex: texto entre aspas), as orientações de processamento (Ex: traduza para português) e as informações que se deseja extrair (Ex: é uma reclamação? É um elogio?)
- Variáveis: O modelo pode conter variáveis, que são definidas entre chaves.
- Output Parser: O prompt pode ainda especificar como se deseja obter a saída. Por exemplo, é possível solicitar que a saída seja um JSON com um atributo “reclamação” e outro “elogio”, por exemplo. Mas, a saída ainda acontecerá em texto. Então o output parser irá converter o texto para JSON e assim a resposta será um objeto ou um Dict, estruturas de dados que a aplicação entende.
Memória
Legal, você teve a primeira resposta, mas agora você deseja continuar a interação e, já que o texto inicial foi uma reclamação, você precisa saber qual a queixa. Mas a API não guarda na memória o que já foi conversado, pois o modelo de conversação é Stateless e cada interação é independente. Então, para que uma memória seja mantida, o que acontece a aplicação precisa acumular tudo que foi conversado com a API (ou seja, todos os inputs e outputs) e sempre enviar isso na requisição.
O LangChain gerencia este histórico num objeto chamado memory, que sempre é transmitido na chamada da API.
Mas, para evitar que esta memória fique muito grande e consuma muitos tokens de processamento, é possível gerenciar o tamanho da memória de algumas maneiras:
- Buffer:Armazena todas as mensagens para que sejam retransmitidas na próxima interação
- Window: Limitar a quantidade de interações que serão mantidas na memória e sempre enviadas à API, o que otimiza o processamento.
- Token: Mantém na memória as interações que consomem um determinado volume de tokens
- Summary: A API resume a conversa com um determinado número de tokens, assim, a API receberá um breve descritivo do histórico e, com base nisso, processará as próximas respostas.
Com isso, limitamos a memória a um volume de tokens.
Há ainda outros tipos de memória, como vetores (para vector search) e entidades. Assim como é possível combinar memórias e utilizá-las quando apropriado. Ainda, as memórias podem ser armazenadas num banco de dados (como o Cassandra, por exemplo).
Chains
Talvez, esta seja a parte mais importante: Como sequenciar as interações e então automatizar todo um processo?
Uma chain simples combina o modelo LLM do GPT com o prompt, produzindo um resultado.
Sequential Chains: Executa uma chain em seguida da outra, sendo que a saída da primeira chain é a entrada da segunda, e assim sucessivamente. Aqui há dois modos:
- SimpleSequentialChain sempre irá trabalhar com uma única saída e uma única entrada.
- SequentialChain: Cada chain define uma variável de saída nomeada, que pode ser usada em outra mais à frente. Por exemplo, se a sequência tiver quatro chains, a quarta poderá referenciar as variáveis de saída produzidas pelas três chains anteriores.
RouterChain: Permite, como o nome diz, rotear entre as chains dependendo de resultados de uma chain específica. É a mais complexa, pois envolve múltiplas chains e um prompt de roteamento muito bem definido. É composta pelas seguintes partes:
- Destination Chain: Um Dict que contém chains candidatas:
- Prompt templates: contém os prompts que poderão ser executados pela RouterChain
- Prompt Infos: Um Dict com um nome e descrição para cada template
- Default chain: Uma chain que resolverá a interação se o roteamento cair em condições não previstas
- RouterChain: especifica quais as opções de roteamento e como o ChatGPT deve avaliar os candidatos. Além disso, também determina como será a saída dos dados.
- OutputParser: Responsável por converter a saída em uma estrutura de dados que a aplicação entenda.
LLM e documentos
Localizar documentos baseado em um determinada necessidade ou extrair contexto de determinados documentos para responder determinadas perguntas será um dos casos mais frequentes com IA. É como se o modelo de linguagem fosse treinado sobre um determinado documento e esta é sua base de conhecimento, o que permite estender a utilização da IA para qualquer caso de uso.
Comparar textos e sua semântica analisando as palavras é complicado e trabalhoso, então ao converter textos em vetores é possível analisar sua similaridade e, com isso, classificar e localizar informações. Isto é feito através do armazenamento destes múltiplos vetores em uma base de dados (em memória ou banco de dados mesmo).
A query então é também convertida em um vetor que é base da busca por similaridade na base de vetores, o que nos aponta o documento (e trecho) original.
Há ainda os tipos de chains que avaliam documentos, que ampliam as capacidades de processamento.
- Stuff: Retorna os documentos (ou trechos) encontrados.
- Map_Reduce: Busca os documentos, e submete a outro LLM para resumir o conteúdo e formular a resposta final.
- Refine: Permite combinar respostas de diferentes documentos de maneira sequencial, até a resposta final.
- Map_Rerank: Ainda experimental, mas é parecido com o Map_Reduce. A diferença é que se baseia num score de similaridade para retornar a resposta final.
Estes métodos podem ser combinados com outras chains, construindo aplicações que partem de dados da empresa, seus documentos e base de conhecimento para resolver processos de aplicações.
Avaliação das respostas
Para controlar a acurácia das respostas, é possível criar prompts e respostas esperadas manualmente. Mas, para escalar este processo é necessário utilizar os próprios documentos como base de teste. Isto é útil para aferir a qualidade do processo, mas principalmente quando se deseja evoluir a aplicação e suas interações. Ou, até mesmo, mudar o modelo LLM e verificar qual sua performance.
Para fazer isso, o LangChain possui o método QAGenerateChain, que produz pares de prompt e resposta baseado nos documentos de entrada.
Com estes pares de prompts e respostas, executamos todo o processo de avaliação. É possível ainda ativar o modo debug para entender o que se passou, o contexto enviado ao modelo, os prompts trocados entre a aplicação e o modelo.
O ponto é que ao enviarmos um prompt ao modelo, não sabemos exatamente como será a resposta, então usar avaliação de texto, regex não funciona. O que o LangChain faz é usar o próprio modelo para avaliar se a resposta ao prompt é similar à resposta esperada. Isto é realizado por um tipo de chain específico chamado QAEvalChain.
Agents
Apesar de parecer, um modelo de LLM não é um repositório de conhecimento, o seu foco está em extrair contexto de uma determinada base e com isso responder o que lhe é perguntado. Mas é claro que há algo armazenado que é utilizado pelo modelo.
Os agentes do LangChain se preocupam em ter um conhecimento sobre determinado assunto e executar determinadas ações, como interagir com bancos de dados ou outras aplicações.
É semelhante ao que o Git Copilot faz, assim, é possível executar código gerado pelo GPT. Parece banal, mas ao debugar uma chamada a um agente, vemos como este agente possui todos os prompts que especificam ao modelo tudo que deve ser feito e retornado à aplicação.
Há alguns agentes já criados no LangChain, como o create_python_agent, que explica ao modelo como resolver uma questão usando o python. Mas também é possível criar seus próprios agentes! Basta uma descrição do que o agente é capaz de fazer, junto a uma chain para resolver suas questões. Com isso, ao submetermos uma questão ao conjunto de agentes, o primeiro passo será identificar o melhor agente para resolver o problema e encarregá-lo da resposta.
Este recurso ainda é experimental, está em evolução então está sujeito a erros.
Conclusão
Integrar às aplicações aos modelos de inteligência artificial, especialmente os Large Language Model, em tempo real é algo que mudará todo resultado entregues aos usuários. E, ainda que isto esteja só começando, os sinais de que esta será uma revolução no mundo tecnológico e, claro, de negócio.
O LangChain é uma ótima opção para fazer a integração entre apps e LLMs, dado que padroniza o processo e possibilita converter resultados do GPT para a aplicação de maneira simples.
PS 1 : O conteúdo deste texto foi construído a partir do curso “LangChain for LLM Application Development”, disponibilizado pela DeepLearning.AI.
PS 2: Este texto não foi escrito pelo ChatGPT