25%Off

Compre com desconto

Começou!

Começou!

00

DIAS

00

HORAS

00

MIN

00

SEG

Alura > Cursos de Inteligência Artificial > Cursos de IA para Programação > Conteúdos de IA para Programação > Primeiras aulas do curso LangChain: desenvolva agentes de inteligência artificial

LangChain: desenvolva agentes de inteligência artificial

Fundamentos e configuração de ambientes em LangChain - Apresentação

Olá, pessoal! Meu nome é Guilherme Silveira, sou instrutor e fundador aqui da Alura e estarei ministrando a aula para vocês. Por questões de acessibilidade, vou me descrever:

Audiodescrição: Guilherme se declara uma pessoa branca de cabelos pretos. Veste uma camiseta cheia de traçados cinzas na vertical e na horizontal. Ao fundo, há uma parede iluminada na cor verde e azul.

O que vamos aprender?

Vamos aprender a utilizar uma inteligência artificial baseada em LLMs (Modelos de Linguagem), dando continuidade ao curso de LangChain.

Pré-requisito

Como pré-requisito, é necessário ter feito o curso de LangChain. Faça isso primeiro, se ainda não o fez, e depois volte aqui para continuar.

Desejamos que nossa inteligência artificial generativa não apenas siga comandos sequenciais, mas também tome decisões automatizando o processo de tomada de decisão.

Por exemplo, suponha que estamos em uma escola de ensino médio, com estudantes se formando todos os anos. Alguns deles estão se formando no terceiro ano de ensino médio e estão procurando universidades para ingressar. Agora, precisamos decidir qual universidade recomendamos para cada estudante, quais vestibulares eles devem fazer, o que precisam estudar mais, o que precisam reforçar.

Temos várias decisões a serem tomadas. Às vezes, conhecemos professores em algumas universidades e entramos em contato para recomendar um estudante. Uma pergunta muito básica como "Que universidade você recomenda?" pode desencadear uma série de considerações.

Quais são os dados do estudante? Como ele se saiu em determinados assuntos no passado e neste ano? Quais são as universidades que têm perfis semelhantes a este? O estudante tem preferências por determinadas universidades? Ele prefere se mudar para fora do Brasil, ficar no Brasil, na cidade em que está?

Temos todas essas considerações e ferramentas à nossa disposição. Conseguimos pegar uma pergunta complexa, quebrá-la em partes menores e tomar decisões de acordo com as ferramentas que temos acesso. Temos rankings de universidades, um banco de dados com os dados do estudante dos últimos anos, contatos de pessoas que trabalham em certas universidades em nossos e-mails.

Como um agente humano, que decisão tomamos? Pegamos a pergunta, quebramos em partes e decidimos como respondê-la. Primeiro, usamos uma ferramenta de busca de dados para buscar os dados do estudante, depois usamos uma ferramenta de busca de dados para buscar professores que estudam em determinada universidade, para ver se temos um contato de algum deles em nossa ferramenta de busca de e-mails.

Todas essas decisões, esse plano de ação, é baseado na pergunta e nas ferramentas que temos disponíveis. Vamos aprender a criar um agente que é uma inteligência artificial. Essa decisão de qual é o plano de ação, quais são as ferramentas que vamos utilizar, e utilizar as ferramentas e executar o plano de ação, é o agente que vai fazer.

Vamos programar um agente capaz de responder diversas perguntas. No arquivo main.py, temos:

// código omitido

pergunta = "Quais os dados de Ana?"
pergunta = "Quais os dados de Bianca?"
pergunta = "Quais os dados de Ana e da Bianca?"
pergunta = "Crie um perfil acadêmico para a Ana!"
pergunta = "Compare o perfil acadêmico da Ana com o da Bianca!"
pergunta = "Tenho sentido Ana desanimada com cursos de matemática. Seria uma boa parear ela com a Bianca?"
pergunta = "Tenho sentido Ana desanimada com cursos de matemática. Seria uma boa parear ela com o Marcos?"
pergunta = "Quais os dados da USP?"
pergunta = "Quais os dados da uNiCAmP?"
pergunta = "Quais os dados da uNi CAmP?"
pergunta = "Quais os dados da uNicomP?"
pergunta = "Dentre USP e UFRJ, qual você recomenda para a acadêmica Ana?"
pergunta = "Dentre uni camp e USP, qual você recomenda para a Ana?"
pergunta = "Quais as faculdades com melhores chances para a Ana entrar?"
pergunta = "Dentre todas as faculdades existentes, quais Ana possui mais chance de entrar?"
pergunta = "Além das faculdades favoritas da Ana existem outras faculdades. Considere elas também. Quais Ana possui mais chance de entrar?"

// código omitido

Por exemplo, "Quais os dados da Ana?", "Quais os dados da Bianca?", "Quais os dados da Ana e da Bianca?", "Crie um perfil acadêmico para ela.", "Compare o perfil acadêmico de duas pessoas.", "Estou sentindo a Ana desanimada com matemática. Será que o Marcos poderia ajudar ela com matemática? Ou será que a Bianca poderia ajudar ela com matemática?".

Continuando: "Quais são os dados da USP?", "Quais são os dados entre a Unicamp e a USP?", "Qual você recomenda para a Ana?", "Além das universidades favoritas da Ana, existem outras universidades que você recomenda?"

O agente que vamos implementar vai quebrar todas as perguntas humanas em um plano de ação. Ele buscará esses dados, cruzará informações e realizará o trabalho necessário. Há condições e tarefas que serão executadas pelo agente, não por nós. É a inteligência artificial do agente que estará atuando, não nós.

Vamos programar esse agente utilizando LLMs.

Até breve!

Fundamentos e configuração de ambientes em LangChain - Criando uma ferramenta para extração de dados de um estudante

Neste projeto, utilizaremos o OpenAI e o LangChain, ferramentas que já foram empregadas no curso anterior, sendo um pré-requisito para este. No arquivo README.md, estão contidas instruções para configurar o ambiente Python.

Criando o diretório .venv e ativando o ambiente

Para iniciar, abrimos um novo terminal clicando nos três pontos "…" na parte superior e depois nas opções "Terminal > New Terminal" ("Ctrl + Shift + '").

Nele, executamos o comando python -m venv .venv para criar o diretório .venv, onde os módulos serão instalados.

python -m venv .venv

Em seguida, ativamos o ambiente com o comando .venv/Scripts/activate.

.venv/Scripts/activate

Será exibido "(.venv) PS" antes de D:\CURSO 3860\langchain-agentes>. Estamos usando o diretório .venv para instalar as dependências.

Instalando as dependências

Agora, vamos instalar as dependências com o comando pip install -r requirements.txt.

pip install -r requirements.txt

As dependências já estão definidas e são as que utilizamos no curso anterior, quase todas. São elas: OpenAI, LangChain e a configuração de chaves no ambiente. Podemos observar isso no arquivo requirements.txt.

requirements.txt

openai==1.13.3
langchain==0.1.11
langchain-openai==0.0.8
python-dotenv==1.0.1

Criando o arquivo main.py

Criaremos o arquivo main.py, que vai definir uma pergunta. Para isso, clicamos com o botão direito no projeto e selecionamos a opção "New File" ("Novo arquivo") e digitamos "main.py". Neste arquivo, digitamos pergunta =.

A pergunta que desejamos que o agente responda é: "Quais os dados da Ana?".

main.py

pergunta = "Quais os dados da Ana?"

Se importarmos uma LLM e fizermos a pergunta "Quais os dados da Ana?", ela inventará uma Ana e os dados, ou dirá que não conhece Ana nem os dados.

O que devemos fazer? Se somos um agente e nos dirigimos a Guilherme, que é um ser inteligente, e perguntamos: "Guilherme, quais são os dados da Ana?", o que Guilherme faz? A primeira coisa é perceber que a pessoa está pedindo os dados de alguém. Então, como agente inteligente, Guilherme processa a pergunta.

O primeiro passo é: precisamos processar essa pergunta e tentar entender qual é a ferramenta que Guilherme tem à disposição na cabeça dele, de matemática, física, geografia, etc., que essa pessoa está desejando acessar. Essa pessoa está desejando acessar dados da Ana.

Então, ela quer acessar as ferramentas do Guilherme que extraem dados da Ana. Essa é a primeira coisa que o agente Guilherme inteligente faz. E é isso que o nosso agente tem que ser capaz de fazer.

Como ela vai ser capaz de fazer isso? Criamos um agente, mas esse agente tem que ter uma ferramenta capaz de extrair dados de um estudante.

Criando a classe DadosDeEstudante()

Vamos criar essa classe? Criaremos essa classe chamada DadosDeEstudante() que estende de BaseTool do OpenAI.

main.py

class DadosDeEstudante(BaseTool):

pergunta = "Quais os dados da Ana?"

Seguiremos a orientação a objetos, conforme recomendado por eles. Para isso, importamos BaseTool usando from langchain.tools import BaseTool.

from langchain.tools import BaseTool

class DadosDeEstudante(BaseTool):

pergunta = "Quais os dados da Ana?"

Agora podemos proceder com a implementação.

Como é que implementamos? Primeiro, definimos qual é o nome dessa ferramenta: name = "DadosDeEstudante". E vamos dar uma descrição (description). Colocamos em várias linhas com aspas.

from langchain.tools import BaseTool

class DadosDeEstudante(BaseTool):
    name = "DadosDeEstudante"
    description = """"""

pergunta = "Quais os dados da Ana?"

O que essa ferramenta vai fazer? Esta ferramenta é para o Guilherme humano. É como se estivéssemos dizendo: Guilherme, esta ferramenta é capaz de, dado o nome de um estudante, extrair informações e dados sobre ele. É isso que vamos inserir na descrição: Esta ferramenta extrai o histórico e preferências de um estudante de acordo com seu histórico.

from langchain.tools import BaseTool

class DadosDeEstudante(BaseTool):
    name = "DadosDeEstudante"
    description = """Esta ferramenta extrai o histórico e preferências de um estudante de acordo com seu histórico"""

pergunta = "Quais os dados da Ana?"

Essa é a descrição do que a ferramenta realiza, ou seja, será capaz de extrair os dados de um estudante. Para fazer isso, precisamos escrever o código. Criamos uma função que executa a tarefa com def run(self, input: str):. Essa função receberá a si mesma e um input, que é a pergunta: "Quais os dados da Ana?".

from langchain.tools import BaseTool

class DadosDeEstudante(BaseTool):
    name = "DadosDeEstudante"
    description = """Esta ferramenta extrai o histórico e preferências de um estudante de acordo com seu histórico"""
    
    def run(self, input: str):

pergunta = "Quais os dados da Ana?"

Dado um string, devolvemos outra string. Para isso, adicionamos -> str. Lembre-se, LLM trabalha com strings. Portanto, recebemos e devolvemos uma string, por padrão.

from langchain.tools import BaseTool

class DadosDeEstudante(BaseTool):
    name = "DadosDeEstudante"
    description = """Esta ferramenta extrai o histórico e preferências de um estudante de acordo com seu histórico"""
    
    def run(self, input: str) -> str:

pergunta = "Quais os dados da Ana?"

Imagine que o input é a pergunta. O que temos que fazer com essa pergunta? Sabemos que essa pessoa quer extrair dados, caso contrário, não estaria utilizando essa ferramenta. Se estamos usando uma régua, é porque a pessoa quer medir algo.

O que ela quer medir? Se analisarmos a frase que ela disse, por exemplo "Guilherme, meça esse mouse para mim, por favor.", precisamos extrair a palavra "mouse". Ou seja, dada a pergunta, sabemos que ela está falando de dados, caso contrário, não estaria usando essa ferramenta. Mas de quem? Precisamos extrair a palavra "Ana".

Então, aqui está a nossa frase: "Quais os dados da Ana?". Precisamos extrair a palavra "Ana". Como fazemos isso? Usamos a LLM. Consultamos a LLM e perguntamos: qual é o nome da pessoa mencionada nessa pergunta? Queremos saber o nome da pessoa mencionada na pergunta.

O que o def run() vai fazer? Irá criar uma LLM para nós. Precisamos de uma LLM, então, digitamos llm =. E usaremos a LLM chamada ChatOpenAI(), que já utilizamos no curso anterior.

O model ("modelo") será o gpt-4o e passamos a nossa chave, que é api_key, igual a os.getenv("OPENAI_API_KEY"). Logo após, precisamos importar o os e o ChatOpenAI de from.langchain_openai import ChatOpenAI.

from langchain.tools import BaseTool
import os
from langchain_openai import ChatOpenAI

# código omitido
    
    def run(self, input: str) -> str:
        llm = ChatOpenAI(model="gpt-4o"),
                                api_key=os.getenv("OPENAI_API_KEY"))

pergunta = "Quais os dados da Ana?"

Agora, criaremos a variável de ambiente.

No curso anterior, usamos uma biblioteca chamada dotenv para isso. Primeiro, carregamos a biblioteca para poder utilizá-la. Usamos from dotenv import load_dotenv. Na sequência, pedimos para carregar o dotenv com load_dotenv().

from langchain.tools import BaseTool
import os
from langchain_openai import ChatOpenAI
from dotenv import load_dotenv

load_dotenv()

# código omitido

O dotenv era um arquivo chamado .env onde escrevíamos a chave openai_key que temos. Para isso, clicamos com o botão direito na raiz do projeto e selecionamos a opção "New File" e digitamos ".env".

Criaremos uma chave usando OPENAI_API_KEY.

.env

OPENAI_API_KEY

Vamos gerá-la agora.

Voltamos ao navegador com a aba aberta no OpenIA. Com a janela "Create new secret key" aberta, clicamos em "Create secret key" na parte inferior direita. Copiamos a chave gerada selecionando "Copy" à direita da sequência de caracteres.

Assim que terminarmos o curso, vamos desabilitar essa chave.

Voltamos ao arquivo .env e colamos a chave:

.env

OPENAI_API_KEY = SequênciaDeCaracteres

Salvamos e temos essa nossa chave.

Portanto, quando chamarmos o método run() do arquivo main.py, ele vai estabelecer uma comunicação com a LLM para nós. Agora é o nosso trabalho decidir o que faremos com essa LLM: desejamos executar uma pequena cadeia de um passo.

Após o llm, digitamos PromptTemplate(). Esse PromptTemplate() recebe um template específico. Vamos colocá-lo entre três aspas, que é: "Você deve analisar a {input} para extrair o nome de usuário informado."

Iremos extrair da palavra "input". Logo após, especificamos o formato de saída. Na linha seguinte, digitamos "Formato de saída: {formato_saida}".

Este é o nosso template. Portanto, inserimos template = antes do PromptTemplate().

main.py

# código omitido
    
def run(self, input: str) -> str:
    llm = ChatOpenAI(model="gpt-4o"),
                            api_key=os.getenv("OPENAI_API_KEY"))
    template = PromptTemplate(template="""Você deve analisar a {input} para extrair o nome de usuário informado.
    Formato de saída:
    {formato_saida}""",
    )

pergunta = "Quais os dados da Ana?"

Lembra do template? Era o prompt template e como o criávamos. Passaremos mais um parâmetro após o formato da saída.

Inserimos as variáveis de input, conhecidas como input_variables. Elas constituem o único tipo de input que utilizamos. Além disso, há as variáveis parciais, chamadas partial_variables, que especificam o formato desejado de saída: partial_variables={"formato_saida" : }.

Precisamos criar um formatador. Para isso, vamos utilizar um parser. Anteriormente, desenvolvemos parser de formatação que incluíam a função get_format_instructions.

# código omitido
    
def run(self, input: str) -> str:
    llm = ChatOpenAI(model="gpt-4o"),
                            api_key=os.getenv("OPENAI_API_KEY"))
    template = PromptTemplate(template="""Você deve analisar a {input} para extrair o nome de usuário informado.
    Formato de saída:
    {formato_saida}""",
    input_variables=["input"],
    partial_variables={"formato_saida" : parser.get_format_instructions()})

pergunta = "Quais os dados da Ana?"

Portanto, precisaremos importar o PromptTemplate e o parse. Subimos para o começo do arquivo e digitamos os seguintes imports:

# código omitido

from langchain.prompts import PromptTemplate
from langchain_core.output_parsers import JsonOutputParser

# código omitido

Precisamos criar o parser que estamos planejando ter.

No template, após o ChatOpenAI(), pulamos uma linha e definimos o formato de saída: um parser chamado jsonOutputParser(), que se baseia em um objeto Python (pydantic_object=) denominado ExtratorDeUEstudante.

# código omitido
    
def run(self, input: str) -> str:
    llm = ChatOpenAI(model="gpt-4o"),
                            api_key=os.getenv("OPENAI_API_KEY"))
    parser = jsonOutputParser(pydantic_object=ExtratorDeUEstudante)

# código omitido

O ExtratorDeEstudante irá extrair um estudante. Para informar isso, subimos o arquivo e, após o load_dotenv(), criaremos uma classe chamada ExtratorDeEstudante() que extrai o nome do estudante para nós.

Essa classe será um modelo BaseModel utilizado na LLM para extrair o nome do estudante, que é um campo em formato de string: estudante: str = Field("Nome do estudante informado, sempre em letras minúsculas. Exemplo: joão, carlos, joana, carla"). São alguns exemplos que podemos utilizar.

# código omitido
    
class ExtratorDeEstudante(BaseModel):
    estudante: str = Field("Nome do estudante informado, sempre em letras minúsculas. Exemplo: joão, carlos, joana, carla")

# código omitido

Precisamos importar tudo isso que mencionamos. Para isso, subimos o código e inserimos o seguinte import:

# código omitido

from langchain_core.pydantic_v1 import Field, BaseModel

# código omitido

Assim, configuramos a llm, definimos o formato de saída com parser e preparamos um modelo para extrair a palavra "Ana" desta parte do texto.

Agora desejamos tentar executar. Para executar, vamos pegar nosso template, passá-lo pela | llm | e, depois de rodar o template pela llm, queremos extrair o parser, a saída.

Qual é a saída do parser? Será a saída da pessoa usuária. Chamaremos isso de cadeia (chain).

# código omitido

partial_variables={"formato_saida" : parser.get_format_instructions()})
cadeia = template | llm | parser

pergunta = "Quais os dados da Ana?"

Essa é a cadeia que desejamos executar.

Invocamos essa cadeia com .invoke({"input" : input}). E tudo isso nos retornará a resposta.

# código omitido

cadeia = template | llm | parser
resposta = cadeia.invoke({"input" : input})

pergunta = "Quais os dados da Ana?"

A resposta vai ter um campo chamado estudante. Usamos print(resposta) para imprimir a resposta para verificá-la, e retornamos resposta['estudante'] para verificar se funciona.

# código omitido

cadeia = template | llm | parser
resposta = cadeia.invoke({"input" : input})
print(resposta)
return resposta['estudante']

pergunta = "Quais os dados da Ana?"

Criamos uma cadeia que extrai o nome do estudante. Por enquanto, apenas extraímos o nome, mas ainda não capturamos os dados completos. Queremos ver o agente Guilherme utilizando essa ferramenta.

Então, vamos executá-lo? Criamos um objeto DadosDeEstudante() após a pergunta, chamamos o método run() com a pergunta. Primeiro, vamos executá-lo isoladamente, depois adicionamos um agente. Para isso, definimos que a resposta é essa e depois imprimimos a resposta com print(resposta).

# código omitido

cadeia = template | llm | parser
resposta = cadeia.invoke({"input" : input})
print(resposta)
return resposta['estudante']

pergunta = "Quais os dados da Ana?"

DadosDeEstudante().run(pergunta)
print(resposta)

Vamos tentar rodar, mas não garantimos que vai funcionar. Para tal, clicamos no ícone de play na parte superior direita.

O retorno abaixo foi parcialmente transcrito:

TypeError: Can't instantiate abstract class DadosDeEstudante without an implementation for abstract method '_run'

Tentamos rodar e ele disse que não pode instanciar porque falta o método run. Realmente, está faltando. O run() tem um underline antes.

# código omitido
    
def _run(self, input: str) -> str:

# código omitido

Rodamos novamente e obtemos:

{'estudante': 'ana'}

Obtivemos o como retorno um JSON, que veio formatado corretamente, onde conseguimos extrair a palavra "Ana". Podemos até remover o print(resposta) que colocamos antes do return resposta['estudante'].

# código omitido

cadeia = template | llm | parser
resposta = cadeia.invoke({"input" : input})
return resposta['estudante']

pergunta = "Quais os dados da Ana?"

DadosDeEstudante().run(pergunta)
print(resposta)

Em resumo, temos uma ferramenta que, por enquanto, só consegue extrair a palavra Ana.

Conclusão e Próximos Passos

Agora, duas coisas são interessantes. Primeiro, ela lê dados de um arquivo CSV, de um banco de dados SQL, ou de qualquer outra fonte onde estejam os dados da Ana. Isso é muito importante.

Mas isso é um trabalho de banco de dados. Resolveremos isso. Mas o mais importante, viemos aqui para ele descobrir que esta é a ferramenta que ele deseja utilizar. A ideia não é simplesmente usar a LLM, já sabemos como usá-la. Aprendemos em um curso do LangChain como usar uma sequência. O que queremos agora é que ela perceba.

A LLM precisa entender que, ao receber a pergunta "Quais os dados da Ana?", deve utilizar essa ferramenta. Não deve inventar, mas sim mencionar as ferramentas disponíveis.

Vamos observar isso acontecer no próximo vídeo. Até lá!

Fundamentos e configuração de ambientes em LangChain - Criando um agente OpenAI

Olá, pessoal! Para continuar, vamos fechar o terminal e o README. Temos uma ferramenta e uma LLM (Language Model) pura, imaginemos, que não conhece essa ferramenta. Vamos criar essa LLM pura, representada por llm e igual a ChatOpenAi, como fizemos antes:

main.py

llm = ChatOpenAI(model="gpt-4o",
                 api_key=os.getenv("OPENAI_API_KEY"))

A llm é uma conexão com uma LLM, e não sabe de nada nem ninguém. E temos várias ferramentas, que são as nossas tools, onde vamos criar uma nova tool.

Criando uma ferramenta

Para criar uma tool, usamos Tool com "T" maiúsculo, e passamos três argumentos: o nome da nossa ferramenta (name), o método "run" dessa ferramenta, que é a função (func), e a descrição (description) dessa ferramenta.

tools = [
    Tool(name=,
         func=,
         description=)
]

Já temos essas três informações na classe DadosDeEstudante. Então, basta instanciarmos DadosDeEstudante.

Para isso, antes de definir as ferramentas que a nossa LLM vai ter em tools, vamos criar a variável dados_de_estudante, que recebe DadosDeEstudante():

dados_de_estudante = DadosDeEstudante()

Agora, na ferramenta Tool, podemos definir o nome como dados_de_estudante.name, a função dados_de_estudante.run e a descrição dados_de_estudante.description. Nossa Tool ficará assim:

tools = [
    Tool(name=dados_de_estudante.name,
         func=dados_de_estudante.run,
         description=dados_de_estudante.description)
]

Vamos comentar as linhas posteriores, de resposta, pois temos interesse apenas em criar essa ferramenta por enquanto.

# resposta = DadosDeEstudante().run(pergunta)
# print(resposta)

Também precisamos importar o Tool de langchain.agents, no topo do arquivo:

from langchain.agents import Tool

Criando um agente

Agora, temos uma LLM que não sabe nada e um conjunto de ferramentas. Queremos agora perguntar para essa LLM: "Quais são os dados da Ana?". Então, a LLM deve conseguir dizer "Para responder essa pergunta, eu preciso usar a ferramenta dadosdeestudante", e, claro, usar a ferramenta dados_de_estudante! Ela precisa realizar as duas tarefas.

Então agora, sim, queremos criar um agente de verdade. Vamos criar uma primeira versão dele usando a OpenAI. Para isso, declaramos agente e chamamos create_openai_tools_agent(), passando a llm limpa como parâmetro, além das ferramentas que temos disponíveis, que estão em tools.

agente = create_openai_tools_agent(llm, tools)

Vamos passar também um prompt de inicialização, porque a LLM não sabe o que é uma ferramenta. Precisamos escrever um prompt descritivo para essa tarefa, como: "Cara LLM, adoraria notificá-la que existe uma ferramenta chamada DadosDeEstudante que, se algum dia, por ventura, uma pessoa desejar extrair dados de estudante, use ela, não use outra".

Mas, dessa forma, precisaríamos fazer isso para cada uma das ferramentas que criarmos. Mas já temos essas informações contidas em tools, com cada nome, função e descrição. Então, vamos passar um prompt para falar para a LLM apenas: "Temos as seguintes ferramentas disponíveis, use-as". Precisamos disso para ter um agente.

Esse prompt já está pronto e disponível no LangSmith, um repositório de vários tipos de prompts e conversações previamente preparadas para nós!

Usando o LangSmith

Vamos entrar na página do LangSmith para consultar esse repositório. Por exemplo, podemos utilizar o openai-functions-agent, cujo prompt de sistema é "You are a helpful assistant" ("Você é um assistente prestativo"), bastante curto.

O instrutor também recomenda o react-chat, cujo prompt é bem mais longo e descritivo. Mas, como estamos usando um modelo de agente que utiliza functions, uma API específica da OpenAI, o prompt pode ser mais genérico.

Vamos retornar ao Visual Studio Code e importá-lo no início do arquivo com o seguinte código fornecido pelo repositório:

from langchain import hub

Agora, abaixo de tools, definimos o prompt como hub.pull("hwchase17/openai-functions-agent"). Podemos dar print nesse prompt para visualizar seu conteúdo quando ele for executado e verificar que está funcionando:

prompt = hub.pull("hwchase17/openai-functions-agent")
print(prompt)

Agora precisamos instalar o repositório langchainhub, porque não tínhamos usado ele antes. Vamos usar a versão mais recente em junho de 2024, a 0.1.15 (se for o caso, você pode usar versões mais recentes):

requirements.txt

langchainhub==0.1.15

Depois, rodamos pip install -r .\requirements.txt no terminal e finalizamos a instalação.

Se executamos main.py, recebemos no terminal o prompt esperado, então isso significa que ele está funcionando!

Agora, no nosso agente, vamos também passar prompt como parâmetro:

main.py

agente = create_openai_tools_agent(llm, tools, prompt)

Agora precisamos importar o create_openai_tools_agent de langchain.agents:

from langchain.agents import create_openai_tools_agent

Com isso, importamos o template de prompt e conseguimos criar o agente ao executar o código. Então, agora temos um agente!

Mas, ter um agente não é o suficiente, porque agora precisamos executá-lo.

Executando um agente

Para executar um agente, precisamos de um executor, ou um AgentExecutor(). Esse método vai receber o agente que criamos, afinal, ele precisa saber que agente executar. O executor também vai receber as ferramentas, tools.

AgentExecutor(agent=agente, tools=tools)

Podemos passar outros argumentos para ele posteriormente, o que faremos um a um para verificar os resultados.

Vamos importar também o AgentExecutor, também do langchain.agents:

from langchain.agents import create_openai_tools_agent, AgentExecutor

Salvamos e rodamos.

Agora, vamos executar esse agente, de fato. Para isso, chamamos o executor junto do método invoke() para invocar algo, como já conhecemos. Vamos usar como input a nossa pergunta, que vai nos devolver uma resposta. Também vamos dar um print na nossa resposta para conferi-la:

resposta = executor.invoke({"input" : pergunta})
print(resposta)

Mas, ao executar o código para testar, recebemos no terminal a resposta: "Para cumprir a solicitação, preciso que você forneça informações sobre a Ana". Por que não funcionou? Para descobrir, vamos analisar o log. Passamos no executor a opção verbose igual a true. Isso vai mostrar para nós passo a passo do que foi executado.

AgentExecutor(agent=agente, tools=tools, verbose=True)

Vamos rodar novamente, e agora podemos acompanhar cada execução conforme elas acontecem. DadosDeEstudante com Ana foi invocado, sendo Ana com "A" maiúsculo. Então, ele devolve a palavra "ana" com "a" minúsculo (a resposta de DadosDeEstudante). Correto!

Mas a resposta da LLM traz o nome da Ana, a idade de 22 anos, a faculdade de Engenharia de Software que ela cursa, o período, preferências de estudo, média geral, áreas de interesse, etc.

Ou seja, a LLM inventou essa resposta, sem base na nossa ferramenta. No entanto, ele encontrou a ferramenta DadosDeEstudante, chamou o estudante certo com a palavra "Ana", devolveu a palavra "ana", mas depois inventou.

Tentando novamente, a LLM não inventa informações, mas responde: "Os dados solicitados são referentes à Ana. Você deseja mais informações específicas sobre ela..?". Isso está correto, porque devolvemos apenas o nome da Ana, mas não os seus dados, então a LLM não pode nos ajudar com o que pedimos.

Às vezes ela decide inventar, e às vezes decide não inventar. Mas, o importante é que ela achou a ferramenta!

Função de busca de dados no CSV

Agora que já sabemos quem é o estudante, podemos substituir o return pela declaração da variável estudante dentro do run, para salvar o resultado nela:

estudante = resposta['estudante']

Agora basta buscar esse estudante no nosso arquivo para retornar os seus dados. Vamos criar uma função, nesse arquivo mesmo, por enquanto, chamada busca_dados_de_estudante, que vai receber o estudante como parâmetro.

Essa função vai usar a biblioteca Pandas para ler um arquivo para nós. Então, chamamos o método pd.read.csv() para ler um arquivo CSV. O caminho para esse arquivo CSV é "documentos/estudantes.csv", que passamos por parâmetro. Isso deve nos devolver os dados, então os salvamos nessa variável: dados = pd.read_csv("documentos/estudantes.csv").

Depois, de dados, vamos buscar o registro cujo campo USUARIO seja igual a esse estudante Vamos salvar esse resultado na variável dados_com_esse_estudante. Essa é uma busca bem simples, mas pode ser feita uma busca no SQL ou uma busca mais inteligente.

E o que vamos retornar dessa função? Se estiver vazio, retornamos vazio. Então, if dados_com_esse_estudante.empty, retornamos um dicionário vazio (return {}). Se não, retornamos a primeira linha de dados_com_esse_estudante, então, no caso do Pandas, usamos iloc[] e passamos :1, que significa "até o 1", representando apenas a primeira linha. Então adicionamos esse resultado a um dicionário com to_dict().

Nossa função ficará assim:

def busca_dados_de_estudante(estudante):
    dados = pd.read_csv("documentos/estudantes.csv")
    dados_com_esse_estudante = dados[dados["USUARIO"] == estudante]
    if dados_com_esse_estudante.empty:
        return {}
    return dados_com_esse_estudante.iloc[:1].to_dict()

Como usamos o Pandas, precisamos importar essa biblioteca no início do nosso arquivo:

main.py

import pandas as pd

Também precisamos adicioná-la ao arquivo de requisições. Vamos instalar a versão atual, 2.2.2:

requirements.txt

pandas==2.2.2

E concluímos a instalação com pip install no terminal.

Se você nunca usou Pandas, não se preocupe. Aprender Pandas não é o objetivo desse curso.

Agora precisamos criar o diretório documentos no nosso projeto e, dentro dele, criar o arquivo estudantes.csv. O conteúdo desse arquivo está disponível para acesso na atividade de preparação de ambiente desta aula. Com ele aberto, copiamos e colamos no arquivo que acabamos de criar.

O VS Code pode sugerir a instalação do plugin Rainbow CSV. Você pode instalá-lo, caso queira deixar o código CSV mais colorido e identificar os dados facilmente.

Temos vários estudantes cadastrados nesse arquivo, como a Ana, que tem o usuário ana, Ariel, Marcos, Bianca, etc. Temos nossa base de dados para consulta.

Chamando a função de busca de dados e devolvendo-os para a LLM

Agora, na classe DadosDeEstudante, quando já temos o estudante, vamos chamar a função busca_dados_de_estudante(), passando estudante como parâmetro, e salvar o resultado na variável dados. Com isso, vamos ler o nosso arquivo ou banco de dados e, depois, retornar os dados desse estudante (return dados).

Mas precisamos tomar cuidado. Nossos dados estão num dicionário do Python, e queremos transformar isso num dicionário em texto string. Para isso, vamos precisar transformar num JSON. Então, no retorno, podemos fazer um json.dumps para criar uma string baseada nos dados desse estudante.

Por fim, podemos imprimir os dados para saber o que foi salvo dentro dessa variável após a execução. Antes, podemos imprimir a string "Achei" para indicar que os dados foram encontrados. Com isso, teremos:

main.py

class DadosDeEstudante(BaseTool):
    def _run(self, input: str) -> str:
# código omitido
        dados = busca_dados_de_estudante(estudante)
        print("Achei")
        print(dados)
        return json.dumps(dados)

Agora precisamos importar o json no início do arquivo:

import json 

Vamos salvar o arquivo e executá-lo para testar. No terminal, podemos visualizar o passo a passo da execução. Primeiramente, temos um indicativo de sucesso da execução com a mensagem "Invoking DadosDeEstudante with Ana".

Depois disso, a mensagem "Achei" com os dados da Ana são impressas em seguida, devolvendo-os para a LLM. Então, a LLM respondeu:

Aqui estão os dados da Ana:

Está tudo correto conforme a base de dados que indicamos para a LLM. Ou seja, nossa função conseguiu ler essa base corretamente e devolver os dados para nós, organizados em um texto.

Então, temos uma ferramenta! Agora, da mesma forma que criamos essa ferramenta, podemos criar inúmeras outras.

Podemos testar a busca de outros estudantes, mudando a pergunta. Para buscar os dados da Bianca, podemos apenas trocar o nome:

pergunta = "Quais os dados de Bianca?"

E executamos o arquivo novamente. Nossa função vai buscar a palavra Bianca no CSV, ou no nosso banco de dados, e trazer os dados dela:

Aqui estão os dados de Bianca:

Para continuar, primeiro podemos melhorar esse código. Depois, vamos adicionar mais ferramentas. Vamos lá!

Sobre o curso LangChain: desenvolva agentes de inteligência artificial

O curso LangChain: desenvolva agentes de inteligência artificial possui 119 minutos de vídeos, em um total de 33 atividades. Gostou? Conheça nossos outros cursos de IA para Programação em Inteligência Artificial, ou leia nossos artigos de Inteligência Artificial.

Matricule-se e comece a estudar com a gente hoje! Conheça outros tópicos abordados durante o curso:

Aprenda IA para Programação acessando integralmente esse e outros cursos, comece hoje!