← Memory
ai-seo-model.md
# AI SEO Model - Estado del Proyecto
> **Objetivo**: Modelo IA fine-tuneado con Master SEO Avanzado Webpositer Academy + fuentes externas
> **Ultima actualizacion**: 07/04/2026
> **VPS**: Hetzner Stargate (`204.168.170.41`) — `/home/ubuntu/proyectos-cloud/ai-seo-model/`
## Estado Actual (07/04/2026)
### FASE 1: Transcripcion master - COMPLETADA
- 108 transcripciones en `transcripts/` (faster-whisper large-v3-turbo, RunPod RTX 4090)
### FASE 2: Transcripcion externa - 35 COMPLETADAS
- 35 videos transcritos via BEE Transcription (subtitulos YouTube)
- API: `x-api-key: btk_fae89fdcf5475dfcfc7e6b837708afa4e2958937` en transcrip.beepeek.com
- Para transcribir mas: `POST /ai/transcribe` con `{"urls":["URL"],"language":"es"}`
- Para recoger: `GET /ai/transcripts` y `GET /ai/transcripts/{id}` (campo `transcript`)
### FASE 3: Chunking - COMPLETADO (489 chunks)
- 489 chunks en `transcripts_chunks/` (446 master + 43 EXT_ nuevos de API Transcrip)
- `fetch_transcrip_api.py` — baja transcripciones de API Transcrip y las chunkea
- `split_transcripts.py` — chunker original para transcripciones master
### FASE 4: Dataset - COMPLETADO (20,666 QA → 20,078 clean)
- **20,078 QA pairs clean** en `dataset/dataset_clean.jsonl`
- 20,666 originales en `dataset/seo_master_dataset.jsonl` (~24MB)
- `validate_dataset.py` ejecutado: limpio duplicados y QA de baja calidad
### FASE 5: Fine-tune SFT - COMPLETADO
- **Modelo base**: Qwen3-8B
- **Tecnica**: QLoRA (4-bit) con Unsloth en RunPod RTX 3090 Ti ($0.27/hr)
- **Hiperparametros**: r=64, alpha=64, epochs=2, batch=2, grad_accum=8, lr=2e-4, weight_decay=0.01, warmup_ratio=0.03, max_seq_length=2048
- **Training**: 2510 steps, loss 1.086 → ~0.70, ~7 horas
- **GGUF export**: Q4_K_M (4.7GB) + Q8_0 (8.2GB) via llama.cpp manual (Unsloth falló por disco)
- **Modelo Ollama**: `seo-master-v2` creado y funcionando en VPS
- **Modelfile**: `Modelfile_v2` (apunta a `qwen3-seo-q4km.gguf`)
### FASE 5b: VectorDB SEO - COMPLETADA
- **23,938 chunks** en ChromaDB (`vectordb/`)
- Re-chunking: 800 chars con 150 overlap desde transcripts/ y transcripts_external/
- Embeddings: `nomic-embed-text` via Ollama local
- Chunks guardados en `chunks_vectordb/`
- `build_vectordb.py` — script de construccion
- **Test**: query "como hacer keyword research" devuelve resultados relevantes
### FASE 6: DPO - SCRIPTS LISTOS, PENDIENTE (post-SFT)
- **Mejora esperada**: ~5% sobre SFT con ~500 pares de preferencias sinteticas
- `generate_dpo_pairs.py` — genera pares (Ollama genera 4 respuestas, Gemini puntua)
- `finetune_dpo.py` — entrena DPO con Unsloth (lr=5e-6, beta=0.1, 3 epochs)
- Ejecutar en VPS (generate) + RunPod (finetune)
### FASE 7: Evaluacion - BENCHMARK COMPLETADO
- `benchmark_seo.json` — 120 preguntas SEO en 3 niveles (factual/applied/expert)
- `evaluate_model.py` — evaluacion automatica (checklist + LLM-as-Judge via Gemini)
- **Resultados**:
- `seo-master` (v1, pre-fine-tune): **0.83/5**
- `seo-master-v2` (SFT fine-tuned): **1.28/5** (+54%)
- Mejor categoria: IA y futuro SEO (2.38/5)
- Peor categoria: Penalizaciones (0.21/5)
- **Problema principal**: respuestas demasiado cortas, no cubren checklist items
- Resultados en `eval_results/eval_results_seo-master-v2_20260407_092908.json`
## Proximos Pasos
1. **DPO training** — ensenar al modelo a dar respuestas mas completas
2. **RAG pipeline** — combinar VectorDB + modelo para contexto extra
3. **Mas contenido** — transcribir mas videos SEO via BEE Transcription API
## Pipeline Completo (orden de ejecucion)
1. ~~`validate_dataset.py`~~ DONE
2. ~~Evaluar modelo base~~ DONE (0.83/5)
3. ~~RunPod fine-tune SFT~~ DONE
4. ~~Export GGUF + crear modelo Ollama~~ DONE (`seo-master-v2`)
5. ~~Evaluar SFT~~ DONE (1.28/5)
6. ~~VectorDB~~ DONE (23,938 chunks)
7. `generate_dpo_pairs.py --samples 500` (en VPS con Ollama) — PENDIENTE
8. RunPod: `python finetune_dpo.py --model ./output --export-gguf` — PENDIENTE
9. Evaluar DPO: `evaluate_model.py --model seo-master-dpo --compare eval_sft.json` — PENDIENTE
## Credenciales
- **Gemini**: `AIzaSyB84Vfxlh12JY-Ni_byuoCnFFlQeWZjoEo`
- **RunPod**: `rpa_EDHV6REIL3EE123Y1VG1G5IZLC64Q5KQRM1LIRMW11bh4q` (configurar SSH keys en web)
- **RunPod S3**: Access `user_3BrbFmqW46H873DffKXIljWOMB4` / Secret `rps_XYNXVCHAUI209P4FDTQF85NQ3J7ILGPN9GINXJWX1qh4jm`
- **RunPod Network Volume**: `impossible_sapphire_ferret` (30GB, EU-RO-1, $2.10/mes)
- **BEE Transcription**: `btk_fae89fdcf5475dfcfc7e6b837708afa4e2958937`
## Archivos VPS: `/home/ubuntu/proyectos-cloud/ai-seo-model/`
- `transcripts/` (108), `transcripts_chunks/` (489), `transcripts_external/` (35)
- `chunks_vectordb/` (23,938 chunks re-chunked a 800 chars)
- `vectordb/` (ChromaDB con nomic-embed-text embeddings)
- `dataset/seo_master_dataset.jsonl` (20,666 QA) + `dataset/dataset_clean.jsonl` (20,078 QA)
- `qwen3-seo-q4km.gguf` (4.7GB, Q4_K_M, modelo fine-tuneado final)
- `generate_dataset.py`, `fetch_transcrip_api.py`, `process_new_chunks.sh`
- `finetune.py`, `finetune_dpo.py`, `generate_dpo_pairs.py`, `requirements_finetune.txt`
- `evaluate_model.py`, `benchmark_seo.json`, `validate_dataset.py`
- `build_vectordb.py` (constructor VectorDB)
- `Modelfile_v2` (config Ollama seo-master-v2)
- `eval_results/` (JSONs con resultados de evaluaciones)
- `chat/app.py` + `chat/index.html` (interfaz Ollama)
## Lessons Learned
- RunPod: `Authorization: Bearer` NO `api-key`. SSH keys configurar en web
- RunPod: pods con volume no provisionan. Usar `containerDiskInGb` sin volume
- RunPod: 50GB disco insuficiente para GGUF export via Unsloth (necesita BF16 intermedio 16GB + safetensors 16GB)
- GGUF export manual: convertir HF→Q8_0 directo (`--outtype q8_0`) evita BF16 intermedio
- llama-quantize: necesita `--allow-requantize` para Q8_0→Q4_K_M
- SSH heredocs con comillas: escribir .py local y SCP, no inline
- API Transcrip: campo es `transcript` no `text` en detalle
- QA_PER_PASS=8 da mejor calidad que 15
- nohup: usar `python3 -u` para logs en tiempo real. `yes |` para evitar prompts interactivos
- Modelo Gemini para generar dataset: flash-lite es suficiente (rapido y barato)
- lora_alpha=rank (64) funciona mejor que 2*rank en practica
- Con 18K+ samples, 2 epochs suficiente (3 = riesgo overfit)
- Unsloth `save_pretrained_gguf` pide input() en nohup → EOFError. Instalar deps manualmente
- VPS SSH key: `~/.ssh/id_ed25519` (ubuntu@Stargate) — inyectar en pod manualmente