Share
## https://sploitus.com/exploit?id=BE09124A-AA08-51FE-B695-2FA3ACE6110D
# llmbias-tse

Prova de conceito (POC) do projeto **InternetLab × LabDados**: coleta
automatizada de respostas de ferramentas de IA generativa sobre temas
eleitorais, via **automação de navegador** com contas pessoais.

A POC responde à pergunta da issue
[lab-dados/adm#61](https://github.com/lab-dados/adm/issues/61) — *"verificar
a viabilidade técnica de acessar LLMs pelos chats próprios"* — e serve de
esqueleto para o e2e de coleta das quatro rodadas previstas no projeto.

> A proposta detalhada do projeto (parceria InternetLab × LabDados) é um
> documento interno e **não** está versionada neste repositório público.

## Ideia em uma frase

Abre o **Google Chrome real** numa porta de debug (CDP) com um **perfil
persistente** → você loga **uma vez** nas ferramentas → o script conecta na
sessão logada via [Patchright](https://github.com/Kaliiiiiiiiii-Vinyzu/patchright)
(Playwright stealth), manda prompts, captura as respostas e **salva tudo** em
formato estruturado pronto para a etapa de avaliação de viés (LLM-as-a-judge).

## Fluxo e2e

```mermaid
flowchart LR
    A["launch(abre Chrome na porta CDPperfil persistente)"] --> B["login manual(uma vez por ferramenta)"]
    B --> C["run(conecta via CDP,manda prompts)"]
    C --> D["data/<run_id>/exchanges.jsonl+ artefatos HTML/PNG"]
    D --> E["LLM-juiz(avaliação de viés)"]
```

As duas fases (`launch` e `run`) são **desacopladas de propósito**: o Chrome
fica aberto e logado entre execuções, então dá para iterar no script de
coleta sem relançar o browser nem refazer login.

## Arquitetura

```mermaid
flowchart TD
    subgraph chrome["Google Chrome real (porta CDP 9333)"]
        prof["perfil persistentetmp/profile(cookies/logins)"]
        ctxL["context logado(suas contas)"]
        ctxA["context anônimo(new_context, sem cookies)"]
    end

    runner["poc.run()"] -->|connect_over_cdp| chrome
    runner --> drivers
    runner --> store

    subgraph drivers["drivers (1 por ferramenta)"]
        d1["chatgpt"]
        d2["gemini"]
        d3["claude"]
        d4["metaai"]
        d5["whatsapp_metaai"]
    end

    drivers -->|seletores + captura| capture["capture(digitar, esperar fimda geração, snapshot)"]
    store["storageRunStore + Exchange"] --> jsonl[("exchanges.jsonl+ artifacts/")]
```

| Módulo (`src/llmbias_tse/`) | Papel |
| --- | --- |
| `__init__.py` | CLI (`launch` / `run` / `tools`) |
| `browser.py` | lança Chrome na porta CDP + `connect()` + context logado/anônimo |
| `drivers.py` | um driver por ferramenta com os **seletores** (parte que mais muda) |
| `capture.py` | helpers genéricos: digitar, detectar fim da geração, snapshot |
| `storage.py` | `RunStore` + `Exchange` → JSONL + artefatos brutos |
| `prompts.py` | prompts de "sabor eleitoral leve" (andaime) |
| `poc.py` | runner: itera sessões × ferramentas × prompts |

## Eixo de sessão: logado vs deslogado

A resposta autenticada pode diferir da anônima (o projeto fala em "sessões
virtuais para simulação de usuários"). O mesmo prompt roda nas duas e cada
registro é marcado com `session`, para o juiz comparar lado a lado.

```mermaid
flowchart LR
    P["mesmo prompt"] --> L["session=logged_in(perfil persistente)"]
    P --> A["session=anon(context isolado,janela anônima)"]
    L --> R[("registrosmarcados com session")]
    A --> R
```

- **logged_in**: usa o context persistente (suas contas).
- **anon**: cria um context isolado (`browser.new_context()`), sem cookies —
  equivale a uma janela anônima/deslogada. Fechado ao fim da coleta.

## Como rodar

```sh
uv sync

# 1) Abre o Chrome (porta 9333, perfil ./tmp/profile). Deixe aberto.
uv run llmbias-tse launch
#    -> faça login: chatgpt.com, gemini.google.com, claude.ai,
#       www.meta.ai e (QR) web.whatsapp.com

# 2) Em OUTRO terminal: coleta. Conecta no Chrome já logado.
uv run llmbias-tse run                                  # padrão: 4 ferramentas, 2 prompts
uv run llmbias-tse run --tools gemini --session both    # logado + deslogado
uv run llmbias-tse run --tools chatgpt gemini --n 3
uv run llmbias-tse run --tools whatsapp_metaai --n 1

uv run llmbias-tse tools                                 # lista as ferramentas
```

### WhatsApp / Meta AI

Abra a conversa do **Meta AI** manualmente (chat ativo) — o driver opera no
chat aberto e envia `/reset-all-ais` antes de cada prompt para garantir
contexto limpo (não há "chat novo" no WhatsApp).

## Saída

Cada execução cria `data//`:

- `exchanges.jsonl` — **uma linha por troca** (prompt → resposta) com
  metadados: ferramenta, sessão, prompt, resposta, timestamps, URL da
  conversa, status. É o insumo do LLM-juiz.
- `artifacts////{ok,error}.{html,png}` — snapshot bruto
  de cada troca, para auditoria e reprodutibilidade.

`tmp/` (perfil/cookies) e `data/` (coletas) ficam fora do versionamento.

## Detecção de fim da resposta

As respostas fazem *streaming*. Em vez de depender de estabilidade do texto
(frágil — chips de citação e cursores re-renderizam), o fim é detectado pelo
**indicador de "gerando"** (ex.: botão de parar do ChatGPT). Quando some, a
geração terminou. Para ferramentas sem esse indicador conhecido, há fallback
de estabilidade de texto.

> ⚠️ Os seletores de cada UI mudam com frequência — são a parte que mais
> quebra. Quando um driver parar de capturar, ajuste em `drivers.py` usando o
> snapshot HTML salvo em `data//artifacts/...` para achar o seletor novo.

## Configuração (env vars, opcionais)

| Var                | Default         | O quê                            |
| ------------------ | --------------- | -------------------------------- |
| `LLMBIAS_CDP_PORT` | `9333`          | porta de debug do Chrome         |
| `LLMBIAS_PROFILE`  | `./tmp/profile` | diretório do perfil persistente  |
| `CHROME_PATH`      | autodetect      | caminho do `chrome.exe`          |

## Status

POC validada ponta-a-ponta. O protocolo real de prompts (categorias de viés,
multiturno, paráfrases) e os critérios de avaliação serão definidos com o
InternetLab. Ferramentas com e2e confirmado: **ChatGPT**, **Gemini**
(logado + anônimo), **WhatsApp/Meta AI**. Pendentes de calibração de
seletores: Claude, Meta AI (web).