Orquestração de agentes de LLMs: guia passo a passo com LangChain e Granite

Autor

Vrunda Gadesha

AI Advocate | Technical Content Author

A orquestração de agentes de LLMs refere-se ao processo de gerenciar e coordenar as interações entre um grande modelo de linguagem (LLM) e várias ferramentas, APIs ou processos para executar tarefas complexas dentro de sistemas de IA. Envolve a estruturação de fluxos de trabalho em que um agente de IA, impulsionado por inteligência artificial, atua como o tomador de decisão central ou mecanismo de raciocínio, orquestrando suas ações com base em entradas, contexto e saídas de sistemas externos. Usando um framework de orquestração, os LLMs podem se integrar sem dificuldades com APIs, bancos de dados e outras aplicações de IA, permitindo funcionalidades como chatbots e ferramentas de automação. Frameworks de agentes de código aberto aprimoram ainda mais a adaptabilidade desses sistemas, tornando os LLMs mais eficazes em cenários do mundo real.

Muitas pessoas não entendem a diferença entre a orquestração de LLMs e a orquestração de agentes de LLMs. A ilustração a seguir destaca as principais diferenças:

Diagrama com as principais diferenças entre a orquestração de LLMs e a orquestração de agentes de LLMs Principais diferenças entre a orquestração de LLMs e a orquestração de agentes de LLMs

Neste tutorial, você aprenderá a criar um agente autônomo impulsionado por grandes modelos de linguagem (LLMs) usando os modelos IBM® Granite e o LangChain. Exploraremos como os agentes aproveitam os principais componentes, como memória, planejamento e ação, para executar tarefas inteligentes. Você também implementará um sistema prático que processa texto de um livro, responde a consultas de forma dinâmica e avalia o desempenho usando métricas de precisão como BLEU, precisão, recall e pontuação F1.

Framework para agentes autônomos baseados em LLMs

O framework apresentado na figura 1 fornece um design holístico para agentes autônomos baseados em grandes modelos de linguagem (LLMs), enfatizando a interação entre os principais componentes: perfil, memória, planejamento e ação. Cada componente representa um estágio crítico na criação de um agente autônomo capaz de raciocinar, tomar decisões e interagir com ambientes dinâmicos.1

Framework para agentes autônomos baseados em LLMs Framework para agentes autônomos baseados em LLMs

1. Perfil: definição da identidade do agente

O perfil dá ao agente sua identidade por meio do embedding de informações como demografia, traços de personalidade e contexto social. Esse processo garante que o agente possa interagir de forma personalizada. Os perfis podem ser criados manualmente, gerados por modelos de IA generativa, como os modelos IBM Granite ou o GPT (generative pretrained transformer) da OpenAI, ou alinhados com conjuntos de dados específicos para atender a requisitos de tarefas. Aproveitando a engenharia de prompts, os perfis podem ser refinados dinamicamente para otimizar as respostas. Além disso, dentro da orquestração multiagentes, o perfil ajuda a definir funções e comportamentos, garantindo a coordenação perfeita entre algoritmos de IA e sistemas de tomada de decisão.

2. Memória: armazenamento e uso de contexto

A memória ajuda o agente a reter e recuperar interações passadas, possibilitando respostas contextuais. Pode ser unificada (todos os dados em um só lugar) ou híbrida (estruturada e não estruturada). Operações incluindo leitura, escrita e reflexão permitem que o agente aprenda com a experiência e forneça saídas consistentes e informadas. Uma memória bem estruturada aprimora a orquestração de vários agentes, garantindo que diferentes agentes, incluindo agentes especializados projetados para uma tarefa específica, possam compartilhar e recuperar dados relevantes de forma eficiente. Em frameworks como AutoGen e Crew AI, a memória desempenha um papel crucial na manutenção da continuidade no ecossistema de agentes colaboradores, garantindo uma coordenação perfeita e a execução otimizada de tarefas.

3. Planejamento: ações de estratégia

O componente de planejamento permite que o agente elabore estratégias para atingir metas. Ele pode seguir etapas predefinidas ou adaptar-se dinamicamente com base no feedback do ambiente, dos humanos ou do próprio LLM. Ao integrar algoritmos de IA e aproveitar uma base de conhecimento, o planejamento pode ser otimizado para melhorar a eficiência do raciocínio e a precisão da resolução de problemas. Nas aplicações de LLM, o planejamento desempenha um papel crucial para garantir que a natural language understanding e a Tomada de decisão estejam alinhados com os objetivos do agente. Além disso, as técnicas de recuperação-aumentada aprimoram a capacidade do agente de acessar informações relevantes de forma dinâmica, melhorando a precisão da resposta. Essa flexibilidade garante que o agente permaneça eficaz em mudanças de cenários, especialmente na orquestração multiagente, em que vários agentes coordenam seus planos para alcançar objetivos complexos, mantendo a escalabilidade para lidar com tarefas grandes e diversas.

4. Ação: execução das decisões

As ações são a forma do agente interagir com o mundo, seja concluindo tarefas, coletando informações ou se comunicando. Ele usa memória e planejamento para orientar a execução, emprega ferramentas quando necessário e adapta seu estado interno com base em resultados para melhoria contínua. A otimização do algoritmo de execução da ação garante eficiência, especialmente na integração de modelos de raciocínio impulsionados por GPT e técnicas de IA generativa para a tomada de decisão em tempo real.

Ao combinar esses componentes, o framework transforma os LLMs em agentes adaptáveis capazes de raciocinar, aprender e realizar tarefas de forma autônoma. Esse design modular o torna ideal para aplicações como atendimento ao cliente, assistência em pesquisa e resolução de problemas criativos.

Caso de uso: criação de um agente de conhecimento consultável

Este tutorial demonstra a criação de um agente de conhecimento consultável projetado para processar grandes documentos de texto (como livros) e responder às consultas dos usuários com precisão. Usando os modelos IBM Granite e o LangChain, o agente é criado seguindo os princípios descritos no framework para agentes autônomos baseados em LLMs. Os componentes do framework se alinham sem dificuldades ao fluxo de trabalho do agente para garantir adaptabilidade e respostas inteligentes.

Vamos entender como o framework se aplica a nosso caso de uso.

Aplicação do framework Aplicação do framework

Perfil: o agente foi projetado com um perfil de "assistente de conhecimento", com foco em tarefas de sumarização, resposta a perguntas e raciocínio. Seu contexto é personalizado para processar um documento específico (por exemplo, As aventuras de Sherlock Holmes).

Memória: o agente emprega memória híbrida incorporando partes do livro em um armazenamento de vetores FAISS. Essa capacidade permite recuperar o contexto relevante dinamicamente durante as consultas. Operações de memória, como leitura (recuperação) e escrita (atualização de embeddings), garantem que o agente possa se adaptar a novas consultas ao longo do tempo.

Planejamento: a resolução de consultas envolve o raciocínio de caminho único. O agente recupera partes relevantes do texto, gera respostas usando o LLM Granite da IBM e avalia a saída quanto à precisão. O planejamento sem feedback garante simplicidade, enquanto a modularidade do sistema permite que ciclos de feedback sejam incorporados a iterações futuras.

Ação: o agente executa a resolução de consultas integrando a recuperação de memória e o processamento do LLM. Ele realiza tarefas como gerar respostas, calcular métricas de precisão (BLEU, precisão, recall e pontuação F1) e visualizar resultados para interpretação do usuário. Essas saídas refletem a capacidade do agente de agir de forma inteligente com base em raciocínio e planejamento.

Pré-requisitos

Você precisa de uma conta do IBM Cloud para criar um projeto do watsonx.ai .

Etapas

Etapa 1. Configure seu ambiente

Embora possa escolher entre várias ferramentas, este tutorial vai orientar você em como configurar uma conta da IBM usando um Jupyter Notebook.

  1. Faça login no watsonx.ai usando sua conta do IBM Cloud.
  2. 2. Crie um projeto do watsonx.ai. Você pode obter a ID do projeto a partir de seu projeto. Clique na guia Gerenciar. Em seguida, copie a ID do projeto da seção Detalhes da página Geral. Você precisa dessa ID para este tutorial.
  3. 3. Crie um Jupyter Notebook.

Essa etapa abre um ambiente do Notebook, onde você poderá copiar o código deste tutorial. Ou então, você pode baixar esse Notebook em seu sistema local e carregá-lo como um ativo em seu projeto do watsonx.ai. Para ver mais tutoriais do Granite, consulte a Comunidade IBM Granite. Este tutorial também está disponível no GitHub.

Etapa 2. Configure o serviço watsonx.ai Runtime e a chave de API

  1. Crie uma instância do serviço watsonx.ai Runtime (escolha o plano Lite, que é uma instância gratuita).
  2. Gere uma chave de interface de programação de aplicativos (API) .
  3. Associe o serviço watsonx.ai Runtime ao projeto que você criou no watsonx.ai

Etapa 3. Instalação dos pacotes

Para trabalhar com o framework LangChain e integrar o IBM WatsonxLLM, precisamos instalar algumas bibliotecas essenciais. Vamos começar instalando os pacotes necessários:

Observação: se você estiver usando uma versão antiga dopip , você poderá usar o comandopip install --upgrade pip para fazer o upgrade para facilitar a instalação dos pacotes mais recentes, que podem não ser compatíveis com versões mais antigas. Mas, se você já estiver usando a versão mais recente ou atualizou seus pacotes recentemente, pode ignorar este comando.

!pip install --upgrade pip
!pip install langchain faiss-cpu pandas sentence-transformers
%pip install langchain
!pip install langchain-ibm

Na célula de código anterior,

  • LangChain  é o framework central para a criação de aplicações com modelos de linguagem.
  • faiss-cpu  é para uma pesquisa eficiente por similaridade, usada na criação e consulta de índices de vetores.
  • pandas  é para manipulação e análise de dados.
  • sentence-transformers  é para gerar embeddings para pesquisa semântica.
  • langchain-ibm  é para integrar o IBM WatsonxLLM (neste tutorial, é o granite-3-8b-instruct) ao LangChain.

Esta etapa garante que seu ambiente esteja pronto para as tarefas futuras.

Etapa 4. Importe as bibliotecas necessárias

Agora que instalamos as bibliotecas necessárias, vamos importar os módulos necessários para este tutorial:

import os
from langchain_ibm import WatsonxLLM
from langchain.embeddings import HuggingFaceEmbeddings
from langchain.vectorstores import FAISS
from langchain.text_splitter import RecursiveCharacterTextSplitter
import pandas as pd
import getpass

Na célula de código anterior,

  • os  fornece uma maneira de interagir com o sistema operacional (por exemplo, acessando variáveis de ambiente).
  • langchain_ibm.WatsonxLLM  permite-nos usar o LLM IBM Watson Granite sem dificuldades dentro do framework do LangChain.
  • langchain.embeddings.HuggingFaceEmbeddings  gera embeddings para texto usando modelos do HuggingFace, essenciais para pesquisa semântica.
  • langchain.vectorstores.FAISS  é uma biblioteca para armazenamento eficiente de vetores e pesquisa por similaridade, que nos permite construir e consultar um índice de vetores.
  • RecursiveCharacterTextSplitter  ajuda a dividir grandes blocos de texto em fragmentos menores, o que é crítico para o processamento eficiente de documentos.
  • pandas  é uma biblioteca poderosa para análise e manipulação de dados, usada aqui para lidar com dados tabulares.
  • getpass  é uma maneira segura de capturar entradas confidenciais, como chaves de API, sem exibi-las na tela.

Essa etapa configura todas as ferramentas e módulos necessários para processar texto, criar embeddings, armazená-los em um banco de dados de vetores e interagir com o WatsonxLLM da IBM.

Etapa 5. Configure as credenciais

Esse código configura credenciais para acessar a API do IBM Watson Machine Learning e garante que a ID do projeto esteja configurada corretamente.

  • Um dicionáriocredenciais é criado com aURL de serviço do WML eChave de api . A chave de API é coletada de forma segura usando "getpass.getpass" para evitar a exposição de informações confidenciais.
  • O código tenta buscar a PROJECT_ID  das variáveis de ambiente usando os.environ . Se o PROJECT_ID  não for encontrado, será solicitado que o usuário o insira manualmente por meio de uma entrada.
# Set up credentials
credentials = {
      "url": "https://us-south.ml.cloud.ibm.com", # Replace with the correct region if needed
      "apikey": getpass.getpass("Please enter your WML API key (hit enter): ")
     }
# Set up project_id
try:
     project_id = os.environ["PROJECT_ID"]
except KeyError:
     project_id = input("Please enter your project_id (hit enter): ")

Etapa 6. Inicialize o grande modelo de linguagem

Este código inicializa o IBM WatsonxLLM para uso na aplicação:

  1. Esse código cria uma instância do WatsonxLLM usando o modeloibm/granite-3-8b-instruct, projetado para tarefas de IA generativa baseadas em instruções.
  2. Ourl ,chave de api  eproject_id valores das credenciais configuradas anteriormente são passados para autenticação e conexão com o serviço do IBM WatsonxLLM.
  3. Configura o parâmetromax_new_tokens para limitar o número de tokens gerados pelo modelo em cada resposta (150 tokens neste caso).

Essa etapa prepara o watsonxLLM para gerar respostas no fluxo de trabalho.

# Initialize the IBM Granite LLM
llm = WatsonxLLM(
      model_id="ibm/granite-3-8b-instruct",
      url=credentials["url"],
      apikey=credentials["apikey"],
      project_id=project_id,
      params={
           "max_new_tokens": 150
      }
)

Etapa 7. Defina uma função para extrair texto de um arquivo

Para processar o texto de um documento, precisamos de uma função que possa ler e extrair seu conteúdo. A seguinte função foi projetada para lidar com arquivos de texto simples:

def extract_text_from_txt(file_path):
      """Extracts text from a plain text file."""
           with open(file_path, "r", encoding="utf-8") as file:
           text = file.read()
return text

Essa função,extract_text_from_txt , foi projetada para ler e extrair o conteúdo de um arquivo de texto simples. Ela aceita o caminho do arquivo como argumento e abre o arquivo no modo de leitura comcodificação UTF-8 , garantindo que os caracteres especiais sejam tratados corretamente.

Todo o conteúdo do arquivo é lido em uma variável chamadatexto , que é então retornado. Essa função desempenha um papel crucial na preparação dos dados de entrada, extraindo o texto bruto do documento, preparando-o para operações subsequentes, como fragmentação, embedding e consulta. Ela oferece uma maneira simples e eficiente de processar dados textuais de qualquer arquivo de texto simples.

Essa função nos permite processar o arquivo de entrada (As Aventuras de Sherlock Holmes) e extrair seu conteúdo para operações adicionais, como fragmentação e embedding de texto. Garante que o texto bruto esteja prontamente disponível para análise.

Etapa 8. Divida o texto em fragmentos

Para processar e indexar grandes blocos de texto com eficiência, precisamos dividir o texto em fragmentos menores e gerenciáveis. A seguinte função lida com essa tarefa:

def split_text_into_chunks(text, chunk_size=500, chunk_overlap=50):
           """Splits text into smaller chunks for indexing."""
           splitter = RecursiveCharacterTextSplitter(chunk_size=chunk_size, chunk_overlap=chunk_overlap)
return splitter.split_text(text)

OA função split_text_into_chunks foi projetada para dividir grandes blocos de texto em fragmentos menores e gerenciáveis para processamento e indexação eficientes. Ela utiliza o texto bruto como entrada, juntamente com dois parâmetros opcionais:chunk_size , que define o tamanho máximo de cada fragmento (o padrão é 500 caracteres) echunk_overlap , que especifica o número de caracteres sobrepostos entre fragmentos consecutivos (o padrão é 50).

Essa função garante a continuidade contextual em todos os fragmentos. A função utiliza osRecursiveCharacterTextSplitter deLangChain , que divide o texto de forma inteligente preservando o contexto. Ao retornar uma lista de fragmentos de texto menores, essa função prepara a entrada para operações adicionais, como embedding e indexação.

É essencial ao trabalhar com grandes documentos, pois os modelos de linguagem muitas vezes têm limitações de tokens e não podem processar textos longos diretamente.

Etapa 9: Crie um índice de vetores

Para habilitar uma pesquisa semântica eficiente, precisamos converter fragmentos de texto em embeddings e armazená-los em um índice pesquisável. Essa etapa usa os embeddings FAISS e HuggingFace para criar o índice de vetores, formando a base para recuperar informações relevantes com base em consultas.

def create_vector_index(chunks):
           """Creates a FAISS vector index from text chunks."""
               embeddings = HuggingFaceEmbeddings(model_name="sentence-transformers/all-MiniLM-L6-v2")
               vector_store = FAISS.from_texts(chunks, embeddings)
return vector_store

OA função create_vector_index cria umíndice de vetores FAISS a partir dos fragmentos de texto gerados na etapa anterior. Essa função é crucial para permitir a pesquisa semântica ao mapear cada fragmento em um espaço de vetores de alta dimensão usando embeddings.

Primeiro, ela inicializa um modelo HuggingFaceEmbeddings sentence-transformers/all-MiniLM-L6-v2 , que gera embeddings de vetores para os fragmentos de texto. Esses embeddings capturam o significado semântico de cada fragmento.

Então, a função usaFAISS para criar um armazenamento de vetores indexando esses embeddings, permitindo uma pesquisa de similaridade eficiente posteriormente.

O armazenamento de vetores resultante é retornado e será usado para encontrar fragmentos relevantes com base nas consultas dos usuários, formando a espinha dorsal do processo de busca e recuperação do agente.

Etapa 10. Consulte o índice de vetores com o Granite

Essa etapa envolve a consulta ao índice de vetores para recuperar informações relevantes e o uso do LLM Granite da IBM para gerar uma resposta refinada. Ao integrar a pesquisa de similaridade e o raciocínio de LLMs, a função fornece um processo de resolução de consultas dinâmico e inteligente.

def query_index_with_granite_dynamic(vector_store, query, llm):
         """Searches the vector index, uses Granite to refine the response, and returns all components."""
             # Perform similarity search
             print("\n> Entering new AgentExecutor chain...")
             thought = f"The query '{query}' requires context from the book to provide an accurate response."
             print(f" Thought: {thought}")
             action = "Search FAISS Vector Store"
             print(f" Action: {action}")
             action_input = query
             print(f" Action Input: \"{action_input}\"")
             # Retrieve context
             results = vector_store.similarity_search(query, k=3)
             observation = "\n".join([result.page_content for result in results])
             print(f" Observation:\n{observation}\n")
            # Generate response with Granite
            prompt = f"Context:\n{observation}\n\nQuestion: {query}\nAnswer:"
            print(f" Thought: Combining retrieved context with the query to generate a detailed answer.")
            final_answer = llm(prompt)
            print(f" Final Answer: {final_answer.strip()}")
            print("\n> Finished chain.")
            # Return all components as a dictionary
            return {
                    "Thought": thought,
                     "Action": action,
                     "Action Input": action_input,
                     "Observation": observation,
                     "Final Answer": final_answer.strip()
                     }

OA função query_index_with_granite_dynamic utiliza três entradas: primeira — o armazenamento de vetores (vector_store ), segunda — a consulta do usuário (query ) e terceira — a instância do LLM Granite (LLM ).

Primeiro, ela realiza uma pesquisa por similaridade no índice de vetores para recuperar as partes mais relevantes do texto. Esses fragmentos, chamados deobservação , são combinados em um único bloco de contexto.

Em seguida, a função cria um prompt combinando a consulta e o contexto recuperado. Esse prompt é passado aoLLM Granite , que gera uma resposta detalhada e contextualmente precisa (final_answer ).

Durante todo o processo, etapas intermediárias, como aentrada de ação ,Ação  ede pensamento do agente  são impressas para transparência.

Finalmente, a função retorna um dicionário contendo todos os componentes, incluindo o processo de pensamento, ação tomada, observação recuperada e a resposta final.

Essa etapa é crítica para transformar a recuperação de dados brutos em insights praticáveis e significativos usando os recursos de raciocínio do LLM.

Etapa 11. Gere um "DataFrame" para os resultados das consultas

Essa etapa processa dinamicamente várias consultas, recupera informações relevantes e salva os resultados em um formato estruturado para análise. A função integra recursos de consulta, estruturação de dados e exportação.

def dynamic_output_to_dataframe(vector_store, queries, llm, csv_filename="output.csv"):
           """Generates a DataFrame dynamically for multiple queries and saves it as a CSV file."""
           # List to store all query outputs
           output_data = []
           # Process each query
           for query in queries:
           # Capture the output dynamically
           output = query_index_with_granite_dynamic(vector_store, query, llm)
           output_data.append(output)
           # Convert the list of dictionaries into a DataFrame
           df = pd.DataFrame(output_data)
           # Display the DataFrame
           print("\nFinal DataFrame:")
           print(df)
           # Save the DataFrame as a CSV file
           df.to_csv(csv_filename, index=False)
           print(f"\nOutput saved to {csv_filename}")

OA função dynamic_output_to_dataframe  aceita quatro entradas: o armazenamento de vetores (vector_store ), uma lista de consultas (queries ), a instância do LLM Granite (LLM ) e um nome de arquivo CSV opcional (csv_filename , o padrão éoutput.csv ).

Para cada consulta, usa a funçãoA função query_index_with_granite_dynamic  para recuperar o contexto relevante e gerar uma resposta usando o LLM. Os resultados, incluindo componentes intermediários comoPensamento ,Observação  eResposta final  estão armazenados em uma lista.

Depois que todas as consultas forem processadas, a lista de resultados será convertida em um DataFrame do pandas. Esse formato tabular facilita a análise e a visualização dos resultados das consultas. O DataFrame é impresso para avaliação e salvo como arquivo CSV para uso futuro.

Essa etapa é essencial para organizar a saída em um formato fácil e simples, permitindo tarefas posteriores, como avaliação de precisão e visualização.

Etapa 12: Execute o fluxo de trabalho principal

Esta etapa combina todas as etapas anteriores em um único fluxo de trabalho para processar um arquivo de texto, responder às consultas do usuário e salvar os resultados em um formato estruturado. Afunção main_workflow serve como orquestrador central do tutorial.

def main_workflow():
           # Replace with your text file
           file_path = "aosh.txt"
           # Extract text from the text file
           text = extract_text_from_txt(file_path)
           # Split the text into chunks
           chunks = split_text_into_chunks(text)
           # Create a vector index
           vector_store = create_vector_index(chunks)
           # Define queries
           queries = [
                     "What is the plot of 'A Scandal in Bohemia'?",
                     "Who is Dr. Watson, and what role does he play in the stories?",
                     "Describe the relationship between Sherlock Holmes and Irene Adler.",
                     "What methods does Sherlock Holmes use to solve cases?"
                     ]
           # Generate and save output dynamically
          dynamic_output_to_dataframe(vector_store, queries, llm)

Vamos entender como esse fluxo de trabalho é executado:

Insira um arquivo de texto: avariável file_path especifica o arquivo de texto a ser processado. Neste tutorial, o arquivo de entrada é"aosh.txt" , contendo o texto de As aventuras de Sherlock Holmes.

Extração de texto:extract_text_from_txt é chamada para ler e extrair o conteúdo do arquivo de texto de entrada.

Fragmentação de texto: o texto extraído é dividido em fragmentos menores usando aA função split_text_into_chunks função para facilitar o embedding e a indexação.

Crie um índice de vetores: os fragmentos de texto são convertidos em embeddings e armazenados em umíndice de vetores FAISS índice usando oA função create_vector_index funções de negócios.

Defina consultas: é fornecida uma lista de exemplos de consultas, cada uma projetada para recuperar informações específicas do texto. Essas consultas serão respondidas pelo agente.

Consultas de processo: aA função dynamic_output_to_dataframe  função processa as consultas usando o índice de vetores e o LLM Granite da IBM. Ela recupera o contexto relevante, gera respostas e salva os resultados como um arquivo CSV para análise adicional.

Essa etapa integra todos os componentes do tutorial em um fluxo de trabalho coeso. Ela automatiza o processo desde a extração de texto até a resolução da consulta, permitindo que você teste os recursos do agente e examine os resultados em um formato estruturado.

Para executar o fluxo de trabalho, basta chamar a funçãomain_workflow()  , e todo o pipeline será executado sem problemas.

# Run the workflow
main_workflow()

Saída


 

> Entering new AgentExecutor chain...
 Thought: The query 'What is the plot of 'A Scandal in Bohemia'?' requires context from the book to provide an accurate response.
 Action: Search FAISS Vector Store
 Action Input: "What is the plot of 'A Scandal in Bohemia'?"
 Observation:
I. A SCANDAL IN BOHEMIA


I.
“I was aware of it,” said Holmes dryly.

“The circumstances are of great delicacy, and every precaution has to
be taken to quench what might grow to be an immense scandal and
seriously compromise one of the reigning families of Europe. To speak
plainly, the matter implicates the great House of Ormstein, hereditary
kings of Bohemia.”

“I was also aware of that,” murmured Holmes, settling himself down in
his armchair and closing his eyes.
Contents

   I.     A Scandal in Bohemia
   II.    The Red-Headed League
   III.   A Case of Identity
   IV.    The Boscombe Valley Mystery
   V.     The Five Orange Pips
   VI.    The Man with the Twisted Lip
   VII.   The Adventure of the Blue Carbuncle
   VIII.  The Adventure of the Speckled Band
   IX.    The Adventure of the Engineer’s Thumb
   X.     The Adventure of the Noble Bachelor
   XI.    The Adventure of the Beryl Coronet
   XII.   The Adventure of the Copper Beeches

 Thought: Combining retrieved context with the query to generate a detailed answer.
/var/folders/4w/smh16qdx6l98q0534hr9v52r0000gn/T/ipykernel_2648/234523588.py:23: LangChainDeprecationWarning: The method `BaseLLM.__call__` was deprecated in langchain-core 0.1.7 and will be removed in 1.0. Use :meth:`~invoke` instead.
  final_answer = llm(prompt)
 Final Answer: Step 1: Identify the main characters and their roles.
- Sherlock Holmes: The detective who is approached by a client with a delicate matter.
- An unnamed client: A representative of the great House of Ormstein, hereditary kings of Bohemia, who seeks Holmes' help to prevent a potential scandal.

Step 2: Understand the main issue or conflict.
- The main issue is a delicate matter that, if exposed, could lead to a massive scandal and compromise one of the reigning families of Europe, specifically the House of Ormstein.

Step 3: Ident

> Finished chain.

> Entering new AgentExecutor chain...
 Thought: The query 'Who is Dr. Watson, and what role does he play in the stories?' requires context from the book to provide an accurate response.
 Action: Search FAISS Vector Store
 Action Input: "Who is Dr. Watson, and what role does he play in the stories?"
 Observation:
“Sarasate plays at the St. James’s Hall this afternoon,” he remarked.
“What do you think, Watson? Could your patients spare you for a few
hours?”

“I have nothing to do to-day. My practice is never very absorbing.”
“Try the settee,” said Holmes, relapsing into his armchair and putting
his fingertips together, as was his custom when in judicial moods. “I
know, my dear Watson, that you share my love of all that is bizarre and
outside the conventions and humdrum routine of everyday life. You have
shown your relish for it by the enthusiasm which has prompted you to
chronicle, and, if you will excuse my saying so, somewhat to embellish
so many of my own little adventures.”
“My God! It’s Watson,” said he. He was in a pitiable state of reaction,
with every nerve in a twitter. “I say, Watson, what o’clock is it?”

“Nearly eleven.”

“Of what day?”

“Of Friday, June 19th.”

“Good heavens! I thought it was Wednesday. It is Wednesday. What d’you
want to frighten a chap for?” He sank his face onto his arms and began
to sob in a high treble key.

“I tell you that it is Friday, man. Your wife has been waiting this two
days for you. You should be ashamed of yourself!”

 Thought: Combining retrieved context with the query to generate a detailed answer.
 Final Answer: Dr. Watson is a character in the Sherlock Holmes stories, written by Sir Arthur Conan Doyle. He is a former military surgeon who becomes the narrator and chronicler of Holmes' adventures. Watson is a close friend and confidant of Holmes, often accompanying him on cases and providing a more human perspective to the stories. He is known for his enthusiasm for the bizarre and unconventional, as well as his skill in recording the details of their investigations. Watson's role is crucial in presenting the narrative and offering insights into Holmes' character and methods.

> Finished chain.


Final DataFrame:
                                             Thought  \
0  The query 'What is the plot of 'A Scandal in B...   
1  The query 'Who is Dr. Watson, and what role do...   
2  The query 'Describe the relationship between S...   
3  The query 'What methods does Sherlock Holmes u...   

                      Action  \
0  Search FAISS Vector Store   
1  Search FAISS Vector Store   
2  Search FAISS Vector Store   
3  Search FAISS Vector Store   

                                        Action Input  \
0        What is the plot of 'A Scandal in Bohemia'?   
1  Who is Dr. Watson, and what role does he play ...   
2  Describe the relationship between Sherlock Hol...   
3  What methods does Sherlock Holmes use to solve...   

                                         Observation  \
0  I. A SCANDAL IN BOHEMIA\n\n\nI.\n“I was aware ...   
1  “Sarasate plays at the St. James’s Hall this a...   
2  “You have really got it!” he cried, grasping S...   
3  to learn of the case was told me by Sherlock H...   

                                        Final Answer  
0  Step 1: Identify the main characters and their...  
1  Dr. Watson is a character in the Sherlock Holm...  
2  Sherlock Holmes and Irene Adler have a profess...  
3  Sherlock Holmes uses a variety of methods to s...  

Output saved to output.csv

 

Após executar amain_workflow() função, processamos um arquivo de texto (aosh.txt) e executamos quatro consultas definidas pelo usuário sobre The Adventures of Sherlock Holmes. A saída fornece um detalhamento de como cada consulta foi tratada:

  • O pensamento descreve o raciocínio por trás da consulta e o contexto necessário para uma resposta precisa.
  • A ação indica a etapa executada, que, neste caso, está realizando uma pesquisa de similaridade usando o índice de vetores FAISS.
  • A entrada de ação é a consulta específica que está sendo processada em uma iteração.
  • A observação são os fragmentos de texto recuperados do índice de vetores que são relevantes para a consulta.
  • A resposta final é a resposta detalhada gerada pelo LLM Granite da IBM usando o contexto recuperado.

Além disso, os resultados de todas as consultas foram estruturados em um DataFrame e salvos comooutput.csv . Ests arquivo contém todos os componentes acima para análise adicional ou compartilhamento.

Nesse processo, combinamos a recuperação de texto com o raciocínio do LLM para responder a consultas complexas sobre o livro. O agente recuperou informações relevantes de forma dinâmica, utilizou o contexto para gerar respostas precisas e organizou a produção em um formato estruturado para facilitar a análise.

Visualização dos resultados

Com o arquivo output.csv criado, agora vamos prosseguir para visualizar os resultados da consulta e as métricas de precisão associadas, fornecendo insights mais profundos sobre o desempenho do agente.

Na célula de código a seguir, carregamos os resultados da consulta salvos dooutput.csv arquivo em um DataFrame do pandas para se preparar para visualização e análise. O DataFrame nos permite manipular e explorar os dados em um formato estruturado.

# Load the output.csv file into a DataFrame
df = pd.read_csv("output.csv")
print(df.head()) # Display the first few rows

SAÍDA


Thought  \
0  The query 'What is the plot of 'A Scandal in B...   
1  The query 'Who is Dr. Watson, and what role do...   
2  The query 'Describe the relationship between S...   
3  The query 'What methods does Sherlock Holmes u...   

                      Action  \
0  Search FAISS Vector Store   
1  Search FAISS Vector Store   
2  Search FAISS Vector Store   
3  Search FAISS Vector Store   

                                        Action Input  \
0        What is the plot of 'A Scandal in Bohemia'?   
1  Who is Dr. Watson, and what role does he play ...   
2  Describe the relationship between Sherlock Hol...   
3  What methods does Sherlock Holmes use to solve...   

                                         Observation  \
0  I. A SCANDAL IN BOHEMIA\n\n\nI.\n“I was aware ...   
1  “Sarasate plays at the St. James’s Hall this a...   
2  “You have really got it!” he cried, grasping S...   
3  to learn of the case was told me by Sherlock H...   

                                        Final Answer  
0  Step 1: Identify the main characters and their...  
1  Dr. Watson is a character in the Sherlock Holm...  
2  Sherlock Holmes and Irene Adler have a profess...  
3  Sherlock Holmes uses a variety of methods to s...

Nesse código, o DataFrame inclui componentes importantes, comoPensamento ,Ação ,Observação  eresposta final  para cada consulta. Ao exibir as primeiras linhas usandodf.head() , garantimos que os dados estejam formatados corretamente e prontos para a próxima etapa: criar visualizações significativas.

Importar bibliotecas de visualização

Para criar visualizações dos resultados da consulta, importamos as bibliotecas necessárias:

import matplotlib.pyplot as plt
from wordcloud import WordCloud

 

matplotlib.pyplot  é uma biblioteca amplamente utilizada para criar visualizações estáticas, interativas e animadas em Python. Ele será usado para gerar gráficos de barras, gráficos de pizza e outras visualizações.

wordcloud  é uma biblioteca para criar nuvens de palavras, que destacam visualmente as palavras mais frequentes nos dados. Essa etapa ajuda a resumir e explorar o contexto recuperado do texto.

Observação importante: se você encontrar o erro"WordCloud not found" , você pode resolvê-lo instalando a biblioteca usando o comandopip install wordcloud .

Visualizar comprimentos de observação e resposta

Esse código cria um gráfico de barras horizontais para comparar os tamanhos das observações (contexto recuperado) e das respostas (respostas geradas) para cada consulta. Essa visualização fornece insights sobre quanto contexto o agente usa em comparação com o tamanho das respostas geradas.

def visualize_lengths_with_queries(df):
"""Visualizes the lengths of observations and answers with queries on the y-axis."""
df["Observation Length"] = df["Observation"].apply(len)
df["Answer Length"] = df["Final Answer"].apply(len)
# Extract relevant data
queries = df["Action Input"]
observation_lengths = df["Observation Length"]
answer_lengths = df["Answer Length"]
# Create a horizontal bar chart
plt.figure(figsize=(10, 6))
bar_width = 0.4
y_pos = range(len(queries))
plt.barh(y_pos, observation_lengths, bar_width, label="Observation Length", color="skyblue", edgecolor="black")
plt.barh([y + bar_width for y in y_pos], answer_lengths, bar_width, label="Answer Length", color="lightgreen", edgecolor="black")
plt.yticks([y + bar_width / 2 for y in y_pos], queries, fontsize=10)
plt.xlabel("Length (characters)", fontsize=14)
plt.ylabel("Queries", fontsize=14)
plt.title("Observation and Answer Lengths by Query", fontsize=16)
plt.legend(fontsize=12)
plt.tight_layout()
plt.show()
# Call the visualization function
visualize_lengths_with_queries(df)
 
Comprimentos de observações e respostas por consulta

Essa função,visualize_lengths_with_queries , cria um gráfico de barras horizontais para comparar os comprimentos das observações (contexto recuperado) e das respostas (respostas geradas) para cada consulta.

Ele calcula os comprimentos de caracteres de observações e respostas, adicionando-os como novas colunas (Comprimento da observação  eComprimento da resposta ) ao DataFrame. Usandomatplotlib , ele traça esses comprimentos para cada consulta, com as consultas exibidas no eixo y para melhor legibilidade.

O gráfico de barras é codificado por cores para diferenciar entre comprimentos de observação e resposta, e inclui rótulos, uma legenda e um título para maior clareza.

Essa visualização ajuda a analisar o equilíbrio entre o tamanho do contexto recuperado e os detalhes da resposta gerada, oferecendo insights sobre como o agente processa e responde às consultas.

Veja a proporção de texto utilizado em observações

Essa etapa visualiza quanto do texto total processado pelo agente é usado em observações (contexto recuperado) em comparação com o texto restante. Um gráfico de pizza é criado para fornecer uma representação intuitiva da proporção.

def visualize_text_proportion(df):
     """Visualizes the proportion of text used in observations."""
     total_text_length = sum(df["Observation"].apply(len)) + sum(df["Final Answer"].apply(len))
     observation_text_length = sum(df["Observation"].apply(len))
     sizes = [observation_text_length, total_text_length - observation_text_length]
     labels = ["Observation Text", "Remaining Text"]
     colors = ["#66b3ff", "#99ff99"]
     plt.figure(figsize=(4, 4))
     plt.pie(sizes, labels=labels, colors=colors, autopct="%1.1f%%", startangle=140)
     plt.title("Proportion of Text Used in Observations", fontsize=16)
     plt.show()
# Call the visualization function
visualize_text_proportion(df)
Diagrama da proporção de texto usado em observações

Ovisualize_text_proportion  cria um gráfico de pizza para ilustrar a proporção do texto total usado em observações (contexto recuperado) em comparação com o texto restante. Ele calcula o comprimento total do texto somando os comprimentos de caracteres de todas as observações e respostas e, em seguida, determina a parte que teve a contribuição apenas de observações.

Esses dados são visualizados em um gráfico de pizza, com rótulos claros para"Texto de observação" e"Texto restante" e cores distintas para melhorar a legibilidade. O gráfico inclui valores percentuais para facilitar a interpretação das proporções.

Essa visualização fornece uma visão geral de alto nível de quanto texto o agente usa como contexto durante o processamento de consultas, oferecendo insights sobre a eficiência e o foco do processo de recuperação.

Gere nuvens de palavras para observações e respostas finais

Esse código gera duas nuvens de palavras para representar visualmente as palavras que ocorrem com mais frequência nos Observação e Resposta final textos.

def generate_wordclouds_side_by_side(df):
      """Generates and displays word clouds for Observations and Final Answers side by side."""
      # Combine text for Observations and Final Answers
      observation_text = " ".join(df["Observation"])
      final_answer_text = " ".join(df["Final Answer"])
      # Create word clouds
      observation_wordcloud = WordCloud(width=800, height=400, background_color="white").generate(observation_text)
      final_answer_wordcloud = WordCloud(width=800, height=400, background_color="white").generate(final_answer_text)
      # Create a side-by-side visualization
      plt.figure(figsize=(16, 8))
      # Plot the Observation word cloud
      plt.subplot(1, 2, 1)
      plt.imshow(observation_wordcloud, interpolation="bilinear")
      plt.axis("off")
      plt.title("Word Cloud of Observations", fontsize=16)
      # Plot the Final Answer word cloud
      plt.subplot(1, 2, 2)
      plt.imshow(final_answer_wordcloud, interpolation="bilinear")
      plt.axis("off")
      plt.title("Word Cloud of Final Answers", fontsize=16)
      plt.tight_layout()
      plt.show()
# Call the function to generate and display the word clouds
generate_wordclouds_side_by_side(df)
 
Nuvem de palavras de textos de

Esse código gera duas nuvens de palavras para representar visualmente as palavras que ocorrem com mais frequência nosObservação eResposta final observações, exibindo-os lado a lado para facilitar a comparação. A bibliotecaObservação  eResposta final  textos são primeiro concatenados em duas strings separadas usando" ".join()  para combinar todas as linhas das respectivas colunas. A bibliotecaWordCloud é então usada para gerar nuvens de palavras para cada texto com configurações específicas.

Para criar uma visualização lado a lado, são usados subplots: o primeiro subplot exibe a nuvem de palavras paraObservação , e o segundo exibe a deResposta final . A funçãotight_layout()  garante um espaçamento claro entre os gráficos. Essas nuvens de palavras nos permitem analisar intuitivamente o desempenho do agente, destacando os principais termos recuperados do contexto (Observação ) e aqueles enfatizadas nas respostas (Resposta final ).

Teste a precisão do agente

Nesta seção, avaliamos o desempenho do agente usando várias métricas de precisão:Correspondência de palavras-chave ,Pontuações BLEU ,Precisão/recall  ePontuações F1 . Essas métricas fornecem uma visão abrangente de quão bem o agente gera respostas precisas e relevantes com base nas consultas do usuário.

Importe as bibliotecas necessárias

Antes de começarmos os testes, importamos as bibliotecas necessárias para avaliação da precisão.

from sklearn.feature_extraction.text import CountVectorizer
from nltk.translate.bleu_score import sentence_bleu
from sklearn.metrics import precision_score, recall_score

Essas bibliotecas incluem ferramentas para correspondência de palavras-chave, cálculo de pontuação BLEU e avaliação de precisão e recall. Certifique-se de ter instalado essas bibliotecas em seu ambiente para evitar erros de importação.

Precisão da correspondência de palavras-chave

Esse teste avalia o quão bem as respostas geradas incluem as palavras-chave das consultas. Ele usaCountVectorizer para tokenizar e extrair palavras-chave das consultas e respostas. A função calcula a proporção de palavras-chave de consulta presentes na resposta gerada, marcando a resposta como precisa se essa proporção exceder um limite (0,5 por padrão). Os resultados são adicionados ao DataFrame sob as colunasPontuação de correspondência de palavras-chave eÉ precisoColunas precisas .

def keyword_matching_accuracy(df):
      """Checks if key phrases from the query are present in the final answer."""
      vectorizer = CountVectorizer(stop_words='english')
      def check_keywords(query, answer):
      query_keywords = set(vectorizer.build_tokenizer()(query.lower()))
      answer_keywords = set(vectorizer.build_tokenizer()(answer.lower()))
      common_keywords = query_keywords & answer_keywords
      return len(common_keywords) / len(query_keywords) # Proportion of matched keywords
      df["Keyword Match Score"] = df.apply(lambda row: check_keywords(row["Action Input"], row["Final Answer"]), axis=1)
      df["Is Accurate"] = df["Keyword Match Score"] >= 0.5 # Set a threshold for accuracy
      return df
# Apply keyword matching
df = keyword_matching_accuracy(df)
df.to_csv("output_with_accuracy.csv", index=False)
df
Este é o resultado da produção de uma célula de código para o tutorial em uma página do Think.

Cálculo da pontuação BLEU

Esse teste mede o quanto as respostas geradas correspondem às observações recuperadas.BLEU (Bilingual Evaluation Understudy) é uma métrica popular para avaliar a similaridade do texto com base emn-gram sobreposições. A função calculapontuações BLEU  para cada par de consulta-resposta e os anexa ao DataFrame na coluna de pontuação BLEU.

def calculate_bleu_scores(df):
    """Calculates BLEU scores for answers against observations."""
    df["BLEU Score"] = df.apply(
       lambda row: sentence_bleu([row["Observation"].split()], row["Final Answer"].split()),
       axis=1
       )
    return df
# Apply BLEU score calculation
df = calculate_bleu_scores(df)
df.to_csv("output_with_bleu.csv", index=False)
Diagrama criado para um tutorial na página do Think.

Precisão e recall

A precisão e o recall são calculados para avaliar a relevância e a integralidade das respostas. A precisão mede a proporção de palavras recuperadas na resposta que são relevantes, enquanto o recall mede a proporção de palavras relevantes na observação que aparecem na resposta.

Essas métricas são anexadas ao DataFrame nas colunas e Recall Precisão.

def calculate_precision_recall(df):
     """Calculates precision and recall for extractive answers."""
         def precision_recall(observation, answer):
                observation_set = set(observation.lower().split())
                answer_set = set(answer.lower().split())
                precision = len(observation_set & answer_set) / len(answer_set) if answer_set else 0
                recall = len(observation_set & answer_set) / len(observation_set) if observation_set else 0
         return precision, recall
        df[["Precision", "Recall"]] = df.apply(
        lambda row: pd.Series(precision_recall(row["Observation"], row["Final Answer"])),
        axis=1
        )
return df
# Apply precision/recall
df = calculate_precision_recall(df)
df.to_csv("output_with_precision_recall.csv", index=False)
df
Saída de uma célula de código para o tutorial localizada em uma página do Think

Cálculo da pontuação F1

A pontuação F1 combina precisão e recall em uma única métrica, fornecendo uma avaliação equilibrada de relevância e integridade. A fórmula para a pontuação F1 é: F1 Score = 2 * (Precision * Recall) / (Precision + Recall)

Aspontuações F1 calculadas são adicionadas ao DataFrame na coluna de pontuação F1.

def calculate_f1(df):
      """Calculates F1 scores based on precision and recall."""
          df["F1 Score"] = 2 * (df["Precision"] * df["Recall"]) / (df["Precision"] + df["Recall"])
          df["F1 Score"].fillna(0, inplace=True) # Handle divide by zero
          return df
# Apply F1 calculation
df = calculate_f1(df)
df.to_csv("output_with_f1.csv", index=False)
df
Saída de uma célula de código para o tutorial localizada em uma página do Think

Resuma as métricas de precisão

Por fim, uma função de resumo consolida todas as métricas para apresentar uma visão geral do desempenho do agente. Ela calcula o número total de consultas, a contagem e o percentual de respostas precisas e as pontuações médias BLEU e F1.

def summarize_accuracy_metrics(df):
      """Summarizes overall accuracy metrics."""
          total_entries = len(df)
          accurate_entries = df["Is Accurate"].sum()
          average_bleu = df["BLEU Score"].mean()
          average_f1 = df["F1 Score"].mean()
          print(f"Total Entries: {total_entries}")
          print(f"Accurate Entries: {accurate_entries} ({accurate_entries / total_entries * 100:.2f}%)")
          print(f"Average BLEU Score: {average_bleu:.2f}")
          print(f"Average F1 Score: {average_f1:.2f}")
# Call summary function
summarize_accuracy_metrics(df)

SAÍDA


 

Total Entries: 4
Accurate Entries: 4 (100.00%)
Average BLEU Score: 0.04
Average F1 Score: 0.24

Esses testes de precisão oferecem uma avaliação detalhada da capacidade do agente de gerar respostas relevantes e precisas. Cada teste se concentra em um aspecto específico, desde a inclusão de palavras-chave até a similaridade do texto e a integridade das respostas. O resumo consolida essas métricas para fornecer um instantâneo do desempenho geral.

Resumo

Este tutorial orientou você durante a construção de um agente autônomo baseado no LLM Granite e no LangChain da IBM. Começando com a extração de texto até a vetorização e a resolução de consultas, cobrimos todo o processo de projeto e implementação de um agente funcional baseado em LLM. As principais etapas incluíram o gerenciamento de memória com armazenamentos de vetores, processamento de consultas e geração de respostas usando o Granite.

Avaliamos o desempenho do agente usando métricas de precisão, como correspondência de palavras-chave, pontuações BLEU, precisão, recall e pontuações F1. Visualizações como gráficos de barras, gráficos de pizza e nuvens de palavras forneceram insights adicionais sobre o comportamento e a eficácia do agente.

Ao concluir este tutorial, você aprendeu a projetar, testar e visualizar o desempenho de um agente de LLM. Essa base pode ser estendida para lidar com conjuntos de dados mais complexos, melhorar a precisão e explorar funcionalidades avançadas, como sistemas multiagentes.

Soluções relacionadas
Agentes de IA para empresas

Crie, implemente e gerencie assistentes e agentes de IA potentes que automatizam fluxos de trabalho e processos com a IA generativa.

    Explore o watsonx Orchestrate
    Soluções de agentes de IA da IBM

    Construa o futuro do seu negócio com soluções de IA em que você pode confiar.

    Explore soluções de agentes de IA
    Serviços de IA do IBM® Consulting

    Os serviços de IA da IBM Consulting ajudam a reinventar a forma como as empresas trabalham com IA para gerar transformação.

    Explore os serviços de inteligência artificial
    Dê o próximo passo

    Se você optar por personalizar aplicativos e habilidades criados previamente ou criar e implementar serviços agênticos personalizados usando um estúdio de IA, a plataforma IBM watsonx tem aquilo de que você precisa.

    Explore o watsonx Orchestrate Explore o watsonx.ai