Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,13 @@ OLLAMA_EMBEDDING_MODEL=bge-m3
OLLAMA_KEEP_ALIVE=24h
# URL do serviço RepoAnalyzer quando o backend roda em Docker Compose.
REPO_ANALYZER_URL=http://ai-service:8000
# Segredo para o backend autorizar chamadas internas ao endpoint de ingestão de documentos.
# Gere um segredo privado com: openssl rand -hex 32
DOCUMENT_INGESTION_TOKEN=
# Similaridade coseno mínima para aceitar candidatos encontrados somente pelo vetor; calibrar com a bateria S2-17.
SEARCH_MIN_VECTOR_SIMILARITY=0.55
# Ranking textual mínimo para retornar correspondências full-text; calibrar com S2-17.
SEARCH_MIN_TEXT_RANK=0.05
# Limite de arquivos priorizados nos perfis do RepoAnalyzer (Complete usa MAX_FILES).
ANALYZER_QUICK_FILES=8
ANALYZER_BALANCED_FILES=80
Expand All @@ -53,6 +60,8 @@ DOCUMENT_EVENTS_WEBHOOK_URL=
BACKEND_PORT=3001
FRONTEND_PORT=5173
AI_SERVICE_PORT=8000
# Endereço de bind da porta do ai-service no host (Docker DNS continua interno)
AI_SERVICE_BIND=127.0.0.1

# --- Configurações Regionais ---
GENERIC_TIMEZONE=America/Sao_Paulo
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ jobs:
- name: Validate Docker Compose configuration
run: |
cp .env.example .env
docker compose config --quiet
DOCUMENT_INGESTION_TOKEN=ci-validation-placeholder-32-characters docker compose config --quiet

validate-backend:
name: Build & Typecheck Backend (Node.js)
Expand Down
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,10 @@ n8n/local-files/*
htmlcov/
.pytest_cache/

# Resultados de avaliação local da busca
ai-service/evaluation/results/
ai-service/test_workspace/

# armazenamento local de documentos enviados
backend/storage/
storage/
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,10 @@ A primeira sprint teve como objetivo construir a **base funcional do Sinapse** e

📌 [Planejamento completo da Sprint](docs/PLANEJAMENTO_SCRUM.md)

### 📍 Estado atual do trabalho

A Sprint 1 foi concluída em **27/09/2026**. A Sprint 2 está planejada para **05/10 a 25/10/2026**. A implementação candidata de ingestão, busca e avaliação está em [PR de revisão](https://github.com/Galaticos-API/API-4/pull/44) e ainda não foi aceita: o baseline encontrou latência acima de 2 s e pendências de relevância (Q008 e Q022). Veja o [registro QA](docs/STATUS_REVISAO_2026-10-02.md) para evidências, limites e próximos passos.

---

## 🖥️ Conheça o Sinapse
Expand Down Expand Up @@ -289,6 +293,8 @@ cp .env.example .env

```powershell
Copy-Item .env.example .env
# Gere com: python -c "import secrets; print(secrets.token_hex(32))"
# Cole o resultado na variável DOCUMENT_INGESTION_TOKEN dentro do arquivo .env
```

---
Expand All @@ -299,6 +305,8 @@ Copy-Item .env.example .env
docker compose up --build -d
```

O Compose exige esse segredo compartilhado pelo backend e pelo serviço local de IA; não há mais token padrão no código.

Verifique os containers:

```bash
Expand Down
9 changes: 9 additions & 0 deletions ai-service/config.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from pathlib import Path
from pydantic import model_validator
from pydantic_settings import BaseSettings, SettingsConfigDict
from dotenv import load_dotenv, find_dotenv

Expand All @@ -9,6 +10,8 @@
class Settings(BaseSettings):
AI_SERVICE_PORT: int = 8000
AI_SERVICE_HOST: str = "0.0.0.0"
NODE_ENV: str = "development"
DOCUMENT_INGESTION_TOKEN: str = ""

# Ollama integration
OLLAMA_BASE_URL: str = "http://localhost:11434"
Expand All @@ -24,5 +27,11 @@ class Settings(BaseSettings):

model_config = SettingsConfigDict(env_file=".env", extra="ignore")

@model_validator(mode="after")
def require_private_ingestion_token_in_production(self):
if self.NODE_ENV.lower() == "production" and len(self.DOCUMENT_INGESTION_TOKEN) < 32:
raise ValueError("Configure um DOCUMENT_INGESTION_TOKEN aleatório e privado em produção.")
return self


settings = Settings()
102 changes: 102 additions & 0 deletions ai-service/evaluation/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
# Avaliação da busca do acervo

Esta pasta contém as baterias versionadas da S2-17, um executor HTTP e um avaliador independente do mecanismo de busca. A v1 é a referência imutável; a v2 adiciona metadados de localizador e duas consultas por conteúdo para comparar os dois jeitos de busca.

## Privacidade e corpus

O corpus usa recortes de documentação pública dos projetos API-1, API-2 e API-3. A política PRE-06 exclui código-fonte, configuração de IDE e anexos de teste; o repositório API-2 contém anexos de teste com dados pessoais. Esta suíte não coleta nem armazena esses anexos, nomes de autores ou dados pessoais. Consulte `database/seed/fixtures/historical-v1.json`, `database/seed/fixtures/historical-v2.json` e `docs/backlog/README.md` para a origem e as transformações documentadas.

## Dataset

`datasets/search-ptbr-v1.json` contém 24 consultas em português brasileiro, IDs estáveis, categoria, projeto obrigatório, filtros opcionais e IDs das evidências relevantes. Inclui paráfrases semânticas, identificadores/termos exatos, consultas com intenção combinada, consultas sem evidência, filtros de nível e casos de escopo de projeto para detectar vazamento.

`datasets/search-ptbr-v2.json` preserva as 24 consultas e gabaritos da v1, aponta para a fixture `pre06-historical-v2` com IDs próprios, e acrescenta duas consultas de conteúdo sobre os requisitos GRF. As consultas `GRF-01` e `GRF-08` continuam na categoria de identificadores exatos. Assim medimos tanto a busca por código quanto pela descrição do que a funcionalidade faz.

Os IDs de evidência correspondem aos IDs de `chunk` da fixture PRE-06. Toda consulta escolhe explicitamente um projeto, em linha com a regra de que distância semântica não concede acesso entre projetos. A consulta é julgada somente contra o escopo indicado; o avaliador também acusa qualquer resultado retornado de outro projeto.

## Formato normalizado de resultados

O executor `run_search_suite.py` converte a resposta da rota S2-06 para este formato. Cada consulta precisa aparecer exatamente uma vez; `results` mantém a ordem de ranking e os scores que a API fornecer.

```json
{
"dataset": "sinapse-search-ptbr",
"version": 1,
"runs": [
{
"query_id": "Q001",
"latency_ms": 145,
"results": [
{
"source_id": "60000000-0000-4000-8000-000000000003",
"project_id": "60000000-0000-4000-8000-000000000001"
}
]
}
]
}
```

`source_id` deve ser o identificador do chunk/evidência recuperada e `project_id` deve ser o projeto retornado junto à fonte. O avaliador compara o escopo retornado com o projeto pedido e, quando a fonte pertence ao corpus PRE-06, também verifica contra o manifesto curado. Fontes desconhecidas no corpus podem ser avaliadas como ruído, mas nunca deixam de ser verificadas quanto ao projeto. Não adapte por título ou texto aproximado: isso esconderia erros de identidade e isolamento. O avaliador falha a execução se qualquer resultado estiver fora do projeto consultado; uma violação de isolamento é bloqueadora, não somente uma métrica baixa. O relatório por consulta inclui posição, ID e score retornado para facilitar diagnóstico de falso positivo/falso negativo sem alterar o gabarito.

## Métricas

- `Recall@5` macro: fração das evidências relevantes encontradas entre os cinco primeiros resultados, calculada nas consultas com resposta esperada.
- `Precision@5` macro: relevantes recuperados divididos por cinco nas consultas com resposta esperada; resultados ausentes contam como posições vazias.
- `MRR@5`: média do inverso da posição da primeira evidência relevante, nas consultas com resposta esperada.
- Acurácia de ausência: proporção das consultas sem evidência que retornaram lista vazia.
- Latência: percentis p50/p95 e proporção de consultas em até 2.000 ms, limite descrito no PBI-02.3.1.
- Isolamento: contagem de resultados cujo `project_id` difere do projeto pedido. O objetivo de segurança é zero violações.
- Métricas por categoria: repetem recuperação, acurácia sem evidência e p95 para cada tipo de consulta, permitindo localizar regressões que uma média global esconde.
- Filtros de nível e tecnologia são enviados pelo executor quando declarados na consulta. A fixture PRE-06 não contém associações de tecnologia curadas; por isso a avaliação quantitativa de tecnologia requer um corpus/fixture versionado com tags revisadas, enquanto o filtro combinado já é coberto por teste PostgreSQL de integração.

O backlog não define um mínimo de Recall/Precision/MRR. Portanto, o relatório mede e registra a linha de base, sem inventar um critério de aprovação. O time/PO deve definir metas de relevância depois de observar a busca real. Para latência, os 2.000 ms são limite por consulta; reporte ambiente, hardware, concorrência e se houve aquecimento do Ollama para que comparações sejam reproduzíveis.

## Executar

Executar as 24 consultas contra o backend local (a sessão deve ter acesso ao projeto de cada consulta):

```bash
export SINAPSE_SESSION_COOKIE="<cookie de sessão>"
python ai-service/evaluation/run_search_suite.py --output ai-service/evaluation/results/search-ptbr-v1-results.json
python ai-service/evaluation/evaluate_search.py --results ai-service/evaluation/results/search-ptbr-v1-results.json
```

Para executar a v2, passe o dataset e grave resultados em local descartável; substitua o caminho de saída conforme sua política de retenção:

```bash
python ai-service/evaluation/run_search_suite.py --dataset ai-service/evaluation/datasets/search-ptbr-v2.json --output tmp/search-ptbr-v2-results.json
python ai-service/evaluation/evaluate_search.py --dataset ai-service/evaluation/datasets/search-ptbr-v2.json --results tmp/search-ptbr-v2-results.json
```

O executor lê a credencial de `SINAPSE_SESSION_COOKIE`, envia `projeto_id` em toda chamada, cronometra a latência HTTP real, e não imprime nem grava a credencial. `SINAPSE_API_BASE_URL` altera a URL base (padrão `http://localhost:3001`), mas por segurança só aceita HTTP em `localhost` ou endereço loopback; cookies de sessão nunca são enviados para hosts remotos. O executor registra SHA do commit, se o working tree estava modificado, hash do snapshot de arquivos versionados/não ignorados, hash do dataset e do arquivo de corpus. `.env*` e `tmp/` ficam fora do hash e do relatório. O arquivo de resultados não deve ser commitado quando incluir nomes, conteúdo ou outros dados que não sejam da fixture aprovada.

Também é possível fornecer resultados normalizados de outro executor:

```bash
python ai-service/evaluation/evaluate_search.py --results caminho/para/resultados.json
```

Por padrão calcula métricas em `k=5` e orçamento de 2.000 ms. Os parâmetros `--k`, `--latency-budget-ms` e `--dataset` permitem reproduzir outra execução sem alterar o dataset versionado. A ferramenta retorna código 2 para arquivos, versões, consultas ou resultados inválidos.

Testar o avaliador sem Ollama, Postgres ou endpoint S2-06:

```bash
python -m unittest discover -s ai-service/tests -p "test_search*.py"
```

Com Postgres descartável que já tenha pgvector instalado e banco terminado em `_test`, o teste S2-06 de integração pode ser executado dentro de `backend` após definir `HYBRID_SEARCH_TEST_DATABASE_URL`:

```bash
npm run test:integration:search
```

## Baseline ponta a ponta executada; aceite de relevância pendente

Em 02/10/2026 o executor rodou as 24 consultas contra API, PostgreSQL/pgvector e Ollama locais, usando embeddings da fixture PRE-06. A execução e a comparação de corte foram feitas em banco descartável; veja números, limitações e plano de ação no [registro da revisão QA](../../docs/STATUS_REVISAO_2026-10-02.md).

Na execução inicial da v1, `SEARCH_MIN_VECTOR_SIMILARITY=0.3` obteve Recall@5 de 88,9%, acurácia de ausência de 16,7%, p95 de 145 ms e zero violações de isolamento. O experimento v1 com `0.6` elevou a acurácia de ausência para 100%, mas baixou Recall@5 para 72,2%. Esses números são referências históricas da v1; os resultados atuais da v2 e a matriz de cortes estão no [relatório da avaliação](../../docs/QA_SEARCH_V2_2026-10-02.md). `0.55` foi escolhido como padrão local provisório, pendente de reavaliação com corpus maior e metas formais de relevância.

Queries com formato de identificador (`GRF-01`) usam correspondência literal sobre `metadata.source_locator` e não recebem fallback semântico quando não há correspondência. O avaliador também rejeita qualquer resultado fora do projeto solicitado. A v2 com identificadores e perguntas de conteúdo foi executada ponta a ponta; `0.55` é o padrão local provisório escolhido pelo usuário, ainda sem aceite final das metas de qualidade. Diagnóstico da avaliação v2 e limitações remanescentes estão no [relatório QA](../../docs/QA_SEARCH_V2_2026-10-02.md). O runner atual exige URL local, registra hashes de proveniência e o avaliador mostra o ranking por consulta. Após qualquer mudança no runner, corpus, configuração ou busca, reexecute a bateria real contra a API e preserve apenas relatório sanitizado.

Não modifique silenciosamente versão publicada do corpus ou gabarito. Alterações exigem revisão e nova versão do dataset/corpus.
Loading
Loading