feat: Add Streamlit dashboard with Blueprint compliance (v2.1.0)

Dashboard Features:
- 8 navigation sections: Overview, Outcomes, Poor CX, FCR, Churn, Agent, Call Explorer, Export
- Beyond Brand Identity styling (colors #6D84E3, Outfit font)
- RCA Sankey diagram (Driver → Outcome → Churn Risk flow)
- Correlation heatmaps (driver co-occurrence, driver-outcome)
- Outcome Deep Dive (root causes, correlation, duration analysis)
- Export functionality (Excel, HTML, JSON)

Blueprint Compliance:
- FCR: 4 categories (Primera Llamada/Rellamada × Sin/Con Riesgo de Fuga)
- Churn: Binary view (Sin Riesgo de Fuga / En Riesgo de Fuga)
- Agent: Talento Para Replicar / Oportunidades de Mejora
- Fixed FCR rate calculation (only FIRST_CALL counts as success)

Technical:
- Streamlit + Plotly for interactive visualizations
- Light theme configuration (.streamlit/config.toml)
- Fixed Plotly colorbar titlefont deprecation

Documentation:
- Updated PROJECT_CONTEXT.md, TODO.md, CHANGELOG.md
- Added 4 new technical decisions (TD-014 to TD-017)
- Created TROUBLESHOOTING.md with 10 common issues

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
sujucu70
2026-01-19 16:27:30 +01:00
commit 75e7b9da3d
110 changed files with 28247 additions and 0 deletions

317
docs/API_REFERENCE.md Normal file
View File

@@ -0,0 +1,317 @@
# API_REFERENCE.md
> Documentación de funciones públicas principales
---
## Transcription Module
### `AssemblyAITranscriber`
```python
from src.transcription import AssemblyAITranscriber
class AssemblyAITranscriber(Transcriber):
def __init__(self, api_key: str, language: str = "es"):
"""
Initialize AssemblyAI transcriber.
Args:
api_key: AssemblyAI API key
language: Language code (default: "es" for Spanish)
"""
async def transcribe(self, audio_path: Path) -> Transcript:
"""
Transcribe a single audio file.
Args:
audio_path: Path to MP3/WAV file
Returns:
Transcript with speaker diarization
Raises:
TranscriptionError: If API fails
"""
async def transcribe_batch(
self,
audio_paths: list[Path],
max_concurrent: int = 5
) -> list[Transcript]:
"""
Transcribe multiple audio files in parallel.
Args:
audio_paths: List of paths to audio files
max_concurrent: Max parallel requests
Returns:
List of Transcripts
"""
```
**Example:**
```python
transcriber = AssemblyAITranscriber(api_key=os.getenv("ASSEMBLYAI_API_KEY"))
transcript = await transcriber.transcribe(Path("call_001.mp3"))
print(f"Duration: {transcript.metadata.audio_duration_sec}s")
print(f"Turns: {len(transcript.turns)}")
```
---
## Inference Module
### `CallAnalyzer`
```python
from src.inference import CallAnalyzer, AnalyzerConfig
class CallAnalyzer:
def __init__(self, config: AnalyzerConfig | None = None):
"""
Initialize call analyzer.
Args:
config: Analyzer configuration (optional)
"""
async def analyze(self, transcript: Transcript) -> CallAnalysis:
"""
Analyze a single transcript.
Args:
transcript: Transcript to analyze
Returns:
CallAnalysis with RCA labels and evidence
"""
async def analyze_batch(
self,
transcripts: list[Transcript],
batch_id: str,
progress_callback: Callable | None = None
) -> list[CallAnalysis]:
"""
Analyze multiple transcripts in parallel.
Args:
transcripts: List of transcripts
batch_id: Batch identifier
progress_callback: Optional progress callback
Returns:
List of CallAnalysis results
"""
```
**Example:**
```python
config = AnalyzerConfig(
model="gpt-4o-mini",
use_compression=True,
max_concurrent=5,
)
analyzer = CallAnalyzer(config)
analyses = await analyzer.analyze_batch(
transcripts=transcripts,
batch_id="batch_001",
progress_callback=lambda current, total: print(f"{current}/{total}")
)
```
---
## Aggregation Module
### `aggregate_batch`
```python
from src.aggregation import aggregate_batch
def aggregate_batch(
batch_id: str,
analyses: list[CallAnalysis]
) -> BatchAggregation:
"""
Aggregate call analyses into statistics and RCA tree.
Args:
batch_id: Batch identifier
analyses: List of call analyses
Returns:
BatchAggregation with frequencies, severities, and RCA tree
"""
```
**Example:**
```python
aggregation = aggregate_batch("batch_001", analyses)
print(f"Lost sales drivers: {len(aggregation.lost_sales_frequencies)}")
print(f"Top driver: {aggregation.rca_tree.top_lost_sales_drivers[0]}")
```
---
## Pipeline Module
### `CXInsightsPipeline`
```python
from src.pipeline import CXInsightsPipeline, PipelineConfig
class CXInsightsPipeline:
def __init__(
self,
config: PipelineConfig | None = None,
progress_callback: Callable | None = None
):
"""
Initialize pipeline.
Args:
config: Pipeline configuration
progress_callback: Optional progress callback
"""
def run(
self,
batch_id: str,
audio_files: list[Path] | None = None,
transcripts: list[Transcript] | None = None,
resume: bool = True
) -> BatchAggregation:
"""
Run full pipeline.
Args:
batch_id: Batch identifier
audio_files: Optional list of audio files
transcripts: Optional pre-loaded transcripts
resume: Whether to resume from checkpoint
Returns:
BatchAggregation with full results
"""
```
**Example:**
```python
config = PipelineConfig(
input_dir=Path("data/audio"),
output_dir=Path("data/output"),
export_formats=["json", "excel", "pdf"],
)
pipeline = CXInsightsPipeline(config)
result = pipeline.run(
batch_id="batch_001",
audio_files=list(Path("data/audio").glob("*.mp3")),
)
```
---
## Export Module
### `export_to_json`
```python
from src.exports import export_to_json
def export_to_json(
batch_id: str,
aggregation: BatchAggregation,
analyses: list[CallAnalysis],
output_dir: Path
) -> Path:
"""
Export results to JSON files.
Args:
batch_id: Batch identifier
aggregation: Aggregated results
analyses: Individual call analyses
output_dir: Output directory
Returns:
Path to summary.json
"""
```
### `export_to_excel`
```python
from src.exports import export_to_excel
def export_to_excel(
batch_id: str,
aggregation: BatchAggregation,
analyses: list[CallAnalysis],
output_dir: Path
) -> Path:
"""
Export results to Excel workbook.
Creates sheets:
- Summary
- Lost Sales Drivers
- Poor CX Drivers
- Call Details
- Emergent Patterns
Returns:
Path to .xlsx file
"""
```
### `export_to_pdf`
```python
from src.exports import export_to_pdf
def export_to_pdf(
batch_id: str,
aggregation: BatchAggregation,
output_dir: Path
) -> Path:
"""
Export executive report to PDF/HTML.
Falls back to HTML if weasyprint not installed.
Returns:
Path to .pdf or .html file
"""
```
---
## Compression Module
### `TranscriptCompressor`
```python
from src.compression import TranscriptCompressor
class TranscriptCompressor:
def compress(self, transcript: Transcript) -> CompressedTranscript:
"""
Compress transcript by extracting key information.
Args:
transcript: Full transcript
Returns:
CompressedTranscript with >60% token reduction
"""
```
---
**Última actualización**: 2026-01-19

839
docs/ARCHITECTURE.md Normal file
View File

@@ -0,0 +1,839 @@
# CXInsights - Arquitectura del Sistema
## Visión del Producto
CXInsights transforma 5,000-20,000 llamadas de contact center en **RCA Trees ejecutivos** que identifican las causas raíz de:
- **Lost Sales**: Oportunidades de venta perdidas
- **Poor CX**: Experiencias de cliente deficientes
---
## Principios de Diseño Críticos
### 1. Separación Estricta: Observed vs Inferred
**Todo dato debe estar claramente clasificado como HECHO o INFERENCIA.**
```
┌─────────────────────────────────────────────────────────────────────────────┐
│ OBSERVED vs INFERRED │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ OBSERVED (Hechos medibles) INFERRED (Opinión del modelo) │
│ ───────────────────────── ────────────────────────────── │
│ ✓ Duración de la llamada ✗ Sentimiento del cliente │
│ ✓ Número de transfers ✗ Motivo de pérdida de venta │
│ ✓ Tiempo en hold (medido) ✗ Calidad del agente │
│ ✓ Silencios detectados (>N seg) ✗ Clasificación de intent │
│ ✓ Texto transcrito ✗ Resumen de la llamada │
│ ✓ Quién habló cuánto (%) ✗ Outcome (sale/no_sale/resolved) │
│ ✓ Timestamp de eventos ✗ Drivers de RCA │
│ │
│ Regla: Si el LLM lo genera → es INFERRED │
│ Si viene del audio/STT → es OBSERVED │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
```
**Impacto**: RCA defendible ante stakeholders. Auditoría clara. Separación de hechos y opinión.
### 2. Evidencia Obligatoria por Driver
**Regla dura: Sin `evidence_spans` → el driver NO EXISTE**
```json
{
"rca_code": "LONG_HOLD",
"confidence": 0.77,
"evidence_spans": [
{"start": "02:14", "end": "03:52", "text": "[silence - hold]", "source": "observed"}
]
}
```
Un driver sin evidencia timestamped será rechazado por validación.
### 3. Versionado de Prompts + Schema
**Todo output incluye metadatos de versión para reproducibilidad.**
```json
{
"_meta": {
"schema_version": "1.0.0",
"prompt_version": "call_analysis_v1.2",
"model": "gpt-4o-mini",
"model_version": "2024-07-18",
"processed_at": "2024-01-15T10:30:00Z"
}
}
```
### 4. Taxonomía RCA Cerrada + Canal de Emergentes
**Solo códigos del enum. Única excepción controlada: `OTHER_EMERGENT`**
```json
{
"rca_code": "OTHER_EMERGENT",
"proposed_label": "agent_rushed_due_to_queue_pressure",
"evidence_spans": [...]
}
```
Los `OTHER_EMERGENT` se revisan manualmente y se promueven a taxonomía oficial en siguiente versión.
### 5. Eventos de Journey como Estructura
**No texto libre. Objetos tipados con timestamp.**
```json
{
"journey_events": [
{"type": "CALL_START", "t": "00:00"},
{"type": "GREETING", "t": "00:03"},
{"type": "TRANSFER", "t": "01:42"},
{"type": "HOLD_START", "t": "02:10"},
{"type": "HOLD_END", "t": "03:40"},
{"type": "NEGATIVE_SENTIMENT", "t": "04:05", "source": "inferred"},
{"type": "RESOLUTION_ATTEMPT", "t": "05:20"},
{"type": "CALL_END", "t": "06:15"}
]
}
```
### 6. Adaptador de STT (Sin Lock-in)
**Interfaz abstracta. El proveedor es intercambiable.**
```
┌─────────────────────────────────────────────────────────────────────────────┐
│ TRANSCRIBER INTERFACE │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ Interface: Transcriber │
│ ├─ transcribe(audio_path) → TranscriptContract │
│ └─ transcribe_batch(paths) → List[TranscriptContract] │
│ │
│ Implementations: │
│ ├─ AssemblyAITranscriber (default) │
│ ├─ WhisperTranscriber (local/offline) │
│ ├─ GoogleSTTTranscriber (alternative) │
│ └─ AWSTranscribeTranscriber (alternative) │
│ │
│ TranscriptContract (output normalizado): │
│ ├─ call_id: str │
│ ├─ utterances: List[Utterance] │
│ ├─ observed_events: List[ObservedEvent] │
│ └─ metadata: TranscriptMetadata │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
```
---
## Diagrama de Flujo End-to-End
```
┌─────────────────────────────────────────────────────────────────────────────────┐
│ CXINSIGHTS PIPELINE │
└─────────────────────────────────────────────────────────────────────────────────┘
INPUT PROCESSING OUTPUT
───── ────────── ──────
┌──────────────┐
│ 5K-20K │
│ Audio Files │
│ (.mp3/.wav) │
└──────┬───────┘
╔══════════════════════════════════════════════════════════════════════════════╗
║ MODULE 1: BATCH TRANSCRIPTION (via Transcriber Interface) ║
║ ┌────────────────────────────────────────────────────────────────────────┐ ║
║ │ Transcriber Adapter (pluggable: AssemblyAI, Whisper, Google, AWS) │ ║
║ │ ├─ Parallel uploads (configurable concurrency) │ ║
║ │ ├─ Spanish language model │ ║
║ │ ├─ Speaker diarization (Agent vs Customer) │ ║
║ │ └─ Output: TranscriptContract (normalized) │ ║
║ └────────────────────────────────────────────────────────────────────────┘ ║
║ │ ║
║ ▼ ║
║ 📁 data/transcripts/{call_id}.json (TranscriptContract) ║
╚══════════════════════════════════════════════════════════════════════════════╝
╔══════════════════════════════════════════════════════════════════════════════╗
║ MODULE 2: FEATURE EXTRACTION (OBSERVED ONLY) ║
║ ┌────────────────────────────────────────────────────────────────────────┐ ║
║ │ Extrae SOLO hechos medibles del transcript: │ ║
║ │ ├─ Duración total │ ║
║ │ ├─ % habla agente vs cliente (ratio) │ ║
║ │ ├─ Silencios > 5s (timestamp + duración) │ ║
║ │ ├─ Interrupciones detectadas │ ║
║ │ ├─ Transfers (si detectables por audio/metadata) │ ║
║ │ └─ Palabras clave literales (sin interpretación) │ ║
║ │ │ ║
║ │ Output: observed_features (100% verificable) │ ║
║ └────────────────────────────────────────────────────────────────────────┘ ║
║ │ ║
║ ▼ ║
║ 📁 data/transcripts/{call_id}_features.json ║
╚══════════════════════════════════════════════════════════════════════════════╝
╔══════════════════════════════════════════════════════════════════════════════╗
║ MODULE 3: PER-CALL INFERENCE (MAP) - Separación Observed/Inferred ║
║ ┌────────────────────────────────────────────────────────────────────────┐ ║
║ │ LLM Analysis (GPT-4o-mini / Claude 3.5 Sonnet) │ ║
║ │ │ ║
║ │ Input al LLM: │ ║
║ │ ├─ Transcript comprimido │ ║
║ │ ├─ observed_features (contexto factual) │ ║
║ │ └─ Taxonomía RCA (enum cerrado) │ ║
║ │ │ ║
║ │ Output estructurado: │ ║
║ │ ├─ OBSERVED (pass-through, no inferido): │ ║
║ │ │ └─ observed_outcome (si explícito en audio: "venta cerrada") │ ║
║ │ │ │ ║
║ │ ├─ INFERRED (con confidence + evidence obligatoria): │ ║
║ │ │ ├─ intent: {code, confidence, evidence_spans[]} │ ║
║ │ │ ├─ outcome: {code, confidence, evidence_spans[]} │ ║
║ │ │ ├─ sentiment: {score, confidence, evidence_spans[]} │ ║
║ │ │ ├─ lost_sale_driver: {rca_code, confidence, evidence_spans[]} │ ║
║ │ │ ├─ poor_cx_driver: {rca_code, confidence, evidence_spans[]} │ ║
║ │ │ └─ agent_quality: {scores{}, confidence, evidence_spans[]} │ ║
║ │ │ │ ║
║ │ └─ JOURNEY_EVENTS (structured timeline): │ ║
║ │ └─ events[]: {type, t, source: observed|inferred} │ ║
║ └────────────────────────────────────────────────────────────────────────┘ ║
║ │ ║
║ ▼ ║
║ 📁 data/processed/{call_id}_analysis.json ║
╚══════════════════════════════════════════════════════════════════════════════╝
╔══════════════════════════════════════════════════════════════════════════════╗
║ MODULE 4: VALIDATION & QUALITY GATE ║
║ ┌────────────────────────────────────────────────────────────────────────┐ ║
║ │ Validación estricta antes de agregar: │ ║
║ │ ├─ ¿Tiene evidence_spans todo driver? → Si no, RECHAZAR driver │ ║
║ │ ├─ ¿rca_code está en taxonomía? → Si no, marcar OTHER_EMERGENT │ ║
║ │ ├─ ¿Confidence > umbral? → Si no, marcar low_confidence │ ║
║ │ ├─ ¿Schema version match? → Si no, ERROR │ ║
║ │ └─ ¿Journey events tienen timestamps válidos? │ ║
║ │ │ ║
║ │ Output: validated_analysis.json + validation_report.json │ ║
║ └────────────────────────────────────────────────────────────────────────┘ ║
╚══════════════════════════════════════════════════════════════════════════════╝
╔══════════════════════════════════════════════════════════════════════════════╗
║ MODULE 5: AGGREGATION (REDUCE) ║
║ ┌────────────────────────────────────────────────────────────────────────┐ ║
║ │ Consolidación estadística (solo datos validados): │ ║
║ │ ├─ Conteo por rca_code (taxonomía cerrada) │ ║
║ │ ├─ Distribuciones con confidence_weighted │ ║
║ │ ├─ Separación: high_confidence vs low_confidence │ ║
║ │ ├─ Lista de OTHER_EMERGENT para revisión manual │ ║
║ │ ├─ Cross-tabs (intent × outcome × driver) │ ║
║ │ └─ Correlaciones observed_features ↔ inferred_outcomes │ ║
║ └────────────────────────────────────────────────────────────────────────┘ ║
║ │ ║
║ ▼ ║
║ 📁 data/outputs/aggregated_stats.json ║
║ 📁 data/outputs/emergent_drivers_review.json ║
╚══════════════════════════════════════════════════════════════════════════════╝
╔══════════════════════════════════════════════════════════════════════════════╗
║ MODULE 6: RCA TREE GENERATION ║
║ ┌────────────────────────────────────────────────────────────────────────┐ ║
║ │ Construcción de árboles (determinístico, no LLM): │ ║
║ │ │ ║
║ │ 🔴 LOST SALES RCA TREE │ ║
║ │ └─ Lost Sales (N=1,250, 25%) │ ║
║ │ ├─ PRICING (45%, avg_conf=0.82) │ ║
║ │ │ ├─ TOO_EXPENSIVE (30%, n=375) │ ║
║ │ │ │ └─ evidence_samples: ["...", "..."] │ ║
║ │ │ └─ COMPETITOR_CHEAPER (15%, n=187) │ ║
║ │ │ └─ evidence_samples: ["...", "..."] │ ║
║ │ └─ ... │ ║
║ │ │ ║
║ │ Cada nodo incluye: │ ║
║ │ ├─ rca_code (del enum) │ ║
║ │ ├─ count, pct │ ║
║ │ ├─ avg_confidence │ ║
║ │ ├─ evidence_samples[] (verbatims representativos) │ ║
║ │ └─ call_ids[] (para drill-down) │ ║
║ └────────────────────────────────────────────────────────────────────────┘ ║
║ │ ║
║ ▼ ║
║ 📁 data/outputs/rca_lost_sales.json ║
║ 📁 data/outputs/rca_poor_cx.json ║
╚══════════════════════════════════════════════════════════════════════════════╝
╔══════════════════════════════════════════════════════════════════════════════╗
║ MODULE 7: EXECUTIVE REPORTING ║
║ ┌────────────────────────────────────────────────────────────────────────┐ ║
║ │ Formatos de salida: │ ║
║ │ ├─ 📊 Streamlit Dashboard (con filtro observed/inferred) │ ║
║ │ ├─ 📑 PDF Executive Summary (incluye confidence disclaimers) │ ║
║ │ ├─ 📈 Excel con drill-down (link a evidence_spans) │ ║
║ │ └─ 🖼️ PNG de árboles RCA (con leyenda de confidence) │ ║
║ └────────────────────────────────────────────────────────────────────────┘ ║
╚══════════════════════════════════════════════════════════════════════════════╝
```
---
## Modelo de Datos (Actualizado)
### TranscriptContract (Module 1 output)
```json
{
"_meta": {
"schema_version": "1.0.0",
"transcriber": "assemblyai",
"transcriber_version": "2024-07",
"processed_at": "2024-01-15T10:30:00Z"
},
"call_id": "c001",
"observed": {
"duration_seconds": 245,
"language_detected": "es",
"speakers": [
{"id": "A", "label": "agent", "talk_time_pct": 0.45},
{"id": "B", "label": "customer", "talk_time_pct": 0.55}
],
"utterances": [
{
"speaker": "A",
"text": "Buenos días, gracias por llamar a Movistar...",
"start_ms": 0,
"end_ms": 3500
}
],
"detected_events": [
{"type": "SILENCE", "start_ms": 72000, "end_ms": 80000, "duration_ms": 8000},
{"type": "CROSSTALK", "start_ms": 45000, "end_ms": 46500}
]
}
}
```
### CallAnalysis (Module 3 output) - CON SEPARACIÓN OBSERVED/INFERRED
```json
{
"_meta": {
"schema_version": "1.0.0",
"prompt_version": "call_analysis_v1.2",
"model": "gpt-4o-mini",
"model_version": "2024-07-18",
"processed_at": "2024-01-15T10:35:00Z"
},
"call_id": "c001",
"observed": {
"duration_seconds": 245,
"agent_talk_pct": 0.45,
"customer_talk_pct": 0.55,
"silence_total_seconds": 38,
"silence_events": [
{"start": "01:12", "end": "01:20", "duration_s": 8}
],
"transfer_count": 0,
"hold_events": [
{"start": "02:14", "end": "03:52", "duration_s": 98}
],
"explicit_outcome": null
},
"inferred": {
"intent": {
"code": "SALES_INQUIRY",
"confidence": 0.91,
"evidence_spans": [
{"start": "00:15", "end": "00:28", "text": "Quería información sobre la fibra de 600 megas"}
]
},
"outcome": {
"code": "NO_SALE",
"confidence": 0.85,
"evidence_spans": [
{"start": "05:40", "end": "05:52", "text": "Lo voy a pensar y ya les llamo yo"}
]
},
"sentiment": {
"overall_score": -0.3,
"evolution": [
{"segment": "start", "score": 0.2},
{"segment": "middle", "score": -0.1},
{"segment": "end", "score": -0.6}
],
"confidence": 0.78,
"evidence_spans": [
{"start": "04:10", "end": "04:25", "text": "Es que me parece carísimo, la verdad"}
]
},
"lost_sale_driver": {
"rca_code": "PRICING_TOO_EXPENSIVE",
"confidence": 0.83,
"evidence_spans": [
{"start": "03:55", "end": "04:08", "text": "59 euros al mes es mucho dinero"},
{"start": "04:10", "end": "04:25", "text": "Es que me parece carísimo, la verdad"}
],
"secondary_driver": {
"rca_code": "COMPETITOR_CHEAPER",
"confidence": 0.71,
"evidence_spans": [
{"start": "04:30", "end": "04:45", "text": "En Vodafone me lo dejan por 45"}
]
}
},
"poor_cx_driver": {
"rca_code": "LONG_HOLD",
"confidence": 0.77,
"evidence_spans": [
{"start": "02:14", "end": "03:52", "text": "[hold - 98 segundos]", "source": "observed"}
]
},
"agent_quality": {
"overall_score": 6,
"dimensions": {
"empathy": 7,
"product_knowledge": 8,
"objection_handling": 4,
"closing_skills": 5
},
"confidence": 0.72,
"evidence_spans": [
{"start": "04:50", "end": "05:10", "text": "Bueno, es el precio que tenemos...", "dimension": "objection_handling"}
]
},
"summary": "Cliente interesado en fibra 600Mb abandona por precio (59€) comparando con Vodafone (45€). Hold largo de 98s. Agente no rebatió objeción de precio."
},
"journey_events": [
{"type": "CALL_START", "t": "00:00", "source": "observed"},
{"type": "GREETING", "t": "00:03", "source": "observed"},
{"type": "INTENT_STATED", "t": "00:15", "source": "inferred"},
{"type": "HOLD_START", "t": "02:14", "source": "observed"},
{"type": "HOLD_END", "t": "03:52", "source": "observed"},
{"type": "PRICE_OBJECTION", "t": "03:55", "source": "inferred"},
{"type": "COMPETITOR_MENTION", "t": "04:30", "source": "inferred"},
{"type": "NEGATIVE_SENTIMENT_PEAK", "t": "04:10", "source": "inferred"},
{"type": "SOFT_DECLINE", "t": "05:40", "source": "inferred"},
{"type": "CALL_END", "t": "06:07", "source": "observed"}
]
}
```
### RCA Tree Node (Module 6 output)
```json
{
"_meta": {
"schema_version": "1.0.0",
"generated_at": "2024-01-15T11:00:00Z",
"taxonomy_version": "rca_taxonomy_v1.0",
"total_calls_analyzed": 5000,
"confidence_threshold_used": 0.70
},
"tree_type": "lost_sales",
"total_affected": {
"count": 1250,
"pct_of_total": 25.0
},
"root": {
"label": "Lost Sales",
"children": [
{
"rca_code": "PRICING",
"label": "Pricing Issues",
"count": 562,
"pct_of_parent": 45.0,
"avg_confidence": 0.82,
"children": [
{
"rca_code": "PRICING_TOO_EXPENSIVE",
"label": "Too Expensive",
"count": 375,
"pct_of_parent": 66.7,
"avg_confidence": 0.84,
"evidence_samples": [
{"call_id": "c001", "text": "59 euros al mes es mucho dinero", "t": "03:55"},
{"call_id": "c042", "text": "No puedo pagar tanto", "t": "02:30"}
],
"call_ids": ["c001", "c042", "c078", "..."]
},
{
"rca_code": "PRICING_COMPETITOR_CHEAPER",
"label": "Competitor Cheaper",
"count": 187,
"pct_of_parent": 33.3,
"avg_confidence": 0.79,
"evidence_samples": [
{"call_id": "c001", "text": "En Vodafone me lo dejan por 45", "t": "04:30"}
],
"call_ids": ["c001", "c015", "..."]
}
]
}
]
},
"other_emergent": [
{
"proposed_label": "agent_rushed_due_to_queue_pressure",
"count": 23,
"evidence_samples": [
{"call_id": "c234", "text": "Perdona que voy con prisa que hay cola", "t": "01:15"}
],
"recommendation": "Considerar añadir a taxonomía v1.1"
}
]
}
```
---
## Taxonomía RCA (config/rca_taxonomy.yaml)
```yaml
# config/rca_taxonomy.yaml
# Version: 1.0.0
# Last updated: 2024-01-15
_meta:
version: "1.0.0"
author: "CXInsights Team"
description: "Closed taxonomy for RCA classification. Only these codes are valid."
# ============================================================================
# INTENTS (Motivo de la llamada)
# ============================================================================
intents:
- SALES_INQUIRY # Consulta de venta
- SALES_UPGRADE # Upgrade de producto
- SUPPORT_TECHNICAL # Soporte técnico
- SUPPORT_BILLING # Consulta de facturación
- COMPLAINT # Queja/reclamación
- CANCELLATION # Solicitud de baja
- GENERAL_INQUIRY # Consulta general
- OTHER_EMERGENT # Captura de nuevos intents
# ============================================================================
# OUTCOMES (Resultado de la llamada)
# ============================================================================
outcomes:
- SALE_COMPLETED # Venta cerrada
- SALE_LOST # Venta perdida
- ISSUE_RESOLVED # Problema resuelto
- ISSUE_UNRESOLVED # Problema no resuelto
- ESCALATED # Escalado a supervisor/otro depto
- CALLBACK_SCHEDULED # Callback programado
- OTHER_EMERGENT
# ============================================================================
# LOST SALE DRIVERS (Por qué se perdió la venta)
# ============================================================================
lost_sale_drivers:
# Pricing cluster
PRICING:
- PRICING_TOO_EXPENSIVE # "Es muy caro"
- PRICING_COMPETITOR_CHEAPER # "En X me lo dan más barato"
- PRICING_NO_DISCOUNT # No se ofreció descuento
- PRICING_PAYMENT_TERMS # Condiciones de pago no aceptables
# Product fit cluster
PRODUCT_FIT:
- PRODUCT_FEATURE_MISSING # Falta funcionalidad requerida
- PRODUCT_WRONG_OFFERED # Se ofreció producto equivocado
- PRODUCT_COVERAGE_AREA # Sin cobertura en su zona
- PRODUCT_TECH_REQUIREMENTS # No cumple requisitos técnicos
# Process cluster
PROCESS:
- PROCESS_TOO_COMPLEX # Proceso demasiado complicado
- PROCESS_DOCUMENTATION # Requiere mucha documentación
- PROCESS_ACTIVATION_TIME # Tiempo de activación largo
- PROCESS_CONTRACT_TERMS # Términos de contrato no aceptables
# Agent cluster
AGENT:
- AGENT_COULDNT_CLOSE # No cerró la venta
- AGENT_POOR_OBJECTION # Mal manejo de objeciones
- AGENT_LACK_URGENCY # No creó urgencia
- AGENT_MISSED_UPSELL # Perdió oportunidad de upsell
# Timing cluster
TIMING:
- TIMING_NOT_READY # Cliente no está listo
- TIMING_COMPARING # Comparando opciones
- TIMING_BUDGET_PENDING # Presupuesto pendiente
# Catch-all
OTHER_EMERGENT: []
# ============================================================================
# POOR CX DRIVERS (Por qué fue mala experiencia)
# ============================================================================
poor_cx_drivers:
# Wait time cluster
WAIT_TIME:
- WAIT_INITIAL_LONG # Espera inicial larga (>2min)
- WAIT_HOLD_LONG # Hold durante llamada largo (>1min)
- WAIT_CALLBACK_NEVER # Callback prometido no llegó
# Resolution cluster
RESOLUTION:
- RESOLUTION_NOT_ACHIEVED # Problema no resuelto
- RESOLUTION_NEEDED_ESCALATION # Necesitó escalación
- RESOLUTION_CALLBACK_BROKEN # Callback prometido incumplido
- RESOLUTION_INCORRECT # Resolución incorrecta
# Agent behavior cluster
AGENT_BEHAVIOR:
- AGENT_LACK_EMPATHY # Falta de empatía
- AGENT_RUDE # Grosero/dismissive
- AGENT_RUSHED # Con prisas
- AGENT_NOT_LISTENING # No escuchaba
# Information cluster
INFORMATION:
- INFO_WRONG_GIVEN # Información incorrecta
- INFO_INCONSISTENT # Información inconsistente
- INFO_COULDNT_ANSWER # No supo responder
# Process/System cluster
PROCESS_SYSTEM:
- SYSTEM_DOWN # Sistema caído
- POLICY_LIMITATION # Limitación de política
- TOO_MANY_TRANSFERS # Demasiados transfers
- AUTH_ISSUES # Problemas de autenticación
# Catch-all
OTHER_EMERGENT: []
# ============================================================================
# JOURNEY EVENT TYPES (Eventos del timeline)
# ============================================================================
journey_event_types:
# Observed (vienen del audio/STT)
observed:
- CALL_START
- CALL_END
- GREETING
- SILENCE # >5 segundos
- HOLD_START
- HOLD_END
- TRANSFER
- CROSSTALK # Hablan a la vez
# Inferred (vienen del LLM)
inferred:
- INTENT_STATED
- PRICE_OBJECTION
- COMPETITOR_MENTION
- NEGATIVE_SENTIMENT_PEAK
- POSITIVE_SENTIMENT_PEAK
- RESOLUTION_ATTEMPT
- SOFT_DECLINE
- HARD_DECLINE
- COMMITMENT
- ESCALATION_REQUEST
```
---
## Diagrama de Componentes (Actualizado)
```
┌─────────────────────────────────────────────────────────────────────────────┐
│ CXINSIGHTS COMPONENTS │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ TRANSCRIBER INTERFACE (Adapter Pattern) │ │
│ │ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ ┌────────────┐ │ │
│ │ │ AssemblyAI │ │ Whisper │ │ Google STT │ │ AWS │ │ │
│ │ │ Transcriber │ │ Transcriber │ │ Transcriber │ │ Transcribe │ │ │
│ │ └──────┬───────┘ └──────┬───────┘ └──────┬───────┘ └─────┬──────┘ │ │
│ │ └────────────────┴────────────────┴───────────────┘ │ │
│ │ ▼ │ │
│ │ TranscriptContract (normalized output) │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │
│ │ Feature │ │ Inference │ │ Validation │ │
│ │ Extractor │───▶│ Service │───▶│ Gate │ │
│ │ (observed only) │ │ (observed/infer)│ │ (evidence check)│ │
│ └─────────────────┘ └─────────────────┘ └─────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ AGGREGATION LAYER │ │
│ │ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ │
│ │ │ Stats Engine │ │ RCA Builder │ │ Emergent │ │ │
│ │ │ (by rca_code)│ │(deterministic│ │ Collector │ │ │
│ │ └──────────────┘ └──────────────┘ └──────────────┘ │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ VISUALIZATION LAYER │ │
│ │ ┌────────────┐ ┌────────────┐ ┌────────────┐ ┌────────────┐ │ │
│ │ │ Dashboard │ │ PDF │ │ Excel │ │ PNG │ │ │
│ │ │(obs/infer) │ │ (disclaim) │ │(drill-down)│ │ (legend) │ │ │
│ │ └────────────┘ └────────────┘ └────────────┘ └────────────┘ │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ CONFIG LAYER │ │
│ │ ┌────────────────┐ ┌────────────────┐ ┌────────────────┐ │ │
│ │ │ rca_taxonomy │ │ prompts/ + │ │ settings │ │ │
│ │ │ v1.0 (enum) │ │ VERSION FILE │ │ (.env) │ │ │
│ │ └────────────────┘ └────────────────┘ └────────────────┘ │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
```
---
## Reglas de Validación (Quality Gate)
```python
# Pseudocódigo de validación
def validate_call_analysis(analysis: CallAnalysis) -> ValidationResult:
errors = []
warnings = []
# REGLA 1: Todo driver debe tener evidence_spans
for driver in [analysis.inferred.lost_sale_driver, analysis.inferred.poor_cx_driver]:
if driver and not driver.evidence_spans:
errors.append(f"Driver {driver.rca_code} sin evidence_spans → RECHAZADO")
# REGLA 2: rca_code debe estar en taxonomía
if driver.rca_code not in TAXONOMY:
if driver.rca_code != "OTHER_EMERGENT":
errors.append(f"rca_code {driver.rca_code} no está en taxonomía")
else:
if not driver.proposed_label:
errors.append("OTHER_EMERGENT requiere proposed_label")
# REGLA 3: Confidence mínima
if driver.confidence < CONFIDENCE_THRESHOLD:
warnings.append(f"Driver {driver.rca_code} con low confidence: {driver.confidence}")
# REGLA 4: Schema version debe coincidir
if analysis._meta.schema_version != EXPECTED_SCHEMA_VERSION:
errors.append(f"Schema mismatch: {analysis._meta.schema_version}")
# REGLA 5: Journey events deben tener timestamps válidos
for event in analysis.journey_events:
if not is_valid_timestamp(event.t):
errors.append(f"Invalid timestamp in event: {event}")
return ValidationResult(
valid=len(errors) == 0,
errors=errors,
warnings=warnings
)
```
---
## Versionado de Prompts
```
config/prompts/
├── versions.yaml # Registry de versiones
├── call_analysis/
│ ├── v1.0/
│ │ ├── system.txt
│ │ ├── user.txt
│ │ └── schema.json # JSON Schema esperado
│ ├── v1.1/
│ │ ├── system.txt
│ │ ├── user.txt
│ │ └── schema.json
│ └── v1.2/ # Current
│ ├── system.txt
│ ├── user.txt
│ └── schema.json
└── rca_synthesis/
└── v1.0/
├── system.txt
└── user.txt
```
```yaml
# config/prompts/versions.yaml
current:
call_analysis: "v1.2"
rca_synthesis: "v1.0"
history:
call_analysis:
v1.0: "2024-01-01"
v1.1: "2024-01-10" # Added secondary_driver support
v1.2: "2024-01-15" # Added journey_events structure
```
---
## Estimaciones
### Tiempo Total (5,000 llamadas, ~4min promedio)
| Stage | Tiempo Estimado |
|-------|-----------------|
| Transcription | 3-4 horas |
| Feature Extraction | 15 min |
| Inference | 2-3 horas |
| Validation | 10 min |
| Aggregation | 10 min |
| RCA Tree Build | 5 min |
| Reporting | 5 min |
| **Total** | **6-8 horas** |
### Costes (ver TECH_STACK.md para detalle)
| Volumen | Transcription | Inference | Total |
|---------|---------------|-----------|-------|
| 5,000 calls | ~$300 | ~$15 | ~$315 |
| 20,000 calls | ~$1,200 | ~$60 | ~$1,260 |
---
## Implementation Status (2026-01-19)
| Module | Status | Location |
|--------|--------|----------|
| Transcription | ✅ Done | `src/transcription/` |
| Feature Extraction | ✅ Done | `src/features/` |
| Compression | ✅ Done | `src/compression/` |
| Inference | ✅ Done | `src/inference/` |
| Validation | ✅ Done | Built into models |
| Aggregation | ✅ Done | `src/aggregation/` |
| RCA Trees | ✅ Done | `src/aggregation/rca_tree.py` |
| Pipeline | ✅ Done | `src/pipeline/` |
| Exports | ✅ Done | `src/exports/` |
| CLI | ✅ Done | `cli.py` |
**Última actualización**: 2026-01-19 | **Versión**: 1.0.0

0
docs/BENCHMARKS.md Normal file
View File

171
docs/CHANGELOG.md Normal file
View File

@@ -0,0 +1,171 @@
# CHANGELOG
> Registro de cambios del proyecto CXInsights
---
## [2.1.0] - 2026-01-19 - Streamlit Dashboard + Blueprint Compliance
### Added
#### Streamlit Dashboard (`dashboard/`)
- **Main Application** (`app.py`)
- 8 navigation sections: Overview, Outcomes, Poor CX, FCR, Churn, Agent, Call Explorer, Export
- Automatic batch detection and selection
- Beyond brand header with logo and metadata
- **Brand Configuration** (`config.py`)
- Beyond Brand Identity colors: Black #000000, Blue #6D84E3, Grey #B1B1B0
- Custom CSS with Outfit font (Google Fonts)
- McKinsey-style chart formatting
- **Data Loading** (`data_loader.py`)
- `load_batch_data()` - Load summary and individual analyses
- `calculate_kpis()` - Dashboard KPI calculations
- `aggregate_drivers()` - Cross-analysis driver aggregation
- **Visualization Components** (`components.py`)
- `render_kpi_cards()` - Metric cards with deltas
- `render_outcome_chart()` - Horizontal bar chart
- `render_driver_analysis()` - Driver frequency bars
- `render_fcr_analysis()` - FCR 4-category view per blueprint
- `render_churn_risk_analysis()` - Binary churn risk view per blueprint
- `render_agent_performance()` - Talento Para Replicar / Oportunidades de Mejora
- `render_rca_sankey()` - RCA flow: Driver → Outcome → Churn Risk
- `render_driver_correlation_heatmap()` - Jaccard similarity co-occurrence
- `render_driver_outcome_heatmap()` - Driver distribution by outcome
- `render_outcome_deep_dive()` - Root causes, correlation, duration analysis
- `render_call_explorer()` - Filterable call detail table
- **Export Functionality** (`exports.py`)
- `create_excel_export()` - 8-sheet workbook with all analysis data
- `create_executive_summary_html()` - Branded HTML report
- `create_json_export()` - Raw JSON data package
- **Theme Configuration** (`.streamlit/config.toml`)
- Light theme base
- Brand primary color
- Port 8510 configuration
### Changed
#### Blueprint Terminology Compliance
- **FCR Analysis**: Updated from simple distribution to 4 categories
- Primera Llamada Sin Riesgo de Fuga
- Primera Llamada Con Riesgo de Fuga
- Rellamada Sin Riesgo de Fuga
- Rellamada Con Riesgo de Fuga
- **Churn Risk Analysis**: Updated to binary categories
- Sin Riesgo de Fuga (LOW, MEDIUM)
- En Riesgo de Fuga (AT_RISK, HIGH)
- **Agent Performance**: Updated labels to blueprint terminology
- Talento Para Replicar (positive skills)
- Oportunidades de Mejora (improvement areas)
#### FCR Rate Calculation Fix
- **Before**: `FIRST_CALL + RESOLVED` counted as success
- **After**: Only `FIRST_CALL` counts as FCR success (per blueprint)
- Updated in `data_loader.py` and `exports.py`
### Fixed
- Plotly colorbar `titlefont` deprecated property → `title.font`
- Streamlit dark theme issue → Light theme in config.toml
- Port conflicts → Using port 8510
---
## [2.0.0] - 2026-01-19 - Blueprint Alignment
### Added
#### New Analysis Dimensions (High Priority Gaps)
- **FCR Detection Module**
- `FCRStatus` enum: `FIRST_CALL`, `REPEAT_CALL`, `UNKNOWN`
- `fcr_status` field in `CallAnalysis`
- `fcr_failure_drivers` field for tracking repeat call causes
- **Churn Risk Classification**
- `ChurnRisk` enum: `NO_RISK`, `AT_RISK`, `UNKNOWN`
- `churn_risk` field in `CallAnalysis`
- `churn_risk_drivers` field for churn indicators
- **Agent Skill Assessment**
- `AgentClassification` enum: `GOOD_PERFORMER`, `NEEDS_IMPROVEMENT`, `MIXED`, `UNKNOWN`
- `AgentSkillIndicator` model with skill_code, skill_type, evidence, coaching recommendations
- `agent_positive_skills` and `agent_improvement_areas` fields in `CallAnalysis`
- **Enhanced RCALabel Structure**
- `DriverOrigin` enum: `AGENT`, `CUSTOMER`, `COMPANY`, `PROCESS`, `UNKNOWN`
- `origin` field in `RCALabel` for responsibility attribution
- `corrective_action` field for actionable recommendations
- `replicable_practice` field for positive behaviors to replicate
#### New Taxonomy Categories
- `churn_risk` drivers: PRICE_DISSATISFACTION, SERVICE_QUALITY_ISSUES, REPEATED_PROBLEMS, COMPETITOR_MENTION, CONTRACT_ISSUES, BILLING_PROBLEMS
- `fcr_failure` drivers: INCOMPLETE_RESOLUTION, PENDING_ACTION_REQUIRED, MISSING_INFORMATION, UNCLEAR_NEXT_STEPS, SYSTEM_LIMITATIONS, PROMISED_CALLBACK
- `agent_skills.positive`: EFFECTIVE_CLOSING, GOOD_RAPPORT, OBJECTION_MASTERY, PRODUCT_KNOWLEDGE, ACTIVE_LISTENING, EMPATHY_SHOWN, SOLUTION_ORIENTED, CLEAR_COMMUNICATION
- `agent_skills.improvement_needed`: POOR_CLOSING, MISSED_OPPORTUNITIES, OBJECTION_FAILURES, KNOWLEDGE_GAPS, PASSIVE_LISTENING, LOW_EMPATHY, PROBLEM_FOCUSED, UNCLEAR_COMMUNICATION
#### New Files
- `config/prompts/call_analysis/v2.0/system.txt` - System prompt for v2.0
- `config/prompts/call_analysis/v2.0/user.txt` - User prompt with all taxonomy sections
- `config/prompts/call_analysis/v2.0/schema.json` - JSON schema for v2.0 response
- `docs/GAP_ANALYSIS.md` - Comprehensive gap analysis vs BeyondCX blueprints
### Changed
#### Models (`src/models/call_analysis.py`)
- Added 4 new enums: `FCRStatus`, `ChurnRisk`, `AgentClassification`, `DriverOrigin`
- Extended `RCALabel` with `origin`, `corrective_action`, `replicable_practice` fields
- Added `AgentSkillIndicator` model
- Extended `CallAnalysis` with 7 new fields
#### Inference (`src/inference/`)
- `prompt_manager.py`: Added `TaxonomyTexts` dataclass, updated `load_taxonomy_for_prompt()` to return all sections
- `analyzer.py`: Updated to parse all v2.0 fields, added `_parse_agent_skills()` method
- Default `prompt_version` changed from `v1.0` to `v2.0`
#### Aggregation (`src/aggregation/`)
- `models.py`: Added `DriverCategory` type, extended `RCATree` and `BatchAggregation` with v2.0 fields
- `statistics.py`: Updated `calculate_frequencies()` to return dict with 6 categories, added FCR/churn/agent metrics to `calculate_outcome_rates()`
#### Configuration
- `config/prompts/versions.yaml`: Changed active version from v1.0 to v2.0
- `config/rca_taxonomy.yaml`: Added 3 new top-level sections
### Tests Updated
- `tests/unit/test_inference.py`: Updated active version assertion to v2.0
- `tests/unit/test_aggregation.py`: Updated sample_analyses fixture with v2.0 fields, updated frequency tests
### Documentation
- Updated `docs/PROJECT_CONTEXT.md` with v2.0 status
- Updated `docs/TODO.md` with completed and new tasks
- Created `docs/CHANGELOG.md` (this file)
---
## [1.0.0] - 2026-01-19 - MVP Complete
### Added
- Complete pipeline: transcription → features → inference → aggregation → exports
- CP1-CP8 checkpoints completed
- AssemblyAI transcription with diarization
- GPT-4o-mini inference with JSON strict mode
- Transcript compression (>60% token reduction)
- RCA tree building with severity scoring
- Export formats: JSON, Excel, PDF/HTML
- CLI interface with resume support
- Comprehensive test suite
### Features
- `CallAnalysis` model with observed vs inferred separation
- `RCALabel` with mandatory `evidence_spans[]`
- Versioned prompts system
- Checkpoint/resume mechanism
- Batch processing with rate limiting
---
**Última actualización**: 2026-01-19

289
docs/DATA_CONTRACTS.md Normal file
View File

@@ -0,0 +1,289 @@
# DATA_CONTRACTS.md
> Schemas de todos los datos que fluyen por el sistema
---
## Regla de oro
> Si cambias un schema, actualiza este doc PRIMERO, luego implementa el código.
---
## Schema: Transcript
**Archivo**: `src/transcription/models.py`
```python
@dataclass
class SpeakerTurn:
speaker: Literal["agent", "customer"]
text: str
start_time: float # seconds
end_time: float # seconds
confidence: float = 1.0
@dataclass
class TranscriptMetadata:
audio_duration_sec: float
language: str = "es"
provider: str = "assemblyai"
job_id: str | None = None
created_at: datetime = field(default_factory=datetime.now)
@dataclass
class Transcript:
call_id: str
turns: list[SpeakerTurn]
metadata: TranscriptMetadata
detected_events: list[Event] = field(default_factory=list)
```
---
## Schema: Event
**Archivo**: `src/models/call_analysis.py`
```python
class EventType(str, Enum):
HOLD_START = "hold_start"
HOLD_END = "hold_end"
TRANSFER = "transfer"
ESCALATION = "escalation"
SILENCE = "silence"
INTERRUPTION = "interruption"
@dataclass
class Event:
event_type: EventType
timestamp: float # seconds from call start
duration_sec: float | None = None
metadata: dict = field(default_factory=dict)
```
---
## Schema: CompressedTranscript
**Archivo**: `src/compression/models.py`
```python
@dataclass
class CustomerIntent:
intent_type: IntentType # CANCEL, INQUIRY, COMPLAINT, etc.
text: str
timestamp: float
confidence: float = 0.8
@dataclass
class AgentOffer:
offer_type: OfferType # DISCOUNT, UPGRADE, RETENTION, etc.
text: str
timestamp: float
@dataclass
class CustomerObjection:
objection_type: ObjectionType # PRICE, SERVICE, COMPETITOR, etc.
text: str
timestamp: float
@dataclass
class CompressedTranscript:
call_id: str
customer_intents: list[CustomerIntent]
agent_offers: list[AgentOffer]
objections: list[CustomerObjection]
resolutions: list[ResolutionStatement]
key_moments: list[KeyMoment]
compression_ratio: float = 0.0 # tokens_after / tokens_before
```
---
## Schema: CallAnalysis
**Archivo**: `src/models/call_analysis.py`
```python
@dataclass
class EvidenceSpan:
text: str
start_time: float | None = None
end_time: float | None = None
@dataclass
class RCALabel:
driver_code: str # From rca_taxonomy.yaml
confidence: float # 0.0-1.0
evidence_spans: list[EvidenceSpan] # Min 1 required!
reasoning: str | None = None
@dataclass
class ObservedFeatures:
audio_duration_sec: float
agent_talk_ratio: float | None = None
customer_talk_ratio: float | None = None
hold_time_total_sec: float | None = None
transfer_count: int = 0
silence_count: int = 0
@dataclass
class Traceability:
schema_version: str
prompt_version: str
model_id: str
processed_at: datetime = field(default_factory=datetime.now)
class CallOutcome(str, Enum):
SALE_COMPLETED = "sale_completed"
SALE_LOST = "sale_lost"
INQUIRY_RESOLVED = "inquiry_resolved"
INQUIRY_UNRESOLVED = "inquiry_unresolved"
COMPLAINT_RESOLVED = "complaint_resolved"
COMPLAINT_UNRESOLVED = "complaint_unresolved"
class ProcessingStatus(str, Enum):
SUCCESS = "success"
PARTIAL = "partial"
FAILED = "failed"
@dataclass
class CallAnalysis:
call_id: str
batch_id: str
status: ProcessingStatus
observed: ObservedFeatures
outcome: CallOutcome | None = None
lost_sales_drivers: list[RCALabel] = field(default_factory=list)
poor_cx_drivers: list[RCALabel] = field(default_factory=list)
traceability: Traceability | None = None
error_message: str | None = None
```
---
## Schema: BatchAggregation
**Archivo**: `src/aggregation/models.py`
```python
@dataclass
class DriverFrequency:
driver_code: str
category: Literal["lost_sales", "poor_cx"]
total_occurrences: int
calls_affected: int
total_calls_in_batch: int
occurrence_rate: float # occurrences / total_calls
call_rate: float # calls_affected / total_calls
avg_confidence: float
min_confidence: float
max_confidence: float
class ImpactLevel(str, Enum):
CRITICAL = "critical"
HIGH = "high"
MEDIUM = "medium"
LOW = "low"
@dataclass
class DriverSeverity:
driver_code: str
category: Literal["lost_sales", "poor_cx"]
base_severity: float
frequency_factor: float
confidence_factor: float
co_occurrence_factor: float
severity_score: float # 0-100
impact_level: ImpactLevel
@dataclass
class RCATree:
batch_id: str
total_calls: int
calls_with_lost_sales: int
calls_with_poor_cx: int
calls_with_both: int
top_lost_sales_drivers: list[str]
top_poor_cx_drivers: list[str]
nodes: list[RCANode] = field(default_factory=list)
@dataclass
class BatchAggregation:
batch_id: str
total_calls_processed: int
successful_analyses: int
failed_analyses: int
lost_sales_frequencies: list[DriverFrequency]
poor_cx_frequencies: list[DriverFrequency]
lost_sales_severities: list[DriverSeverity]
poor_cx_severities: list[DriverSeverity]
rca_tree: RCATree | None = None
emergent_patterns: list[dict] = field(default_factory=list)
```
---
## Schema: PipelineManifest
**Archivo**: `src/pipeline/models.py`
```python
class PipelineStage(str, Enum):
TRANSCRIPTION = "transcription"
FEATURE_EXTRACTION = "feature_extraction"
COMPRESSION = "compression"
INFERENCE = "inference"
AGGREGATION = "aggregation"
EXPORT = "export"
class StageStatus(str, Enum):
PENDING = "pending"
RUNNING = "running"
COMPLETED = "completed"
FAILED = "failed"
SKIPPED = "skipped"
@dataclass
class StageManifest:
stage: PipelineStage
status: StageStatus = StageStatus.PENDING
started_at: datetime | None = None
completed_at: datetime | None = None
total_items: int = 0
processed_items: int = 0
failed_items: int = 0
errors: list[dict] = field(default_factory=list)
metadata: dict = field(default_factory=dict)
@dataclass
class PipelineManifest:
batch_id: str
created_at: datetime = field(default_factory=datetime.now)
status: StageStatus = StageStatus.PENDING
current_stage: PipelineStage | None = None
total_audio_files: int = 0
stages: dict[PipelineStage, StageManifest] = field(default_factory=dict)
```
---
## Validation Rules
### RCALabel
- `evidence_spans` MUST have at least 1 element
- `driver_code` MUST be in rca_taxonomy.yaml OR be "OTHER_EMERGENT"
- `confidence` MUST be between 0.0 and 1.0
### CallAnalysis
- `traceability` MUST be present
- If `status == SUCCESS`, `outcome` MUST be present
- If `outcome == SALE_LOST`, `lost_sales_drivers` SHOULD have entries
### BatchAggregation
- `total_calls_processed` == `successful_analyses` + `failed_analyses`
---
**Última actualización**: 2026-01-19

889
docs/DEPLOYMENT.md Normal file
View File

@@ -0,0 +1,889 @@
# CXInsights - Deployment Guide
## Modelo de Deployment
```
┌─────────────────────────────────────────────────────────────────────────────┐
│ DEPLOYMENT MODEL │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ CXInsights está diseñado para ejecutarse como LONG-RUNNING BATCH JOBS │
│ en un servidor dedicado (físico o VM), NO como microservicio elástico. │
│ │
│ ✅ Modelo principal: Servidor dedicado con ejecución via tmux/systemd │
│ ⚠️ Modelo secundario: Cloud VM (misma arquitectura, diferente hosting) │
│ 📦 Opcional: Docker (para portabilidad, no para orquestación) │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
```
---
## Prerequisitos
### Software requerido
| Software | Versión | Propósito |
|----------|---------|-----------|
| Python | 3.11+ | Runtime |
| Git | 2.40+ | Control de versiones |
| ffmpeg | 6.0+ | Validación de audio (opcional) |
| tmux | 3.0+ | Sesiones persistentes para batch jobs |
### Cuentas y API Keys
| Servicio | URL | Necesario para |
|----------|-----|----------------|
| AssemblyAI | https://assemblyai.com | Transcripción STT |
| OpenAI | https://platform.openai.com | Análisis LLM |
| Anthropic | https://console.anthropic.com | Backup LLM (opcional) |
---
## Capacity Planning (Sizing Estático)
### Requisitos de Hardware
El sizing es **estático** para el volumen máximo esperado. No hay auto-scaling.
```
┌─────────────────────────────────────────────────────────────────────────────┐
│ CAPACITY PLANNING │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ VOLUMEN: 5,000 llamadas / batch │
│ ├─ CPU: 4 cores (transcripción es I/O bound, no CPU bound) │
│ ├─ RAM: 8 GB │
│ ├─ Disco: 50 GB SSD (audio + transcripts + outputs) │
│ └─ Red: 100 Mbps (upload audio a STT API) │
│ │
│ VOLUMEN: 20,000 llamadas / batch │
│ ├─ CPU: 4-8 cores │
│ ├─ RAM: 16 GB │
│ ├─ Disco: 200 GB SSD │
│ └─ Red: 100+ Mbps │
│ │
│ NOTA: El cuello de botella es el rate limit de APIs externas, │
│ no el hardware local. Más cores no acelera el pipeline. │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
```
### Estimación de espacio en disco
```
Por cada 1,000 llamadas (AHT = 7 min):
├─ Audio original: ~2-4 GB (depende de bitrate)
├─ Transcripts raw: ~100 MB
├─ Transcripts compressed: ~40 MB
├─ Features: ~20 MB
├─ Labels (processed): ~50 MB
├─ Outputs finales: ~10 MB
└─ TOTAL: ~2.5-4.5 GB por 1,000 calls
Recomendación:
├─ 5K calls: 50 GB disponibles
└─ 20K calls: 200 GB disponibles
```
---
## Deployment Estándar (Servidor Dedicado)
### 1. Preparar servidor
```bash
# Ubuntu 22.04 LTS (o similar)
sudo apt update
sudo apt install -y python3.11 python3.11-venv git ffmpeg tmux
```
### 2. Clonar repositorio
```bash
# Ubicación recomendada: /opt/cxinsights o ~/cxinsights
cd /opt
git clone https://github.com/tu-org/cxinsights.git
cd cxinsights
```
### 3. Crear entorno virtual
```bash
python3.11 -m venv .venv
source .venv/bin/activate
```
### 4. Instalar dependencias
```bash
# Instalación base
pip install -e .
# Con PII detection (recomendado)
pip install -e ".[pii]"
# Con herramientas de desarrollo
pip install -e ".[dev]"
```
### 5. Configurar variables de entorno
```bash
cp .env.example .env
nano .env
```
Contenido de `.env`:
```bash
# === API KEYS ===
ASSEMBLYAI_API_KEY=your_assemblyai_key_here
OPENAI_API_KEY=sk-your_openai_key_here
ANTHROPIC_API_KEY=sk-ant-your_anthropic_key_here # Opcional
# === THROTTLING (ajustar manualmente según tier y pruebas) ===
# Estos son LÍMITES INTERNOS, no promesas de las APIs
MAX_CONCURRENT_TRANSCRIPTIONS=30 # AssemblyAI: empezar conservador
LLM_REQUESTS_PER_MINUTE=200 # OpenAI: depende de tu tier
LLM_BACKOFF_BASE=2.0 # Segundos base para retry
LLM_BACKOFF_MAX=60.0 # Máximo backoff
LLM_MAX_RETRIES=5
# === LOGGING ===
LOG_LEVEL=INFO
LOG_DIR=./data/logs
# === RUTAS ===
DATA_DIR=./data
CONFIG_DIR=./config
```
### 6. Crear estructura de datos persistente
```bash
# Script de inicialización (ejecutar una sola vez)
./scripts/init_data_structure.sh
```
O manualmente:
```bash
mkdir -p data/{raw/audio,raw/metadata}
mkdir -p data/{transcripts/raw,transcripts/compressed}
mkdir -p data/features
mkdir -p data/processed
mkdir -p data/outputs
mkdir -p data/logs
mkdir -p data/.checkpoints
```
### 7. Verificar instalación
```bash
python -m cxinsights.pipeline.cli --help
```
---
## Configuración de Throttling
### Concepto clave
Los parámetros `MAX_CONCURRENT_*` y `*_REQUESTS_PER_MINUTE` son **throttles internos** que tú ajustas manualmente según:
1. Tu tier en las APIs (OpenAI, AssemblyAI)
2. Pruebas reales de comportamiento
3. Errores 429 observados
```
┌─────────────────────────────────────────────────────────────────────────────┐
│ THROTTLING CONFIGURATION │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ ASSEMBLYAI: │
│ ├─ Default: 100 concurrent transcriptions (según docs) │
│ ├─ Recomendación inicial: 30 (conservador) │
│ └─ Ajustar según errores observados │
│ │
│ OPENAI: │
│ ├─ Tier 1 (free): 500 RPM → configurar 200 RPM interno │
│ ├─ Tier 2: 5000 RPM → configurar 2000 RPM interno │
│ ├─ Tier 3+: 5000+ RPM → configurar según necesidad │
│ └─ SIEMPRE dejar margen (40-50% del límite real) │
│ │
│ Si ves errores 429: │
│ 1. Reducir *_REQUESTS_PER_MINUTE │
│ 2. El backoff exponencial manejará picos │
│ 3. Loguear y ajustar para siguiente batch │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
```
---
## Ejecución de Batch Jobs
### Modelo de ejecución: Long-running batch jobs
CXInsights ejecuta **procesos de larga duración** (6-24+ horas). Usa tmux o systemd para persistencia.
### Opción A: tmux (recomendado para operación manual)
```bash
# Crear sesión tmux
tmux new-session -s cxinsights
# Dentro de tmux, ejecutar pipeline
source .venv/bin/activate
python -m cxinsights.pipeline.cli run \
--input ./data/raw/audio/batch_2024_01 \
--batch-id batch_2024_01
# Detach de tmux: Ctrl+B, luego D
# Re-attach: tmux attach -t cxinsights
# Ver logs en otra ventana tmux
# Ctrl+B, luego C (nueva ventana)
tail -f data/logs/pipeline_*.log
```
### Opción B: systemd (recomendado para ejecución programada)
```ini
# /etc/systemd/system/cxinsights-batch.service
[Unit]
Description=CXInsights Batch Processing
After=network.target
[Service]
Type=simple
User=cxinsights
WorkingDirectory=/opt/cxinsights
Environment="PATH=/opt/cxinsights/.venv/bin"
ExecStart=/opt/cxinsights/.venv/bin/python -m cxinsights.pipeline.cli run \
--input /opt/cxinsights/data/raw/audio/current_batch \
--batch-id current_batch
Restart=no
StandardOutput=append:/opt/cxinsights/data/logs/systemd.log
StandardError=append:/opt/cxinsights/data/logs/systemd.log
[Install]
WantedBy=multi-user.target
```
```bash
# Activar y ejecutar
sudo systemctl daemon-reload
sudo systemctl start cxinsights-batch
# Ver estado
sudo systemctl status cxinsights-batch
journalctl -u cxinsights-batch -f
```
### Comando básico
```bash
python -m cxinsights.pipeline.cli run \
--input ./data/raw/audio/batch_2024_01 \
--batch-id batch_2024_01
```
### Opciones disponibles
```bash
python -m cxinsights.pipeline.cli run --help
# Opciones:
# --input PATH Carpeta con archivos de audio [required]
# --output PATH Carpeta de salida [default: ./data]
# --batch-id TEXT Identificador del batch [required]
# --config PATH Archivo de configuración [default: ./config/settings.yaml]
# --stages TEXT Stages a ejecutar (comma-separated) [default: all]
# --skip-transcription Saltar transcripción (usar existentes)
# --skip-inference Saltar inferencia (usar existentes)
# --dry-run Mostrar qué se haría sin ejecutar
# --verbose Logging detallado
```
### Ejecución por stages (útil para debugging)
```bash
# Solo transcripción
python -m cxinsights.pipeline.cli run \
--input ./data/raw/audio/batch_01 \
--batch-id batch_01 \
--stages transcription
# Solo features (requiere transcripts)
python -m cxinsights.pipeline.cli run \
--batch-id batch_01 \
--stages features
# Solo inferencia (requiere transcripts + features)
python -m cxinsights.pipeline.cli run \
--batch-id batch_01 \
--stages inference
# Agregación y reportes (requiere labels)
python -m cxinsights.pipeline.cli run \
--batch-id batch_01 \
--stages aggregation,visualization
```
### Resumir desde checkpoint
```bash
# Si el pipeline falló o se interrumpió
python -m cxinsights.pipeline.cli resume --batch-id batch_01
# El sistema detecta automáticamente:
# - Transcripciones completadas
# - Features extraídos
# - Labels ya generados
# - Continúa desde donde se quedó
```
### Estimación de costes antes de ejecutar
```bash
python -m cxinsights.pipeline.cli estimate --input ./data/raw/audio/batch_01
# Output:
# ┌─────────────────────────────────────────────────┐
# │ COST ESTIMATION (AHT=7min) │
# ├─────────────────────────────────────────────────┤
# │ Files found: 5,234 │
# │ Total duration: ~611 hours │
# │ Avg duration/call: 7.0 min │
# ├─────────────────────────────────────────────────┤
# │ Transcription (STT): $540 - $600 │
# │ Inference (LLM): $2.50 - $3.50 │
# │ TOTAL ESTIMATED: $543 - $604 │
# └─────────────────────────────────────────────────┘
# Proceed? [y/N]:
```
---
## Política de Logs y Retención
### Estructura de logs
```
data/logs/
├── pipeline_2024_01_15_103000.log # Log principal del batch
├── pipeline_2024_01_15_103000.err # Errores separados
├── transcription_2024_01_15.log # Detalle STT
├── inference_2024_01_15.log # Detalle LLM
└── systemd.log # Si usas systemd
```
### Política de retención
```
┌─────────────────────────────────────────────────────────────────────────────┐
│ RETENTION POLICY │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ LOGS: │
│ ├─ Pipeline logs: 30 días │
│ ├─ Error logs: 90 días │
│ └─ Rotación: diaria, compresión gzip después de 7 días │
│ │
│ DATOS: │
│ ├─ Audio raw: borrar tras procesamiento exitoso (o retener 30 días) │
│ ├─ Transcripts raw: borrar tras 30 días │
│ ├─ Transcripts compressed: borrar tras procesamiento LLM │
│ ├─ Features: retener mientras existan labels │
│ ├─ Labels (processed): retener indefinidamente (sin PII) │
│ ├─ Outputs (stats, RCA): retener indefinidamente │
│ └─ Checkpoints: borrar tras completar batch │
│ │
│ IMPORTANTE: Los logs NUNCA contienen transcripts completos │
│ Solo: call_id, timestamps, errores, métricas │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
```
### Configuración de logrotate (Linux)
```bash
# /etc/logrotate.d/cxinsights
/opt/cxinsights/data/logs/*.log {
daily
rotate 30
compress
delaycompress
missingok
notifempty
create 644 cxinsights cxinsights
}
```
### Script de limpieza manual
```bash
# scripts/cleanup_old_data.sh
#!/bin/bash
# Ejecutar periódicamente (cron semanal)
DATA_DIR="/opt/cxinsights/data"
RETENTION_DAYS=30
echo "Cleaning data older than $RETENTION_DAYS days..."
# Logs antiguos
find "$DATA_DIR/logs" -name "*.log" -mtime +$RETENTION_DAYS -delete
find "$DATA_DIR/logs" -name "*.gz" -mtime +90 -delete
# Transcripts raw antiguos
find "$DATA_DIR/transcripts/raw" -name "*.json" -mtime +$RETENTION_DAYS -delete
# Checkpoints de batches completados (manual review recomendado)
echo "Review and delete completed checkpoints manually:"
ls -la "$DATA_DIR/.checkpoints/"
echo "Cleanup complete."
```
---
## Dashboard (Visualización)
```bash
# Lanzar dashboard
streamlit run src/visualization/dashboard.py -- --batch-id batch_2024_01
# Acceder en: http://localhost:8501
# O si es servidor remoto: http://servidor:8501
```
### Con autenticación (proxy nginx)
Ver TECH_STACK.md sección "Streamlit - Deploy" para configuración de nginx con basic auth.
---
## Estructura de Outputs
Después de ejecutar el pipeline:
```
data/outputs/batch_2024_01/
├── aggregated_stats.json # Estadísticas consolidadas
├── call_matrix.csv # Todas las llamadas con labels
├── rca_lost_sales.json # Árbol RCA de ventas perdidas
├── rca_poor_cx.json # Árbol RCA de CX deficiente
├── emergent_drivers_review.json # OTHER_EMERGENT para revisión
├── validation_report.json # Resultado de quality gate
├── executive_summary.pdf # Reporte ejecutivo
├── full_analysis.xlsx # Excel con drill-down
└── figures/
├── rca_tree_lost_sales.png
├── rca_tree_poor_cx.png
└── ...
```
---
## Script de Deployment (deploy.sh)
Script para configuración inicial del entorno persistente.
```bash
#!/bin/bash
# deploy.sh - Configuración inicial de entorno persistente
# Ejecutar UNA VEZ al instalar en nuevo servidor
set -e
INSTALL_DIR="${INSTALL_DIR:-/opt/cxinsights}"
PYTHON_VERSION="python3.11"
echo "======================================"
echo "CXInsights - Initial Deployment"
echo "======================================"
echo "Install directory: $INSTALL_DIR"
echo ""
# 1. Verificar Python
if ! command -v $PYTHON_VERSION &> /dev/null; then
echo "ERROR: $PYTHON_VERSION not found"
echo "Install with: sudo apt install python3.11 python3.11-venv"
exit 1
fi
echo "✓ Python: $($PYTHON_VERSION --version)"
# 2. Verificar que estamos en el directorio correcto
if [ ! -f "pyproject.toml" ]; then
echo "ERROR: pyproject.toml not found. Run from repository root."
exit 1
fi
echo "✓ Repository structure verified"
# 3. Crear entorno virtual (si no existe)
if [ ! -d ".venv" ]; then
echo "Creating virtual environment..."
$PYTHON_VERSION -m venv .venv
fi
source .venv/bin/activate
echo "✓ Virtual environment: .venv"
# 4. Instalar dependencias
echo "Installing dependencies..."
pip install -q --upgrade pip
pip install -q -e .
echo "✓ Dependencies installed"
# 5. Configurar .env (si no existe)
if [ ! -f ".env" ]; then
if [ -f ".env.example" ]; then
cp .env.example .env
echo "⚠ Created .env from template - CONFIGURE API KEYS"
else
echo "ERROR: .env.example not found"
exit 1
fi
else
echo "✓ .env exists"
fi
# 6. Crear estructura de datos persistente (idempotente)
echo "Creating data directory structure..."
mkdir -p data/raw/audio
mkdir -p data/raw/metadata
mkdir -p data/transcripts/raw
mkdir -p data/transcripts/compressed
mkdir -p data/features
mkdir -p data/processed
mkdir -p data/outputs
mkdir -p data/logs
mkdir -p data/.checkpoints
# Crear .gitkeep para preservar estructura en git
touch data/raw/audio/.gitkeep
touch data/raw/metadata/.gitkeep
touch data/transcripts/raw/.gitkeep
touch data/transcripts/compressed/.gitkeep
touch data/features/.gitkeep
touch data/processed/.gitkeep
touch data/outputs/.gitkeep
touch data/logs/.gitkeep
echo "✓ Data directories created"
# 7. Verificar API keys en .env
source .env
if [ -z "$ASSEMBLYAI_API_KEY" ] || [ "$ASSEMBLYAI_API_KEY" = "your_assemblyai_key_here" ]; then
echo ""
echo "⚠ WARNING: ASSEMBLYAI_API_KEY not configured in .env"
fi
if [ -z "$OPENAI_API_KEY" ] || [ "$OPENAI_API_KEY" = "sk-your_openai_key_here" ]; then
echo "⚠ WARNING: OPENAI_API_KEY not configured in .env"
fi
# 8. Verificar instalación
echo ""
echo "Verifying installation..."
python -m cxinsights.pipeline.cli --help > /dev/null 2>&1
if [ $? -eq 0 ]; then
echo "✓ CLI verification passed"
else
echo "ERROR: CLI verification failed"
exit 1
fi
echo ""
echo "======================================"
echo "Deployment complete!"
echo "======================================"
echo ""
echo "Next steps:"
echo " 1. Configure API keys in .env"
echo " 2. Copy audio files to data/raw/audio/your_batch/"
echo " 3. Start tmux session: tmux new -s cxinsights"
echo " 4. Activate venv: source .venv/bin/activate"
echo " 5. Run pipeline:"
echo " python -m cxinsights.pipeline.cli run \\"
echo " --input ./data/raw/audio/your_batch \\"
echo " --batch-id your_batch"
echo ""
```
```bash
# Uso:
chmod +x deploy.sh
./deploy.sh
```
---
## Docker (Opcional)
Docker es una opción para **portabilidad**, no el camino principal de deployment.
```
┌─────────────────────────────────────────────────────────────────────────────┐
│ DOCKER - DISCLAIMER │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ Docker es OPCIONAL y se proporciona para: │
│ ├─ Entornos donde no se puede instalar Python directamente │
│ ├─ Reproducibilidad exacta del entorno │
│ └─ Integración con sistemas de CI/CD existentes │
│ │
│ Docker NO es necesario para: │
│ ├─ Ejecución normal en servidor dedicado │
│ ├─ Obtener mejor rendimiento │
│ └─ Escalar horizontalmente (no aplica a este workload) │
│ │
│ El deployment estándar (venv + tmux/systemd) es preferido. │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
```
### Dockerfile
```dockerfile
FROM python:3.11-slim
WORKDIR /app
# Dependencias del sistema
RUN apt-get update && \
apt-get install -y ffmpeg && \
rm -rf /var/lib/apt/lists/*
# Copiar código
COPY pyproject.toml .
COPY src/ src/
COPY config/ config/
# Instalar dependencias Python
RUN pip install --no-cache-dir -e .
# Volumen para datos persistentes
VOLUME ["/app/data"]
ENTRYPOINT ["python", "-m", "cxinsights.pipeline.cli"]
```
### Uso
```bash
# Build
docker build -t cxinsights:latest .
# Run (montar volumen de datos)
docker run -it \
-v /path/to/data:/app/data \
--env-file .env \
cxinsights:latest run \
--input /app/data/raw/audio/batch_01 \
--batch-id batch_01
```
---
## Cloud VM (Opción Secundaria)
```
┌─────────────────────────────────────────────────────────────────────────────┐
│ CLOUD VM - DISCLAIMER │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ Usar Cloud VM (AWS EC2, GCP Compute, Azure VM) cuando: │
│ ├─ No tienes servidor físico disponible │
│ ├─ Necesitas acceso remoto desde múltiples ubicaciones │
│ └─ Quieres delegar mantenimiento de hardware │
│ │
│ La arquitectura es IDÉNTICA al servidor dedicado: │
│ ├─ Mismo sizing estático (no auto-scaling) │
│ ├─ Mismo modelo de ejecución (long-running batch) │
│ ├─ Misma configuración de throttling manual │
│ └─ Solo cambia dónde está el servidor │
│ │
│ COSTE ADICIONAL: $30-100/mes por la VM (según specs) │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
```
### Setup en Cloud VM
```bash
# 1. Crear VM (ejemplo AWS)
# - Ubuntu 22.04 LTS
# - t3.xlarge (4 vCPU, 16 GB RAM) para 20K calls
# - 200 GB gp3 SSD
# - Security group: SSH (22), HTTP opcional (8501 para dashboard)
# 2. Conectar
ssh -i key.pem ubuntu@vm-ip
# 3. Seguir pasos de "Deployment Estándar" arriba
# (idéntico a servidor dedicado)
```
---
## Troubleshooting
### Error: API key inválida
```
Error: AssemblyAI authentication failed
```
**Solución**: Verificar `ASSEMBLYAI_API_KEY` en `.env`
### Error: Rate limit exceeded (429)
```
Error: OpenAI rate limit exceeded
```
**Solución**:
1. Reducir `LLM_REQUESTS_PER_MINUTE` en `.env`
2. El backoff automático manejará picos temporales
3. Revisar tu tier en OpenAI dashboard
### Error: Memoria insuficiente
```
MemoryError: Unable to allocate array
```
**Solución**:
- Procesar en batches más pequeños
- Aumentar RAM del servidor
- Usar `--stages` para ejecutar por partes
### Error: Transcripción fallida
```
Error: Transcription failed for call_xxx.mp3
```
**Solución**:
- Verificar archivo: `ffprobe call_xxx.mp3`
- Verificar que no excede 5 horas (límite AssemblyAI)
- El pipeline continúa con las demás llamadas
### Ver logs detallados
```bash
# Log principal del pipeline
tail -f data/logs/pipeline_*.log
# Verbose mode
python -m cxinsights.pipeline.cli run ... --verbose
# Si usas systemd
journalctl -u cxinsights-batch -f
```
---
## Checklist Pre-Ejecución
```
SERVIDOR:
[ ] Python 3.11+ instalado
[ ] tmux instalado
[ ] Suficiente espacio en disco (ver Capacity Planning)
[ ] Conectividad de red estable
APLICACIÓN:
[ ] Repositorio clonado
[ ] Entorno virtual creado y activado
[ ] Dependencias instaladas (pip install -e .)
[ ] .env configurado con API keys
[ ] Throttling configurado según tu tier
DATOS:
[ ] Archivos de audio en data/raw/audio/batch_id/
[ ] Estimación de costes revisada (estimate command)
[ ] Estructura de directorios creada
EJECUCIÓN:
[ ] Sesión tmux iniciada (o systemd configurado)
[ ] Logs monitoreables
```
---
## Makefile (Comandos útiles)
```makefile
.PHONY: install dev test lint run dashboard status logs clean-logs
# Instalación
install:
pip install -e .
install-pii:
pip install -e ".[pii]"
dev:
pip install -e ".[dev]"
# Testing
test:
pytest tests/ -v
test-cov:
pytest tests/ --cov=src --cov-report=html
# Linting
lint:
ruff check src/
mypy src/
format:
ruff format src/
# Ejecución
run:
python -m cxinsights.pipeline.cli run --input $(INPUT) --batch-id $(BATCH)
estimate:
python -m cxinsights.pipeline.cli estimate --input $(INPUT)
resume:
python -m cxinsights.pipeline.cli resume --batch-id $(BATCH)
dashboard:
streamlit run src/visualization/dashboard.py -- --batch-id $(BATCH)
# Monitoreo
status:
@echo "=== Pipeline Status ==="
@ls -la data/.checkpoints/ 2>/dev/null || echo "No active checkpoints"
@echo ""
@echo "=== Recent Logs ==="
@ls -lt data/logs/*.log 2>/dev/null | head -5 || echo "No logs found"
logs:
tail -f data/logs/pipeline_*.log
# Limpieza (CUIDADO: no borrar datos de producción)
clean-logs:
find data/logs -name "*.log" -mtime +30 -delete
find data/logs -name "*.gz" -mtime +90 -delete
clean-checkpoints:
@echo "Review before deleting:"
@ls -la data/.checkpoints/
@read -p "Delete all checkpoints? [y/N] " confirm && [ "$$confirm" = "y" ] && rm -rf data/.checkpoints/*
```
Uso:
```bash
make install
make run INPUT=./data/raw/audio/batch_01 BATCH=batch_01
make logs
make status
make dashboard BATCH=batch_01
```

366
docs/GAP_ANALYSIS.md Normal file
View File

@@ -0,0 +1,366 @@
# GAP ANALYSIS: CXInsights vs BeyondCX Blueprints
> **Generated**: 2026-01-19
> **Blueprints Analyzed**: 4 documents in `docs/blueprints/`
---
## Executive Summary
CXInsights currently implements a **subset** of the BeyondCX blueprint requirements. The project has strong foundations for RCA (Root Cause Analysis) but is missing several key frameworks defined in the blueprints.
| Framework | Coverage | Status |
|-----------|----------|--------|
| **Ventas (Sales Analysis)** | ~40% | Partial - conversion tracking, some agent failures |
| **Close The Loop (CX)** | ~30% | Partial - CX drivers only |
| **FCR (First Call Resolution)** | ~5% | Missing - no FCR detection |
---
## Blueprint 1: Contexto BeyondCX (General Framework)
### What it defines:
- Role: Expert auditor for call analysis (energy sector - Endesa B2C)
- Three strategic frameworks: Ventas, Close the Loop, FCR
- Rules: Evidence-based, no invented info, professional analysis
### CXInsights Alignment:
| Requirement | CXInsights Status | Notes |
|-------------|-------------------|-------|
| Evidence-based analysis | ✅ Implemented | `evidence_spans[]` required for all RCA labels |
| No invented info | ✅ Implemented | LLM must quote transcript |
| Professional output | ✅ Implemented | Structured JSON with traceability |
| Three frameworks | ⚠️ Partial | Only partial coverage of each |
---
## Blueprint 2: Análisis de Llamadas de Venta (Sales Framework)
### What it defines (5 KPIs):
#### KPI 1: Tasa de Conversión de Venta
| Blueprint Requirement | CXInsights Implementation | Gap |
|-----------------------|---------------------------|-----|
| Determine Venta/No Venta | ✅ `CallOutcome.SALE_COMPLETED/SALE_LOST` | Covered |
| Max 5 key factors for success | ⚠️ `lost_sales_drivers[]` | Different structure |
| Max 5 factors for failure | ⚠️ `lost_sales_drivers[]` with RCALabel | Missing detailed actions |
| Origin attribution (agent/client/company) | ❌ Missing | No origin field |
| Specific corrective actions | ❌ Missing | Only `reasoning` field |
**Gap Details:**
- CXInsights captures WHAT caused lost sale (driver_code)
- Blueprint requires: WHO is responsible + HOW to fix it + EXAMPLE from call
#### KPI 2: Efectividad de Campañas
| Blueprint Requirement | CXInsights Implementation | Gap |
|-----------------------|---------------------------|-----|
| Campaign effectiveness analysis | ❌ Not implemented | **MAJOR GAP** |
| Product-message fit detection | ❌ Not implemented | |
| Customer feedback capture | ❌ Not implemented | |
| Segmentation analysis | ❌ Not implemented | |
**Gap Details:**
- CXInsights has no concept of "campaign"
- No way to track which campaign a call belongs to
- No analysis of product-customer fit
#### KPI 3: Habilidades del Equipo de Ventas
| Blueprint Requirement | CXInsights Implementation | Gap |
|-----------------------|---------------------------|-----|
| "Buen Comercial" / "Necesita Mejora" classification | ❌ Not implemented | **MAJOR GAP** |
| Agent strengths identification | ⚠️ Partial via drivers | Implicit only |
| Agent weaknesses documentation | ⚠️ `OBJECTION_NOT_HANDLED`, `POOR_PITCH` | Limited |
| Coaching recommendations | ❌ Not implemented | |
**Gap Details:**
- No agent skill scoring or classification
- No explicit "good practices to replicate" output
- No coaching action recommendations
#### KPI 4: Argumentarios y Objeciones
| Blueprint Requirement | CXInsights Implementation | Gap |
|-----------------------|---------------------------|-----|
| Script quality evaluation | ❌ Not implemented | **GAP** |
| Objection handling quality | ⚠️ `OBJECTION_NOT_HANDLED` driver | Binary only |
| Improvement recommendations | ❌ Not implemented | |
**Gap Details:**
- CXInsights only detects IF objection was handled poorly
- Blueprint requires HOW to improve + specific examples
#### KPI 5: Ciclo de Venta
| Blueprint Requirement | CXInsights Implementation | Gap |
|-----------------------|---------------------------|-----|
| Actions that lengthen sales cycle | ❌ Not implemented | **GAP** |
| Friction points in call | ⚠️ Events (HOLD, SILENCE) | Partial |
| Optimization recommendations | ❌ Not implemented | |
---
## Blueprint 3: Close The Loop (CX Framework)
### What it defines (5 Pillars):
#### Pilar 1: Mejorar Experiencia de Cliente (CX)
| Blueprint Requirement | CXInsights Implementation | Gap |
|-----------------------|---------------------------|-----|
| "Buen CX" / "CX Mejorable" classification | ❌ Not implemented | No binary CX outcome |
| Positive CX factors | ❌ Not implemented | Only negative drivers |
| Friction points | ✅ `poor_cx_drivers[]` | Covered |
| Customer feedback (explicit/implicit) | ❌ Not implemented | |
**Gap Details:**
- CXInsights only captures PROBLEMS (poor CX drivers)
- No capture of positive CX factors
- No explicit CX quality rating
#### Pilar 2: Reducir Fuga de Clientes (Churn)
| Blueprint Requirement | CXInsights Implementation | Gap |
|-----------------------|---------------------------|-----|
| "Sin riesgo de fuga" / "En riesgo de fuga" | ❌ Not implemented | **MAJOR GAP** |
| Churn risk factors | ❌ Not implemented | |
| Origin attribution (company/agent/client) | ❌ Not implemented | |
| Prevention factors | ❌ Not implemented | |
**Gap Details:**
- CXInsights has no churn risk classification
- `CallOutcome.CANCELLATION_*` exists but no risk prediction
- No churn drivers taxonomy
#### Pilar 3: Eficiencia Operativa
| Blueprint Requirement | CXInsights Implementation | Gap |
|-----------------------|---------------------------|-----|
| Process inefficiencies | ⚠️ `COMPLEX_PROCESS`, `SYSTEM_ERROR` | Very partial |
| Agent efficiency analysis | ❌ Not implemented | |
| Optimal efficiency factors | ❌ Not implemented | |
| Process improvement proposals | ❌ Not implemented | |
#### Pilar 4: Valor del Cliente (Customer Value)
| Blueprint Requirement | CXInsights Implementation | Gap |
|-----------------------|---------------------------|-----|
| Explicit customer needs | ❌ Not implemented | **MAJOR GAP** |
| Latent/implicit needs | ❌ Not implemented | |
| Upsell/cross-sell opportunities | ❌ Not implemented | |
| Customer value maximization | ❌ Not implemented | |
#### Pilar 5: Talento Interno
| Blueprint Requirement | CXInsights Implementation | Gap |
|-----------------------|---------------------------|-----|
| Positive agent behaviors | ❌ Not implemented | |
| Agent skills to replicate | ❌ Not implemented | |
| Improvement areas | ⚠️ Agent-related drivers | Limited |
| Coaching recommendations | ❌ Not implemented | |
---
## Blueprint 4: FCR (First Call Resolution)
### What it defines:
| Blueprint Requirement | CXInsights Implementation | Gap |
|-----------------------|---------------------------|-----|
| First call vs repeat call detection | ❌ Not implemented | **CRITICAL GAP** |
| Factors causing repeat calls | ❌ Not implemented | |
| Churn risk combined with FCR | ❌ Not implemented | |
| 4 categories: Primera Llamada (sin/con riesgo) + Rellamada (sin/con riesgo) | ❌ Not implemented | |
**Gap Details:**
- CXInsights has `CALLBACK_REQUIRED` driver but no FCR detection
- No mechanism to identify if call is first contact or repeat
- No churn-FCR cross-analysis
---
## Output Format Comparison
### Blueprint Required Output Structure:
```
Factor Clave 1: [etiqueta breve]
- Descripción objetiva basada en transcripción
- Por qué ayuda/impide [objetivo]
- Qué tiene que hacer el agente para [corregir/replicar]
- Ejemplo/fragmento de la transcripción
Separados por: "··· "
```
### CXInsights Current Output Structure:
```python
RCALabel(
driver_code="PRICE_TOO_HIGH",
confidence=0.85,
evidence_spans=[
EvidenceSpan(
text="Es muy caro para mí",
start_time=45.2,
end_time=47.8
)
],
reasoning="Customer objected to price"
)
```
### Gap:
| Blueprint Field | CXInsights Field | Gap |
|-----------------|------------------|-----|
| Etiqueta breve | `driver_code` | ✅ Equivalent |
| Descripción detallada | `reasoning` | ⚠️ Too brief |
| Acciones correctivas | ❌ Missing | **GAP** |
| Ejemplo con fragmento | `evidence_spans[].text` | ✅ Covered |
| Origin (agent/client/company) | ❌ Missing | **GAP** |
| Max 5 factors | No limit | ⚠️ Different |
---
## Summary: Required Changes
### HIGH PRIORITY (Core Functionality Gaps)
1. **Add FCR Detection Module**
- Detect first call vs repeat call
- Track call reason/topic
- Link to churn risk
2. **Add Churn Risk Classification**
- Binary: "Sin riesgo" / "En riesgo de fuga"
- Churn drivers taxonomy
- Prevention factors
3. **Add Agent Skill Assessment**
- Binary: "Buen Comercial" / "Necesita Mejora"
- Skill dimensions scoring
- Coaching recommendations
4. **Enhance RCALabel Structure**
```python
class RCALabel:
driver_code: str
confidence: float
evidence_spans: list[EvidenceSpan]
reasoning: str
# NEW FIELDS:
origin: Literal["agent", "customer", "company", "process"]
corrective_action: str | None # What to do to fix
replicable_practice: str | None # What to replicate
```
### MEDIUM PRIORITY (Enhanced Analysis)
5. **Add Customer Value Analysis**
- Explicit needs detection
- Implicit/latent needs
- Upsell opportunities
6. **Add Campaign Tracking**
- Campaign ID field
- Campaign effectiveness metrics
- Product-customer fit analysis
7. **Add Positive Factors Output**
- Not just problems (drivers) but also successes
- "Buen CX" factors
- "Effective sales" factors
### LOW PRIORITY (Refinements)
8. **Enhance Output Format**
- Limit to max 5 factors per category
- Add structured corrective actions
- Match blueprint output structure
9. **Add Script Quality Analysis**
- Argumentario quality scoring
- Objection handling quality
- Improvement suggestions
---
## Taxonomy Extension Required
### New Driver Categories Needed:
```yaml
# CHURN RISK DRIVERS (New)
churn_risk:
PRICE_DISSATISFACTION:
description: "Customer unhappy with pricing"
SERVICE_DISSATISFACTION:
description: "Customer unhappy with service"
COMPETITOR_INTEREST:
description: "Customer considering competitors"
CONTRACT_ENDING:
description: "Contract ending soon"
REPEATED_ISSUES:
description: "Customer has called multiple times for same issue"
# FCR DRIVERS (New)
fcr_failure:
INCOMPLETE_RESOLUTION:
description: "Issue not fully resolved"
MISSING_INFORMATION:
description: "Agent didn't provide all needed info"
PENDING_ACTION:
description: "Action pending from company side"
UNCLEAR_NEXT_STEPS:
description: "Customer unclear on what happens next"
# POSITIVE CX FACTORS (New - inverse of poor_cx)
good_cx:
QUICK_RESOLUTION:
description: "Issue resolved quickly"
EMPATHETIC_SERVICE:
description: "Agent showed empathy"
PROACTIVE_HELP:
description: "Agent anticipated needs"
CLEAR_COMMUNICATION:
description: "Agent communicated clearly"
# AGENT SKILLS (New)
agent_skills:
EFFECTIVE_CLOSING:
description: "Agent closed sale effectively"
GOOD_RAPPORT:
description: "Agent built good rapport"
OBJECTION_MASTERY:
description: "Agent handled objections well"
PRODUCT_KNOWLEDGE:
description: "Agent demonstrated product knowledge"
```
---
## Recommended Implementation Phases
### Phase 1: Core Gaps (Essential)
1. Add `churn_risk` field to CallAnalysis
2. Add `fcr_status` field (first_call / repeat_call)
3. Add `origin` field to RCALabel
4. Add `corrective_action` field to RCALabel
### Phase 2: Enhanced Analysis
5. Add positive factors capture (good_cx, effective_sales)
6. Add agent skill classification
7. Add customer value analysis
### Phase 3: Campaign & Optimization
8. Add campaign tracking
9. Add sales cycle analysis
10. Match exact blueprint output format
---
## Files Requiring Modification
| File | Changes Required |
|------|------------------|
| `config/rca_taxonomy.yaml` | Add churn, FCR, positive factors |
| `src/models/call_analysis.py` | Add churn_risk, fcr_status, origin fields |
| `src/inference/prompts.py` | Update prompt for new fields |
| `src/inference/analyzer.py` | Parse new output structure |
| `src/aggregation/` | Aggregate new metrics |
| `src/exports/` | Include new fields in exports |
---
**Última actualización**: 2026-01-19

225
docs/MODULE_GUIDES.md Normal file
View File

@@ -0,0 +1,225 @@
# MODULE_GUIDES.md
> Guía de implementación para cada módulo
---
## Guía: Transcription Module
### Archivos involucrados
```
src/transcription/
├── __init__.py
├── base.py # Interface Transcriber + MockTranscriber
├── assemblyai.py # AssemblyAITranscriber
└── models.py # Transcript, SpeakerTurn, TranscriptMetadata
```
### Cómo funciona
1. Audio file entra al transcriber
2. AssemblyAI procesa con diarización (agent/customer)
3. Retorna `Transcript` con `SpeakerTurn[]` y metadata
### Cómo testear
```bash
pytest tests/unit/test_transcription.py -v
```
### Cómo extender
- Para nuevo provider: implementar `Transcriber` interface
- Para modificar output: editar `models.py`
### Troubleshooting
- "API key invalid" → Check `.env` ASSEMBLYAI_API_KEY
- "Audio format not supported" → Convert to MP3/WAV
---
## Guía: Feature Extraction Module
### Archivos involucrados
```
src/features/
├── __init__.py
├── event_detector.py # HOLD, TRANSFER, SILENCE detection
└── turn_metrics.py # Talk ratio, interruptions
```
### Cómo funciona
1. Transcript entra
2. Regex + reglas detectan eventos (HOLD, TRANSFER, etc.)
3. Métricas calculadas (talk ratio, speaking time)
4. Transcript enriquecido con `detected_events[]`
### Eventos soportados
- `HOLD_START` / `HOLD_END`
- `TRANSFER`
- `ESCALATION`
- `SILENCE` (> umbral)
- `INTERRUPTION`
### Cómo testear
```bash
pytest tests/unit/test_features.py -v
```
---
## Guía: Compression Module
### Archivos involucrados
```
src/compression/
├── __init__.py
├── compressor.py # TranscriptCompressor
└── models.py # CompressedTranscript, CustomerIntent, etc.
```
### Cómo funciona
1. Transcript completo entra
2. Regex español extrae:
- Customer intents (cancelar, consultar)
- Agent offers (descuento, upgrade)
- Objections (precio, competencia)
- Resolutions
3. Genera `CompressedTranscript` con >60% reducción
### Patrones español
```python
INTENT_PATTERNS = {
IntentType.CANCEL: [r"quiero\s+cancelar", r"dar\s+de\s+baja"],
IntentType.INQUIRY: [r"quería\s+saber", r"información\s+sobre"],
}
```
### Cómo testear
```bash
pytest tests/unit/test_compression.py -v
```
---
## Guía: Inference Module
### Archivos involucrados
```
src/inference/
├── __init__.py
├── analyzer.py # CallAnalyzer (main class)
├── llm_client.py # OpenAIClient
└── prompts.py # Spanish MAP prompt
```
### Cómo funciona
1. CompressedTranscript entra
2. Prompt construido con taxonomía + transcript
3. LLM genera JSON con:
- `outcome`
- `lost_sales_drivers[]` con evidence
- `poor_cx_drivers[]` con evidence
4. Response parseada a `CallAnalysis`
### Configuración
```python
AnalyzerConfig(
model="gpt-4o-mini",
use_compression=True,
max_concurrent=5,
)
```
### Cómo testear
```bash
pytest tests/unit/test_inference.py -v
```
---
## Guía: Aggregation Module
### Archivos involucrados
```
src/aggregation/
├── __init__.py
├── statistics.py # StatisticsCalculator
├── severity.py # SeverityCalculator
├── rca_tree.py # RCATreeBuilder
└── models.py # DriverFrequency, RCATree, etc.
```
### Cómo funciona
1. List[CallAnalysis] entra
2. Statistics: frecuencias por driver
3. Severity: puntuación ponderada
4. RCA Tree: árbol jerárquico ordenado
### Fórmula de severidad
```python
severity = (
base_severity * 0.4 +
frequency_factor * 0.3 +
confidence_factor * 0.2 +
co_occurrence_factor * 0.1
) * 100
```
### Cómo testear
```bash
pytest tests/unit/test_aggregation.py -v
```
---
## Guía: Pipeline Module
### Archivos involucrados
```
src/pipeline/
├── __init__.py
├── models.py # PipelineManifest, StageManifest, Config
└── pipeline.py # CXInsightsPipeline
```
### Stages
1. TRANSCRIPTION
2. FEATURE_EXTRACTION
3. COMPRESSION
4. INFERENCE
5. AGGREGATION
6. EXPORT
### Resume
- Manifest JSON guardado por batch
- `get_resume_stage()` detecta dónde continuar
### Cómo testear
```bash
pytest tests/unit/test_pipeline.py -v
```
---
## Guía: Exports Module
### Archivos involucrados
```
src/exports/
├── __init__.py
├── json_export.py # Summary + analyses
├── excel_export.py # Multi-sheet workbook
└── pdf_export.py # HTML executive report
```
### Formatos
- **JSON**: `summary.json` + `analyses/*.json`
- **Excel**: 5 sheets (Summary, Lost Sales, Poor CX, Details, Patterns)
- **PDF/HTML**: Executive report con métricas
### Cómo testear
```bash
pytest tests/unit/test_pipeline.py::TestExports -v
```
---
**Última actualización**: 2026-01-19

271
docs/PROJECT_CONTEXT.md Normal file
View File

@@ -0,0 +1,271 @@
# PROJECT_CONTEXT.md
> **Este archivo es tu 'norte'. SIEMPRE léelo primero.**
---
## 1. ¿Qué es CXInsights?
CXInsights es un pipeline standalone para analizar grabaciones de call centers en español (5,000-20,000 llamadas por batch), identificando automáticamente las causas raíz de ventas perdidas y mala experiencia de cliente mediante transcripción, extracción de features, inferencia LLM y agregación estadística.
---
## 2. Problema que resuelve
**Para quién:** Equipos de análisis de call centers (BeyondCX.ai → Entelgy pilot)
**Por qué importa:**
- Miles de llamadas diarias imposibles de revisar manualmente
- Causas de pérdida de ventas ocultas en conversaciones
- Métricas de CX basadas en surveys, no en comportamiento real
- Necesidad de insights accionables con evidencia verificable
---
## 3. Estado actual del proyecto
| Campo | Valor |
|-------|-------|
| **Última actualización** | 2026-01-19 |
| **Fase** | Production Ready (v2.1 Dashboard + Blueprint Compliance) |
| **Completitud** | 100% (9/9 checkpoints + Dashboard) |
### Checkpoints completados
- [x] CP1: Project Setup & Contracts
- [x] CP2: Transcription Module
- [x] CP3: RCA Schemas & Data Contracts
- [x] CP4: Feature & Event Extraction
- [x] CP5: Inference Engine
- [x] CP6: Transcript Compression
- [x] CP7: Aggregation & RCA Trees
- [x] CP8: End-to-End Pipeline & Delivery
- [x] **CP-GAPS: v2.0 Blueprint Alignment** (2026-01-19)
- [x] **CP-DASH: Streamlit Dashboard** ← NEW (2026-01-19)
### Checkpoints pendientes
- [ ] CP9: Optimization & Benchmarking (OPTIONAL)
### v2.0 Blueprint Alignment (completado 2026-01-19)
- [x] Gap Analysis vs BeyondCX Blueprints (4 docs)
- [x] FCR Detection Module (FIRST_CALL/REPEAT_CALL/UNKNOWN)
- [x] Churn Risk Classification (NO_RISK/AT_RISK/UNKNOWN)
- [x] Agent Skill Assessment (GOOD_PERFORMER/NEEDS_IMPROVEMENT/MIXED)
- [x] Enhanced RCALabel with origin, corrective_action, replicable_practice
- [x] Prompt v2.0 with all new fields
- [x] Updated aggregation statistics for v2.0 metrics
### Dashboard Streamlit (completado 2026-01-19)
- [x] Beyond Brand Identity styling (colores, tipografía)
- [x] 8 secciones: Overview, Outcomes, Poor CX, FCR, Churn, Agent, Call Explorer, Export
- [x] RCA Sankey Diagram (Driver → Outcome → Churn Risk)
- [x] Correlation Heatmaps (co-occurrence, driver-outcome)
- [x] Outcome Deep Dive (root causes, correlation, duration analysis)
- [x] Export functionality (Excel, HTML, JSON)
- [x] Blueprint terminology compliance (FCR 4 categorías, Churn Sin/En Riesgo, Talento)
---
## 4. Stack tecnológico (decisiones tomadas)
| Componente | Decisión | Rationale |
|------------|----------|-----------|
| **STT** | AssemblyAI | Best Spanish diarization, competitive cost (~$0.04/call) |
| **LLM** | OpenAI GPT-4o-mini (default) | Cost-effective, JSON strict mode, good Spanish |
| **Data Models** | Pydantic v2 | Type safety, validation, serialization |
| **Storage** | Filesystem JSON | Simplicity, debuggability, checkpoint/resume |
| **Async** | asyncio + aiohttp | Batch processing with rate limiting |
| **Deploy** | Local Python CLI | Phase 1 MVP, no infrastructure overhead |
| **Excel Export** | openpyxl | Standard, no external dependencies |
| **PDF Export** | HTML fallback (weasyprint optional) | Works without system dependencies |
| **Dashboard** | Streamlit + Plotly | Rapid development, interactive charts |
| **Brand Styling** | Custom CSS + Beyond colors | Corporate identity compliance |
---
## 5. Estructura del proyecto (mapa mental)
```
cxinsights/
├── cli.py [✅ Done] Main entry point
├── src/
│ ├── transcription/ [✅ Done] AssemblyAI STT with diarization
│ │ ├── base.py Interface + MockTranscriber
│ │ ├── assemblyai.py AssemblyAI implementation
│ │ └── models.py Transcript, SpeakerTurn
│ ├── features/ [✅ Done] Deterministic event extraction
│ │ ├── event_detector.py HOLD, TRANSFER, SILENCE detection
│ │ └── turn_metrics.py Talk ratio, interruptions
│ ├── compression/ [✅ Done] Token reduction (>60%)
│ │ ├── compressor.py Spanish regex patterns
│ │ └── models.py CompressedTranscript
│ ├── inference/ [✅ Done] LLM-based RCA extraction
│ │ ├── analyzer.py CallAnalyzer with batch processing
│ │ ├── llm_client.py OpenAI client with retry/repair
│ │ └── prompts.py Spanish MAP prompt
│ ├── aggregation/ [✅ Done] Statistics & RCA trees
│ │ ├── statistics.py Frequency calculations
│ │ ├── severity.py Weighted severity scoring
│ │ └── rca_tree.py Deterministic tree builder
│ ├── pipeline/ [✅ Done] Orchestration
│ │ ├── models.py Manifest, Config, Stages
│ │ └── pipeline.py CXInsightsPipeline
│ ├── exports/ [✅ Done] Output generation
│ │ ├── json_export.py Summary + individual analyses
│ │ ├── excel_export.py Multi-sheet workbook
│ │ └── pdf_export.py Executive HTML report
│ └── models/ [✅ Done] Core data contracts
│ └── call_analysis.py CallAnalysis, RCALabel, Evidence
├── config/
│ ├── rca_taxonomy.yaml [✅ Done] Lost Sales + Poor CX drivers
│ └── settings.yaml [✅ Done] Batch size, limits, retries
├── tests/
│ └── unit/ [✅ Done] Comprehensive test suite
├── notebooks/ [✅ Done] Validation notebooks 01-05
├── dashboard/ [✅ Done] Streamlit visualization
│ ├── app.py Main dashboard application
│ ├── config.py Beyond brand colors, CSS
│ ├── data_loader.py Batch data loading utilities
│ ├── components.py Plotly visualization components
│ └── exports.py Export functionality
├── .streamlit/
│ └── config.toml [✅ Done] Theme configuration
└── data/
├── examples/ [✅ Done] Sample CallAnalysis JSONs
└── output/ Generated results go here
```
---
## 6. Cómo navegar este proyecto (para Claude Code)
| Si necesitas... | Lee... |
|-----------------|--------|
| Entender arquitectura | `docs/ARCHITECTURE.md` |
| Implementar features | `docs/MODULE_GUIDES.md` |
| Decisiones técnicas | `docs/TECHNICAL_DECISIONS.md` |
| Troubleshooting | `docs/TROUBLESHOOTING.md` |
| Costs/performance | `docs/BENCHMARKS.md` |
| Schemas de datos | `docs/DATA_CONTRACTS.md` |
| Empezar rápido | `docs/QUICK_START.md` |
---
## 7. Contexto de negocio
| Campo | Valor |
|-------|-------|
| **Usuario principal** | BeyondCX.ai team (Susana) |
| **Cliente objetivo** | Entelgy (demo/pilot) |
| **Idioma de llamadas** | Español (España/LATAM) |
| **Volumen típico** | 5,000-20,000 llamadas por batch |
### KPIs críticos
| KPI | Target | Status |
|-----|--------|--------|
| Cost per call | < €0.50 | TBD (benchmark pending) |
| Processing time | < 24h for 5k calls | TBD |
| RCA accuracy | > 80% (manual validation) | TBD |
---
## 8. Decisiones pendientes (para no repetir análisis)
| Decisión | Status | Notas |
|----------|--------|-------|
| AssemblyAI como STT provider | ✅ DECIDED | Best Spanish diarization |
| OpenAI GPT-4o-mini como LLM default | ✅ DECIDED | Cost-effective, configurable |
| Dashboard Streamlit | ✅ DECIDED | Implemented with Beyond branding |
| Multi-idioma support | ⏳ PENDING | Fase 2 |
| DuckDB para analytics | ⏳ PENDING | Consider for large batches |
---
## 9. Prohibiciones (para evitar sobre-ingeniería)
-**NO** diseñar para integración con BeyondDiagnosticPrototipo (Fase 2)
-**NO** asumir outcome labels (sale, churn) disponibles en audio
-**NO** implementar features sin validar con usuario
-**NO** cambiar taxonomía RCA sin aprobación explícita
-**NO** añadir dependencias pesadas (Docker, DBs) en Fase 1
-**NO** optimizar prematuramente sin benchmarks reales
---
## 10. Principios de diseño (inmutables)
### OBSERVED vs INFERRED
Todo dato se clasifica como:
- **OBSERVED**: Determinístico, extraído sin LLM (duración, eventos, métricas)
- **INFERRED**: Requiere LLM, DEBE tener `evidence_spans[]` con timestamps
### Evidence-backed RCA
```
RCALabel SIN evidence = RECHAZADO
```
Cada driver inferido requiere:
- `driver_code`: Código de taxonomía
- `confidence`: 0.0-1.0 (< 0.6 si evidencia débil)
- `evidence_spans[]`: Mínimo 1 span con texto y timestamps
### Traceability
Todo output incluye:
```python
Traceability(
schema_version="1.0.0",
prompt_version="v2.0", # Updated from v1.0
model_id="gpt-4o-mini"
)
```
### v2.0 Analysis Dimensions
El prompt v2.0 (Blueprint-aligned) incluye:
- **FCR Status**: FIRST_CALL / REPEAT_CALL / UNKNOWN
- **Churn Risk**: NO_RISK / AT_RISK / UNKNOWN
- **Agent Classification**: GOOD_PERFORMER / NEEDS_IMPROVEMENT / MIXED
- **Driver Origin**: AGENT / CUSTOMER / COMPANY / PROCESS
---
## 11. Comandos principales
```bash
# Ejecutar pipeline completo
python cli.py run my_batch -i data/audio -o data/output
# Ver estado de un batch
python cli.py status my_batch
# Con opciones específicas
python cli.py run my_batch --model gpt-4o --formats json,excel,pdf
# Sin compresión (más tokens, más costo)
python cli.py run my_batch --no-compression
# Sin resume (empezar de cero)
python cli.py run my_batch --no-resume
# Lanzar dashboard de visualización
python -m streamlit run dashboard/app.py
# Dashboard disponible en http://localhost:8510
```
---
## 12. Archivos críticos (no modificar sin revisión)
| Archivo | Razón |
|---------|-------|
| `config/rca_taxonomy.yaml` | Define todos los drivers - cambios afectan inferencia |
| `src/models/call_analysis.py` | Contrato central - cambios rompen downstream |
| `src/inference/prompts.py` | Prompt MAP - cambios afectan calidad RCA |
| `src/aggregation/severity.py` | Fórmula de severidad - cambios afectan priorización |
---
**Última actualización**: 2026-01-19 | **Autor**: Claude Code | **Versión**: 2.0.0 (Blueprint Aligned)

574
docs/PROJECT_STRUCTURE.md Normal file
View File

@@ -0,0 +1,574 @@
# CXInsights - Estructura del Proyecto
## Árbol de Carpetas Completo
```
cxinsights/
├── 📁 data/ # Datos (ignorado en git excepto .gitkeep)
│ ├── raw/ # Input original
│ │ ├── audio/ # Archivos de audio (.mp3, .wav)
│ │ │ └── batch_2024_01/
│ │ │ ├── call_001.mp3
│ │ │ └── ...
│ │ └── metadata/ # CSV con metadatos opcionales
│ │ └── calls_metadata.csv
│ │
│ ├── transcripts/ # Output de STT
│ │ └── batch_2024_01/
│ │ ├── raw/ # Transcripciones originales del STT
│ │ │ └── call_001.json
│ │ └── compressed/ # Transcripciones reducidas para LLM
│ │ └── call_001.json
│ │
│ ├── features/ # Output de extracción de features (OBSERVED)
│ │ └── batch_2024_01/
│ │ └── call_001_features.json
│ │
│ ├── processed/ # Output de LLM (Labels con INFERRED)
│ │ └── batch_2024_01/
│ │ └── call_001_labels.json
│ │
│ ├── outputs/ # Output final
│ │ └── batch_2024_01/
│ │ ├── aggregated_stats.json
│ │ ├── call_matrix.csv
│ │ ├── rca_lost_sales.json
│ │ ├── rca_poor_cx.json
│ │ ├── emergent_drivers_review.json
│ │ ├── executive_summary.pdf
│ │ ├── full_analysis.xlsx
│ │ └── figures/
│ │ ├── rca_tree_lost_sales.png
│ │ └── rca_tree_poor_cx.png
│ │
│ ├── .checkpoints/ # Estado del pipeline para resume
│ │ ├── transcription_state.json
│ │ ├── features_state.json
│ │ ├── inference_state.json
│ │ └── pipeline_state.json
│ │
│ └── logs/ # Logs de ejecución
│ └── pipeline_2024_01_15.log
├── 📁 src/ # Código fuente
│ ├── __init__.py
│ │
│ ├── 📁 transcription/ # Module 1: STT (SOLO transcripción)
│ │ ├── __init__.py
│ │ ├── base.py # Interface abstracta Transcriber
│ │ ├── assemblyai_client.py # Implementación AssemblyAI
│ │ ├── whisper_client.py # Implementación Whisper (futuro)
│ │ ├── batch_processor.py # Procesamiento paralelo
│ │ ├── compressor.py # SOLO reducción de texto para LLM
│ │ └── models.py # Pydantic models: TranscriptContract
│ │
│ ├── 📁 features/ # Module 2: Extracción OBSERVED
│ │ ├── __init__.py
│ │ ├── turn_metrics.py # talk ratio, interruptions, silence duration
│ │ ├── event_detector.py # HOLD, TRANSFER, SILENCE events
│ │ └── models.py # Pydantic models: ObservedFeatures, Event
│ │
│ ├── 📁 inference/ # Module 3: LLM Analysis (INFERRED)
│ │ ├── __init__.py
│ │ ├── client.py # OpenAI/Anthropic client wrapper
│ │ ├── prompt_manager.py # Carga y renderiza prompts versionados
│ │ ├── analyzer.py # Análisis por llamada → CallLabels
│ │ ├── batch_analyzer.py # Procesamiento en lote con rate limiting
│ │ ├── rca_synthesizer.py # (opcional) Síntesis narrativa del RCA vía LLM
│ │ └── models.py # CallLabels, InferredData, EvidenceSpan
│ │
│ ├── 📁 validation/ # Module 4: Quality Gate
│ │ ├── __init__.py
│ │ ├── validator.py # Validación de evidence_spans, taxonomy, etc.
│ │ ├── schema_checker.py # Verificación de schema_version
│ │ └── models.py # ValidationResult, ValidationError
│ │
│ ├── 📁 aggregation/ # Module 5-6: Stats + RCA (DETERMINÍSTICO)
│ │ ├── __init__.py
│ │ ├── stats_engine.py # Cálculos estadísticos (pandas + DuckDB)
│ │ ├── rca_builder.py # Construcción DETERMINÍSTICA del árbol RCA
│ │ ├── emergent_collector.py # Recolección de OTHER_EMERGENT para revisión
│ │ ├── correlations.py # Análisis de correlaciones observed↔inferred
│ │ └── models.py # AggregatedStats, RCATree, RCANode
│ │
│ ├── 📁 visualization/ # Module 7: Reports (SOLO presentación)
│ │ ├── __init__.py
│ │ ├── dashboard.py # Streamlit app
│ │ ├── charts.py # Generación de gráficos (plotly/matplotlib)
│ │ ├── tree_renderer.py # Visualización de árboles RCA como PNG/SVG
│ │ ├── pdf_report.py # Generación PDF ejecutivo
│ │ └── excel_export.py # Export a Excel con drill-down
│ │
│ ├── 📁 pipeline/ # Orquestación
│ │ ├── __init__.py
│ │ ├── orchestrator.py # Pipeline principal
│ │ ├── stages.py # Definición de stages
│ │ ├── checkpoint.py # Gestión de checkpoints
│ │ └── cli.py # Interfaz de línea de comandos
│ │
│ └── 📁 utils/ # Utilidades compartidas
│ ├── __init__.py
│ ├── file_io.py # Lectura/escritura de archivos
│ ├── logging_config.py # Setup de logging
│ └── validators.py # Validación de archivos de audio
├── 📁 config/ # Configuración
│ ├── rca_taxonomy.yaml # Taxonomía cerrada de drivers (versionada)
│ ├── settings.yaml # Config general (no secrets)
│ │
│ └── 📁 prompts/ # Templates de prompts LLM (versionados)
│ ├── versions.yaml # Registry de versiones activas
│ ├── call_analysis/
│ │ └── v1.2/
│ │ ├── system.txt
│ │ ├── user.txt
│ │ └── schema.json
│ └── rca_synthesis/
│ └── v1.0/
│ ├── system.txt
│ └── user.txt
├── 📁 tests/ # Tests
│ ├── __init__.py
│ ├── conftest.py # Fixtures compartidas
│ │
│ ├── 📁 fixtures/ # Datos de prueba
│ │ ├── sample_audio/
│ │ │ └── test_call.mp3
│ │ ├── sample_transcripts/
│ │ │ ├── raw/
│ │ │ └── compressed/
│ │ ├── sample_features/
│ │ └── expected_outputs/
│ │
│ ├── 📁 unit/ # Tests unitarios
│ │ ├── test_transcription.py
│ │ ├── test_features.py
│ │ ├── test_inference.py
│ │ ├── test_validation.py
│ │ ├── test_aggregation.py
│ │ └── test_visualization.py
│ │
│ └── 📁 integration/ # Tests de integración
│ └── test_pipeline.py
├── 📁 notebooks/ # Jupyter notebooks para EDA
│ ├── 01_eda_transcripts.ipynb
│ ├── 02_feature_exploration.ipynb
│ ├── 03_prompt_testing.ipynb
│ ├── 04_aggregation_validation.ipynb
│ └── 05_visualization_prototypes.ipynb
├── 📁 scripts/ # Scripts auxiliares
│ ├── estimate_costs.py # Estimador de costes antes de ejecutar
│ ├── validate_audio.py # Validar archivos de audio
│ └── sample_calls.py # Extraer muestra para testing
├── 📁 docs/ # Documentación
│ ├── ARCHITECTURE.md
│ ├── TECH_STACK.md
│ ├── PROJECT_STRUCTURE.md # Este documento
│ ├── DEPLOYMENT.md
│ └── PROMPTS.md # Documentación de prompts
├── .env.example # Template de variables de entorno
├── .gitignore
├── pyproject.toml # Dependencias y metadata
├── Makefile # Comandos útiles
└── README.md # Documentación principal
```
---
## Responsabilidades por Módulo
### 📁 `src/transcription/`
**Propósito**: Convertir audio a texto con diarización. **SOLO STT, sin analítica.**
| Archivo | Responsabilidad |
|---------|-----------------|
| `base.py` | Interface abstracta `Transcriber`. Define contrato de salida. |
| `assemblyai_client.py` | Implementación AssemblyAI. Maneja auth, upload, polling. |
| `whisper_client.py` | Implementación Whisper local (futuro). |
| `batch_processor.py` | Procesa N archivos en paralelo. Gestiona concurrencia. |
| `compressor.py` | **SOLO reducción de texto**: quita muletillas, normaliza, acorta para LLM. **NO extrae features.** |
| `models.py` | `TranscriptContract`, `Utterance`, `Speaker` - schemas Pydantic. |
**Interfaces principales**:
```python
class Transcriber(ABC):
"""Interface abstracta - permite cambiar proveedor STT sin refactor."""
async def transcribe(self, audio_path: Path) -> TranscriptContract
async def transcribe_batch(self, paths: list[Path]) -> list[TranscriptContract]
class TranscriptCompressor:
"""SOLO reduce texto. NO calcula métricas ni detecta eventos."""
def compress(self, transcript: TranscriptContract) -> CompressedTranscript
```
**Output**:
- `data/transcripts/raw/{call_id}.json` → Transcripción original del STT
- `data/transcripts/compressed/{call_id}.json` → Texto reducido para LLM
---
### 📁 `src/features/`
**Propósito**: Extracción **determinística** de métricas y eventos desde transcripts. **100% OBSERVED.**
| Archivo | Responsabilidad |
|---------|-----------------|
| `turn_metrics.py` | Calcula: talk_ratio, interruption_count, silence_total_seconds, avg_turn_duration. |
| `event_detector.py` | Detecta eventos observables: HOLD_START, HOLD_END, TRANSFER, SILENCE, CROSSTALK. |
| `models.py` | `ObservedFeatures`, `ObservedEvent`, `TurnMetrics`. |
**Interfaces principales**:
```python
class TurnMetricsExtractor:
"""Calcula métricas de turno desde utterances."""
def extract(self, transcript: TranscriptContract) -> TurnMetrics
class EventDetector:
"""Detecta eventos observables (silencios, holds, transfers)."""
def detect(self, transcript: TranscriptContract) -> list[ObservedEvent]
```
**Output**:
- `data/features/{call_id}_features.json` → Métricas y eventos OBSERVED
**Nota**: Este módulo **NO usa LLM**. Todo es cálculo determinístico sobre el transcript.
---
### 📁 `src/inference/`
**Propósito**: Analizar transcripciones con LLM para extraer **datos INFERRED**.
| Archivo | Responsabilidad |
|---------|-----------------|
| `client.py` | Wrapper sobre OpenAI/Anthropic SDK. Maneja retries, rate limiting. |
| `prompt_manager.py` | Carga templates versionados, renderiza con variables, valida schema. |
| `analyzer.py` | Análisis de una llamada → `CallLabels` con separación observed/inferred. |
| `batch_analyzer.py` | Procesa N llamadas con rate limiting y checkpoints. |
| `rca_synthesizer.py` | **(Opcional)** Síntesis narrativa del RCA tree vía LLM. NO construye el árbol. |
| `models.py` | `CallLabels`, `InferredData`, `EvidenceSpan`, `JourneyEvent`. |
**Interfaces principales**:
```python
class CallAnalyzer:
"""Genera labels INFERRED con evidence_spans obligatorias."""
async def analyze(self, transcript: CompressedTranscript, features: ObservedFeatures) -> CallLabels
class RCASynthesizer:
"""(Opcional) Genera narrativa ejecutiva sobre RCA tree ya construido."""
async def synthesize_narrative(self, rca_tree: RCATree) -> str
```
**Output**:
- `data/processed/{call_id}_labels.json` → Labels con observed + inferred
---
### 📁 `src/validation/`
**Propósito**: Quality gate antes de agregación. Rechaza datos inválidos.
| Archivo | Responsabilidad |
|---------|-----------------|
| `validator.py` | Valida: evidence_spans presente, rca_code en taxonomía, confidence > umbral. |
| `schema_checker.py` | Verifica que schema_version y prompt_version coinciden con esperados. |
| `models.py` | `ValidationResult`, `ValidationError`. |
**Interfaces principales**:
```python
class CallLabelsValidator:
"""Valida CallLabels antes de agregación."""
def validate(self, labels: CallLabels) -> ValidationResult
# Reglas:
# - Driver sin evidence_spans → RECHAZADO
# - rca_code no en taxonomía → marca como OTHER_EMERGENT o ERROR
# - schema_version mismatch → ERROR
```
---
### 📁 `src/aggregation/`
**Propósito**: Consolidar labels validados en estadísticas y RCA trees. **DETERMINÍSTICO, no usa LLM.**
| Archivo | Responsabilidad |
|---------|-----------------|
| `stats_engine.py` | Cálculos: distribuciones, percentiles, cross-tabs. Usa pandas + DuckDB. |
| `rca_builder.py` | **Construcción DETERMINÍSTICA** del árbol RCA a partir de stats y taxonomía. NO usa LLM. |
| `emergent_collector.py` | Recolecta `OTHER_EMERGENT` para revisión manual y posible promoción a taxonomía. |
| `correlations.py` | Análisis de correlaciones entre observed_features e inferred_outcomes. |
| `models.py` | `AggregatedStats`, `RCATree`, `RCANode`, `Correlation`. |
**Interfaces principales**:
```python
class StatsEngine:
"""Agrega labels validados en estadísticas."""
def aggregate(self, labels: list[CallLabels]) -> AggregatedStats
class RCABuilder:
"""Construye árbol RCA de forma DETERMINÍSTICA (conteo + jerarquía de taxonomía)."""
def build_lost_sales_tree(self, stats: AggregatedStats, taxonomy: RCATaxonomy) -> RCATree
def build_poor_cx_tree(self, stats: AggregatedStats, taxonomy: RCATaxonomy) -> RCATree
class EmergentCollector:
"""Recolecta OTHER_EMERGENT para revisión humana."""
def collect(self, labels: list[CallLabels]) -> EmergentDriversReport
```
**Nota sobre RCA**:
- `rca_builder.py`**Determinístico**: cuenta ocurrencias, agrupa por taxonomía, calcula porcentajes
- `inference/rca_synthesizer.py`**(Opcional) LLM**: genera texto narrativo sobre el árbol ya construido
---
### 📁 `src/visualization/`
**Propósito**: Capa de salida. Genera reportes visuales. **NO recalcula métricas ni inferencias.**
| Archivo | Responsabilidad |
|---------|-----------------|
| `dashboard.py` | App Streamlit: filtros, gráficos interactivos, drill-down. |
| `charts.py` | Funciones para generar gráficos (plotly/matplotlib). |
| `tree_renderer.py` | Visualización de árboles RCA como PNG/SVG. |
| `pdf_report.py` | Generación de PDF ejecutivo con ReportLab. |
| `excel_export.py` | Export a Excel con múltiples hojas y formato. |
**Restricción crítica**: Este módulo **SOLO presenta datos pre-calculados**. No contiene lógica analítica.
**Interfaces principales**:
```python
class ReportGenerator:
"""Genera reportes a partir de datos ya calculados."""
def generate_pdf(self, stats: AggregatedStats, trees: dict[str, RCATree]) -> Path
def generate_excel(self, labels: list[CallLabels], stats: AggregatedStats) -> Path
class TreeRenderer:
"""Renderiza RCATree como imagen."""
def render_png(self, tree: RCATree, output_path: Path) -> None
```
---
### 📁 `src/pipeline/`
**Propósito**: Orquestar el flujo completo de ejecución.
| Archivo | Responsabilidad |
|---------|-----------------|
| `orchestrator.py` | Ejecuta stages en orden, maneja errores, logging. |
| `stages.py` | Define cada stage: `transcribe`, `extract_features`, `analyze`, `validate`, `aggregate`, `report`. |
| `checkpoint.py` | Guarda/carga estado para resume. |
| `cli.py` | Interfaz CLI con argparse/typer. |
---
### 📁 `src/utils/`
**Propósito**: Funciones auxiliares compartidas.
| Archivo | Responsabilidad |
|---------|-----------------|
| `file_io.py` | Lectura/escritura JSON, CSV, audio. Glob patterns. |
| `logging_config.py` | Setup de logging estructurado (consola + archivo). |
| `validators.py` | Validación de archivos de audio (formato, duración). |
---
## Modelo de Datos (Output Artifacts)
### Estructura mínima obligatoria de `labels.json`
Todo archivo `{call_id}_labels.json` **SIEMPRE** incluye estos campos:
```json
{
"_meta": {
"schema_version": "1.0.0", // OBLIGATORIO - versión del schema
"prompt_version": "v1.2", // OBLIGATORIO - versión del prompt usado
"model_id": "gpt-4o-mini", // OBLIGATORIO - modelo LLM usado
"processed_at": "2024-01-15T10:35:00Z"
},
"call_id": "c001", // OBLIGATORIO
"observed": { // OBLIGATORIO - datos del STT/features
"duration_seconds": 245,
"agent_talk_pct": 0.45,
"customer_talk_pct": 0.55,
"silence_total_seconds": 38,
"hold_events": [...],
"transfer_count": 0
},
"inferred": { // OBLIGATORIO - datos del LLM
"intent": { "code": "...", "confidence": 0.91, "evidence_spans": [...] },
"outcome": { "code": "...", "confidence": 0.85, "evidence_spans": [...] },
"lost_sale_driver": { ... } | null,
"poor_cx_driver": { ... } | null,
"sentiment": { ... },
"agent_quality": { ... },
"summary": "..."
},
"events": [ // OBLIGATORIO - timeline estructurado
{"type": "CALL_START", "t": "00:00", "source": "observed"},
{"type": "HOLD_START", "t": "02:14", "source": "observed"},
{"type": "PRICE_OBJECTION", "t": "03:55", "source": "inferred"},
...
]
}
```
### Sobre `events[]`
`events[]` es una **lista estructurada de eventos normalizados**, NO texto libre.
Cada evento tiene:
- `type`: Código del enum (`HOLD_START`, `TRANSFER`, `ESCALATION`, `NEGATIVE_SENTIMENT_PEAK`, etc.)
- `t`: Timestamp en formato `MM:SS` o `HH:MM:SS`
- `source`: `"observed"` (viene de STT/features) o `"inferred"` (viene de LLM)
Tipos de eventos válidos definidos en `config/rca_taxonomy.yaml`:
```yaml
journey_event_types:
observed:
- CALL_START
- CALL_END
- HOLD_START
- HOLD_END
- TRANSFER
- SILENCE
- CROSSTALK
inferred:
- INTENT_STATED
- PRICE_OBJECTION
- COMPETITOR_MENTION
- NEGATIVE_SENTIMENT_PEAK
- RESOLUTION_ATTEMPT
- SOFT_DECLINE
- ESCALATION_REQUEST
```
---
## Flujo de Datos entre Módulos
```
┌─────────────────────────────────────────────────────────────────────────────┐
│ DATA FLOW │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ data/raw/audio/*.mp3 │
│ │ │
│ ▼ │
│ ┌───────────────┐ │
│ │ transcription │ → data/transcripts/raw/*.json │
│ │ (STT only) │ → data/transcripts/compressed/*.json │
│ └───────────────┘ │
│ │ │
│ ▼ │
│ ┌───────────────┐ │
│ │ features │ → data/features/*_features.json │
│ │ (OBSERVED) │ (turn_metrics + detected_events) │
│ └───────────────┘ │
│ │ │
│ ▼ │
│ ┌───────────────┐ │
│ │ inference │ → data/processed/*_labels.json │
│ │ (INFERRED) │ (observed + inferred + events) │
│ └───────────────┘ │
│ │ │
│ ▼ │
│ ┌───────────────┐ │
│ │ validation │ → rechaza labels sin evidence_spans │
│ │ (quality gate)│ → marca low_confidence │
│ └───────────────┘ │
│ │ │
│ ▼ │
│ ┌───────────────┐ │
│ │ aggregation │ → data/outputs/aggregated_stats.json │
│ │(DETERMINISTIC)│ → data/outputs/rca_*.json │
│ └───────────────┘ → data/outputs/emergent_drivers_review.json │
│ │ │
│ ▼ │
│ ┌───────────────┐ │
│ │ visualization │ → data/outputs/executive_summary.pdf │
│ │(PRESENTATION) │ → data/outputs/full_analysis.xlsx │
│ └───────────────┘ → http://localhost:8501 (dashboard) │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
```
---
## Separación de Responsabilidades (Resumen)
| Capa | Módulo | Tipo de Lógica | Usa LLM |
|------|--------|----------------|---------|
| STT | `transcription/` | Conversión audio→texto | No |
| Texto | `transcription/compressor.py` | Reducción de texto | No |
| Features | `features/` | Extracción determinística | No |
| Análisis | `inference/analyzer.py` | Clasificación + evidencia | **Sí** |
| Narrativa | `inference/rca_synthesizer.py` | Síntesis textual (opcional) | **Sí** |
| Validación | `validation/` | Reglas de calidad | No |
| Agregación | `aggregation/` | Estadísticas + RCA tree | No |
| Presentación | `visualization/` | Reportes + dashboard | No |
---
## Convenciones de Código
### Naming
- **Archivos**: `snake_case.py`
- **Clases**: `PascalCase`
- **Funciones/métodos**: `snake_case`
- **Constantes**: `UPPER_SNAKE_CASE`
### Type hints
Usar type hints en todas las funciones públicas. Pydantic para validación de datos.
### Ejemplo de estructura de módulo
```python
# src/features/turn_metrics.py
"""Deterministic extraction of turn-based metrics from transcripts."""
from __future__ import annotations
import logging
from dataclasses import dataclass
from src.transcription.models import TranscriptContract
logger = logging.getLogger(__name__)
@dataclass
class TurnMetrics:
"""Observed metrics extracted from transcript turns."""
agent_talk_pct: float
customer_talk_pct: float
silence_total_seconds: float
interruption_count: int
avg_turn_duration_seconds: float
class TurnMetricsExtractor:
"""Extracts turn metrics from transcript. 100% deterministic, no LLM."""
def extract(self, transcript: TranscriptContract) -> TurnMetrics:
"""Extract turn metrics from transcript utterances."""
utterances = transcript.observed.utterances
# ... cálculos determinísticos ...
return TurnMetrics(...)
```

229
docs/QUICK_START.md Normal file
View File

@@ -0,0 +1,229 @@
# QUICK_START.md
> Para que Claude Code (o cualquier dev) empiece rápido
---
## Para entender el proyecto (5 min)
### Paso 1: Lee PROJECT_CONTEXT.md (2 min)
```
docs/PROJECT_CONTEXT.md
```
Contiene: qué es, estado actual, stack, estructura, prohibiciones.
### Paso 2: Lee ARCHITECTURE.md (2 min)
```
docs/ARCHITECTURE.md
```
Contiene: diagrama de pipeline, módulos, flujo de datos.
### Paso 3: Escanea la estructura (1 min)
```
src/
├── transcription/ # Audio → JSON transcripts
├── features/ # Eventos determinísticos
├── compression/ # Reducción de tokens
├── inference/ # LLM → RCA labels
├── aggregation/ # Stats + RCA trees
├── pipeline/ # Orchestration
├── exports/ # JSON/Excel/PDF
└── models/ # CallAnalysis central
```
---
## Para ejecutar el pipeline
### Instalación
```bash
# Crear virtualenv
python -m venv venv
venv\Scripts\activate # Windows
source venv/bin/activate # Linux/Mac
# Instalar dependencias
pip install -r requirements.txt
# Configurar variables de entorno
cp .env.example .env
# Editar .env con tus API keys
```
### Ejecutar pipeline
```bash
# Con audio files
python cli.py run my_batch -i data/audio -o data/output
# Ver estado
python cli.py status my_batch
# Con opciones
python cli.py run my_batch --model gpt-4o --formats json,excel,pdf
```
---
## Para implementar un feature
### Paso 1: Identifica el módulo
| Si quieres... | Edita... |
|---------------|----------|
| Cambiar transcripción | `src/transcription/` |
| Detectar nuevos eventos | `src/features/event_detector.py` |
| Modificar compresión | `src/compression/compressor.py` |
| Cambiar prompt LLM | `src/inference/prompts.py` |
| Ajustar severidad | `src/aggregation/severity.py` |
| Añadir nuevo export | `src/exports/` |
### Paso 2: Lee el schema en DATA_CONTRACTS.md
```
docs/DATA_CONTRACTS.md
```
### Paso 3: Implementa siguiendo el patrón existente
```python
# Ejemplo: Añadir nuevo evento
# src/features/event_detector.py
class EventType(str, Enum):
# ... existentes ...
NEW_EVENT = "new_event" # Añadir aquí
def _detect_new_event(self, transcript: Transcript) -> list[Event]:
# Implementar detección
pass
```
### Paso 4: Escribe tests
```bash
# Crear test
tests/unit/test_<module>.py
# Ejecutar
pytest tests/unit/test_<module>.py -v
```
### Paso 5: Actualiza documentación
- `CHANGELOG.md` - Log del cambio
- `DATA_CONTRACTS.md` - Si cambias schemas
- `TECHNICAL_DECISIONS.md` - Si tomas decisiones
---
## Para debugging
### Paso 1: Check TROUBLESHOOTING.md
```
docs/TROUBLESHOOTING.md
```
### Paso 2: Ejecutar módulo aislado
```python
# Test transcription solo
from src.transcription import AssemblyAITranscriber
transcriber = AssemblyAITranscriber(api_key="...")
result = transcriber.transcribe(Path("test.mp3"))
print(result)
```
### Paso 3: Logs verbosos
```bash
python cli.py run test_batch -v # Verbose mode
```
### Paso 4: Si resuelves algo nuevo
Añádelo a `docs/TROUBLESHOOTING.md`
---
## Para validar cambios
### Paso 1: Tests
```bash
pytest tests/ -v
```
### Paso 2: Notebooks de validación
```
notebooks/01_transcription_validation.ipynb
notebooks/02_inference_validation.ipynb
notebooks/03_compression_validation.ipynb
notebooks/04_aggregation_validation.ipynb
notebooks/05_full_pipeline_test.ipynb
```
### Paso 3: Actualizar BENCHMARKS.md
Si afecta performance/cost:
```
docs/BENCHMARKS.md
```
---
## Archivos críticos (NO modificar sin revisión)
| Archivo | Por qué |
|---------|---------|
| `config/rca_taxonomy.yaml` | Define todos los drivers |
| `src/models/call_analysis.py` | Contrato central |
| `src/inference/prompts.py` | Prompt afecta calidad |
| `src/aggregation/severity.py` | Fórmula de priorización |
---
## Comandos útiles
```bash
# Ver estructura del proyecto
tree -L 2 src/
# Buscar en código
grep -r "RCALabel" src/
# Ver tests de un módulo
pytest tests/unit/test_inference.py -v
# Coverage
pytest --cov=src tests/
# Type checking (si hay mypy)
mypy src/
```
---
## Principios clave (siempre recordar)
1. **OBSERVED vs INFERRED** - Todo dato clasificado
2. **Evidence obligatoria** - Sin evidence = driver rechazado
3. **Taxonomía cerrada** - Solo códigos del enum
4. **Traceability** - Versiones en todo output
5. **No over-engineering** - Solo lo que se pide
---
## Preguntas frecuentes
### ¿Cómo añado un nuevo driver RCA?
1. Editar `config/rca_taxonomy.yaml`
2. Actualizar `src/inference/prompts.py`
3. Correr tests
4. Documentar en CHANGELOG.md
### ¿Cómo cambio el LLM?
1. Editar `cli.py run --model <model>`
2. O configurar en `src/inference/analyzer.py`
### ¿Cómo proceso más de 20k llamadas?
1. Dividir en batches
2. Usar resume automático
3. Considerar DuckDB para aggregation
### ¿Dónde están los costes?
`docs/BENCHMARKS.md` (pendiente de datos reales)
---
**Última actualización**: 2026-01-19

256
docs/TECHNICAL_DECISIONS.md Normal file
View File

@@ -0,0 +1,256 @@
# TECHNICAL_DECISIONS.md
> Registro de decisiones técnicas con rationale
---
## TD-001: STT Provider
| Campo | Valor |
|-------|-------|
| **Fecha** | 2026-01-19 |
| **Contexto** | Necesitamos transcribir 5k-20k llamadas en español con diarización |
| **Opciones evaluadas** | AssemblyAI, Whisper (local), Google Speech-to-Text, AWS Transcribe |
| **Decisión** | AssemblyAI |
| **Rationale** | Mejor diarización español, API simple, coste competitivo (~$0.04/call) |
| **Trade-offs** | Dependencia de servicio externo, costes recurrentes |
| **Reversibilidad** | Alta - interface abstracta permite cambiar provider |
---
## TD-002: LLM for Inference
| Campo | Valor |
|-------|-------|
| **Fecha** | 2026-01-19 |
| **Contexto** | Necesitamos extraer RCA labels con evidence de transcripts |
| **Opciones evaluadas** | GPT-4o, GPT-4o-mini, Claude 3.5 Sonnet |
| **Decisión** | GPT-4o-mini (default), configurable |
| **Rationale** | Cost-effective, JSON strict mode, buen español, configurable via CLI |
| **Trade-offs** | Menor capacidad que GPT-4o, posible menor precisión |
| **Reversibilidad** | Alta - `--model` flag permite cambiar |
---
## TD-003: Data Storage
| Campo | Valor |
|-------|-------|
| **Fecha** | 2026-01-19 |
| **Contexto** | Almacenar transcripts, analyses, manifests |
| **Opciones evaluadas** | Filesystem JSON, SQLite, DuckDB, PostgreSQL |
| **Decisión** | Filesystem JSON |
| **Rationale** | Simplicidad, debuggability, checkpoint/resume fácil, sin dependencias |
| **Trade-offs** | No óptimo para queries complejos en >50k llamadas |
| **Reversibilidad** | Media - migrar a DB requiere refactor |
---
## TD-004: OBSERVED vs INFERRED Separation
| Campo | Valor |
|-------|-------|
| **Fecha** | 2026-01-19 |
| **Contexto** | Outputs deben ser auditables y defendibles |
| **Opciones evaluadas** | Mezclar todo, separar en campos, separar en objetos |
| **Decisión** | Separar en `ObservedFeatures` y campos inferred |
| **Rationale** | Auditoría clara, stakeholders ven qué es hecho vs opinión |
| **Trade-offs** | Estructura más compleja |
| **Reversibilidad** | Baja - cambiar rompe contratos downstream |
---
## TD-005: Evidence Mandatory
| Campo | Valor |
|-------|-------|
| **Fecha** | 2026-01-19 |
| **Contexto** | RCA labels deben ser verificables |
| **Opciones evaluadas** | Evidence opcional, evidence recomendado, evidence obligatorio |
| **Decisión** | Evidence obligatorio (`evidence_spans[]` min 1) |
| **Rationale** | Sin evidence = alucinación, indefendible ante cliente |
| **Trade-offs** | LLM puede fallar si no encuentra evidence |
| **Reversibilidad** | Baja - relajar validation afecta confianza en outputs |
---
## TD-006: Closed Taxonomy + OTHER_EMERGENT
| Campo | Valor |
|-------|-------|
| **Fecha** | 2026-01-19 |
| **Contexto** | Balance entre estructura y flexibilidad |
| **Opciones evaluadas** | Taxonomía abierta, taxonomía cerrada, híbrido |
| **Decisión** | Taxonomía cerrada + `OTHER_EMERGENT` para captura |
| **Rationale** | Consistencia en aggregation + captura de nuevos patrones |
| **Trade-offs** | Requiere revisión manual de emergent para promover |
| **Reversibilidad** | Alta - añadir códigos no rompe existentes |
---
## TD-007: Transcript Compression
| Campo | Valor |
|-------|-------|
| **Fecha** | 2026-01-19 |
| **Contexto** | Reducir costes de LLM (tokens) |
| **Opciones evaluadas** | No comprimir, extractive summary, rule-based extraction |
| **Decisión** | Rule-based extraction (>60% reducción) |
| **Rationale** | Predecible, sin pérdida de información clave, sin coste adicional |
| **Trade-offs** | Puede perder contexto sutil |
| **Reversibilidad** | Alta - `--no-compression` flag disponible |
---
## TD-008: Severity Scoring Formula
| Campo | Valor |
|-------|-------|
| **Fecha** | 2026-01-19 |
| **Contexto** | Priorizar drivers en RCA tree |
| **Opciones evaluadas** | Solo frecuencia, solo confidence, fórmula ponderada |
| **Decisión** | Fórmula ponderada: base*0.4 + freq*0.3 + conf*0.2 + co-occur*0.1 |
| **Rationale** | Balance múltiples factores, configurable |
| **Trade-offs** | Pesos arbitrarios, pueden necesitar ajuste |
| **Reversibilidad** | Alta - pesos en config |
---
## TD-009: Pipeline Checkpointing
| Campo | Valor |
|-------|-------|
| **Fecha** | 2026-01-19 |
| **Contexto** | Batches grandes (20k) pueden fallar a mitad |
| **Opciones evaluadas** | Sin checkpoint, checkpoint por archivo, checkpoint por stage |
| **Decisión** | Checkpoint por stage con manifest JSON |
| **Rationale** | Resume granular, debuggable, sin estado complejo |
| **Trade-offs** | Más archivos en filesystem |
| **Reversibilidad** | Alta |
---
## TD-010: Export Formats
| Campo | Valor |
|-------|-------|
| **Fecha** | 2026-01-19 |
| **Contexto** | Outputs para diferentes stakeholders |
| **Opciones evaluadas** | Solo JSON, JSON+Excel, JSON+Excel+PDF+Dashboard |
| **Decisión** | JSON + Excel + PDF/HTML |
| **Rationale** | JSON para devs, Excel para analysts, PDF para executives |
| **Trade-offs** | Más código de export, dependencias (openpyxl) |
| **Reversibilidad** | Alta - formats seleccionables |
---
## TD-011: Prompt Versioning Strategy (v2.0)
| Campo | Valor |
|-------|-------|
| **Fecha** | 2026-01-19 |
| **Contexto** | Blueprint alignment requires significant prompt changes |
| **Opciones evaluadas** | Modify v1.0 in-place, create v2.0 with deprecation, feature flags |
| **Decisión** | Create v2.0 with v1.0 deprecated but preserved |
| **Rationale** | Backward compatibility, traceability, easy rollback |
| **Trade-offs** | More prompt files to maintain |
| **Reversibilidad** | Alta - `--prompt-version v1.0` flag can be added |
---
## TD-012: Blueprint Alignment Scope
| Campo | Valor |
|-------|-------|
| **Fecha** | 2026-01-19 |
| **Contexto** | Gap analysis identified ~20 gaps vs BeyondCX blueprints |
| **Opciones evaluadas** | Implement all, implement high priority only, defer all |
| **Decisión** | Implement HIGH priority gaps (FCR, Churn, Agent, RCALabel) |
| **Rationale** | Core functionality gaps, medium priority can wait for Phase 2 |
| **Trade-offs** | Medium/low priority gaps remain (campaign tracking, customer value) |
| **Reversibilidad** | Media - additional gaps can be added incrementally |
---
## TD-013: DriverOrigin Attribution
| Campo | Valor |
|-------|-------|
| **Fecha** | 2026-01-19 |
| **Contexto** | Blueprints require responsibility attribution for each driver |
| **Opciones evaluadas** | 3 origins (agent/customer/company), 4 origins (+process), 5 origins (+unknown) |
| **Decisión** | 5 origins: AGENT, CUSTOMER, COMPANY, PROCESS, UNKNOWN |
| **Rationale** | PROCESS separates systemic issues from company decisions, UNKNOWN for ambiguous cases |
| **Trade-offs** | More categories for LLM to distinguish |
| **Reversibilidad** | Alta - can collapse categories if needed |
---
## TD-014: Dashboard Technology
| Campo | Valor |
|-------|-------|
| **Fecha** | 2026-01-19 |
| **Contexto** | Necesitamos visualizar resultados de análisis para clientes |
| **Opciones evaluadas** | Solo exports (Excel/PDF), Streamlit, Dash, React custom |
| **Decisión** | Streamlit + Plotly |
| **Rationale** | Desarrollo rápido, interactividad, Python nativo, fácil de mantener |
| **Trade-offs** | Menos customizable que React, limitado para muy alto tráfico |
| **Reversibilidad** | Media - componentes Plotly reutilizables |
---
## TD-015: Blueprint Terminology Compliance
| Campo | Valor |
|-------|-------|
| **Fecha** | 2026-01-19 |
| **Contexto** | Dashboard debe usar terminología exacta de blueprints BeyondCX |
| **Opciones evaluadas** | Usar inglés técnico, usar español parcial, compliance total |
| **Decisión** | Compliance total con terminología de blueprints |
| **Rationale** | Consistencia con documentos cliente, menos confusión |
| **Trade-offs** | Labels más largos en algunos casos |
| **Reversibilidad** | Alta - solo cambios de texto |
---
## TD-016: FCR Rate Calculation
| Campo | Valor |
|-------|-------|
| **Fecha** | 2026-01-19 |
| **Contexto** | Blueprint FCR define "Primera Llamada" como éxito |
| **Opciones evaluadas** | FIRST_CALL+RESOLVED, solo FIRST_CALL, custom logic |
| **Decisión** | Solo FIRST_CALL cuenta como FCR success |
| **Rationale** | Blueprint es explícito: "primer contacto por ese motivo" |
| **Trade-offs** | Puede diferir de métricas legacy del cliente |
| **Reversibilidad** | Alta - fórmula configurable |
---
## TD-017: RCA Sankey Visualization
| Campo | Valor |
|-------|-------|
| **Fecha** | 2026-01-19 |
| **Contexto** | Visualizar causalidad Driver → Outcome → Churn |
| **Opciones evaluadas** | Tree diagram, Sunburst, Sankey, Chord |
| **Decisión** | Sankey diagram |
| **Rationale** | Muestra flujo causal, ancho proporcional, muy visual para executives |
| **Trade-offs** | Puede ser confuso con muchos nodos |
| **Reversibilidad** | Alta - componente independiente |
---
## Decisiones Pendientes
| ID | Tema | Status |
|----|------|--------|
| TD-018 | DuckDB para analytics grandes | Pendiente |
| TD-019 | Multi-idioma strategy | Pendiente (Fase 2) |
| TD-020 | Campaign tracking implementation | Pendiente (Fase 2) |
| TD-021 | Customer value analysis | Pendiente (Fase 2) |
---
**Última actualización**: 2026-01-19 (v2.1 Dashboard + Blueprint Compliance)

579
docs/TECH_STACK.md Normal file
View File

@@ -0,0 +1,579 @@
# CXInsights - Stack Tecnológico
## Resumen de Decisiones
| Componente | Elección | Alternativas Soportadas |
|------------|----------|-------------------------|
| **STT (Speech-to-Text)** | AssemblyAI (default) | Whisper, Google STT, AWS Transcribe (via adapter) |
| **LLM** | OpenAI GPT-4o-mini | Claude 3.5 Sonnet (fallback) |
| **Data Processing** | pandas + DuckDB | - |
| **Visualization** | Streamlit (internal dashboard) | - |
| **PDF Generation** | ReportLab | - |
| **Config Management** | Pydantic Settings | - |
| **PII Handling** | Presidio (opcional) + redaction pre-LLM | - |
---
## 1. Speech-to-Text: Arquitectura con Adapter
### Decisión: **AssemblyAI (default)** + alternativas via STT Provider Adapter
El sistema usa una **interfaz abstracta `Transcriber`** que permite cambiar de proveedor sin modificar el código del pipeline.
```
┌─────────────────────────────────────────────────────────────────┐
│ STT PROVIDER ADAPTER │
├─────────────────────────────────────────────────────────────────┤
│ Interface: Transcriber │
│ └─ transcribe(audio) → TranscriptContract │
│ │
│ Implementations: │
│ ├─ AssemblyAITranscriber (DEFAULT - mejor calidad español) │
│ ├─ WhisperTranscriber (local, offline, $0) │
│ ├─ GoogleSTTTranscriber (alternativa cloud) │
│ └─ AWSTranscribeTranscriber (alternativa cloud) │
│ │
│ Config: STT_PROVIDER=assemblyai|whisper|google|aws │
└─────────────────────────────────────────────────────────────────┘
```
### Comparativa de Proveedores
| Criterio | AssemblyAI | Whisper (local) | Google STT | AWS Transcribe |
|----------|------------|-----------------|------------|----------------|
| **Calidad español** | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐ | ⭐⭐⭐⭐ | ⭐⭐⭐ |
| **Speaker diarization** | ✅ Incluido | ❌ Requiere pyannote | ✅ Incluido | ✅ Incluido |
| **Coste/minuto** | $0.015 | $0 (GPU local) | $0.016 | $0.015 |
| **Setup complexity** | Bajo (API key) | Alto (GPU, modelos) | Medio | Medio |
| **Batch processing** | ✅ Async nativo | Manual | ✅ | ✅ |
| **Latencia** | ~0.3x realtime | ~1x realtime | ~0.2x realtime | ~0.3x realtime |
### Por qué AssemblyAI como Default
1. **Mejor modelo para español**: AssemblyAI Best tiene excelente rendimiento en español latinoamericano y castellano
2. **Speaker diarization incluido**: Crítico para separar agente de cliente sin código adicional
3. **API simple**: SDK Python bien documentado, async nativo
4. **Batch processing**: Configurable concurrency, poll por resultados
5. **Sin infraestructura**: No necesitas GPU ni mantener modelos
### Cuándo usar alternativas
| Alternativa | Usar cuando... |
|-------------|----------------|
| **Whisper local** | Presupuesto $0, tienes GPU (RTX 3080+), datos muy sensibles (offline) |
| **Google STT** | Ya usas GCP, necesitas latencia mínima |
| **AWS Transcribe** | Ya usas AWS, integración con S3 |
### Estimación de Costes STT (AHT = 7 min)
```
AssemblyAI pricing: $0.015/minuto
5,000 llamadas × 7 min = 35,000 min
├─ Estimación baja (sin retries): $525
├─ Estimación media: $550
└─ Estimación alta (+10% retries): $580
20,000 llamadas × 7 min = 140,000 min
├─ Estimación baja: $2,100
├─ Estimación media: $2,200
└─ Estimación alta: $2,400
RANGO TOTAL STT:
├─ 5K calls: $525 - $580
└─ 20K calls: $2,100 - $2,400
```
---
## 2. LLM: OpenAI GPT-4o-mini
### Decisión: **GPT-4o-mini** (primary) + **Claude 3.5 Sonnet** (fallback)
### Comparativa
| Criterio | GPT-4o-mini | GPT-4o | Claude 3.5 Sonnet |
|----------|-------------|--------|-------------------|
| **Coste input** | $0.15/1M tokens | $2.50/1M tokens | $3.00/1M tokens |
| **Coste output** | $0.60/1M tokens | $10.00/1M tokens | $15.00/1M tokens |
| **Calidad español** | ⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ |
| **JSON structured** | ✅ Excelente | ✅ Excelente | ✅ Muy bueno |
| **Context window** | 128K | 128K | 200K |
| **Rate limits** | Depende del tier | Depende del tier | Depende del tier |
### Rate Limits y Throttling
**Los rate limits dependen del tier de tu cuenta OpenAI:**
| Tier | RPM (requests/min) | TPM (tokens/min) |
|------|-------------------|------------------|
| Tier 1 (free) | 500 | 200K |
| Tier 2 | 5,000 | 2M |
| Tier 3 | 5,000 | 4M |
| Tier 4+ | 10,000 | 10M |
**Requisitos obligatorios en el código:**
- Implementar throttling con tasa configurable (`LLM_REQUESTS_PER_MINUTE`)
- Exponential backoff en errores 429 (rate limit exceeded)
- Retry con jitter para evitar thundering herd
- Logging de rate limit warnings
```python
# Configuración recomendada (conservadora)
LLM_REQUESTS_PER_MINUTE=300 # Empezar bajo, escalar según tier
LLM_BACKOFF_BASE=2.0 # Segundos base para backoff
LLM_BACKOFF_MAX=60.0 # Máximo backoff
LLM_MAX_RETRIES=5
```
### Estimación de Costes LLM por Llamada
**IMPORTANTE**: Estos cálculos asumen **compresión previa del transcript** (Module 2).
#### Escenario A: Con compresión (RECOMENDADO)
```
Transcript comprimido: ~1,200-1,800 tokens input
Prompt template: ~400-600 tokens
Output esperado: ~250-400 tokens
Total por llamada (comprimido):
├─ Input: ~2,000 tokens × $0.15/1M = $0.0003
├─ Output: ~350 tokens × $0.60/1M = $0.0002
└─ Total: $0.0004 - $0.0006 por llamada
RANGO (5K calls): $2 - $3
RANGO (20K calls): $8 - $12
```
#### Escenario B: Sin compresión (full transcript)
```
Transcript completo: ~4,000-8,000 tokens input (x3-x6)
Prompt template: ~400-600 tokens
Output esperado: ~250-400 tokens
Total por llamada (full transcript):
├─ Input: ~6,000 tokens × $0.15/1M = $0.0009
├─ Output: ~350 tokens × $0.60/1M = $0.0002
└─ Total: $0.0010 - $0.0020 por llamada
RANGO (5K calls): $5 - $10
RANGO (20K calls): $20 - $40
⚠️ RECOMENDACIÓN: Siempre usar compresión para reducir costes 3-6x
```
### Por qué GPT-4o-mini
1. **Coste-efectividad**: 17x más barato que GPT-4o, calidad suficiente para clasificación
2. **Structured outputs**: JSON mode nativo, reduce errores de parsing
3. **Consistencia**: Respuestas muy consistentes con prompts bien diseñados
### Cuándo escalar a GPT-4o
- Análisis que requiera razonamiento complejo
- Casos edge con transcripciones ambiguas
- Síntesis final de RCA trees (pocas llamadas, coste marginal)
### Claude 3.5 Sonnet como fallback
Usar cuando:
- OpenAI tiene downtime
- Necesitas segunda opinión en casos difíciles
- Contexto muy largo (>100K tokens)
---
## 3. Data Processing: pandas + DuckDB
### Decisión: **pandas** (manipulación) + **DuckDB** (queries analíticas)
### Por qué esta combinación
| Componente | Uso | Justificación |
|------------|-----|---------------|
| **pandas** | Load/transform JSON, merge data | Estándar de facto, excelente para datos semi-estructurados |
| **DuckDB** | Queries SQL sobre datos, aggregations | SQL analítico sin servidor, integra con pandas |
### Por qué NO Polars
- Polars es más rápido, pero pandas es suficiente para 20K filas
- Mejor ecosistema y documentación
- Equipo probablemente ya conoce pandas
### Por qué NO SQLite/PostgreSQL
- DuckDB es columnar, optimizado para analytics
- No requiere servidor ni conexión
- Syntax SQL estándar
- Lee/escribe parquet nativamente
### Ejemplo de uso
```python
import pandas as pd
import duckdb
# Cargar todos los labels
labels = pd.read_json("data/processed/*.json") # via glob
# Query analítico con DuckDB
result = duckdb.sql("""
SELECT
lost_sale_driver,
COUNT(*) as count,
COUNT(*) * 100.0 / SUM(COUNT(*)) OVER () as pct
FROM labels
WHERE outcome = 'no_sale'
GROUP BY lost_sale_driver
ORDER BY count DESC
""").df()
```
---
## 4. Visualization: Streamlit
### Decisión: **Streamlit** (dashboard interno)
### Alcance y Limitaciones
```
┌─────────────────────────────────────────────────────────────────┐
│ STREAMLIT - ALCANCE │
├─────────────────────────────────────────────────────────────────┤
│ ✅ ES: │
│ ├─ Dashboard interno para equipo de análisis │
│ ├─ Visualización de resultados de batch procesado │
│ ├─ Drill-down por llamada individual │
│ └─ Exportación a PDF/Excel │
│ │
│ ❌ NO ES: │
│ ├─ Portal enterprise multi-tenant │
│ ├─ Aplicación de producción con SLA │
│ ├─ Dashboard para >50 usuarios concurrentes │
│ └─ Sistema con autenticación compleja │
└─────────────────────────────────────────────────────────────────┘
```
### Comparativa
| Criterio | Streamlit | Plotly Dash | FastAPI+React |
|----------|-----------|-------------|---------------|
| **Setup time** | 1 hora | 4 horas | 2-3 días |
| **Interactividad** | ⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ |
| **Aprendizaje** | Bajo | Medio | Alto |
| **Customización** | Limitada | Alta | Total |
| **Usuarios concurrentes** | ~10-50 | ~50-100 | Sin límite |
### Deploy
```
┌─────────────────────────────────────────────────────────────────┐
│ OPCIONES DE DEPLOY │
├─────────────────────────────────────────────────────────────────┤
│ │
│ OPCIÓN 1: Local (desarrollo/análisis personal) │
│ $ streamlit run src/visualization/dashboard.py │
│ → http://localhost:8501 │
│ │
│ OPCIÓN 2: VM/Servidor interno (equipo pequeño) │
│ $ streamlit run dashboard.py --server.port 8501 │
│ → Sin auth, acceso via VPN/red interna │
│ │
│ OPCIÓN 3: Con proxy + auth básica (recomendado producción) │
│ Nginx/Caddy → Basic Auth → Streamlit │
│ → Auth configurable via .htpasswd o OAuth proxy │
│ │
│ OPCIÓN 4: Streamlit Cloud (demos/POC) │
│ → Gratis, pero datos públicos (no para producción) │
│ │
└─────────────────────────────────────────────────────────────────┘
```
### Configuración de Auth (opcional)
```nginx
# nginx.conf - Basic Auth para Streamlit
server {
listen 443 ssl;
server_name dashboard.internal.company.com;
auth_basic "CXInsights Dashboard";
auth_basic_user_file /etc/nginx/.htpasswd;
location / {
proxy_pass http://localhost:8501;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
}
```
### Alternativa futura
Si necesitas dashboard enterprise:
- Migrar a FastAPI backend + React frontend
- Reusar lógica de aggregation
- Añadir auth, multi-tenant, RBAC
---
## 5. PII Handling
### Decisión: **Redaction pre-LLM obligatoria** + retención controlada
```
┌─────────────────────────────────────────────────────────────────┐
│ PII HANDLING STRATEGY │
├─────────────────────────────────────────────────────────────────┤
│ │
│ PRINCIPIO: Minimizar PII enviado a APIs externas │
│ │
│ 1. REDACTION PRE-LLM (obligatorio) │
│ ├─ Nombres → [NOMBRE] │
│ ├─ Teléfonos → [TELEFONO] │
│ ├─ Emails → [EMAIL] │
│ ├─ DNI/NIE → [DOCUMENTO] │
│ ├─ Tarjetas → [TARJETA] │
│ └─ Direcciones → [DIRECCION] │
│ │
│ 2. RETENCIÓN POR BATCH │
│ ├─ Transcripts raw: borrar tras 30 días o fin de proyecto │
│ ├─ Transcripts compressed: borrar tras procesamiento │
│ ├─ Labels (sin PII): retener para análisis │
│ └─ Aggregated stats: retener indefinidamente │
│ │
│ 3. LOGS │
│ ├─ NUNCA loguear transcript completo │
│ ├─ Solo loguear: call_id, timestamps, errores │
│ └─ Logs en volumen separado, rotación 7 días │
│ │
└─────────────────────────────────────────────────────────────────┘
```
### Implementación
```python
# Opción 1: Regex básico (mínimo viable)
REDACTION_PATTERNS = {
r'\b\d{8,9}[A-Z]?\b': '[DOCUMENTO]', # DNI/NIE
r'\b\d{9}\b': '[TELEFONO]', # Teléfono
r'\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b': '[EMAIL]',
r'\b\d{4}[\s-]?\d{4}[\s-]?\d{4}[\s-]?\d{4}\b': '[TARJETA]',
}
# Opción 2: Presidio (recomendado para producción)
# Más preciso, soporta español, detecta contexto
from presidio_analyzer import AnalyzerEngine
from presidio_anonymizer import AnonymizerEngine
```
---
## 6. Dependencias Python
### Core Dependencies
```toml
[project]
dependencies = [
# STT
"assemblyai>=0.26.0",
# LLM
"openai>=1.40.0",
"anthropic>=0.34.0", # fallback
# Data Processing
"pandas>=2.2.0",
"duckdb>=1.0.0",
"pydantic>=2.8.0",
# Visualization
"streamlit>=1.38.0",
"plotly>=5.24.0",
"matplotlib>=3.9.0",
# PDF/Excel Export
"reportlab>=4.2.0",
"openpyxl>=3.1.0",
"xlsxwriter>=3.2.0",
# Config & Utils
"pydantic-settings>=2.4.0",
"python-dotenv>=1.0.0",
"pyyaml>=6.0.0",
"tqdm>=4.66.0",
"tenacity>=8.5.0", # retry logic
# JSON (performance + validation)
"orjson>=3.10.0", # Fast JSON serialization
"jsonschema>=4.23.0", # Schema validation
# Async
"aiofiles>=24.1.0",
"httpx>=0.27.0",
]
[project.optional-dependencies]
# PII detection (opcional pero recomendado)
pii = [
"presidio-analyzer>=2.2.0",
"presidio-anonymizer>=2.2.0",
"spacy>=3.7.0",
"es-core-news-sm @ https://github.com/explosion/spacy-models/releases/download/es_core_news_sm-3.7.0/es_core_news_sm-3.7.0-py3-none-any.whl",
]
dev = [
"pytest>=8.3.0",
"pytest-asyncio>=0.24.0",
"pytest-cov>=5.0.0",
"ruff>=0.6.0",
"mypy>=1.11.0",
]
```
### Justificación de cada dependencia
| Dependencia | Propósito | Por qué esta |
|-------------|-----------|--------------|
| `assemblyai` | SDK oficial STT | Mejor integración, async nativo |
| `openai` | SDK oficial GPT | Structured outputs, streaming |
| `anthropic` | SDK oficial Claude | Fallback LLM |
| `pandas` | Manipulación datos | Estándar industria |
| `duckdb` | Queries SQL | Analytics sin servidor |
| `pydantic` | Validación schemas | Type safety, JSON parsing |
| `streamlit` | Dashboard | Rápido, Python-only |
| `plotly` | Gráficos interactivos | Mejor para web |
| `matplotlib` | Gráficos estáticos | Export PNG |
| `reportlab` | PDF generation | Maduro, flexible |
| `openpyxl` | Excel read/write | Pandas integration |
| `pydantic-settings` | Config management | .env + validation |
| `tqdm` | Progress bars | UX en CLI |
| `tenacity` | Retry logic | Rate limits, API errors |
| `orjson` | JSON serialization | 10x más rápido que json stdlib |
| `jsonschema` | Schema validation | Validar outputs LLM |
| `httpx` | HTTP client async | Mejor que requests |
| `presidio-*` | PII detection | Precisión en español, contexto |
---
## 7. Versiones de Python
### Decisión: **Python 3.11+**
### Justificación
- 3.11: 10-60% más rápido que 3.10
- 3.11: Better error messages
- 3.12: Algunas libs aún no compatibles
- Match pattern (3.10+) útil para parsing
---
## 8. Consideraciones de Seguridad
### API Keys
```bash
# .env (NUNCA en git)
ASSEMBLYAI_API_KEY=xxx
OPENAI_API_KEY=sk-xxx
ANTHROPIC_API_KEY=sk-ant-xxx # opcional
```
### Rate Limiting (implementación obligatoria)
```python
# src/inference/client.py
from tenacity import retry, wait_exponential, stop_after_attempt
@retry(
wait=wait_exponential(multiplier=2, min=1, max=60),
stop=stop_after_attempt(5),
retry=retry_if_exception_type(RateLimitError)
)
async def call_llm(prompt: str) -> str:
# Throttle requests
await self.rate_limiter.acquire()
# ... llamada a API
```
### Checklist de seguridad
- [ ] API keys en .env, nunca en código
- [ ] .env en .gitignore
- [ ] PII redactado antes de LLM
- [ ] Logs sin transcripts completos
- [ ] Rate limiting implementado
- [ ] Backoff exponencial en errores 429
---
## 9. Alternativas Descartadas
### Whisper Local
- **Pro**: Gratis, offline, datos sensibles
- **Contra**: Necesita GPU, sin diarization nativo, más lento
- **Decisión**: Soportado via adapter, no es default
### LangChain
- **Pro**: Abstracciones útiles, chains
- **Contra**: Overhead innecesario para este caso, complejidad
- **Decisión**: Llamadas directas a SDK son suficientes
### PostgreSQL/MySQL
- **Pro**: Persistencia, queries complejas
- **Contra**: Requiere servidor, overkill para batch
- **Decisión**: DuckDB + archivos JSON/parquet
### Celery/Redis
- **Pro**: Job queue distribuida
- **Contra**: Infraestructura adicional
- **Decisión**: asyncio + checkpointing es suficiente
---
## 10. Resumen de Costes
### Parámetros base
- **AHT (Average Handle Time)**: 7 minutos
- **Compresión de transcript**: Asumida (reducción ~60% tokens)
### Por 5,000 llamadas
| Servicio | Cálculo | Rango |
|----------|---------|-------|
| AssemblyAI STT | 35,000 min × $0.015/min | $525 - $580 |
| OpenAI LLM (comprimido) | 5,000 × $0.0005 | $2 - $3 |
| OpenAI RCA synthesis | ~10 calls × $0.02 | $0.20 |
| **TOTAL** | | **$530 - $590** |
### Por 20,000 llamadas
| Servicio | Cálculo | Rango |
|----------|---------|-------|
| AssemblyAI STT | 140,000 min × $0.015/min | $2,100 - $2,400 |
| OpenAI LLM (comprimido) | 20,000 × $0.0005 | $8 - $12 |
| OpenAI RCA synthesis | ~10 calls × $0.02 | $0.20 |
| **TOTAL** | | **$2,110 - $2,420** |
### Sin compresión (escenario pesimista)
| Volumen | STT | LLM (full transcript) | Total |
|---------|-----|----------------------|-------|
| 5,000 calls | $525-580 | $5-10 | **$530 - $590** |
| 20,000 calls | $2,100-2,400 | $20-40 | **$2,120 - $2,440** |
### Coste de infraestructura
| Opción | Coste |
|--------|-------|
| Local (tu máquina) | $0 |
| VM cloud (procesamiento) | $20-50/mes |
| Streamlit Cloud (demos) | Gratis |
| VM + Nginx (producción) | $30-80/mes |

0
docs/TESTING_STRATEGY.md Normal file
View File

166
docs/TODO.md Normal file
View File

@@ -0,0 +1,166 @@
# TODO.md
> Lista priorizada de tareas pendientes
---
## Checkpoints Completados
### CP1: Project Setup & Contracts ✅
- [x] Crear estructura de carpetas
- [x] Inicializar repo Git
- [x] Crear requirements.txt
- [x] Crear .env.example
- [x] Crear README.md
- [x] Crear config/rca_taxonomy.yaml
- [x] Crear config/settings.yaml
- [x] Crear schemas Pydantic
### CP2: Transcription Module ✅
- [x] Implementar Transcriber interface
- [x] Implementar AssemblyAITranscriber
- [x] Implementar modelos (Transcript, SpeakerTurn)
- [x] Tests unitarios
- [x] Notebook 01_transcription_validation.ipynb
### CP3: RCA Schemas & Data Contracts ✅
- [x] Implementar CallAnalysis
- [x] Implementar RCALabel, EvidenceSpan
- [x] Implementar Event
- [x] Separar observed vs inferred
- [x] Crear data/examples/
### CP4: Feature & Event Extraction ✅
- [x] Implementar event_detector.py
- [x] Implementar turn_metrics.py
- [x] Tests unitarios
### CP5: Inference Engine ✅
- [x] Crear prompt MAP único
- [x] Implementar LLMClient con JSON strict
- [x] Implementar BatchInference con resume
- [x] Tests de evidence obligatorio
- [x] Notebook 02_inference_validation.ipynb
### CP6: Transcript Compression ✅
- [x] Implementar CompressedTranscript
- [x] Validar reducción >60% tokens
- [x] Integrar en inference
- [x] Notebook 03_compression_validation.ipynb
### CP7: Aggregation & RCA Trees ✅
- [x] Implementar statistics.py
- [x] Definir severity_score con reglas explícitas
- [x] Implementar RCATreeBuilder
- [x] Notebook 04_aggregation_validation.ipynb
### CP8: End-to-End Pipeline ✅
- [x] Implementar CXInsightsPipeline
- [x] Implementar manifests por stage
- [x] Implementar resume
- [x] Implementar exports (JSON, Excel, PDF)
- [x] CLI principal
- [x] Notebook 05_full_pipeline_test.ipynb
### CP-GAPS: v2.0 Blueprint Alignment ✅ (2026-01-19)
- [x] Gap Analysis vs BeyondCX Blueprints (4 docs Word)
- [x] Update rca_taxonomy.yaml with new driver categories
- [x] churn_risk drivers
- [x] fcr_failure drivers
- [x] agent_skills (positive + improvement_needed)
- [x] Update call_analysis.py models with new fields
- [x] FCRStatus enum
- [x] ChurnRisk enum
- [x] AgentClassification enum
- [x] DriverOrigin enum
- [x] AgentSkillIndicator model
- [x] Enhanced RCALabel with origin, corrective_action, replicable_practice
- [x] Updated CallAnalysis with new fields
- [x] Create prompt v2.0 (config/prompts/call_analysis/v2.0/)
- [x] system.txt
- [x] user.txt
- [x] schema.json
- [x] Update versions.yaml to active v2.0
- [x] Update prompt_manager.py with TaxonomyTexts
- [x] Update analyzer.py to parse new fields
- [x] Update aggregation models and statistics for v2.0
- [x] Update tests for v2.0 compatibility
### CP-DASH: Streamlit Dashboard ✅ (2026-01-19)
- [x] Create dashboard structure (app.py, config.py, data_loader.py, components.py)
- [x] Implement Beyond Brand Identity styling
- [x] Colors: Black #000000, Blue #6D84E3, Grey #B1B1B0
- [x] Light theme configuration (.streamlit/config.toml)
- [x] Custom CSS with Outfit font
- [x] Implement 8 dashboard sections
- [x] Overview (KPIs, outcomes, drivers, FCR, churn)
- [x] Outcomes Analysis
- [x] Poor CX Analysis
- [x] FCR Analysis
- [x] Churn Risk Analysis
- [x] Agent Performance
- [x] Call Explorer
- [x] Export Insights
- [x] Advanced visualizations
- [x] RCA Sankey Diagram (Driver → Outcome → Churn Risk)
- [x] Correlation Heatmaps (co-occurrence, driver-outcome)
- [x] Outcome Deep Dive (root causes, correlation, duration)
- [x] Export functionality
- [x] Excel multi-sheet workbook
- [x] HTML executive summary report
- [x] JSON raw data export
- [x] Blueprint terminology compliance
- [x] FCR: 4 categorías (Primera Llamada/Rellamada × Sin/Con Riesgo)
- [x] Churn: Sin Riesgo de Fuga / En Riesgo de Fuga
- [x] Agent: Talento Para Replicar / Oportunidades de Mejora
---
## Alta prioridad (Pendiente)
- [ ] **Run real benchmark with v2.0** - Ejecutar pipeline con 50-100 llamadas reales
- [ ] **Measure actual costs** - Documentar costes reales STT + LLM
- [ ] **Validate v2.0 RCA accuracy** - Manual review de 20 llamadas con nuevos campos
- [x] **Documentation** - Completar stubs en docs/ ✅
- [x] **Test v2.0 with real transcripts** - Validado con batch test-07 (30 llamadas) ✅
- [x] **Update exports for v2.0** - Dashboard incluye todos los campos nuevos ✅
- [x] **Dashboard Streamlit** - Implementado con Beyond branding ✅
---
## Media prioridad (CP9 - Optional)
- [ ] Caching por hash de transcript
- [ ] Batch size benchmarks (encontrar óptimo)
- [ ] Comparar STT providers (Whisper, Google)
- [ ] Comparar LLM providers (Claude vs GPT-4o)
- [ ] DuckDB para analytics de grandes batches
---
## Baja prioridad (Fase 2)
- [x] Dashboard Streamlit ✅ (completado 2026-01-19)
- [ ] Docker containerization
- [ ] CI/CD pipeline
- [ ] API REST (FastAPI)
- [ ] Multi-idioma support
- [ ] Real-time processing
- [ ] Integración BeyondDiagnosticPrototipo
- [ ] Campaign tracking (Blueprint KPI 2)
- [ ] Customer value analysis (Blueprint Pilar 4)
- [ ] Sales cycle optimization analysis
---
## Backlog (Ideas)
- [ ] Automatic prompt tuning based on validation results
- [ ] A/B testing de prompts
- [ ] Confidence calibration
- [ ] Active learning loop
- [ ] Cost anomaly detection
---
**Última actualización**: 2026-01-19 (v2.1 Dashboard + Blueprint Compliance completed)

203
docs/TROUBLESHOOTING.md Normal file
View File

@@ -0,0 +1,203 @@
# TROUBLESHOOTING.md
> Guía de problemas comunes y sus soluciones
---
## Dashboard Streamlit
### TS-001: Dashboard muestra fondo negro / tema oscuro
**Síntomas:**
- Texto no visible sobre fondo negro
- Elementos UI con colores incorrectos
**Causa:**
Streamlit usa tema oscuro por defecto basado en preferencias del sistema.
**Solución:**
Crear `.streamlit/config.toml`:
```toml
[theme]
base = "light"
primaryColor = "#6D84E3"
backgroundColor = "#FFFFFF"
secondaryBackgroundColor = "#F8F8F8"
textColor = "#000000"
```
---
### TS-002: Puerto en uso al lanzar Streamlit
**Síntomas:**
```
Error: Address already in use
Port 8501 is in use by another program
```
**Causa:**
Otra instancia de Streamlit o aplicación usando el puerto.
**Solución:**
1. Usar puerto alternativo en `.streamlit/config.toml`:
```toml
[server]
port = 8510
```
2. O especificar en línea de comandos:
```bash
python -m streamlit run dashboard/app.py --server.port 8510
```
---
### TS-003: Plotly ValueError: Invalid property 'titlefont'
**Síntomas:**
```
ValueError: Invalid property specified for object of type plotly.graph_objs.heatmap.ColorBar: 'titlefont'
```
**Causa:**
Plotly deprecó `titlefont` en versiones recientes. Ahora debe usarse estructura anidada.
**Solución:**
```python
# Antes (deprecated)
colorbar=dict(
title="Label",
titlefont=dict(size=12),
)
# Ahora (correcto)
colorbar=dict(
title=dict(text="Label", font=dict(size=12)),
)
```
---
### TS-004: Streamlit 'use_container_width' deprecation warning
**Síntomas:**
```
Please replace `use_container_width` with `width`.
`use_container_width` will be removed after 2025-12-31.
```
**Causa:**
Streamlit cambió la API de `st.dataframe()` y `st.plotly_chart()`.
**Solución:**
```python
# Antes
st.dataframe(df, use_container_width=True)
# Después
st.dataframe(df, width='stretch')
```
**Nota:** Este warning no rompe funcionalidad actualmente.
---
### TS-005: No batch data found
**Síntomas:**
Dashboard muestra "No batch data found" y no carga.
**Causa:**
No hay datos de análisis en `data/output/`.
**Solución:**
1. Ejecutar pipeline primero:
```bash
python cli.py run my_batch -i data/audio -o data/output
```
2. Verificar que existe `data/output/<batch_id>/summary.json`
---
## Pipeline de Análisis
### TS-006: AssemblyAI transcription falla
**Síntomas:**
```
Error: AssemblyAI API error: ...
```
**Soluciones:**
1. Verificar `ASSEMBLYAI_API_KEY` en `.env`
2. Verificar formato de audio (soporta: mp3, wav, m4a, flac)
3. Verificar conectividad a internet
---
### TS-007: OpenAI JSON parsing error
**Síntomas:**
```
Error: Failed to parse JSON response
```
**Causa:**
LLM generó JSON malformado.
**Solución:**
1. El sistema tiene auto-repair built-in
2. Si persiste, usar `--model gpt-4o` (más robusto)
3. Verificar que transcript no está vacío
---
### TS-008: Pipeline resume no funciona
**Síntomas:**
Pipeline reprocesa llamadas ya completadas.
**Causa:**
Manifest corrupto o eliminado.
**Solución:**
1. Verificar `data/output/<batch>/manifests/*.json`
2. Si corrupto, usar `--no-resume` para empezar de cero
3. No eliminar archivos de manifest manualmente
---
## Problemas de Datos
### TS-009: FCR rate muestra 0% cuando hay llamadas
**Causa:**
El campo `fcr_status` no está siendo llenado por el LLM.
**Solución:**
1. Usar `--no-compression` para dar más contexto al LLM
2. Verificar que prompt v2.0 está activo
3. Revisar que transcripts tienen suficiente información
---
### TS-010: Drivers vacíos en análisis
**Síntomas:**
`poor_cx_drivers: []` en todos los análisis.
**Causa:**
- Transcripts muy cortos
- Compresión eliminó información clave
- LLM no encontró evidencia
**Solución:**
1. Usar `--no-compression`
2. Verificar calidad de transcripts
3. Revisar logs para errores de LLM
---
**Última actualización**: 2026-01-19

View File

@@ -0,0 +1,130 @@
Análisis de Llamadas de Venta
Contexto
Misión
El objetivo de este análisis es identificar palancas de mejora en los procesos de venta telefónica, bien sean procesos puros de venta o procesos de venta cruzada dentro de servicios de atención al cliente, para incrementar la conversión, optimizar las campañas, mejorar las habilidades del equipo comercial, afinar los argumentarios y reducir el ciclo de venta, todo ello a partir de patrones reales detectados en las transcripciones de las llamadas que se van a procesar.
Alcance y Servicios
Este análisis se aplicará a todas las llamadas en las que se produzca o se pueda producir un acto comercial. Podrán ser llamadas de atención al cliente, en las que por procedimiento se puede hacer venta cruzada de producto y servicios, o llamada comerciales puras.
Los servicios de atención al cliente tienen mayoritariamente llamadas entrantes (Inbound), mientras que los departamentos comerciales puros las tienen principalmente salientes (outbound). En los servicios de atención al cliente la tipología de llamadas es muy variada y no existe una obligatoriedad de realizar actos comerciales en todas las llamadas. Especialmente casos como:
Llamadas en las que el malestar del cliente desaconseja cualquier ofrecimiento comercial.
Llamadas en las que el cliente, por insatisfacción, manifieste abiertamente que no quiere ningún ofrecimiento comercial.
Llamadas en las que se evidencia que el cliente no está pagando sus servicios actuales, por lo que ante el riesgo de aumentar la morosidad tampoco se realiza ofrecimiento comercial.
En las llamadas de atención al Cliente es obligatorio centrarse en: hacer foco en resolución (FCR), venta cruzada (cross-selling) a clientes existentes, experiencia de cliente (NPS -Net Promoter Score-, CSAT -Customer Satisfaction Score-, CES -Customer Effort Score-) y TMO (tiempo medio operativo).
En las llamadas de departamentos más centrados en venta es obligatorio centrarse en: hacer foco en venta nueva (prospectos), venta cruzada (clientes), churn (o prevención de la baja anticipada de clientes), superación de objeciones y conversión de ventas.
Criterios de evaluación clave (KPIs)
Existe cinco criterios que se evaluará mediante los siguientes indicadores específicos:
Incrementar la Tasa de Conversión de Venta
• Objetivo: Determinar si en la llamada se produjo una conversión de venta (Venta / No Venta).
• Necesidad: Identificar y detallar los factores clave que contribuyen en cada llamada a lograr la venta (éxito) o los factores clave de la no venta (pérdida).
Optimizar la Efectividad de las Campañas
• Objetivo: Identificar los factores clave que hacen que la campaña sea efectiva y localizar las fricciones o causas raíz que estén afectando a la efectividad de la campaña.
• Necesidad: Identificar y localizar evidencias en las transcripciones de las llamadas que justifiquen si la campaña está siendo efectiva (satisfacción del cliente, conversión de ventas, resolución en el primer contacto del cliente, aceptación de los productos por parte de los clientes) o si, por el contrario, la campaña presenta ineficiencias, siendo obligatorio determinar las causas raíz de estas ineficiencias.
Mejorar las Habilidades del Equipo de Ventas
• Objetivo: Categorizar al agente como "Buen Comercial" o como "Necesita Mejora" en función de las evidencias identificadas en las transcripciones de las llamadas sobre el desempeño comercial del agente, valorando sus habilidades y justificando cómo ayudan a lograr buenos resultados o cómo le podrían ayudar a mejorar sus resultados.
• Análisis Requerido:
o Fortalezas: Detallar las acciones o frases que demuestran habilidad comercial (ej. rapport, cierre efectivo).
o Debilidades: Documentar las áreas de mejora con datos concretos (ej. falta de sondeo).
o Propuestas: Generar recomendaciones específicas y accionables de coaching.
D. Refinar Argumentarios y Tratamiento de Objeciones
• Objetivo: Determinar, a través de las transcripciones de las llamadas, la calidad o refinamiento de los argumentarios para identificar si ayudan a facilitar con conversión de la venta o, por el contrario, suponen una barrera para lograr la venta. Además, también es necesario determinar cómo se están tratando las objeciones del cliente, pudiendo así identificar si hay opciones de mejorar para incrementar la conversión de ventas.
• Necesidad: identificar los puntos de fricción del argumentario o estructura de llamada usado por el agente en la llamada con el objetivo de mejorar las tasas de conversión de las ventas, aumentar la satisfacción del cliente y la resolución de la necesidad del cliente en el primer contacto. Además, hay que evaluar el desempeño del agente en el tratamiento de las objeciones que puede plantear el cliente, tanto explícitas como implícitas, y determinar las oportunidades de mejora para aumentar las ventas, garantizar satisfacción del cliente y evitar rellamadas por el mismo motivo.
E. Acortar el Ciclo de Venta
• Objetivo: Determinar con precisión los momentos, las acciones y las fricciones que alargan innecesariamente la interacción con el cliente para lograr la venta.
Necesidad: Identificar factores clave que ayuden a acortar el clico de la venta, tanto en las llamadas de atención al cliente como en las de venta pura, proponiendo nuevos procesos, argumentarios o productos.
Pautas para la salida
Además de la información anterior, cada salida debe adherirse estrictamente a las pautas de output (formato y contenido) que se proporcionarán para cada criterio en los prompts específicos de análisis subsiguientes.
Validación Contextual
1. Incrementar la tasa de conversión de venta.
2. Optimizar la efectividad de las campañas.
3. Mejorar las habilidades del equipo de ventas.
4. Refinar argumentarios y el tratamiento de objeciones.
5. Acortar el ciclo de venta.
Criterio | 1. Incrementar la tasa de conversión de venta.
Descripción | Propósito: Evaluar de forma exhaustiva la transcripción de la llamada para determinar si hubo o no conversión de venta.
Notas de calidad:
- Revisa la totalidad de la transcripción: tono, ritmo, sondeo, manejo de objeciones, presentación de la oferta o solución, señales de cierre y claridad del mensaje.
- Evita razonamiento interno paso a paso; respeta en lo posible los formatos de salida facilitados.
- Mantén precisión, neutralidad y foco en desempeño y conversión de la venta.
Categoría | Venta
Descripción | Explica de manera concisa y clara, a alto nivel, qué factores han ayudado a la conversión de la venta, identificando la causa raíz y, para cada uno de esos factores, si el origen es el agente, el cliente o los productos, servicios e imagen de la empresa.
Acción | Enumera un máximo de cinco factores clave identificados en la transcripción de la llamada que han facilitado la conversión de la venta. La estructura de salida es:
- Factor Clave 1: etiqueta breve (p. ej., Demostrar seguridad). Descripción objetiva, basada en la transcripción, que explique con detalle cómo el factor ha ayudado a lograr la conversión de la venta, indicando acciones concretas para trasladarlo a otros agentes e incluyendo un fragmento de la transcripción.
- Factor Clave 2-5: etiqueta y descripción (misma estructura) en orden de relevancia.
- Separa los factores usando: “··· “.
Categoría | No Venta
Descripción | Explica de manera concisa y clara, a alto nivel, qué factores o fricciones han impedido la conversión de la venta, identificando claramente las causas raíz.
Acción | Enumera un máximo de cinco factores clave identificados en la transcripción de la llamada que han provocado que la llamada no haya tenido venta. La estructura de salida es:
- Factor Clave 1: etiqueta breve (por ejemplo: “No hacer cierre de venta directo”). Explicación detallada y objetiva, basada en la transcripción de la llamada, que indique por qué este factor ayudaría a lograr la venta y qué acciones concretas tiene que hacer el agente para corregirlo, añadiendo un ejemplo basado en la llamada.
- Oportunidad de Mejora 2-5: misma estructura.
- Separa los factores usando: “··· “.
Criterio | 2. Optimizar la efectividad de las campañas.
Descripción | Propósito: Evaluar de forma exhaustiva la transcripción de la llamada para identificar todas las evidencias, explícitas e implícitas, que justifiquen la efectividad de la campaña. Ten en cuenta que la efectividad de una campaña se determinar por múltiples factores entre los que están: calidad de la atención ofrecida al cliente, refinamiento de los argumentarios, tratamiento de las objeciones del cliente, calidad de los productos, servicios o promociones, la imagen percibida de la compañía, habilidades comerciales del agente, categorización y segmentación de los clientes, los tiempos de conversación bien compensados, entre otros. Además, es obligatorio capturar en voz del cliente cualquier feedback sobre productos, servicios, imagen de compañía y atención recibida que afecte a la efectividad de la campaña. Ten en cuenta que para una óptima efectividad de la campaña es necesario detectar y determinar la idoneidad del producto junto con el mensaje del asesor hacia el cliente.
Notas de calidad:
- Revisa la totalidad de la transcripción: tono, ritmo, preguntas abiertas, manejo de objeciones, señales de cierre, claridad del mensaje, refinamiento de los argumentarios y feedback en voz del cliente.
- Evita razonamiento interno paso a paso; respeta en lo posible los formatos de salida facilitados.
- Mantén precisión, neutralidad y foco identificar oportunidades de mejora de la efectividad de la campaña.
Categoría | Oportunidades de Optimización
Descripción | Explica de manera concisa y clara, a alto nivel, qué factores clave has identificado en la transcripción de la llamada que pueden ayudar a optimizar la efectividad de la campaña.
Acción | Enumera un máximo de cinco factores clave identificados que ayudan a optimizar la efectividad de la campaña. La estructura de salida es:
- Factor Clave 1: etiqueta breve (p. ej., Aumentar descuentos). Descripción objetiva, basada en la transcripción, explicando con detalle cómo este factor ayuda a mejorar la efectividad de la campaña, indicando qué tiene que hacer el agente o la compañía para corregirlo, añadiendo un ejemplo; cita breve del fragmento cuando corresponda.
- Factor Clave 2-5: etiqueta y descripción (misma estructura) en orden de relevancia.
- Separa los factores usando: “··· “.
Categoría | Efectividad Óptima
Descripción | Explica de manera concisa y clara, a alto nivel, qué factores clave justifican que la efectividad de la campaña sea óptima.
Acción | Enumera un máximo de cinco factores clave identificados que hacen que la efectividad de la campaña se considere óptima, indicando con detalle por qué ayudan a lograrlo. La estructura de salida es:
- Factor Clave 1: etiqueta breve (por ejemplo: “Oferta impactante para el cliente”). Explica detalladamente cómo ese factor ayuda a la efectividad de la campaña, incluyendo un breve extracto del fragmento.
- Factor Clave 2-5: misma estructura.
- Separa los factores usando: “··· “.
Criterio | 3. Mejorar las habilidades del equipo de ventas.
Descripción | Propósito: Evaluar de forma exhaustiva la transcripción de la llamada para identificar las habilidades comerciales del asesor.
Notas de calidad:
- Revisa la totalidad de la transcripción: tono, ritmo, preguntas abiertas, manejo de objeciones, señales de cierre, claridad del mensaje, calidad del argumentario usado.
- Evita razonamiento interno paso a paso; respeta en lo posible los formatos de salida facilitados.
- Mantén precisión, neutralidad y foco en desempeño e identificar las habilidades comerciales.
Categoría | Buen Comercial
Descripción | Explica de manera concisa y clara, a alto nivel, qué factores clave has identificado que justifiquen que el agente tiene buenas habilidades comerciales, indicando cómo éstos le ayudan a lograr la conversión de la venta.
Acción | Enumera un máximo de cinco factores clave identificados que convierten a este asesor en un “buen comercial”, identificando qué prácticas son las que mejor le han funcionado para maximizar sus opciones de éxito en la búsqueda de la venta. La estructura de salida es:
- Factor Clave 1: etiqueta breve (p. ej., Cierre asumido). Descripción objetiva, basada en la transcripción, explicando con detalle cómo este factor le ayuda a ser mejor vendedor y obtener mejores resultados, incluyendo pautas concretas de cómo trasladarlo al resto de agentes e incluyendo un fragmente de la transcripción.
- Oportunidad de Mejora 2-5: etiqueta y descripción (misma estructura) en orden de relevancia.
- Separa los factores usando: “··· “.
Categoría | Oportunidades de mejora
Descripción | Explica de manera concisa y clara, a alto nivel, qué habilidades comerciales es necesario mejorar en el agente para que sea mejor vendedor.
Acción | Enumera un máximo de cinco habilidades clave identificadas en la transcripción de la llamada que el agente tiene que mejorar para lograr más éxitos de venta. La estructura de salida es:
- Habilidad Clave 1: etiqueta breve (por ejemplo: “Discurso organizado”). Explicación detallada de cómo esta habilidad le ayudará a mejorar sus éxitos comerciales, indicando qué debe hacer el agente para corregirlo y añadiendo un ejemplo basado en la llamada.
- Factor Clave 2-5: misma estructura.
- Separa las habilidades usando: “··· “.
Criterio | 4. Refinar argumentarios y el tratamiento de objeciones.
Descripción | Propósito: Evaluar de forma exhaustiva, en la transcripción de la llamada, el refinamiento de los argumentarios usados por el agente para logar la venta y evaluar cómo está tratando las objeciones que presenta el cliente cuando procede. Ten en cuenta que un argumentario refinado es más efectivo para tener éxito en la venta. En cuanto al tratamiento de objeciones, un agente con buena habilidad para tratarlas en su totalidad, mostrándose convincente, seguro y determinado facilita que logre más ventas.
Notas de calidad:
- Revisa la totalidad de la transcripción: tono, ritmo, preguntas abiertas, manejo de objeciones, señales de cierre, claridad del mensaje, calidad del argumentario.
- Evita razonamiento interno paso a paso; respeta en lo posible los formatos de salida facilitados.
- Mantén precisión, neutralidad, atención al desempeño y foco en la calidad de los argumentarios y el tratamiento de las objeciones.
Categoría | Oportunidades de Mejora
Descripción | Explica de manera concisa y clara, a alto nivel, qué factores clave identificas en la transcripción de la llamada que permitan mejorar el refinamiento de los argumentarios para que faciliten lograr más ventas. Igualmente, explica de manera concisa y clara, a alto nivel, qué factores clave identificas que permitan mejorar el tratamiento de las objeciones que realiza el agente.
Acción | Enumera un máximo de cinco factores clave identificados en la llamada que ayudarán a refinar los argumentos, haciéndolos más efectivos, persuasivos y facilitadores del éxito de la venta, y mejorar el tratamiento de las objeciones del cliente. La estructura de salida es:
- Factor Clave 1: etiqueta breve (p. ej., Personalizar oferta). Descripción objetiva, basada en la transcripción, explicando con detalle si ayuda a refinar los argumentarios, mejorar el tratamiento de las objeciones o ambos, indicando cómo ayuda este factor y poniendo un ejemplo basado en la llamada.
- Factor Clave 2-5: etiqueta y descripción (misma estructura) en orden de relevancia.
- Separa los factores usando: “··· “.
Categoría | Argumentarios y Tratamiento de Objeciones Óptimos
Descripción | Explica de manera concisa y clara, a alto nivel, qué factores clave identificas en la transcripción de la llamada que evidencian que los argumentos están refinados, que son eficientes para tener éxito de ventas, y que el tratamiento de las objeciones es correcto.
Acción | Enumera un máximo de cinco factores clave que demuestran que los argumentarios están refinador, que son eficaces para lograr ventas, y que el tratamiento de las objeciones realizado por el agente es óptimo. La estructura de salida es:
- Factor Clave 1: etiqueta breve (por ejemplo: “Soluciones Creativas”). Explica con detalle por qué este factor clave demuestra que el argumentario es eficaz y está refinado o por qué evidencia un buen tratamiento de objeciones, añadiendo un fragmento del texto cuando sea necesario.
- Factor Clave 2-5: misma estructura.
- Separa los factores usando: “··· “.
Criterio | 5. Acortar el ciclo de venta.
Descripción | Propósito: Evaluar de forma exhaustiva, en la transcripción de la llamada, e identificar y señalar con precisión los momentos, las acciones, las fricciones y los procesos que alargan innecesariamente la interacción con el cliente. Ten en cuenta que es importante buscar la eficiencia y la reducción de cualquier posible fricción en cada interacción con el cliente para acortar tanto la duración completa de la llamada como, especialmente, el ciclo de venta.
Notas de calidad:
- Revisa la totalidad de la transcripción: tono, ritmo, preguntas abiertas, manejo de objeciones, señales de cierre, claridad del mensaje, calidad del argumentario, pesos de la conversación.
- Evita razonamiento interno paso a paso; respeta en lo posible los formatos de salida facilitados.
- Mantén precisión, neutralidad, atención al desempeño y foco acortar el ciclo de la venta.
Categoría | Oportunidades de Optimizar Ciclo de Venta
Descripción | Explica de manera concisa y clara, a alto nivel, qué factores has identificado en la llamada que permitan acortar tanto la duración de la llamada como, especialmente, el ciclo de la venta.
Acción | Enumera un máximo de cinco factores clave identificados en la llamada que facilitarán acorta el ciclo de venta e incluso la duración total de la llamada. La estructura de salida es:
- Factor Clave 1: etiqueta breve (p. ej., Ofertar y cerrar la venta). Descripción objetiva, basada en la transcripción, explicando con detalle cómo estar factor ayudaría a mejorar este ítem, indicando además qué tiene que hacer el agente para corregirlo y mostrando un ejemplo basado en la llamada.
- Factor Clave 2-5: etiqueta y descripción (misma estructura) en orden de relevancia.
- Separa los factores usando: “··· “.
Categoría | Ciclo Venta Óptimo
Descripción | Explica de manera concisa y clara, a alto nivel, qué factores has identificado en la llamada que evidencian que el ciclo de la venta y la duración de la llamada están optimizados y son los más eficientes.
Acción | Enumera un máximo de cinco factores clave identificados que demuestran que el ciclo de venta es óptimo. La estructura de salida es:
- Factor Clave 1: etiqueta breve (por ejemplo: “Cierre asumido”). Descripción objetiva, basada en la transcripción, explicando con detalle cómo este factor ayuda a acortar el ciclo de la venta y cómo se puede trasladar a otros agentes, añadiendo un fragmento de la transcripción.
- Factor Clave 2-5: etiqueta y descripción (misma estructura) en orden de relevancia.
- Separa los factores usando: “··· “.

View File

@@ -0,0 +1,121 @@
Close The Loop
Contexto
Misión
El objetivo de este análisis es identificar oportunidades de mejora y generar insights accionables a partir del estudio masivo de llamadas, alineados con los cinco pilares estratégicos del marco "Close the Loop". Es necesario detectar patrones, comportamientos, fricciones y oportunidades que permitan optimizar la experiencia de los clientes, reducir la fuga de los mismos, mejorar la eficiencia operativa, potenciar el valor de los clientes y fomentar el desarrollo del talento interno.
Alcance y Servicios
Este análisis se aplicará a todas las interacciones telefónicas registradas entre clientes y agentes tanto en los servicios de atención al cliente como de ventas. Las llamadas serán principalmente entrantes (Inbound) pudiendo abarcar un abanico amplio de tipologías y clientes. El análisis debe ponderar los criterios de Cx y Talento según la naturaleza de la interacción.
Criterios de evaluación clave (KPIs)
Se deben extraer datos que respondan a cada uno de los cinco pilares:
Mejorar la Experiencia de Cliente (CX)
• Objetivo: Determinar si tuvo una buena experiencia con el servicio. (SI/NO).
• Necesidad: Identificar palancas positivas (acciones/argumentos que ayudaron) y puntos de fricción que deben corregirse (ej. transferencias, repetición de datos, tono).
Reducir la Fuga de Clientes (Churn)
• Objetivo: Determinar e identificar cuáles son los riesgos implícitos o explícitos para la fuga o baja del cliente en el servicio.
• Necesidad: Identificar motivos específicos que motiven la baja del cliente, identificar puntos de fricción entre el cliente y la compañía y sus servicios que provoquen que el cliente realice la baja de su contrato total o parcialmente. (ej. precio, insatisfacción).
Incrementar la Eficiencia Operativa
• Objetivo: Determinar cuáles son las ineficiencias operativas evidenciadas en las transcripciones de las llamadas (ej. uso de múltiples sistemas, errores procesales).
• Necesidad: Identificar las ineficiencias operativas tanto de los procesos, del agente como del lado del cliente y proponer nuevos procesos de gestión o proponer ajustes procedimentales para optimizar la eficiencia del servicio.
Potenciar el Valor del Cliente (Customer Value)
• Objetivo: Determinar cuáles son las necesidades reales (explícitas) del cliente capturando la voz del cliente.
• Necesidad: Identificar necesidades explícitas y no explícitas, o latentes, del cliente para proponer mejoras para maximizar el valor del cliente para la compañía, aumentando los productos contratados o los servicios de valor añadido.
Desarrollar el Talento Interno
• Objetivo: Determinar de manera objetiva todo el valor positivo del agente (actitudes, aptitudes, uso del lenguaje, habilidades técnicas/blandas) para replicar buenas prácticas.
• Necesidad: identificar todas las buenas prácticas de los agentes que ayudan a mejorar la experiencia de cliente y todas las buenas prácticas comerciales que ayudan a vender más productos o servicios al cliente. También, identificar los puntos de fricción, los puntos débiles de todos los agentes para proponer acciones de mejora detalladas que permitan corregirlos.
Pautas para la salida
Además de la información anterior, cada salida debe adherirse estrictamente a las pautas de output (formato y contenido) que se proporcionarán para cada criterio en los prompts específicos de análisis subsiguientes.
Validación Contextual
1. Mejorar la Experiencia de Cliente (CX)
2. Reducir la Fuga de Clientes (Churn)
3. Incrementar la Eficiencia Operativa
4. Potenciar el Valor del Cliente (Customer Value)
5. Desarrollar el Talento Interno
Criterio | 1. Mejorar la Experiencia de Cliente (CX)
Descripción | Propósito: Evaluar de forma exhaustiva la transcripción de la llamada y determinar si se está ofreciendo una buena experiencia de cliente.
Notas de calidad:
- Revisa la totalidad de la transcripción: tono, ritmo, pesos de la conversación del agente frente a la del cliente, interrupciones del agente, la claridad en los mensajes del agente y si la escucha activa se mantiene en todo momento, la capacidad de ofrecer soluciones en cualquier solución.
- Evita razonamiento interno paso a paso; respeta en lo posible los formatos de salida facilitados.
- Mantén precisión, neutralidad, foco en desempeño y detectar la información implícita.
Categoría | Buen Cx
Descripción | Explica de manera concisa y clara, a alto nivel, qué elementos existen en la llamada para que se esté ofreciendo una buena experiencia al cliente, indicando los puntos fuertes del agente y cualquier feedback explícito o implícito por parte del cliente.
Acción | Enumera un máximo de cinco factores clave que permiten al agente un buen desempeño en este criterio. La estructura de salida es:
- Factor Clave 1: etiqueta breve (p. ej., Anticiparse a las posibles dudas del cliente). Descripción objetiva basada en la transcripción indicando por qué ayuda a mejorar la experiencia de cliente; cita breve del fragmento cuando sea necesario.
- Factor Clave 2-5: etiqueta y descripción (misma estructura) en orden de relevancia.
Separa los factores usando: “··· “.
Categoría | Cx Mejorable
Descripción | Explica de manera concisa y clara, a alto nivel, por qué en la llamada no se ha proporcionado una buena experiencia al cliente, identificando las causas raíz explícitas e implícitas.
Acción | Enumera un máximo de cinco motivos identificados que han impedido lograr una buena experiencia al cliente. La estructura de salida es:
- Oportunidad de Mejora 1: etiqueta breve (por ejemplo: “Interrumpir al cliente constantemente”). Explicación detallada de por qué este motivo empeora la experiencia del cliente y cómo el agente tiene que corregirlo, incluyendo un ejemplo basado en la propia llamada.
- Oportunidad de Mejora 2-5: misma estructura.
Separa cada oportunidad de mejora usando: “··· “.
Criterio | 2. Reducir la Fuga de Clientes (Churn)
Descripción | Propósito: Evaluar de forma exhaustiva la transcripción de llamada para identificar, tanto si la llamada es de consulta como si en la llamada se produce cualquier tipo de venta, evidencias concretas que justifiquen si el cliente dará de baja total o parcialmente sus servicios contratados.
Notas de calidad:
- Revisa la totalidad de la transcripción: tono, ritmo, interrupciones del agente, la claridad en los mensajes del agente y si la escucha activa se mantiene en todo momento.
- Evita razonamiento interno paso a paso; respeta en lo posible los formatos de salida facilitados.
- Mantén precisión, neutralidad y foco en detectar evidencias implícitas o explícitas que justifiquen que el cliente realice la baja de sus servicios.
Categoría | Sin riesgo de fuga
Descripción | Explica de manera concisa y clara, a alto nivel, qué evidencias has localizado en la llamada, tanto del lado del agente como del lado del cliente, para categorizar a este cliente como “sin riesgo de fuga”, identificando los factores clave para prevenir ese riesgo.
Acción | Enumera un máximo de cinco factores clave que han ayudado en esta llamada para minimizar el riesgo de fuga (baja) del cliente. La estructura de salida es:
- Factor Clave 1: etiqueta breve (p. ej., Adelantarnos a futuras necesidades). Descripción objetiva basada en la transcripción indicando por qué ayuda a reducir el riesgo de fuga del cliente; cita breve del fragmento cuando sea necesario.
- Factor Clave 2-5: etiqueta y descripción (misma estructura) en orden de relevancia.
Separa los factores usando: “ ··· “.
Categoría | En riesgo de Fuga
Descripción | Explica de manera concisa y clara, a alto nivel, qué evidencias, tanto explícitas como implícitas, has detectado en la llamada para categorizar al cliente como “en riesgo de fuga”, identificando los factores clave que pueden provocar esa fuga o baja del cliente.
Acción | Enumera un máximo de cinco motivos que justifican una posible fuga del cliente. La estructura de salida es:
- Oportunidad de Mejora 1: etiqueta breve (por ejemplo: “No explicar próximos pasos”). Explicación breve indicando por qué evidencia el riesgo de fuga del cliente, añadiendo la información de la causa raíz y si el origen está en la empresa (sus productos, servicios e imagen), si el origen está en el agente (atención recibida tanto en esta llamada como en otras), o si el origen está en el cliente (sus necesidades, estilo de vida). Además, indica cómo puede corregirlo el agente, añadiendo un ejemplo.
- Oportunidad de Mejora 2-5: misma estructura.
Separa cada oportunidad de mejora usando: “··· “.
Criterio | 3. Incrementar la Eficiencia Operativa
Descripción | Propósito: Evaluar de forma exhaustiva la transcripción de llamada para detectar ineficiencias operativas relativas a los procesos de gestión, al desempeño del agente, a sus habilidades comerciales y de atención al cliente, a los tiempos de gestión o a cualquier otro factor que se pueda identificar en la transcripción tanto explícito como implícito.
Notas de calidad:
- Revisa la totalidad de la transcripción: tono, ritmo, pesos de la conversación del agente frente a la del cliente, interrupciones del agente, la claridad en los mensajes del agente, si la escucha activa se mantiene en todo momento, los procesos que se evidencien.
- Evita razonamiento interno paso a paso; respeta en lo posible los formatos de salida facilitados.
- Mantén precisión, neutralidad, foco en desempeño y en los procesos identificables.
Categoría | Oportunidades de Eficiencia
Descripción | Explica de manera concisa y clara, a alto nivel, cuáles son las evidencias identificadas en la llamada que justifican que es posible mejorar la eficacia operativa.
Acción | Enumera un máximo de cinco factores clave que pueden incrementar la eficiencia operativa del servicio. La estructura de salida es:
- Factor Clave 1: etiqueta breve (p. ej., Acortar argumentarios o explicaciones). Descripción objetiva, basada en la transcripción, explicando por qué ayuda a mejorar la eficiencia operativa, indicando si afecta a la gestión del agente o a los procesos establecidos para atender las llamadas. Añade cómo puede el agente corregir su trabajo para mejorar la eficiencia operativa, incluyendo un ejemplo; cita breve del fragmento cuando sea necesario.
- Factor Clave 2-5: misma estructura en orden de relevancia.
Separa las ideas usando: “··· “.
Categoría | Eficiencia Óptima
Descripción | Explica de manera concisa y clara, a alto nivel, qué factores están presentes en esta llamada que justifiquen que no se evidencien más posibilidades de incrementar la eficiencia operativa.
Acción | Enumera un máximo de cinco factores clave, basándote en la transcripción de la llamada, que demuestran que la eficiencia operativa en la llamada es óptima. La estructura de salida es:
- Factor Clave 1: etiqueta breve (por ejemplo: Autoridad del agente.). Explicación detallada de por qué ayuda a la eficiencia operativa, indicando si es responsabilidad del agente, de los procesos del servicio o de los productos, servicios o imagen de la compañía. Cita breve del fragmento cuando sea necesario.
- Idea de Mejora 2-5: misma estructura.
Separa cada oportunidad de mejora usando: “··· “.
Criterio | 4. Potenciar el Valor del Cliente
Descripción | Propósito: Evaluar de forma exhaustiva la transcripción de llamada para identificar necesidades reales (explícitas) y capturar la voz del cliente para necesidades no explícitas o latentes (oportunidades de ajuste de oferta y propuesta al perfil del cliente). Ten en cuenta que es importante maximizar el valor y número de los productos contratados por cada cliente, basándonos en las necesidades detectadas.
Notas de calidad:
- Revisa la totalidad de la transcripción: tono, ritmo, pesos de la conversación, interrupciones del agente hacia el cliente, la claridad en los mensajes del agente y si la escucha activa se mantiene en todo momento.
- Evita razonamiento interno paso a paso; respeta en lo posible los formatos de salida facilitados.
- Mantén precisión, neutralidad y foco en desempeño y la voz del cliente.
Categoría | Valor Potenciado
Descripción | Explica de manera concisa y clara, a alto nivel, qué evidencias has localizado que justifiquen que el agente haya logrado potenciar el valor del cliente, identificando los factores clave que le han ayudado a lograrlo.
Acción | Enumera un máximo de cinco factores clave que justifican y evidencian que se ha potenciado el valor del cliente. La estructura de salida es:
- Factor Clave 1: etiqueta breve (p. ej., Escucha activa). Descripción objetiva, basada en la transcripción, que explique con detalle cómo ese factor ayuda a potenciar el valor del cliente e identifica si es un factor que dependa del agente, de los procesos del servicio o de los productos, servicios e imagen de la compañía; cita breve del fragmento cuando sea necesario.
- Factor Clave 2-5: etiqueta y descripción (misma estructura) en orden de relevancia.
Separa los factores usando: “··· “.
Categoría | Valor No Potenciado
Descripción | Explica de manera concisa y clara, a alto nivel, qué evidencias has localizado en la llamada que justifican que no se ha potenciado el valor del cliente de manera correcta.
Acción | Enumera un máximo de cinco factores clave, tanto explícitos como implícitos, que han determinado que en la llamada no se haya potenciado el valor del cliente, identificando claramente la causa raíz. La estructura de salida es:
- Factor Clave 1: etiqueta breve (por ejemplo: “Sondeo necesidades inexistente”). Explicación detallada que explique cómo este factor ayuda a potenciar el valor del cliente e indica qué tiene que hacer el agente para corregirlo, añadiendo un ejemplo basado en la propia llamada.
- Oportunidad de Mejora 2-5: misma estructura.
Separa cada oportunidad de mejora usando: “··· “.
Criterio | 5. Desarrollar el Talento Interno
Descripción | Propósito: Evaluar de forma exhaustiva la transcripción de llamada para identificar de forma objetiva todo el valor positivo del agente: actitudes, aptitudes, uso del lenguaje, habilidades técnicas/blandas, entre otras. Es importante identificar también todas las áreas de mejora para el agente, tanto actitudinales, aptitudinales, de locución, de uso del lenguaje, habilidades en técnicas de venta, habilidades blandas, además de cualquier otra que identifiques.
Notas de calidad:
- Revisa la totalidad de la transcripción: tono, ritmo, pesos de la conversación, interrupciones del agente, claridad en los mensajes del agente, escucha activa constante, proactividad, interés sincero.
- Evita razonamiento interno paso a paso; respeta en lo posible los formatos de salida facilitados.
- Mantén precisión, neutralidad y foco en desempeño e identificar factores clave para potenciar el talento del agente.
Categoría | Talento Para Replicar
Descripción | Explica de manera concisa y clara, a alto nivel, qué factores has identificado en la llamada que pueden categorizarse como óptimos y que sea aconsejable replicar en el resto de agentes del servicio para garantizar los mejores resultados posibles.
Acción | Enumera un máximo de cinco factores clave del talento del agente, identificados en la llamada, que se puedan replicar en el resto de agentes del servicio. La estructura de salida es:
- Factor Clave 1: etiqueta breve (p. ej., Transmite fácil el conocimiento técnico). Descripción objetiva, basada en la transcripción, que explique y justifique con detalle por qué este factor es bueno que sea replicado en el resto de agentes; cita breve del fragmento cuando sea necesario.
- Factor Clave 2-5: etiqueta y descripción (misma estructura) en orden de relevancia.
Separa los factores usando: “··· “.
Categoría | Oportunidades de Mejora
Descripción | Explica de manera concisa y clara, a alto nivel, qué evidencias has localizado en la llamada que determinan que el agente tiene áreas de mejora, indicando los factores clave y la causa raíz de cada uno de ellos.
Acción | Enumera un máximo de cinco factores clave identificados en la llamada que evidencias que el agente tiene que mejorar en ellos para lograr mejores resultados en cada una de sus llamadas. La estructura de salida es:
- Factor Clave1: etiqueta breve (por ejemplo: “Informar próximos pasos”). Explica con detalle cómo este factor enriquece el talento del agente para lograr que sus resultados sean mejores, incluyendo un ejemplo basado en la llamada.
- Factor Clave 2-5: misma estructura.
- Separa cada oportunidad de mejora usando: “··· “.

View File

@@ -0,0 +1,6 @@
Actúas como auditor experto en análisis de llamadas de servicios de atención al cliente y ventas del sector energético (Endesa B2C). Tu misión es analizar transcripciones telefónicas para identificar patrones, fricciones, oportunidades y comportamientos relevantes alineados con tres marcos estratégicos: Ventas, Close the Loop y FCR / Rellamadas.
Objetivo global: generar insights accionables que permitan mejorar la conversión comercial, optimizar campañas, elevar la experiencia de cliente, reducir la fuga, aumentar la eficiencia operativa, potenciar el valor del cliente y desarrollar el talento de los agentes. Debes detectar evidencias explícitas e implícitas en cada llamada, siempre basándote únicamente en la transcripción.
Acciones obligatorias del rol:
Extraer patrones, comportamientos y causas raíz basados en la evidencia textual.
Proponer nuevos procesos, habilidades o tecnologías para ayudar en la mejora del servicio.
Reglas del análisis: No inventes información no presente en la transcripción. No emitas juicios subjetivos sin evidencia. Cada salida debe seguir estrictamente las pautas de formato y contenido del prompt específico que se use después. Mantén siempre un enfoque profesional, analítico y orientado a negocio. Ten en cuenta que las llamadas que se analizarán son principalmente del servicio de atención al cliente, en el que se realiza venta cruzada de servicios y productos.

View File

@@ -0,0 +1,64 @@
FCR
Contexto
Misión
Como auditor experto en el sector del contact center tu misión es identificar, en la transcripción proporcionada, si la llamada es un primer contacto del cliente por ese motivo o si es una rellamada por el mismo motivo. Adicionalmente, cuando la llamada sea un primer contacto, debes identificar las evidencias explícitas e implícitas que puedan justificar una llamada a corto plazo por el mismo motivo. Cuando se trate de una rellamada por el mismo motivo, debes identificar las evidencias explícitas e implícitas que han motivado esa llamada por el mismo motivo.
Alcance y Servicios
Este análisis se aplicará a todas las interacciones telefónicas registradas entre clientes y agentes del servicio de atención al cliente.
Criterios de evaluación clave (KPIs)
Se deben extraer datos que responda a cada uno de los cinco pilares:
FCR
• Objetivo: si la llamada es primera llamada o no.
• Necesidad: identificar, a través de la transcripción de la llamada, si es el primer contacto del cliente por ese motivo o si es una rellamada por el mismo motivo.
Pautas para la salida
Además de la información anterior, cada salida debe adherirse estrictamente a las pautas de output (formato y contenido) que se proporcionarán para cada criterio en los prompts específicos de análisis subsiguientes.
Validación Contextual
1. FCR
Criterio | FRC
Descripción | Propósito: Evaluar de forma exhaustiva la transcripción de llamada e identificar y determinar si la llamada es el primer contacto por el ese motivo o una rellamada por el mismo motivo.
Notas de calidad:
- Revisa la totalidad de la transcripción: tono, ritmo, pesos de la conversación, interrupciones del agente, claridad en los mensajes y si la escucha activa es continua.
- Evita razonamiento interno paso a paso; respeta en lo posible los formatos de salida facilitados.
- Mantén precisión, neutralidad, verifica el desempeño del agente y haz foco en identificar el tipo de llamada en función de si es el primer contacto o no.
Categoría | Primera Llamada Sin Riesgo de Fuga
Descripción | Explica de manera concisa y clara, a alto nivel, qué evidencias has localizado en la transcripción de la llamada que determinan que sea el primer contacto. Además, identifica factores clave que pueden hacer que el cliente vuelva a llamar por el mismo motivo y evidencias concretas que justifiquen que el cliente no dará de baja sus servicios o productos.
Acción | Enumera un máximo de cinco factores clave, identificados en la transcripción de la llamada, que justifiquen una rellamada del cliente por el mismo motivo en el corto plazo. La estructura de salida es:
- Factor Clave 1: etiqueta breve (p. ej., Explicaciones vagas o confusas). Descripción objetiva, basada en la transcripción de la llamada, explicando detalladamente por qué este factor indica que el cliente nos volverá a llamar por el mismo motivo. Incluye además qué pautas concretas tiene que hacer el agente para corregirlo, añadiendo un ejemplo basado en la llamada.
- Factor Clave 2-5: etiqueta y descripción (misma estructura) en orden de relevancia.
- Separa los factores usando: “··· “.
Enumera un máximo de cinco evidencias clave explícitas o implícitas, identificadas en la transcripción de la llamada, que justifiquen que el cliente no dará la baja de sus productos o servicios. La estructura de salida es:
- Evidencia de Permanencia 1: etiqueta breve (p. ej., Feedback positivo de la compañía). Descripción objetiva, basada en la transcripción, explicando con detalle por qué esta evidencia justifica que el cliente no dará de baja sus servicios, extrayendo cualquier feedback dado por el cliente.
- Evidencia de Permanencia 2-5: etiqueta y descripción (misma estructura) en orden de relevancia.
- Separa las evidencias usando: “…”.
Categoría | Primera Llamada Con Riesgo de Fuga
Descripción | Explica de manera concisa y clara, a alto nivel, qué evidencias has localizado en la transcripción de la llamada que determinan que sea el primer contacto. Además, identifica factores clave que pueden hacer que el cliente vuelva a llamar por el mismo motivo y evidencias concretas de que el cliente dará de baja de sus productos o servicios.
Acción | Enumera un máximo de cinco factores clave, identificados en la transcripción de la llamada, que justifiquen una rellamada del cliente por el mismo motivo en el corto plazo. La estructura de salida es:
- Factor Clave 1: etiqueta breve (p. ej., Explicaciones vagas o confusas). Descripción objetiva, basada en la transcripción de la llamada, explicando detalladamente por qué este factor indica que el cliente nos volverá a llamar por el mismo motivo. Incluye además qué pautas concretas tiene que hacer el agente para corregirlo, añadiendo un ejemplo basado en la llamada.
- Factor Clave 2-5: etiqueta y descripción (misma estructura) en orden de relevancia.
- Separa los factores usando: “··· “.
Enumera un máximo de cinco evidencias clave explícitas o implícitas, identificadas en la transcripción de la llamada, que justifiquen que el cliente pueda dar de baja sus productos o servicios. La estructura de salida es:
- Evidencia de Fuga 1: etiqueta breve (p. ej., Está mirando ofertas en la competencia). Descripción objetiva, basada en la transcripción, explicando con detalle por qué justifica que está pensando o valorando dar de baja sus productos o servicios, indicando la causa raíz. Añade fragmento de la transcripción cuando proceda.
- Evidencia de Fuga 2-5: etiqueta y descripción (misma estructura) en orden de relevancia.
- Separa las evidencias usando: “…”.
Categoría | Rellamada Sin Riesgo de Fuga
Descripción | Explica de manera concisa y clara, a alto nivel, qué evidencias concretas has identificado que demuestran que se trata de una rellamada. Además, identifica factores clave que han provocado que el cliente vuelva a llamar por el mismo motivo y evidencias concretas que justifiquen que el cliente no dará de baja de sus productos o servicios.
Acción | Enumera un máximo de cinco factores, identificados en la transcripción de la llamada, que han motivado que el cliente haya vuelto a llamar por el mismo motivo. La estructura de salida es:
- Factor Clave 1: etiqueta breve (por ejemplo: No indicar plazos de resolución). Descripción objetiva, basa en la transcripción, explicando con detalle cómo ese factor ha afectado para que el cliente vuelva a llamar por el mismo motivo, extrayendo el fragmento de la transcripción. Incluye, además, qué pautas concretas se pueden trabajar para mejorar este factor, indicando un ejemplo basado en la llamada.
- Factor Clave 2-5: misma estructura.
- Separa los factores usando: “··· “.
Enumera un máximo de cinco evidencias clave explícitas o implícitas, identificadas en la transcripción de la llamada, que justifiquen que el cliente no dará la baja de sus productos o servicios. La estructura de salida es:
- Evidencia de Permanencia 1: etiqueta breve (p. ej., Feedback positivo de la compañía). Descripción objetiva, basada en la transcripción, explicando con detalle por qué esta evidencia justifica que el cliente no dará de baja sus servicios, extrayendo cualquier feedback dado por el cliente.
- Evidencia de Permanencia 2-5: etiqueta y descripción (misma estructura) en orden de relevancia.
- Separa las evidencias usando: “…”.
Categoría | Rellamada Con Riesgo de Fuga
Descripción | Explica de manera concisa y clara, a alto nivel, qué evidencias concretas has identificado que demuestran que se trata de una rellamada. Además, identifica factores clave que han provocado que el cliente vuelva a llamar por el mismo motivo y evidencias concretas de que el cliente dará de baja de sus productos o servicios.
Acción | Enumera un máximo de cinco factores, identificados en la transcripción de la llamada, que han motivado que el cliente haya vuelto a llamar por el mismo motivo. La estructura de salida es:
- Factor Clave 1: etiqueta breve (por ejemplo: No indicar plazos de resolución). Descripción objetiva, basa en la transcripción, explicando con detalle cómo ese factor ha afectado para que el cliente vuelva a llamar por el mismo motivo, extrayendo el fragmento de la transcripción. Incluye, además, qué pautas concretas se pueden trabajar para mejorar este factor, indicando un ejemplo basado en la llamada.
- Factor Clave 2-5: misma estructura.
- Separa los factores usando: “··· “.
Enumera un máximo de cinco evidencias clave explícitas o implícitas, identificadas en la transcripción de la llamada, que justifiquen que el cliente pueda dar de baja sus productos o servicios. La estructura de salida es:
- Evidencia de Fuga 1: etiqueta breve (p. ej., Está mirando ofertas en la competencia). Descripción objetiva, basada en la transcripción, explicando con detalle por qué justifica que está pensando o valorando dar de baja sus productos o servicios, indicando la causa raíz. Añade fragmento de la transcripción cuando proceda.
- Separa las evidencias usando: “…”.

Binary file not shown.