Importante

Você está vendo a versão anterior da nova experiência da Alura que estamos preparando para você. Em breve, ela ganha uma identidade visual novinha totalmente pensada em potencializar seus estudos!

0
respostas

[Projeto] Contornando o problema da depreciação do query_pipeline

Oi pessoal, eu vi que o Yuri postou aqui no fórum uma solução para o fato do query_pipeline usado em classe estar depreciado. Queria contribuir também. Utilizei o Claude para me ajudar em uma solução e o teste foi satisfatório e praticamente idêntico ao usado pela professora na aula:

# Importa as classes principais da API de Workflows do llama_index:
# Workflow: classe base que você deve herdar para criar seu próprio pipeline
# step: decorador que transforma um método comum em uma etapa do workflow
# Event: classe base para criar eventos customizados que trafegam entre os steps
# StartEvent: evento especial que carrega os dados iniciais (o que você passa em w.run(...))
# StopEvent: evento especial que, ao ser retornado, encerra o workflow e devolve o resultado final
from llama_index.core.workflow import Workflow, step, Event, StartEvent, StopEvent

# Define um evento customizado que vai carregar os dados entre o 1º e o 2º step. Ele é um Pydantic model — cada atributo declarado abaixo é um campo obrigatório que precisa ser preenchido quando o evento for criado.
class PandasEvent(Event):
    query_str: str            # a pergunta original do usuário, em linguagem natural
    pandas_instructions: str  # o código Pandas gerado pelo LLM (saída do llm1)
    pandas_output: str        # o resultado da execução desse código sobre o df (saída do pandas_output_parser)

# Cria a classe do Workflow, herdando de Workflow. Cada método decorado com @step é uma "etapa" — o llama_index infere automaticamente as ligações entre os steps olhando o tipo do parâmetro de entrada e o tipo de retorno de cada um (não precisa mais de add_link!).
class PandasRAGWorkflow(Workflow):

    # Primeiro step: recebe o StartEvent (equivalente ao antigo "input"/InputComponent) e retorna um PandasEvent — por isso o llama_index sabe que esse step  deve rodar assim que o workflow começa.
    @step
    async def gerar_instrucoes_pandas(self, ev: StartEvent) -> PandasEvent:
        # Extrai o texto da pergunta que foi passado em w.run(query_str="...")
        query_str = ev.query_str

        # Preenche o template pandas_prompt com a pergunta do usuário (equivalente ao antigo Link "input" -> "pandas_prompt")
        prompt_formatado = pandas_prompt.format(query_str=query_str)

        # Envia o prompt formatado para o LLM (equivalente ao antigo módulo "llm1") "await" porque acomplete é assíncrono — o workflow inteiro roda em modo async
        resposta_llm1 = await llm.acomplete(prompt_formatado)

        # Converte a resposta do LLM (objeto CompletionResponse) para string simples, que é o código Pandas gerado
        pandas_instructions = str(resposta_llm1)

        # Executa esse código Pandas sobre o dataframe real usando o parser (equivalente ao antigo módulo "pandas_output_parser")
        pandas_output = pandas_output_parser.parse(pandas_instructions)

        # Empacota os três dados que o próximo step vai precisar e os retorna como um PandasEvent — isso "dispara" automaticamente o próximo step que aceita PandasEvent como entrada
        return PandasEvent(
            query_str=query_str,
            pandas_instructions=pandas_instructions,
            pandas_output=pandas_output,
        )

    # Segundo step: recebe o PandasEvent retornado pelo step anterior e retorna um StopEvent — por isso o llama_index sabe que esse é o último step
    @step
    async def sintetizar_resposta(self, ev: PandasEvent) -> StopEvent:
        # Preenche o template response_synthesis_prompt com os três dados recebidos do step anterior (equivalente aos três antigos Links que apontavam para "response_synthesis_prompt")
        prompt_formatado = response_synthesis_prompt.format(
            query_str=ev.query_str,
            pandas_instructions=ev.pandas_instructions,
            pandas_output=ev.pandas_output,
        )

        # Envia o prompt final para o LLM (equivalente ao antigo módulo "llm2")
        resposta_llm2 = await llm.acomplete(prompt_formatado)

        # Retorna um StopEvent com o resultado final — isso encerra o workflow e o valor passado em "result" é o que você recebe de volta em w.run(...)
        return StopEvent(result=str(resposta_llm2))

# Instancia o workflow.
# timeout=120 -> tempo máximo (em segundos) que o workflow pode rodar antes de dar erro
# verbose=True -> imprime no console qual step está rodando, útil para debug
w = PandasRAGWorkflow(verbose=True, timeout=120)

Testando:

response = await w.run(query_str = 'Qual é média de gasto por cada tipo de cliente?')
texto_formatado = textwrap.fill(response, width = 100)
print(texto_formatado)

A média de gasto por cada tipo de cliente é de aproximadamente 327,79 para os clientes Membros e
318,12 para os clientes Normais. Isso sugere que os clientes Membros tendem a gastar um pouco mais
em comparação com os clientes Normais.  O código utilizado foi
df.groupby('tipo_cliente')['total'].mean()