caarleexx commited on
Commit
785785b
·
verified ·
1 Parent(s): d9b9bbd

Upload 7 files

Browse files
Files changed (5) hide show
  1. INSTRUCTIONS.md +94 -515
  2. QUICKSTART.txt +1 -4
  3. filter_fields.py +44 -0
  4. query_engine.py +184 -0
  5. rag_builder.py +105 -0
INSTRUCTIONS.md CHANGED
@@ -1,591 +1,170 @@
1
- # 📘 INSTRUCTIONS - Para.AI RAG Cluster
2
 
3
- ## 🎯 Objetivo deste Documento
4
 
5
- Este guia detalha como fazer **deploy** de um micro-cluster RAG no **Hugging Face Spaces (free tier)** para indexar e buscar em jurisprudências do TJPR.
6
 
7
- ---
8
 
9
- ## 📋 Pré-requisitos
 
 
 
 
 
 
 
 
 
 
 
10
 
11
- Antes de começar, você precisa:
12
 
13
- 1. **Conta no Hugging Face** (gratuita)
14
- - Criar em: https://huggingface.co/join
15
 
16
- 2. **Dados no GitHub** (chunks)
17
- - Repositório com chunks `.tar.gz`
18
- - Exemplo: `github.com/caarleexx/para-ai-data`
19
 
20
- 3. **Git instalado localmente**
21
- - Download: https://git-scm.com/downloads
22
-
23
- 4. **Python 3.11+** (apenas para testes locais)
24
- - Download: https://python.org/downloads
25
-
26
- ---
27
-
28
- ## 🚀 PARTE 1: Deploy Rápido (5 minutos)
29
 
30
- ### Passo 1: Criar Space no Hugging Face
31
 
32
  ```bash
33
- # Instalar Hugging Face CLI
34
- pip install huggingface-hub[cli]
35
-
36
  # Login
37
  huggingface-cli login
38
 
39
- # Criar novo Space
40
  huggingface-cli repo create para-ai-rag-0301 --type space --space_sdk docker
41
  ```
42
 
43
- **Resultado esperado:**
44
- ```
45
- ✓ Space criado: https://huggingface.co/spaces/seu-usuario/para-ai-rag-0301
46
- ```
47
-
48
- ---
49
-
50
- ### Passo 2: Clonar e Configurar
51
 
52
  ```bash
53
- # Clonar o Space vazio
54
- git clone https://huggingface.co/spaces/seu-usuario/para-ai-rag-0301
55
- cd para-ai-rag-0301
56
-
57
- # Copiar arquivos deste exemplo
58
- cp -r /caminho/para/hf_space_rag_example/* .
59
-
60
- # Editar config.yaml
61
- nano config.yaml
62
- ```
63
-
64
- **Edite estas linhas em `config.yaml`:**
65
-
66
- ```yaml
67
- cluster_id: "RAG-0301" # Identificador único
68
- chunk_start: 301 # Primeiro chunk
69
- chunk_end: 600 # Último chunk
70
- github_repo: "https://github.com/SEU-USUARIO/para-ai-data.git" # Seu repo
71
- ```
72
-
73
- ---
74
 
75
- ### Passo 3: Deploy
76
-
77
- ```bash
78
- # Adicionar arquivos
79
- git add .
80
 
81
  # Commit
82
- git commit -m "Initial deployment - Chunks 301-600"
 
83
 
84
- # Push para Hugging Face
85
- git push
86
  ```
87
 
88
- **HF Spaces vai automaticamente:**
89
- 1. Detectar `Dockerfile`
90
- 2. Construir container
91
- 3. Executar `entrypoint.sh`
92
- 4. Clonar chunks do GitHub
93
- 5. Construir ChromaDB
94
- 6. Iniciar FastAPI
95
 
96
- **⏱️ Tempo total:** ~10-15 minutos
 
97
 
98
- ---
99
-
100
- ### Passo 4: Verificar Status
101
-
102
- Acesse: `https://huggingface.co/spaces/seu-usuario/para-ai-rag-0301`
103
-
104
- **Você verá:**
105
- - 🟢 **Building** (5-10 min) → Container sendo construído
106
- - 🟡 **Running** (5-10 min) → Clonando dados e construindo ChromaDB
107
- - 🟢 **Ready** → API disponível!
108
-
109
- **Testar API:**
110
 
111
  ```bash
112
- curl https://seu-usuario-para-ai-rag-0301.hf.space/cluster/info
113
- ```
114
-
115
- **Resposta esperada:**
116
- ```json
117
- {
118
- "cluster_id": "RAG-0301",
119
- "chunk_range": [301, 600],
120
- "total_records": 295432,
121
- "status": "ready"
122
- }
123
- ```
124
-
125
- ---
126
-
127
- ## 🔧 PARTE 2: Customização Avançada
128
-
129
- ### Opção 1: Mudar Modelo de Embedding
130
-
131
- **Em `config.yaml`:**
132
-
133
- ```yaml
134
- # Modelo atual (leve, rápido)
135
- embedding_model: "sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2"
136
- embedding_dim: 384
137
-
138
- # Alternativas:
139
-
140
- # Modelo maior (melhor qualidade, mais lento)
141
- embedding_model: "sentence-transformers/paraphrase-multilingual-mpnet-base-v2"
142
- embedding_dim: 768
143
-
144
- # Modelo português-específico
145
- embedding_model: "neuralmind/bert-base-portuguese-cased"
146
- embedding_dim: 768
147
- ```
148
-
149
- **⚠️ Atenção:**
150
- - Modelos maiores usam mais RAM
151
- - Verifique se cabe no free tier (16GB)
152
 
153
- ---
154
-
155
- ### Opção 2: Adicionar Mais Campos
156
-
157
- **Editar `config.yaml`:**
158
-
159
- ```yaml
160
- campos_filter:
161
- - id
162
- - ementa
163
- - data_decisao # Adicionar
164
- - relator # Adicionar
165
- - orgao_julgador # Adicionar
166
- ```
167
-
168
- **Editar `filter_fields.py`:**
169
-
170
- Já está pronto! Ele lê automaticamente de `config.yaml`.
171
-
172
- **Editar `query_engine.py`:**
173
-
174
- Se quiser buscar por campos adicionais:
175
-
176
- ```python
177
- def search_by_metadata(self, field: str, value: str):
178
- """Busca por campo de metadata"""
179
- results = self.collection.query(
180
- query_texts=[""],
181
- n_results=100,
182
- where={field: value}
183
- )
184
- return results
185
- ```
186
-
187
- ---
188
-
189
- ### Opção 3: Ajustar Performance
190
-
191
- **Em `config.yaml`:**
192
-
193
- ```yaml
194
- # Batch size para embeddings
195
- embedding_batch_size: 64 # Padrão
196
- # Aumentar para 128 se tiver RAM disponível
197
- # Diminuir para 32 se ficar sem RAM
198
-
199
- # Workers
200
- max_workers: 2 # Padrão (free tier tem 2 vCPU)
201
- ```
202
-
203
- **Em `app.py` (FastAPI):**
204
-
205
- ```python
206
- # Aumentar workers do Uvicorn
207
- uvicorn.run(app, host="0.0.0.0", port=7860, workers=2) # Padrão: 1
208
  ```
209
 
210
- **⚠️ Cuidado:** Mais workers = mais RAM
211
-
212
- ---
213
-
214
- ## 📊 PARTE 3: Múltiplos Clusters
215
 
216
- Para cobrir **todos os 4500 chunks**, crie **15 Spaces**:
217
 
218
- | Space ID | Chunks | Registros | HF Space URL |
219
- |----------|--------|-----------|--------------|
220
- | RAG-0001 | 1-300 | ~300k | /para-ai-rag-0001 |
221
- | RAG-0301 | 301-600 | ~300k | /para-ai-rag-0301 |
222
- | RAG-0601 | 601-900 | ~300k | /para-ai-rag-0601 |
223
- | RAG-0901 | 901-1200 | ~300k | /para-ai-rag-0901 |
224
- | ... | ... | ... | ... |
225
- | RAG-4201 | 4201-4500 | ~300k | /para-ai-rag-4201 |
226
 
227
- ### Script de Deploy em Massa
228
 
229
  ```bash
230
  #!/bin/bash
231
- # deploy_all_clusters.sh
232
-
233
  for START in 1 301 601 901 1201 1501 1801 2101 2401 2701 3001 3301 3601 3901 4201; do
234
  END=$((START + 299))
235
- CLUSTER_ID=$(printf "RAG-%04d" $START)
236
  SPACE_NAME="para-ai-rag-$(printf "%04d" $START)"
237
 
238
- echo "Criando $SPACE_NAME (chunks $START-$END)..."
239
-
240
- # Criar Space
241
  huggingface-cli repo create $SPACE_NAME --type space --space_sdk docker
242
-
243
- # Clonar
244
- git clone https://huggingface.co/spaces/seu-usuario/$SPACE_NAME
245
  cd $SPACE_NAME
246
 
247
- # Copiar template
248
  cp -r ../hf_space_rag_example/* .
249
-
250
- # Atualizar config
251
- sed -i "s/cluster_id: .*/cluster_id: "$CLUSTER_ID"/" config.yaml
252
  sed -i "s/chunk_start: .*/chunk_start: $START/" config.yaml
253
  sed -i "s/chunk_end: .*/chunk_end: $END/" config.yaml
254
 
255
- # Deploy
256
  git add .
257
- git commit -m "Deploy $CLUSTER_ID"
258
  git push
259
-
260
  cd ..
261
-
262
- echo "✅ $SPACE_NAME deployed!"
263
- sleep 5 # Esperar 5s para não sobrecarregar HF
264
  done
265
-
266
- echo "🎉 Todos os 15 clusters deployados!"
267
- ```
268
-
269
- ---
270
-
271
- ## 🌐 PARTE 4: Gateway Agregador (Opcional)
272
-
273
- Para buscar em **todos os clusters** ao mesmo tempo, crie um **Space Gateway**:
274
-
275
- ### Arquitetura do Gateway
276
-
277
- ```python
278
- # gateway_app.py
279
- from fastapi import FastAPI
280
- import asyncio
281
- import httpx
282
-
283
- app = FastAPI(title="Para.AI Gateway")
284
-
285
- # Lista de todos os clusters
286
- CLUSTERS = [
287
- "https://seu-usuario-para-ai-rag-0001.hf.space",
288
- "https://seu-usuario-para-ai-rag-0301.hf.space",
289
- "https://seu-usuario-para-ai-rag-0601.hf.space",
290
- # ... todos os 15
291
- ]
292
-
293
- @app.post("/search/embedding")
294
- async def search_all_clusters(query: str, top_k: int = 10):
295
- """Busca em todos os clusters e agrega resultados"""
296
-
297
- async with httpx.AsyncClient(timeout=30.0) as client:
298
- tasks = [
299
- client.post(
300
- f"{cluster}/search/embedding",
301
- json={"query": query, "top_k": top_k}
302
- )
303
- for cluster in CLUSTERS
304
- ]
305
-
306
- responses = await asyncio.gather(*tasks, return_exceptions=True)
307
-
308
- # Agregar resultados
309
- all_results = []
310
- for resp in responses:
311
- if isinstance(resp, Exception):
312
- continue
313
- data = resp.json()
314
- all_results.extend(data['results'])
315
-
316
- # Ordenar por score
317
- all_results.sort(key=lambda x: x['score'], reverse=True)
318
-
319
- return {
320
- "clusters_consulted": len(CLUSTERS),
321
- "total_found": len(all_results),
322
- "results": all_results[:top_k] # Top K global
323
- }
324
- ```
325
-
326
- **Deploy do Gateway:**
327
-
328
- ```bash
329
- huggingface-cli repo create para-ai-gateway --type space --space_sdk docker
330
- # ... copiar gateway_app.py, Dockerfile, etc.
331
  ```
332
 
333
- ---
334
-
335
- ## 🧪 PARTE 5: Testes Locais (Desenvolvimento)
336
-
337
- ### Testar sem Deploy
338
-
339
- ```bash
340
- # Entrar na pasta
341
- cd hf_space_rag_example
342
-
343
- # Instalar dependências
344
- pip install -r requirements.txt
345
 
346
- # Editar config (usar poucos chunks para teste)
347
- # config.yaml: chunk_start: 301, chunk_end: 305 (apenas 5 chunks!)
348
 
349
- # Executar passos manualmente
350
-
351
- # 1. Filtrar campos
352
- python filter_fields.py --input /tmp/test.jsonl --output /tmp/filtered.jsonl
353
-
354
- # 2. Build ChromaDB
355
- python rag_builder.py --input /tmp/filtered.jsonl
356
-
357
- # 3. Iniciar API
358
- python app.py
359
- # ou
360
- uvicorn app:app --reload
361
- ```
362
-
363
- **Testar API localmente:**
364
-
365
- ```bash
366
- # Teste 1: Info do cluster
367
- curl http://localhost:7860/cluster/info
368
-
369
- # Teste 2: Busca semântica
370
- curl -X POST http://localhost:7860/search/embedding \
371
- -H "Content-Type: application/json" \
372
- -d '{"query": "despejo", "top_k": 3}'
373
-
374
- # Teste 3: Busca por keywords
375
- curl -X POST http://localhost:7860/search/keywords \
376
- -H "Content-Type: application/json" \
377
- -d '{"keywords": ["despejo", "aluguel"], "operator": "AND"}'
378
- ```
379
-
380
- ---
381
-
382
- ## 🐛 PARTE 6: Troubleshooting
383
-
384
- ### Problema 1: Build Timeout no HF
385
-
386
- **Sintoma:** Space fica em "Building" por >1h e falha
387
-
388
- **Causa:** Download de chunks muito grande
389
-
390
- **Solução:**
391
-
392
- 1. Verificar se sparse checkout está funcionando:
393
-
394
- ```bash
395
- # No entrypoint.sh, adicionar debug:
396
- echo "Chunks encontrados:"
397
- find chunks_dados -name "*.tar.gz" | wc -l
398
- ```
399
-
400
- 2. Reduzir intervalo de chunks temporariamente:
401
 
402
  ```yaml
403
- chunk_start: 301
404
- chunk_end: 320 # Apenas 20 chunks para teste
 
 
 
405
  ```
406
 
407
- ---
408
-
409
- ### Problema 2: Out of Memory (OOM)
410
-
411
- **Sintoma:** Space reinicia continuamente
412
-
413
- **Causa:** Modelo de embedding muito grande ou muitos chunks
414
-
415
- **Soluções:**
416
-
417
- 1. **Usar modelo menor:**
418
 
419
  ```yaml
420
- embedding_model: "sentence-transformers/all-MiniLM-L6-v2" # Apenas 80MB!
 
421
  embedding_dim: 384
422
- ```
423
-
424
- 2. **Reduzir batch size:**
425
-
426
- ```yaml
427
- embedding_batch_size: 32 # Ao invés de 64
428
- ```
429
-
430
- 3. **Reduzir número de chunks:**
431
-
432
- ```yaml
433
- chunk_end: 400 # Ao invés de 600
434
- ```
435
-
436
- ---
437
-
438
- ### Problema 3: Git Clone Muito Lento
439
-
440
- **Sintoma:** Clonagem demora >30min
441
-
442
- **Causa:** Sparse checkout não funcionou
443
 
444
- **Solução:**
445
-
446
- Verificar se pattern está correto:
447
-
448
- ```bash
449
- # No entrypoint.sh
450
- echo "Pattern de sparse checkout:"
451
- echo "$PATTERN"
452
-
453
- # Deve mostrar algo como:
454
- # chunks_dados/chunk_dados_0301.tar.gz chunks_dados/chunk_dados_0302.tar.gz ...
455
- ```
456
-
457
- Se estiver errado, corrigir geração do pattern:
458
-
459
- ```bash
460
- # Bash expansion correta
461
- for i in $(seq -f "%04g" $CHUNK_START $CHUNK_END); do
462
- echo "chunks_dados/chunk_dados_$i.tar.gz"
463
- done
464
- ```
465
-
466
- ---
467
-
468
- ### Problema 4: API Retorna 503
469
-
470
- **Sintoma:** API acessível mas sempre retorna erro 503
471
-
472
- **Causa:** ChromaDB não foi construído ou está corrompido
473
-
474
- **Solução:**
475
-
476
- 1. Ver logs do Space no HF
477
- 2. Verificar se `/app/chromadb` existe e tem conteúdo
478
- 3. Forçar rebuild:
479
-
480
- ```bash
481
- # No entrypoint.sh, remover check de persistência:
482
- # if [ -d "/app/chromadb" ] && [ "$(ls -A /app/chromadb)" ]; then
483
- # ...
484
- # fi
485
-
486
- # Comentar essas linhas para sempre rebuildar
487
- ```
488
-
489
- ---
490
-
491
- ## 📈 PARTE 7: Monitoramento
492
-
493
- ### Logs do Space
494
-
495
- Acesse: `https://huggingface.co/spaces/seu-usuario/para-ai-rag-0301/logs`
496
-
497
- **O que procurar:**
498
-
499
- ```
500
- ✅ BOM:
501
- ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
502
- ✓ 300 chunks clonados
503
- ✓ 295432 registros concatenados
504
- ✓ Campos filtrados
505
- ✓ ChromaDB pronto!
506
- ✓ Para.AI RAG Cluster RAG-0301 ONLINE
507
-
508
- ❌ RUIM:
509
- ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
510
- ✗ Erro ao clonar repositório
511
- ✗ Out of memory
512
- ✗ ChromaDB corrupted
513
- ```
514
-
515
- ---
516
-
517
- ### Métricas de Performance
518
-
519
- Adicionar em `app.py`:
520
-
521
- ```python
522
- from prometheus_client import Counter, Histogram, generate_latest
523
-
524
- # Métricas
525
- search_requests = Counter('search_requests_total', 'Total search requests')
526
- search_latency = Histogram('search_latency_seconds', 'Search latency')
527
-
528
- @app.get("/metrics")
529
- async def metrics():
530
- return generate_latest()
531
  ```
532
 
533
- ---
534
-
535
- ## 🎓 PARTE 8: Próximos Passos
536
-
537
- Após ter os clusters funcionando:
538
-
539
- 1. **Integrar com aplicação front-end**
540
- - Criar Gradio UI que consulta os clusters
541
- - Deploy em outro HF Space
542
-
543
- 2. **Adicionar cache**
544
- - Redis para cachear queries frequentes
545
- - Reduz latência e custo
546
-
547
- 3. **Fine-tuning do modelo**
548
- - Treinar embedding model específico para jurisprudência
549
- - Melhora qualidade dos resultados
550
-
551
- 4. **Adicionar re-ranking**
552
- - Cross-encoder para re-ranquear top results
553
- - Aumenta precisão
554
-
555
- ---
556
-
557
- ## 📚 Recursos Adicionais
558
-
559
- - **Documentação HF Spaces:** https://huggingface.co/docs/hub/spaces
560
- - **ChromaDB Docs:** https://docs.trychroma.com/
561
- - **Sentence Transformers:** https://www.sbert.net/
562
- - **FastAPI Docs:** https://fastapi.tiangolo.com/
563
 
564
- ---
 
 
565
 
566
- ## Checklist Final
 
 
 
567
 
568
- Antes de considerar o deploy completo:
 
 
569
 
570
- - [ ] Space está **Ready** (não Building)
571
- - [ ] `/cluster/info` retorna dados corretos
572
- - [ ] Busca semântica funciona
573
- - [ ] Busca por keywords funciona
574
- - [ ] Busca por ID funciona
575
- - [ ] Latência < 200ms para queries
576
- - [ ] Logs sem erros
577
- - [ ] README.md atualizado com URL do Space
578
- - [ ] Testado com queries reais
579
 
580
- ---
 
 
 
 
581
 
582
- ## 🎉 Conclusão
583
 
584
- Parabéns! Você agora tem um **micro-cluster RAG** funcional no HF Spaces, capaz de:
585
 
586
- Indexar ~300k jurisprudências
587
- ✅ Buscar em <100ms
588
- ✅ Custo zero (free tier)
589
- ✅ Escalável para milhões de registros
590
 
591
- **⚖️ InJustiça não para o Paraná!**
 
1
+ # 📘 Para.AI RAG Cluster - Instructions
2
 
3
+ ## 🎯 Objetivo
4
 
5
+ Deploy de micro-cluster RAG no Hugging Face Spaces (free tier) para indexar ~300k jurisprudências do TJPR.
6
 
7
+ ## 📋 Arquivos do Projeto
8
 
9
+ | Arquivo | Função |
10
+ |---------|---------|
11
+ | `config.yaml` | Configuração (EDITE AQUI!) |
12
+ | `Dockerfile` | Container Docker |
13
+ | `entrypoint.sh` | Script de inicialização |
14
+ | `requirements.txt` | Dependências Python |
15
+ | `filter_fields.py` | Filtrar campos JSONL |
16
+ | `rag_builder.py` | Construir ChromaDB |
17
+ | `query_engine.py` | Engine de busca |
18
+ | `app.py` | FastAPI REST API |
19
+ | `README.md` | Documentação básica |
20
+ | `.gitignore` | Arquivos ignorados |
21
 
22
+ ## 🚀 Deploy Step-by-Step
23
 
24
+ ### 1. Preparar Configuração
 
25
 
26
+ Editar `config.yaml`:
 
 
27
 
28
+ ```yaml
29
+ cluster_id: "RAG-0301" # Seu ID único
30
+ chunk_start: 301 # Primeiro chunk
31
+ chunk_end: 600 # Último chunk (300 chunks = ~300k registros)
32
+ github_repo: "https://github.com/SEU-USUARIO/para-ai-data.git"
33
+ ```
 
 
 
34
 
35
+ ### 2. Criar Space no HF
36
 
37
  ```bash
 
 
 
38
  # Login
39
  huggingface-cli login
40
 
41
+ # Criar Space
42
  huggingface-cli repo create para-ai-rag-0301 --type space --space_sdk docker
43
  ```
44
 
45
+ ### 3. Fazer Upload
 
 
 
 
 
 
 
46
 
47
  ```bash
48
+ cd hf_space_rag_example
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
49
 
50
+ # Inicializar Git
51
+ git init
52
+ git remote add origin https://huggingface.co/spaces/SEU-USUARIO/para-ai-rag-0301
 
 
53
 
54
  # Commit
55
+ git add .
56
+ git commit -m "Initial deployment"
57
 
58
+ # Push
59
+ git push origin main
60
  ```
61
 
62
+ ### 4. Aguardar Build
 
 
 
 
 
 
63
 
64
+ - Acesse: https://huggingface.co/spaces/SEU-USUARIO/para-ai-rag-0301
65
+ - Status: Building (5-10 min) → Running (5-10 min) → Ready ✅
66
 
67
+ ### 5. Testar
 
 
 
 
 
 
 
 
 
 
 
68
 
69
  ```bash
70
+ # Info do cluster
71
+ curl https://SEU-USUARIO-para-ai-rag-0301.hf.space/cluster/info
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
72
 
73
+ # Busca semântica
74
+ curl -X POST https://SEU-USUARIO-para-ai-rag-0301.hf.space/search/embedding \
75
+ -H "Content-Type: application/json" \
76
+ -d '{"query": "despejo falta pagamento", "top_k": 5}'
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
77
  ```
78
 
79
+ ## 🌐 Arquitetura Distribuída
 
 
 
 
80
 
81
+ Para cobrir todos os 4.5M registros, crie 15 Spaces:
82
 
83
+ | Space | Chunks | Config |
84
+ |-------|--------|--------|
85
+ | para-ai-rag-0001 | 1-300 | chunk_start: 1, chunk_end: 300 |
86
+ | para-ai-rag-0301 | 301-600 | chunk_start: 301, chunk_end: 600 |
87
+ | para-ai-rag-0601 | 601-900 | chunk_start: 601, chunk_end: 900 |
88
+ | ... | ... | ... |
89
+ | para-ai-rag-4201 | 4201-4500 | chunk_start: 4201, chunk_end: 4500 |
 
90
 
91
+ **Script de deploy automático** (bash):
92
 
93
  ```bash
94
  #!/bin/bash
 
 
95
  for START in 1 301 601 901 1201 1501 1801 2101 2401 2701 3001 3301 3601 3901 4201; do
96
  END=$((START + 299))
 
97
  SPACE_NAME="para-ai-rag-$(printf "%04d" $START)"
98
 
 
 
 
99
  huggingface-cli repo create $SPACE_NAME --type space --space_sdk docker
100
+ git clone https://huggingface.co/spaces/SEU-USUARIO/$SPACE_NAME
 
 
101
  cd $SPACE_NAME
102
 
103
+ # Copiar template e atualizar config
104
  cp -r ../hf_space_rag_example/* .
 
 
 
105
  sed -i "s/chunk_start: .*/chunk_start: $START/" config.yaml
106
  sed -i "s/chunk_end: .*/chunk_end: $END/" config.yaml
107
 
 
108
  git add .
109
+ git commit -m "Deploy cluster $START-$END"
110
  git push
 
111
  cd ..
 
 
 
112
  done
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
113
  ```
114
 
115
+ ## 🔧 Customização
 
 
 
 
 
 
 
 
 
 
 
116
 
117
+ ### Adicionar Mais Campos
 
118
 
119
+ Em `config.yaml`:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
120
 
121
  ```yaml
122
+ campos_filter:
123
+ - id
124
+ - ementa
125
+ - data_decisao # Adicionar
126
+ - relator # Adicionar
127
  ```
128
 
129
+ ### Trocar Modelo de Embedding
 
 
 
 
 
 
 
 
 
 
130
 
131
  ```yaml
132
+ # Opção 1: Mais leve (padrão)
133
+ embedding_model: "sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2"
134
  embedding_dim: 384
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
135
 
136
+ # Opção 2: Melhor qualidade
137
+ embedding_model: "sentence-transformers/paraphrase-multilingual-mpnet-base-v2"
138
+ embedding_dim: 768
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
139
  ```
140
 
141
+ ## 🐛 Troubleshooting
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
142
 
143
+ ### Build muito lento
144
+ - Verificar se sparse checkout está funcionando
145
+ - Reduzir número de chunks temporariamente
146
 
147
+ ### Out of Memory
148
+ - Usar modelo menor: `all-MiniLM-L6-v2`
149
+ - Reduzir `embedding_batch_size: 32`
150
+ - Diminuir intervalo de chunks
151
 
152
+ ### API retorna 503
153
+ - Ver logs do Space no HF
154
+ - Verificar se ChromaDB foi construído
155
 
156
+ ## 📊 Recursos Utilizados
 
 
 
 
 
 
 
 
157
 
158
+ | Recurso | Usado | Disponível (Free) |
159
+ |---------|-------|-------------------|
160
+ | RAM | ~2GB | 16GB ✅ |
161
+ | Disco | ~2.6GB | 50GB ✅ |
162
+ | CPU | 1 core | 2 cores ✅ |
163
 
164
+ ## ⚖️ Sobre Para.AI
165
 
166
+ Projeto open-source para democratizar o acesso à justiça no Paraná usando IA.
167
 
168
+ 🐝 **InJustiça não para o Paraná!**
 
 
 
169
 
170
+ 📧 github.com/caarleexx/para-ai
QUICKSTART.txt CHANGED
@@ -18,7 +18,6 @@
18
 
19
  3. Deploy:
20
 
21
- $ cd hf_space_rag_example
22
  $ git init
23
  $ git remote add origin https://huggingface.co/spaces/SEU-USUARIO/para-ai-rag-0301
24
  $ git add .
@@ -33,8 +32,6 @@
33
 
34
  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
35
 
36
- 📖 LEIA:
37
- • README.md - Docs da API
38
- • INSTRUCTIONS.md - Guia completo
39
 
40
  ✅ PRONTO! Seu RAG está online!
 
18
 
19
  3. Deploy:
20
 
 
21
  $ git init
22
  $ git remote add origin https://huggingface.co/spaces/SEU-USUARIO/para-ai-rag-0301
23
  $ git add .
 
32
 
33
  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
34
 
35
+ 📖 LEIA: INSTRUCTIONS.md para guia completo
 
 
36
 
37
  ✅ PRONTO! Seu RAG está online!
filter_fields.py ADDED
@@ -0,0 +1,44 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """
3
+ Filtrar campos de JSONL mantendo apenas os especificados
4
+ """
5
+ import json
6
+ import yaml
7
+ from pathlib import Path
8
+ import argparse
9
+ from tqdm import tqdm
10
+
11
+ def filter_jsonl(input_path: str, output_path: str, keep_fields: list = None):
12
+ """Filtra campos de arquivo JSONL"""
13
+
14
+ # Carregar campos da config se não especificados
15
+ if keep_fields is None:
16
+ with open('config.yaml') as f:
17
+ config = yaml.safe_load(f)
18
+ keep_fields = config['campos_filter']
19
+
20
+ print(f"📥 Input: {input_path}")
21
+ print(f"📤 Output: {output_path}")
22
+ print(f"🔧 Mantendo campos: {keep_fields}")
23
+
24
+ # Contar linhas
25
+ with open(input_path) as f:
26
+ total = sum(1 for _ in f)
27
+
28
+ # Filtrar
29
+ with open(input_path) as fin, open(output_path, 'w') as fout:
30
+ for line in tqdm(fin, total=total, desc="Filtrando"):
31
+ record = json.loads(line)
32
+ filtered = {k: record[k] for k in keep_fields if k in record}
33
+ fout.write(json.dumps(filtered, ensure_ascii=False) + '\n')
34
+
35
+ print(f"✅ {total} registros filtrados!")
36
+
37
+ if __name__ == "__main__":
38
+ parser = argparse.ArgumentParser()
39
+ parser.add_argument('--input', required=True)
40
+ parser.add_argument('--output', required=True)
41
+ parser.add_argument('--keep', nargs='+', default=None)
42
+ args = parser.parse_args()
43
+
44
+ filter_jsonl(args.input, args.output, args.keep)
query_engine.py ADDED
@@ -0,0 +1,184 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """
3
+ Engine de busca para ChromaDB
4
+ """
5
+ import yaml
6
+ import chromadb
7
+ from sentence_transformers import SentenceTransformer
8
+ from typing import List, Dict, Optional
9
+ import logging
10
+
11
+ logger = logging.getLogger(__name__)
12
+
13
+ class QueryEngine:
14
+ """Engine de busca com ChromaDB"""
15
+
16
+ def __init__(self, config_path: str = 'config.yaml'):
17
+ # Carregar config
18
+ with open(config_path) as f:
19
+ self.config = yaml.safe_load(f)
20
+
21
+ # Carregar modelo de embedding
22
+ logger.info(f"Carregando modelo {self.config['embedding_model']}...")
23
+ self.model = SentenceTransformer(self.config['embedding_model'])
24
+
25
+ # Conectar ao ChromaDB
26
+ logger.info(f"Conectando ao ChromaDB...")
27
+ self.client = chromadb.PersistentClient(path=self.config['chromadb_path'])
28
+ self.collection = self.client.get_collection(self.config['collection_name'])
29
+
30
+ logger.info(f"✅ QueryEngine pronto ({self.collection.count():,} registros)")
31
+
32
+ def search_by_embedding(
33
+ self,
34
+ query: str,
35
+ top_k: int = 10,
36
+ return_embeddings: bool = False
37
+ ) -> Dict:
38
+ """Busca por similaridade semântica"""
39
+
40
+ # Gerar embedding da query
41
+ query_embedding = self.model.encode(query).tolist()
42
+
43
+ # Buscar no ChromaDB
44
+ results = self.collection.query(
45
+ query_embeddings=[query_embedding],
46
+ n_results=top_k,
47
+ include=['documents', 'metadatas', 'distances', 'embeddings'] if return_embeddings
48
+ else ['documents', 'metadatas', 'distances']
49
+ )
50
+
51
+ # Formatar resposta
52
+ formatted_results = []
53
+ for i in range(len(results['ids'][0])):
54
+ result = {
55
+ 'id': results['ids'][0][i],
56
+ 'ementa': results['documents'][0][i],
57
+ 'distance': results['distances'][0][i],
58
+ 'score': 1.0 - results['distances'][0][i] # Converter distância para score
59
+ }
60
+
61
+ if return_embeddings and 'embeddings' in results:
62
+ result['embedding'] = results['embeddings'][0][i]
63
+
64
+ formatted_results.append(result)
65
+
66
+ return {
67
+ 'cluster_id': self.config['cluster_id'],
68
+ 'chunk_range': [self.config['chunk_start'], self.config['chunk_end']],
69
+ 'results': formatted_results,
70
+ 'total_found': len(formatted_results)
71
+ }
72
+
73
+ def search_by_keywords(
74
+ self,
75
+ keywords: List[str],
76
+ operator: str = 'AND',
77
+ top_k: int = 20
78
+ ) -> Dict:
79
+ """Busca por termos-chave (full-text search)"""
80
+
81
+ # Construir query string
82
+ if operator.upper() == 'AND':
83
+ query_str = ' '.join(keywords)
84
+ else: # OR
85
+ query_str = '|'.join(keywords)
86
+
87
+ # Buscar usando where_document (full-text search do ChromaDB)
88
+ results = self.collection.query(
89
+ query_texts=[query_str],
90
+ n_results=top_k,
91
+ include=['documents', 'metadatas']
92
+ )
93
+
94
+ # Formatar resposta
95
+ formatted_results = []
96
+ for i in range(len(results['ids'][0])):
97
+ # Verificar quais keywords foram matchadas
98
+ doc = results['documents'][0][i].lower()
99
+ matched = [kw for kw in keywords if kw.lower() in doc]
100
+
101
+ formatted_results.append({
102
+ 'id': results['ids'][0][i],
103
+ 'ementa': results['documents'][0][i],
104
+ 'matched_keywords': matched
105
+ })
106
+
107
+ return {
108
+ 'cluster_id': self.config['cluster_id'],
109
+ 'results': formatted_results,
110
+ 'total_found': len(formatted_results)
111
+ }
112
+
113
+ def search_by_ids(
114
+ self,
115
+ ids: List[str],
116
+ return_embeddings: bool = False
117
+ ) -> Dict:
118
+ """Busca direta por ID(s)"""
119
+
120
+ # Buscar por IDs
121
+ try:
122
+ results = self.collection.get(
123
+ ids=ids,
124
+ include=['documents', 'metadatas', 'embeddings'] if return_embeddings
125
+ else ['documents', 'metadatas']
126
+ )
127
+ except Exception as e:
128
+ logger.error(f"Erro ao buscar IDs: {e}")
129
+ return {
130
+ 'cluster_id': self.config['cluster_id'],
131
+ 'results': [],
132
+ 'not_found': ids,
133
+ 'total_found': 0
134
+ }
135
+
136
+ # Formatar resposta
137
+ formatted_results = []
138
+ found_ids = set(results['ids'])
139
+
140
+ for i in range(len(results['ids'])):
141
+ result = {
142
+ 'id': results['ids'][i],
143
+ 'ementa': results['documents'][i]
144
+ }
145
+
146
+ if return_embeddings and 'embeddings' in results:
147
+ result['embedding'] = results['embeddings'][i]
148
+
149
+ formatted_results.append(result)
150
+
151
+ # IDs não encontrados
152
+ not_found = [id for id in ids if id not in found_ids]
153
+
154
+ return {
155
+ 'cluster_id': self.config['cluster_id'],
156
+ 'results': formatted_results,
157
+ 'not_found': not_found,
158
+ 'total_found': len(formatted_results)
159
+ }
160
+
161
+ def get_cluster_info(self) -> Dict:
162
+ """Retorna informações do cluster"""
163
+ import os
164
+
165
+ # Calcular tamanho do ChromaDB
166
+ db_path = self.config['chromadb_path']
167
+ total_size = 0
168
+ for dirpath, dirnames, filenames in os.walk(db_path):
169
+ for f in filenames:
170
+ fp = os.path.join(dirpath, f)
171
+ total_size += os.path.getsize(fp)
172
+
173
+ db_size_mb = total_size / (1024 * 1024)
174
+
175
+ return {
176
+ 'cluster_id': self.config['cluster_id'],
177
+ 'chunk_range': [self.config['chunk_start'], self.config['chunk_end']],
178
+ 'total_records': self.collection.count(),
179
+ 'embedding_model': self.config['embedding_model'],
180
+ 'embedding_dim': self.config['embedding_dim'],
181
+ 'campos_disponiveis': self.config['campos_filter'],
182
+ 'db_size_mb': round(db_size_mb, 2),
183
+ 'status': 'ready'
184
+ }
rag_builder.py ADDED
@@ -0,0 +1,105 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """
3
+ Constrói ChromaDB com embeddings a partir de JSONL filtrado
4
+ """
5
+ import json
6
+ import yaml
7
+ from pathlib import Path
8
+ import argparse
9
+ import chromadb
10
+ from sentence_transformers import SentenceTransformer
11
+ from tqdm import tqdm
12
+ import logging
13
+
14
+ logging.basicConfig(level=logging.INFO)
15
+ logger = logging.getLogger(__name__)
16
+
17
+ def build_chromadb(input_jsonl: str, config_path: str = 'config.yaml'):
18
+ """Constrói ChromaDB a partir de JSONL"""
19
+
20
+ # Carregar config
21
+ with open(config_path) as f:
22
+ config = yaml.safe_load(f)
23
+
24
+ logger.info("="*80)
25
+ logger.info("🔧 CONSTRUINDO CHROMADB")
26
+ logger.info("="*80)
27
+ logger.info(f"Cluster ID: {config['cluster_id']}")
28
+ logger.info(f"Chunks: {config['chunk_start']} - {config['chunk_end']}")
29
+ logger.info(f"Embedding Model: {config['embedding_model']}")
30
+
31
+ # Carregar modelo de embedding
32
+ logger.info("\n📥 Carregando modelo de embedding...")
33
+ model = SentenceTransformer(config['embedding_model'])
34
+ logger.info(f"✅ Modelo carregado (dim={config['embedding_dim']})")
35
+
36
+ # Inicializar ChromaDB
37
+ logger.info(f"\n💾 Inicializando ChromaDB em {config['chromadb_path']}...")
38
+ client = chromadb.PersistentClient(path=config['chromadb_path'])
39
+
40
+ # Criar/obter collection
41
+ try:
42
+ collection = client.get_collection(config['collection_name'])
43
+ logger.info(f"⚠️ Collection '{config['collection_name']}' já existe! Apagando...")
44
+ client.delete_collection(config['collection_name'])
45
+ except:
46
+ pass
47
+
48
+ collection = client.create_collection(
49
+ name=config['collection_name'],
50
+ metadata={
51
+ "cluster_id": config['cluster_id'],
52
+ "chunk_start": config['chunk_start'],
53
+ "chunk_end": config['chunk_end']
54
+ }
55
+ )
56
+ logger.info(f"✅ Collection criada")
57
+
58
+ # Carregar registros
59
+ logger.info(f"\n📖 Carregando registros de {input_jsonl}...")
60
+ records = []
61
+ with open(input_jsonl) as f:
62
+ for line in f:
63
+ records.append(json.loads(line))
64
+
65
+ total = len(records)
66
+ logger.info(f"✅ {total:,} registros carregados")
67
+
68
+ # Processar em batches
69
+ batch_size = config['embedding_batch_size']
70
+ logger.info(f"\n🚀 Gerando embeddings em batches de {batch_size}...")
71
+
72
+ for i in tqdm(range(0, total, batch_size), desc="Embedding"):
73
+ batch = records[i:i+batch_size]
74
+
75
+ # IDs
76
+ ids = [str(r['id']) for r in batch]
77
+
78
+ # Documentos (usar ementa para embedding)
79
+ documents = [r.get('ementa', '') for r in batch]
80
+
81
+ # Metadatas
82
+ metadatas = [{'id': r['id']} for r in batch]
83
+
84
+ # Gerar embeddings
85
+ embeddings = model.encode(documents, show_progress_bar=False).tolist()
86
+
87
+ # Adicionar ao ChromaDB
88
+ collection.add(
89
+ ids=ids,
90
+ embeddings=embeddings,
91
+ documents=documents,
92
+ metadatas=metadatas
93
+ )
94
+
95
+ logger.info(f"\n✅ ChromaDB construído com sucesso!")
96
+ logger.info(f"📊 Total de registros: {collection.count():,}")
97
+ logger.info("="*80)
98
+
99
+ if __name__ == "__main__":
100
+ parser = argparse.ArgumentParser()
101
+ parser.add_argument('--input', required=True, help='JSONL filtrado')
102
+ parser.add_argument('--config', default='config.yaml')
103
+ args = parser.parse_args()
104
+
105
+ build_chromadb(args.input, args.config)