# Spec: Entity Type Validation no Reflexion Pass

> Generated from `.vibeflow/prds/entity-type-validation.md` on 2026-03-20
> Budget: ≤ 2 files

## Objective

Corrigir classificações erradas de entity type (ex: "Rio de Janeiro" como `person`) via reflexion pass, sem código novo — apenas mudança de prompts.

## Context

O LLM da extração classifica entity types errado quando mensagens têm muitas entidades misturadas. O reflexion pass já roda pós-extração como revisão de qualidade, mas só valida completude, duplicatas, nomes e aliases. Não valida entity types. O `ENTITY_SCAN_PROMPT` (multi-pass) lista os types válidos mas não enfatiza a importância de classificar corretamente.

O reflexion pass roda apenas no modo single-pass. No modo multi-pass, o `ENTITY_SCAN_PROMPT` é a única chance de acertar o type.

## Definition of Done

1. **`REFLEXION_SYSTEM_PROMPT` inclui validação de entity type**: O checklist do prompt tem um item 6 que instrui o LLM a verificar se o type de cada entity é consistente com os facts e o contexto da mensagem (ex: "a city cannot be type person").

2. **`ENTITY_SCAN_PROMPT` reforça classificação de types**: O prompt multi-pass inclui instrução explícita para classificar types com cuidado, especialmente distinguindo person vs place quando ambos aparecem na mesma mensagem.

3. **Teste valida correção de type**: Teste usando `FakeLLMProvider` que simula uma extração com type errado (place marcado como person) e verifica que o reflexion pass retorna o type corrigido.

## Scope

### 1. `src/arandu/write/extract.py` — Atualizar prompts

- `REFLEXION_SYSTEM_PROMPT`: Adicionar item 6 ao checklist:
  ```
  6. Is the type of each entity correct? A city or country cannot be "person".
     A company cannot be "place". Cross-reference entity types with the facts —
     if a fact says "X is a neighborhood in Y", then Y must be type "place", not "person".
  ```

- `ENTITY_SCAN_PROMPT`: Adicionar instrução após a lista de types:
  ```
  - Pay careful attention to entity type classification. Cities, countries, and
    neighborhoods are "place", not "person" — even when mentioned alongside many people.
  ```

### 2. `tests/test_write_pipeline.py` — Teste de correção de type

- Adicionar teste em `TestExtract` que:
  1. Cria extração com entity `{"name": "Rio de Janeiro", "type": "person"}` (type errado)
  2. Configura `FakeLLMProvider` com resposta corrigida (`type: "place"`)
  3. Chama `_reflexion_pass()` com a extração errada
  4. Verifica que o output tem `type: "place"`

## Anti-scope

- Validação heurística no código Python (regex, NLP)
- Correção de entities já persistidas no banco
- Mudança no `EXTRACTION_SYSTEM_PROMPT` (single-pass extraction) — o reflexion já cobre
- Mudança no entity resolution
- Nova chamada LLM dedicada
- Mudança em qualquer outro prompt (reconciliation, retrieval, etc.)

## Technical Decisions

### 1. Só prompts, zero código
**Decisão**: Mudar apenas texto de prompts. Nenhuma lógica Python nova.
**Justificativa**: O reflexion pass já tem toda a infraestrutura (recebe mensagem original + output, chama LLM, retorna corrigido). O problema é que o prompt não pede pra validar types. Adicionar a instrução é suficiente.

### 2. Reforçar ENTITY_SCAN_PROMPT além do reflexion
**Decisão**: Também adicionar instrução no ENTITY_SCAN_PROMPT (multi-pass).
**Justificativa**: O reflexion pass só roda no modo single-pass. No modo multi-pass (default), a única chance de acertar o type é no entity scan. Reforçar a instrução lá reduz o problema na origem.

### 3. Teste simula reflexion, não extração end-to-end
**Decisão**: Testar `_reflexion_pass()` diretamente com input errado e output corrigido.
**Justificativa**: Testar end-to-end exigiria que o LLM real errasse e depois corrigisse — impossível com FakeLLMProvider. Testar o reflexion isolado com canned responses valida que a infraestrutura passa o type correto adiante.

## Applicable Patterns

- **LLM Interaction** (`patterns/llm-interaction.md`): Prompts são constantes module-level. Reflexion segue o pattern de system prompt + user prompt + fail-safe.
- **Testing** (`patterns/testing.md`): `FakeLLMProvider` com respostas canned. `_make_extraction_response()` para construir input. `run_async()` para chamar funções async.

## Risks

| Risco | Probabilidade | Mitigação |
|---|---|---|
| LLM ignora a instrução nova e continua errando types | Baixa | A instrução é clara e com exemplo concreto. Se persistir, opção B (heurística Python) vira scope de um próximo spec. |
| Reflexion "corrige" types que estavam certos (false positive) | Muito baixa | A instrução pede pra cross-referenciar com facts — sem fact contraditório, não muda. |
| Teste com FakeLLMProvider não prova que o LLM real vai corrigir | Certa | Limitação aceita. O teste valida que a infraestrutura funciona. A eficácia do prompt é validada manualmente pela equipe de integração. |
