Skip to content

Instantly share code, notes, and snippets.

@charlenopires
Created June 8, 2025 14:50
Show Gist options
  • Save charlenopires/0886f557f5151d2cd2b8fe73b42e7e82 to your computer and use it in GitHub Desktop.
Save charlenopires/0886f557f5151d2cd2b8fe73b42e7e82 to your computer and use it in GitHub Desktop.
Agente de Google ADK que lê conteúdo de um Doc em PDF e interage por meio de um Chat em Streamlit
import os
from pathlib import Path
import google.generativeai as genai
import pdfplumber
import streamlit as st
# Tenta importar o Agent do Google ADK.
# Se não estiver disponível, define Agent como None para fallback.
try:
from google.adk.agents import Agent
except ImportError:
Agent = None # Fallback caso o ADK não esteja instalado
from dotenv import load_dotenv
# Carrega variáveis de ambiente do arquivo .env localizado um nível acima.
load_dotenv(dotenv_path="../.env")
class DocumentAnalyzerWithADK:
"""
Classe principal para analisar documentos PDF usando o Google ADK Agent.
Mantém o contexto do PDF para responder a perguntas.
"""
def __init__(self, pdf_path: str | None = None):
"""
Inicializa o analisador de documentos.
Args:
pdf_path (str | None, optional): Caminho para o arquivo PDF. Defaults to None.
"""
self.pdf_content = "" # Conteúdo textual extraído do PDF
self.agent: Agent | None = None # Instância do agente ADK ou None
# Se um caminho de PDF for fornecido e o arquivo existir, extrai o conteúdo e inicializa o agente com contexto.
if pdf_path and os.path.exists(pdf_path):
self.pdf_content = self._extract_pdf_content(pdf_path)
self._initialize_agent_with_context()
else:
# Caso contrário, inicializa um agente básico sem contexto de documento.
self._initialize_basic_agent()
def _extract_pdf_content(self, pdf_path: str) -> str:
"""
Extrai o conteúdo textual de um arquivo PDF.
Args:
pdf_path (str): Caminho para o arquivo PDF.
Returns:
str: Conteúdo textual extraído do PDF. Retorna string vazia em caso de erro.
"""
try:
with pdfplumber.open(pdf_path) as pdf:
text_content = ""
# Itera sobre cada página do PDF
for page in pdf.pages:
page_text = page.extract_text() # Extrai texto da página atual
if page_text:
text_content += (
page_text + "\n\n"
) # Adiciona o texto da página e duas quebras de linha
return (
text_content.strip()
) # Remove espaços em branco extras do início e fim
except Exception as e:
st.error(f"Erro ao extrair conteúdo do PDF: {str(e)}")
return ""
def _initialize_agent_with_context(self):
"""
Inicializa o agente do Google ADK com o contexto do documento PDF carregado.
Define as instruções e o modelo a ser usado pelo agente.
"""
# Instrução detalhada para o agente, incluindo o conteúdo do PDF.
context_instruction = f"""
Você é um assistente especializado em análise de documentos. Você já tem conhecimento completo do seguinte documento:
CONTEÚDO DO DOCUMENTO:
{self.pdf_content}
INSTRUÇÕES:
- Use o conteúdo do documento acima para responder às perguntas dos usuários
- Seja preciso e cite partes específicas do documento quando relevante
- Se a pergunta não puder ser respondida com base no documento, informe isso claramente
- Mantenha suas respostas organizadas e fáceis de entender
- Você pode fazer análises, resumos, extrair informações específicas, etc.
"""
if Agent is not None:
# Se o ADK Agent estiver disponível, cria uma instância.
self.agent = Agent(
name="DocumentAnalyzer", # Nome do agente
model="gemini-2.0-flash", # Modelo de IA a ser usado
instruction=context_instruction, # Instruções para o agente
)
else:
# Se o ADK não estiver disponível, define o agente como None e imprime um aviso.
self.agent = None
print(
"Aviso: Google ADK não está disponível. Usando fallback com Gemini API diretamente."
)
def _initialize_basic_agent(self):
"""
Inicializa um agente básico do Google ADK sem contexto de documento.
Usado quando nenhum PDF é carregado ou encontrado.
"""
if Agent is not None:
# Se o ADK Agent estiver disponível, cria uma instância com instruções genéricas.
self.agent = Agent(
name="DocumentAnalyzer",
model="gemini-2.0-flash",
instruction="""
Você é um assistente para análise de documentos.
Atualmente não há nenhum documento carregado.
Informe ao usuário que precisa carregar um documento primeiro.
""",
)
else:
# Se o ADK não estiver disponível, define o agente como None e imprime um aviso.
self.agent = None
print(
"Aviso: Google ADK não está disponível. Usando fallback com Gemini API diretamente."
)
def chat(self, user_message: str) -> str:
"""
Processa uma mensagem do usuário e retorna a resposta do agente.
Usa a API Gemini diretamente como fallback se o ADK não estiver configurado
ou para incluir o contexto do documento dinamicamente.
Args:
user_message (str): Mensagem enviada pelo usuário.
Returns:
str: Resposta gerada pelo modelo de IA.
"""
try:
# Configura a API do Google Generative AI com a chave da variável de ambiente.
genai.configure(api_key=os.getenv("GOOGLE_API_KEY"))
model = genai.GenerativeModel(
"gemini-2.0-flash"
) # Modelo Gemini para geração de conteúdo
if self.pdf_content:
# Se houver conteúdo de PDF, monta o prompt com o contexto do documento.
prompt = f"""
CONTEXTO DO DOCUMENTO:
{self.pdf_content}
INSTRUÇÕES:
- Você é um assistente especializado em análise de documentos
- Use o contexto do documento acima para responder à pergunta do usuário
- Seja preciso e cite partes específicas do documento quando relevante
- Se a pergunta não puder ser respondida com base no documento, informe isso claramente
PERGUNTA DO USUÁRIO: {user_message}
RESPOSTA:
"""
else:
# Se não houver conteúdo de PDF, monta um prompt informando a necessidade de carregar um documento.
prompt = f"""
Você é um assistente para análise de documentos, mas atualmente não há nenhum documento carregado.
Informe ao usuário que precisa carregar um documento primeiro.
Pergunta do usuário: {user_message}
"""
# Gera a resposta usando o modelo Gemini.
response = model.generate_content(prompt)
return response.text
except Exception as e:
return f"Erro ao processar mensagem: {str(e)}"
def has_document(self) -> bool:
"""
Verifica se um documento PDF foi carregado e seu conteúdo extraído.
Returns:
bool: True se o documento estiver carregado, False caso contrário.
"""
return bool(self.pdf_content)
def get_document_summary(self) -> str:
"""
Retorna um breve resumo ou preview do documento carregado.
Returns:
str: Um preview do conteúdo do documento ou uma mensagem indicando que nenhum documento foi carregado.
"""
if not self.pdf_content:
return "Nenhum documento carregado."
# Cria um preview com os primeiros 300 caracteres do documento.
preview = (
self.pdf_content[:300] + "..."
if len(self.pdf_content) > 300
else self.pdf_content
)
return f"Documento carregado ({len(self.pdf_content)} caracteres). Preview: {preview}"
def main():
"""
Função principal que executa a aplicação Streamlit para o analisador de documentos.
Configura a página, gerencia o estado da sessão e a interação com o usuário.
"""
# Configurações da página Streamlit
st.set_page_config(
page_title="Analisador de Documentos com Google ADK",
page_icon="📄",
layout="wide",
)
st.title("📄 Analisador de Documentos com Google ADK")
st.markdown("Chat com um agente de IA que conhece o conteúdo do documento PDF!")
# Verifica se a chave da API do Google está configurada nas variáveis de ambiente.
if not os.getenv("GOOGLE_API_KEY"):
st.error("❌ Por favor, configure a variável GOOGLE_API_KEY no arquivo .env")
st.stop() # Interrompe a execução se a chave não estiver configurada.
# Inicializa o agente no estado da sessão se ainda não existir.
if "agent" not in st.session_state:
# Define o caminho para o arquivo PDF padrão.
pdf_path = Path(__file__).parent / "doc.pdf"
st.session_state.agent = DocumentAnalyzerWithADK(str(pdf_path))
# Exibe uma mensagem de sucesso ou aviso dependendo se o documento foi carregado.
if st.session_state.agent.has_document():
st.success("✅ Documento PDF carregado com sucesso!")
else:
st.warning("⚠️ Documento PDF não encontrado em doc.pdf")
# Inicializa o histórico de mensagens no estado da sessão se ainda não existir.
if "messages" not in st.session_state:
st.session_state.messages = []
# Adiciona uma mensagem de boas-vindas ao histórico.
if st.session_state.agent.has_document():
welcome_msg = "Olá! Eu já conheço o conteúdo do documento PDF. Pode fazer qualquer pergunta sobre ele!"
else:
welcome_msg = "Olá! Não encontrei o arquivo doc.pdf. Por favor, adicione o documento para começarmos."
st.session_state.messages.append({"role": "assistant", "content": welcome_msg})
# Barra lateral (sidebar) para informações do documento e ações.
with st.sidebar:
st.header("📊 Status do Documento")
if st.session_state.agent.has_document():
st.success("✅ Documento carregado")
# Expansor para mostrar um resumo do documento.
with st.expander("📋 Resumo do Documento"):
st.text_area(
"Conteúdo:",
st.session_state.agent.get_document_summary(),
height=200,
disabled=True,
)
else:
st.error("❌ Nenhum documento carregado")
st.info("Coloque o arquivo doc.pdf na pasta docanalyzer/")
# Botão para recarregar o documento.
if st.button("🔄 Recarregar Documento"):
pdf_path = Path(__file__).parent / "doc.pdf"
st.session_state.agent = DocumentAnalyzerWithADK(str(pdf_path))
if st.session_state.agent.has_document():
st.success("✅ Documento recarregado!")
st.rerun() # Reinicia a aplicação para refletir as mudanças.
else:
st.error("❌ Falha ao carregar documento")
# Seção principal para o chat com o agente.
st.header("💬 Chat com o Agente")
# Exibe as mensagens do histórico de chat.
for message in st.session_state.messages:
with st.chat_message(message["role"]):
st.markdown(message["content"])
# Campo de entrada para o usuário enviar mensagens.
if prompt := st.chat_input("Faça uma pergunta sobre o documento..."):
# Adiciona a mensagem do usuário ao histórico e a exibe.
st.session_state.messages.append({"role": "user", "content": prompt})
with st.chat_message("user"):
st.markdown(prompt)
# Processa a mensagem do usuário com o agente e exibe a resposta.
with st.chat_message("assistant"):
with st.spinner("🤔 Analisando..."): # Mostra um indicador de carregamento.
response = st.session_state.agent.chat(prompt)
st.markdown(response)
# Adiciona a resposta do assistente ao histórico.
st.session_state.messages.append({"role": "assistant", "content": response})
# Botão para limpar o histórico de chat.
col1, col2 = st.columns([1, 4]) # Layout em colunas para o botão
with col1:
if st.button("🗑️ Limpar Chat"):
st.session_state.messages = [] # Limpa as mensagens.
# Readiciona a mensagem de boas-vindas apropriada.
if st.session_state.agent.has_document():
welcome_msg = (
"Chat limpo! Pode continuar fazendo perguntas sobre o documento."
)
else:
welcome_msg = "Chat limpo! Adicione um documento para começar."
st.session_state.messages.append(
{"role": "assistant", "content": welcome_msg}
)
st.rerun() # Reinicia a aplicação para refletir o chat limpo.
# Ponto de entrada da aplicação: executa a função main se o script for rodado diretamente.
if __name__ == "__main__":
main()
# Para rodar, use o comando: uv run streamlit run agent.py
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment