# Sistema de RAG — Rota Viva

Panorama da infraestrutura de *Retrieval Augmented Generation* (RAG) para criação e adaptação
de roteiros turísticos.

> RAG **sem vetores**: o *retrieval* é SQL determinístico sobre o catálogo de atrativos; o
> resultado é serializado em JSON e injetado no *prompt* do LLM, que apenas interpreta a entrada
> e narra a rota já selecionada. Não há embeddings/pgvector no projeto.

## Onde existe

A funcionalidade vive na camada de serviços de IA e de roteiro, orquestrada pelos controllers
públicos. Há duas jornadas: **geração de roteiro** e **adaptação à chuva**.

## Arquivos envolvidos

### Retrieval (contexto recuperado do banco)
- `app/Contracts/AtrativoRepository.php` — interface do repositório.
- `app/Repositories/EloquentAtrativoRepository.php` — `available()`: atrativos `is_disponivel` com `categoria`, `horarios`, `recursosAcessibilidade`.
- `app/Services/Itinerary/ItineraryService.php` — filtra, pontua e seleciona paradas (sem LLM).
- `app/Services/Adaptation/RouteAdaptationService.php` — recupera roteiro e busca substitutos cobertos.

### Augmentation + Generation (prompt e narração)
- `app/Contracts/AiProvider.php` — interface `generateStructured(systemPrompt, userPrompt, schema)`.
- `app/Services/Ai/AiPreferenceInterpreter.php` — texto livre → `VisitorPreferencesDTO` (com `LocalPreferenceInterpreter` como fallback).
- `app/Services/Ai/AiExperienceWriter.php` — narra a rota selecionada.
- `app/Services/Ai/AiAdaptationWriter.php` — narra a adaptação à chuva.

### Orquestração
- `app/Http/Controllers/Public/ItineraryController.php` — `routes.store` (geração).
- `app/Http/Controllers/Public/AdaptationController.php` — `routes.adapt.rain` (adaptação).

### Domínio / DTOs
- `app/Models/Atrativo.php` — campos usados no retrieval/score (`categoria_id`, `tags`, `is_disponivel`, `is_ar_livre`, `duracao_minutos`, `custo_medio`, `adequado_criancas`, `intensidade`).
- DTOs em `app/DTOs/` — `VisitorPreferencesDTO`, `GeneratedItineraryDTO`, `ItineraryStopDTO`, `ExperienceNarrativeDTO`, `AdaptedItineraryDTO`, `AdaptationNarrativeDTO`.

## Configurações

- **Provedor ativo**: `AI_PROVIDER` no `.env`, resolvido em `app/Providers/AppServiceProvider.php:36` via `config('services.ai.provider')` (`config/services.php`).
- **Rate limits** (`AppServiceProvider::boot`): `route-generation` (6/min), `route-adaptation` (10/min), `login` (5/min), `admin-actions` (30/min).
- **Tamanho de entrada**: descrição validada com `min:3` / `max:2000` (`ItineraryController::store`).
- **Timeout de geração**: `set_time_limit(150)` no `store`.
- O isolamento de município (tenant) é aplicado no escopo da requisição; o repositório não filtra por município explicitamente.

## Como o RAG funciona (foco)

### Princípio
A IA **nunca escolhe atrativos**. A seleção é determinística (dados + score). O LLM atua em duas
frentes: interpretar a linguagem natural do visitante e narrar a rota montada pelo backend.

### Etapa 1 — Retrieval
1. `EloquentAtrativoRepository::available()` retorna os atrativos disponíveis, já com relações carregadas.
2. `ItineraryService::generate()` aplica:
   - filtros obrigatórios (crianças, recursos de acessibilidade);
   - `calculateThematicScore()` — categoria + tags vs. interesses/humores (pesos 30/20/15/10);
   - `calculateScore()` — soma tema + intensidade + proporção de custo;
   - ordenação por score e encaixe em tempo/orçamento/horário de funcionamento.
3. Na adaptação, `RouteAdaptationService` remove paradas a céu aberto e recupera substitutos cobertos ordenados por compatibilidade.

### Etapa 2 — Augmentation
- Os objetos selecionados viram arrays e são passados como `userPrompt` (JSON) para o LLM.
- `AiExperienceWriter::buildPrompt()` injeta só `place_id`, nome, categoria, duração e custo.
- `AiAdaptationWriter::buildPrompt()` injeta rota original, removidos, adicionados e nova rota.
- O `place_id` devolvido pela IA é validado contra o conjunto selecionado; IDs estranhos são descartados (anti-alucinação).

### Etapa 3 — Generation
- Chamada única `AiProvider::generateStructured(systemPrompt, userPrompt, schema)`.
- O modelo responde JSON conforme `schema` (preferências, ou título/resumo/justificativas, ou mudanças).
- Qualquer falha cai em *fallback* local determinístico — a aplicação funciona sem o LLM.

### Fluxos de ponta a ponta
**Geração** (`routes.store`): valida → interpreta descrição (LLM+fallback) → registra demanda →
recupera/seleciona atrativos (sem LLM) → narra rota (LLM+fallback) → persiste → exibe.

**Adaptação à chuva** (`routes.adapt.rain`): recupera roteiro → remove a céu aberto → busca
substitutos → narra as mudanças (LLM+fallback).

## Resiliência
- Seleção de atrativos é 100% SQL/score — independente do LLM.
- Toda chamada de IA tem fallback local; o sistema operا mesmo se o provider falhar.

## Extensão
Para RAG semântico real, trocar o score por similaridade de cosseno (ex.: `pgvector` + embeddings
dos atrativos) em `EloquentAtrativoRepository`/`ItineraryService`. Hoje o match é por categoria/tags
exatos e filtros booleanos.
