Skip to content

RonaldMonteiro/first-answer

Repository files navigation

logo

🔎 Detecção de marcas em respostas de IA

Teste técnico para a First Answer

Python 3.12 uv Docker tests ruff

LangChain Pydantic spaCy scikit-learn OpenRouter


Dado um texto (resposta de IA) e uma marca_monitorada, devolve own_brand_mentioned (bool) e other_brands (lista). São duas pipelines com a mesma interface (spaCy (NER) e LLM) e um eval que as compara por Cohen's kappa.

Como rodar

Requer uv. Comandos via make (rode make help para ver todos):

make install   # cria o venv + instala deps e o modelo pt do spaCy
make cases     # os 3 casos oficiais nas 2 pipelines (imprime os outputs)
make eval      # compara spaCy + 6 LLMs por kappa -> data/results.md   (precisa de chave)
make check     # lint + formatação (ruff)

A pipeline LLM e o eval precisam de uma chave: cp .env.example .env e preencha OPENROUTER_API_KEY. make cases na pipeline spaCy roda offline.

Abordagem

Em vez de uma solução só, duas pipelines com a mesma interface e um eval que decide com números. Modelo comum: extrair o conjunto de marcas → own = marca ∈ conjunto, others = conjunto − marca; a parte difícil ("é a mesma marca?") fica em normalize.py.

  • spaCy (NER): baseline offline e determinístico (pt_core_news_lg, entidades ORG/MISC, sem gazetteer). Grátis e reprodutível, mas a NER genérica erra.
  • LLM: ChatOpenAI via OpenRouter + with_structured_output(BrandResult) (Pydantic, sem parse de JSON). Preciso e robusto a vocabulário aberto, mas custa e não é determinístico.

O eval gera 50 casos sintéticos com gabarito e roda spaCy + 6 modelos (OpenAI, Google, Anthropic, grandes e pequenos). Métrica: Cohen's kappa (θ̂), com matching fuzzy de marcas. Como o dataset foi gerado pelo gpt-5.4, incluir modelos que não o geraram (Gemini, Claude) é o que torna a comparação honesta.

Outputs

3 casos oficiais (pipeline LLM, gpt-5.4):

// Nubank        -> {"own_brand_mentioned": true,  "other_brands": ["Banco Inter","C6 Bank","BTG Pactual"]}
// Nike          -> {"own_brand_mentioned": false, "other_brands": ["Olympikus","Asics","Mizuno","New Balance"]}
// First Answer  -> {"own_brand_mentioned": true,  "other_brands": ["ChatGPT","Gemini","Claude","Perplexity","Copilot","Profound","Brandlight","Peec AI","AthenaHQ"]}

Eval (50 casos) com Cohen's kappa (relatório em data/results.md):

pipeline / modelo kappa own kappa brands
spaCy 0.841 0.809
openai/gpt-5.4 (gerador) 1.000 0.932
openai/gpt-5.4-nano 1.000 0.951
google/gemini-3.5-flash 1.000 0.947
google/gemini-3.1-flash-lite 1.000 0.959
anthropic/claude-sonnet-4.6 1.000 0.955
anthropic/claude-haiku-4.5 1.000 0.963

Os LLMs zeram a tarefa binária; quem discrimina é o kappa de brands, onde o gerador ficou em último entre os LLMs (o vazamento não o favoreceu). spaCy fica atrás, mas é o único baseline sem custo.


CI em .github/workflows/ci.yml: lint (ruff) · test (pytest) · docker (build).

About

Identifica a marca monitorada e outras marcas citadas em respostas de IA utilizando spaCy e LLMs, avaliados por Cohen's kappa.

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors