Pular para conteúdo

Processamento de mídia

Paciente pode enviar:

  • Imagem (receita médica, carteirinha de convênio, print de pedido)
  • PDF (pedido de exame emitido pelo médico)
  • Áudio (prefere falar que digitar)

O workflow transforma tudo em texto antes de chamar o agente.

Imagens — Google Gemini

# processors.py
import google.generativeai as genai
from config import GEMINI_API_KEY, GEMINI_MODEL
import requests, base64

genai.configure(api_key=GEMINI_API_KEY)
_model = genai.GenerativeModel(GEMINI_MODEL)  # ex: "gemini-2.5-flash"

def process_image(image_url: str, mime_type: str = "image/jpeg") -> str:
    # Baixa a imagem
    resp = requests.get(image_url, timeout=30)
    resp.raise_for_status()
    image_data = resp.content

    prompt = """Esta imagem foi enviada por um paciente no WhatsApp de uma clínica.
Descreva em PT-BR:
- Tipo de documento (pedido médico, carteirinha de convênio, receita, outro)
- Informações relevantes: nome do paciente, CPF, data de nascimento,
  nome do médico, convênio, procedimento/exame solicitado, data do pedido.
- Se não for documento médico, descreva o que é em 1-2 frases.

Responda apenas com os dados extraídos, formato chave: valor.
"""
    response = _model.generate_content([
        prompt,
        {"mime_type": mime_type, "data": image_data},
    ])
    return response.text.strip()

Uso típico no prompt do Gemini: detectar pedido médico → extrair nome do médico, especialidade, exame → o agente usa no fluxo de agendamento.

PDFs — também via Gemini

def process_document(doc_url: str, mime_type: str = "application/pdf") -> str:
    resp = requests.get(doc_url, timeout=30)
    resp.raise_for_status()
    doc_data = resp.content

    prompt = "Extraia dados relevantes do documento anexo..."
    response = _model.generate_content([
        prompt,
        {"mime_type": mime_type, "data": doc_data},
    ])
    return response.text.strip()

Gemini aceita PDFs nativamente até ~20 páginas. Para mais, seria necessário pré-processar.

Áudio — OpenAI Whisper

# processors.py
from openai import OpenAI
from config import OPENAI_API_KEY
import requests, tempfile, os

_openai = OpenAI(api_key=OPENAI_API_KEY)

def process_audio(audio_url: str) -> str:
    resp = requests.get(audio_url, timeout=30)
    resp.raise_for_status()

    with tempfile.NamedTemporaryFile(suffix=".mp3", delete=False) as f:
        f.write(resp.content)
        tmp_path = f.name

    try:
        with open(tmp_path, "rb") as audio_file:
            transcript = _openai.audio.transcriptions.create(
                model="whisper-1",
                file=audio_file,
                language="pt",
            )
        return transcript.text
    finally:
        os.unlink(tmp_path)

Roteamento no workflow

# workflow.py (resumo)
def processar_conteudo(content: dict) -> str:
    tipo = content.get("type", "TEXT")
    if tipo == "TEXT":
        return content.get("text", "")

    details = content.get("details") or {}
    file_info = details.get("file") or {}
    url = file_info.get("publicUrl") or file_info.get("publicUrlDownload")
    mime = file_info.get("mimeType", "")

    if not url:
        return ""

    if tipo == "IMAGE":
        texto = process_image(url, mime)
        return f"[imagem enviada pelo paciente]\n{texto}"

    if tipo == "DOCUMENT":
        texto = process_document(url, mime)
        return f"[documento enviado pelo paciente]\n{texto}"

    if tipo == "AUDIO":
        texto = process_audio(url)
        return f"[audio transcrito]\n{texto}"

    return ""

Metadado [imagem enviada] no prefixo

Prefixar a mensagem com [imagem enviada pelo paciente]\n... dá contexto ao LLM. Ele entende que aquilo é descrição do modelo de visão, não algo que o paciente digitou.

Custo

Provedor Modelo Custo (ordem de grandeza)
Gemini (imagem) gemini-2.5-flash ~$0.0005 por imagem
Gemini (PDF) gemini-2.5-flash ~$0.001 por doc
Whisper (áudio) whisper-1 ~$0.006 por minuto

Média por sessão com mídia: ~$0.01. Em alta demanda (clínica grande), vale acompanhar.

Erros e fallback

Se a API do Gemini/Whisper falha:

def processar_conteudo(content):
    try:
        ...
    except Exception as e:
        log_error("media_error", str(e), session_id, phone)
        return "[paciente enviou uma mídia que não pude processar]"

O agente lê [paciente enviou uma midia...] e pede ao paciente para descrever em texto, ou transfere.

Limites

  • Imagem: até ~7 MB é ok; além disso o Gemini pode negar.
  • PDF: até ~20 páginas recomendado.
  • Áudio: até ~25 MB (limite Whisper). Áudios > 5 min começam a ficar imprecisos.

Tipos especiais não tratados

  • Stickers, emojis como imagem: Gemini descreve, pode entrar no contexto como ruído. Considerar filtrar por tamanho.
  • Vídeo: nenhum projeto processa vídeo. System prompt orienta a transferir se o paciente manda vídeo.
  • Localização: idem — tratar manualmente se relevante.