Biblioteca · Atención al cliente y agentes de voz

Atención al cliente con IA: sistema de tickets, RAG sobre la base de conocimiento y soporte inteligente

Creador65 minActualizado: octubre de 2026
60 de 105 en la biblioteca

Tiempo: unos 25 min de teoría + 40 min de práctica

Lo esencial

Imagina una recepción inteligente en un hospital grande. Todos los pacientes pasan primero por ahí. La mayoría de las preguntas las resuelve la propia recepción: cómo sacar cita, dónde está el estacionamiento, qué documentos se necesitan. Otra parte la resume y se la pasa al médico indicado. Los casos críticos los avisa de inmediato al médico de guardia. Los médicos se dedican solo a lo que les toca.

La atención al cliente con IA funciona igual. No reemplaza al equipo de soporte: trabaja a su lado. Se queda con lo típico y libera a las personas para lo complejo.

En esta lección construimos un sistema completo: un sistema de tickets con clasificación, RAG sobre la base de conocimiento de la empresa, respuestas automáticas y escalamiento inteligente. El resultado es una base con la que puedes armar un servicio para clientes.

Conceptos clave

  • RAG (Retrieval-Augmented Generation): Claude responde apoyándose en documentos concretos, no inventa
  • Clasificación de tickets: típico / atípico / crítico
  • Escalamiento: pasar en automático los casos complejos a una persona
  • Deflection Rate: el porcentaje de preguntas que la IA resolvió sin una persona
  • CSAT: la calificación del cliente (Customer Satisfaction Score)

Teoría

Para qué sirve la atención al cliente con IA

Sin IA, la primera respuesta de soporte muchas veces llega horas después. Eso es normal para una pregunta compleja. Pero la mayoría de las preguntas son típicas: "cómo cambio de plan", "dónde está mi paquete", "cómo conecto la integración". Por esas, el cliente espera horas y recibe una respuesta de dos líneas que el agente copió del FAQ.

Con IA:

  • Pregunta típica → respuesta automática en 30 segundos (cliente contento)
  • Atípica → borrador de la IA para el agente, el agente lo edita → 5 minutos en vez de 20
  • Crítica → escalamiento inmediato + aviso al agente

El agente ahora solo atiende lo atípico y lo crítico. Su carga baja de forma notable y la calidad de las respuestas difíciles sube, porque hay enfoque.


Arquitectura del sistema

Código
El cliente escribe una pregunta
    ↓
Clasificación (Claude): típica / atípica / crítica
    ↓
Típica → RAG sobre la base de conocimiento → respuesta automática (la mayor parte, referencia 80%)
Atípica → borrador de la IA para el agente → el agente edita → envía (referencia 15%)
Crítica → escalamiento inmediato + aviso → el agente responde él mismo (referencia 5%)

Tres capas:

  1. Clasificador: decide a dónde enviar
  2. RAG sobre la base de conocimiento: encuentra la respuesta exacta en los documentos
  3. Integración con el canal (Intercom / Telegram / correo): recibe la pregunta y manda la respuesta

RAG: el acordeón que la IA siempre tiene a mano

RAG significa Retrieval-Augmented Generation (generación aumentada con recuperación). Suena complicado, pero la idea es simple.

Claude, tal cual, responde con su conocimiento general. Eso está bien para preguntas generales. Está mal si necesitas una respuesta sobre una empresa concreta: sus planes, su política de devoluciones, los detalles de su producto.

RAG es cuando le das a Claude un acordeón directo en el prompt. Documentos de FAQ, instrucciones, políticas: todo eso se mete en el prompt de sistema o en el contexto. Claude ve la base de conocimiento y responde estrictamente con ella. Si la respuesta no está en la base, lo dice con honestidad, no inventa.

Imagínalo así: no es un estudiante tratando de acordarse, es un estudiante con acordeón. Responde con precisión. Cita la fuente. Si no está en el acordeón, lo dice.


Práctica

Paso 1. Estructura de la base de conocimiento

Crea una carpeta knowledge-base/ con archivos MD. Un archivo por tema:

Código
knowledge-base/
  billing.md          — preguntas sobre pagos
  shipping.md         — envíos y devoluciones
  integrations.md     — conectar integraciones
  plans.md            — planes
  account.md          — administración de la cuenta

Ejemplo de billing.md:

markdown
## Cómo cambiar de plan

Entra a tu cuenta → Configuración → Plan → elige el plan nuevo.
El cambio se aplica de inmediato. La diferencia se cobra de forma proporcional.

## Reembolsos

El reembolso es posible dentro de los 14 días posteriores al pago en el primer pedido.
Para pedirlo, escribe a [email protected] con el asunto "Reembolso" y el número de pedido.
Plazo de procesamiento: 5 días hábiles.

## En qué se diferencian los planes Basic y Pro

Basic: hasta 5 usuarios, 10 GB de almacenamiento, soporte por correo.
Pro: usuarios ilimitados, 100 GB de almacenamiento, soporte prioritario, acceso a la API.

Paso 2. Cargar la base de conocimiento y responder un ticket

python
import anthropic
import json
import time
from pathlib import Path
from datetime import datetime, timezone

client = anthropic.Anthropic()


def load_knowledge_base(kb_path: str) -> str:
    """Carga la base de conocimiento desde archivos MD"""
    kb_content = []
    for md_file in Path(kb_path).glob("**/*.md"):
        kb_content.append(f"\n## {md_file.stem}\n{md_file.read_text()}")
    return "\n".join(kb_content)


KB = load_knowledge_base("knowledge-base/")


def answer_support_ticket(question: str) -> dict:
    """Clasifica el ticket y responde o escala"""

    start_time = time.time()

    response = client.messages.create(
        model="claude-sonnet-5-5",
        max_tokens=800,
        system=f"""Eres un agente de IA de atención al cliente.

BASE DE CONOCIMIENTO DE LA EMPRESA:
{KB}

Reglas:
1. Si la respuesta está en la base de conocimiento, responde con exactitud usándola, no agregues nada propio
2. Al final de la respuesta indica: Fuente: [nombre de la sección de la base de conocimiento]
3. Si la respuesta no está en la base de conocimiento, escribe ESCALATE en la primera línea y explica por qué se necesita una persona
4. Si la pregunta es sobre un reembolso, escribe SIEMPRE ESCALATE (aunque la respuesta esté en la base)
5. Si la pregunta es sobre una falla técnica del cliente: ESCALATE
6. Tono: amable, concreto, sin relleno""",
        messages=[{"role": "user", "content": question}]
    )

    answer = "".join(b.text for b in response.content if b.type == "text")
    response_time = time.time() - start_time

    return {
        "answer": answer,
        "needs_human": answer.strip().startswith("ESCALATE"),
        "response_time_sec": round(response_time, 2),
        "confidence": "low" if answer.strip().startswith("ESCALATE") else "high"
    }


def track_metrics(result: dict, question: str):
    """Escribe las métricas en un log"""
    log_entry = {
        "ts": datetime.now(timezone.utc).isoformat(),
        "escalated": result["needs_human"],
        "response_time_sec": result["response_time_sec"],
        "confidence": result["confidence"],
        "question_length": len(question)
    }
    with open("support-metrics.jsonl", "a") as f:
        f.write(json.dumps(log_entry, ensure_ascii=False) + "\n")


# Ejemplo de uso
if __name__ == "__main__":
    questions = [
        "¿Cómo cambio de plan?",
        "Quiero que me devuelvan el dinero de la suscripción",
        "No me funciona la integración con Slack, se rompió todo"
    ]

    for question in questions:
        print(f"\nPregunta: {question}")
        result = answer_support_ticket(question)
        track_metrics(result, question)

        if result["needs_human"]:
            print(f"ESCALAMIENTO -> se pasa a un agente humano")
            print(f"Motivo: {result['answer']}")
        else:
            print(f"Respuesta automática ({result['response_time_sec']} s):")
            print(result["answer"])

Paso 3. Integración con Intercom

python
from flask import Flask, request
import requests
import os

app = Flask(__name__)

INTERCOM_TOKEN = os.environ["INTERCOM_TOKEN"]
AI_BOT_ID = os.environ["INTERCOM_BOT_ID"]


def assign_to_human_agent(conversation_id: str, priority: str = "normal"):
    """Asigna el ticket a un agente humano y le pone una etiqueta"""
    requests.post(
        f"https://api.intercom.io/conversations/{conversation_id}/parts",
        headers={
            "Authorization": f"Bearer {INTERCOM_TOKEN}",
            "Content-Type": "application/json"
        },
        json={
            "type": "admin",
            "admin_id": AI_BOT_ID,
            "message_type": "assignment",
            "assignee_id": None  # lo asigna al equipo, no a un agente concreto
        }
    )

    # Ponemos la etiqueta de prioridad
    if priority == "high":
        requests.post(
            f"https://api.intercom.io/conversations/{conversation_id}/tags",
            headers={"Authorization": f"Bearer {INTERCOM_TOKEN}"},
            json={"id": os.environ["INTERCOM_HIGH_PRIORITY_TAG_ID"]}
        )


@app.post("/intercom-webhook")
def handle_message():
    data = request.json

    if data.get("type") != "conversation.user.created":
        return {"status": "ignored"}

    item = data["data"]["item"]
    conversation_id = item["id"]
    parts = item["conversation_parts"]["conversation_parts"]

    if not parts:
        return {"status": "no_message"}

    message = parts[0]["body"]

    # Responde la IA
    result = answer_support_ticket(message)
    track_metrics(result, message)

    if not result["needs_human"]:
        # Mandamos la respuesta automática a nombre del bot
        requests.post(
            f"https://api.intercom.io/conversations/{conversation_id}/reply",
            headers={
                "Authorization": f"Bearer {INTERCOM_TOKEN}",
                "Content-Type": "application/json"
            },
            json={
                "type": "admin",
                "admin_id": AI_BOT_ID,
                "message_type": "comment",
                "body": result["answer"]
            }
        )
    else:
        # Le avisamos al cliente que vamos a conectar a una persona
        requests.post(
            f"https://api.intercom.io/conversations/{conversation_id}/reply",
            headers={
                "Authorization": f"Bearer {INTERCOM_TOKEN}",
                "Content-Type": "application/json"
            },
            json={
                "type": "admin",
                "admin_id": AI_BOT_ID,
                "message_type": "comment",
                "body": "Tu pregunta pasó a un especialista. Te respondemos en un plazo de 2 horas."
            }
        )
        assign_to_human_agent(conversation_id, priority="high")

    return {"status": "ok"}


if __name__ == "__main__":
    app.run(port=5000)

Paso 4. Un bot de Telegram para soporte

Si el cliente no tiene Intercom, un bot de Telegram resuelve la tarea en un par de horas.

En Latinoamérica muchos clientes escriben por WhatsApp. Ahí el camino es la API oficial de WhatsApp Business de Meta, con sus propias reglas de uso y costos (revísalos en el sitio de Meta). Aquí usamos Telegram porque crear un bot es gratis y rápido; la lógica de clasificar, responder y escalar es la misma en cualquier canal.

python
import asyncio
import os
from telegram import Update, Bot
from telegram.ext import Application, MessageHandler, filters

TELEGRAM_BOT_TOKEN = os.environ["TELEGRAM_BOT_TOKEN"]
SUPPORT_TEAM_CHAT = os.environ["SUPPORT_TEAM_CHAT_ID"]

bot = Bot(token=TELEGRAM_BOT_TOKEN)


async def handle_support_message(update: Update, context):
    user_message = update.message.text
    user_id = update.effective_user.id
    username = update.effective_user.username or str(user_id)

    # Indicador de que el bot está trabajando
    await update.message.reply_text("Revisando...")

    result = answer_support_ticket(user_message)
    track_metrics(result, user_message)

    if not result["needs_human"]:
        await update.message.reply_text(result["answer"])
    else:
        # Al cliente: mensaje de que conectamos a una persona
        await update.message.reply_text(
            "Tu pregunta necesita la atención de un especialista. "
            "Te respondemos en un plazo de 2 horas en horario laboral."
        )
        # Al equipo: aviso con todo el contexto
        escalation_text = (
            f"Escalamiento de @{username} (id: {user_id})\n\n"
            f"Pregunta: {user_message}\n\n"
            f"Motivo del escalamiento: {result['answer']}"
        )
        await bot.send_message(
            chat_id=SUPPORT_TEAM_CHAT,
            text=escalation_text
        )


def run_bot():
    application = Application.builder().token(TELEGRAM_BOT_TOKEN).build()
    application.add_handler(
        MessageHandler(filters.TEXT & ~filters.COMMAND, handle_support_message)
    )
    application.run_polling()


if __name__ == "__main__":
    run_bot()

Paso 5. Métricas

Sin métricas no sabes si el sistema funciona. Cuatro números que hay que seguir:

Métrica Qué mide Meta
First Response Time Tiempo hasta la primera respuesta < 1 min (IA), < 4 h (persona)
Deflection Rate % de tickets resueltos sin una persona > 75%
Resolution Rate % de tickets cerrados con la primera respuesta > 60%
CSAT Score Calificación del cliente de 1 a 5 > 4.2
python
def generate_support_report(metrics_file: str = "support-metrics.jsonl") -> dict:
    """Calcula las métricas principales del periodo"""
    entries = []
    with open(metrics_file) as f:
        for line in f:
            entries.append(json.loads(line))

    if not entries:
        return {"error": "no hay datos"}

    total = len(entries)
    escalated = sum(1 for e in entries if e["escalated"])
    deflection_rate = round((total - escalated) / total * 100, 1)
    avg_response_time = round(
        sum(e["response_time_sec"] for e in entries) / total, 2
    )

    return {
        "total_tickets": total,
        "escalated": escalated,
        "auto_resolved": total - escalated,
        "deflection_rate_pct": deflection_rate,
        "avg_response_time_sec": avg_response_time
    }


# Ejemplo de salida:
# {
#   "total_tickets": 150,
#   "escalated": 28,
#   "auto_resolved": 122,
#   "deflection_rate_pct": 81.3,
#   "avg_response_time_sec": 2.4
# }

Paso 6. Actualizar la base de conocimiento

La base de conocimiento envejece. Planes nuevos, funciones nuevas, cambiaron las reglas de devolución. Un proceso simple de actualización:

  1. Editas el archivo MD en knowledge-base/
  2. Reinicias el servidor (o agregas hot-reload)
  3. Claude responde al instante con los datos nuevos

Esa es la gran ventaja de los archivos MD frente a las bases vectoriales: se actualizan en 30 segundos, sin reindexar. El enfoque funciona mientras la base de conocimiento quepa en el contexto del modelo. Si la base es grande y hay muchas preguntas, activa el prompt caching (lección Prompt Caching y Batch API): la parte repetida del prompt sale más barata.


Herramientas y recursos

Herramienta Para qué Precio
Intercom Sistema de tickets principal (enterprise) De pago, planes en su sitio
Crisp Alternativa a Intercom (más simple y más barata) Planes en su sitio
Telegram Bot API Canal de soporte gratuito Gratis
Flask Servidor de webhooks para integraciones Gratis
Python-telegram-bot Biblioteca para bots de Telegram Gratis
Claude Sonnet Modelo principal (equilibrio precio/calidad) Depende del tamaño de la base de conocimiento y de la respuesta; Lo vigente

Stack del MVP mínimo:

  • Python 3.11+
  • anthropic: el SDK de Claude
  • python-telegram-bot: si usas Telegram
  • flask: si la integración es por webhook
  • Archivos MD: la base de conocimiento

Construir para vender

Esto se puede armar como un servicio para empresas pequeñas donde de soporte se encargan 1 a 3 personas. Si aparecen clientes y cuánto pagan depende del nicho, del mercado y de tu trabajo. No hay garantías.

Las cuentas para el cliente:

Haz el cálculo junto con el cliente, con sus propios datos. Horas al día dedicadas a preguntas típicas × tarifa del empleado = costo de esas preguntas por día. La IA libera solo una parte de esas horas: los tickets complejos y la revisión de respuestas siguen en manos de personas. Tiempo de recuperación = precio del servicio ÷ ahorro real por día. Ejemplo con números inventados: 4 horas × $15 = $60 al día antes de implementarlo, y si la IA resuelve la mitad de las preguntas, el ahorro es de unos $30 al día.

Estructura de la propuesta:

Pon los precios según tus costos y el valor para el cliente; más detalle en la lección Cómo poner precio.

Opción Qué incluye
Setup Desarrollo + configuración + primera base de conocimiento
Mantenimiento mensual Hosting + monitoreo + actualizaciones de la base de conocimiento
Enterprise Integración a la medida + capacitación del equipo

Tiempo de desarrollo del MVP: 4-6 horas (bot de Telegram + Claude + FAQ en MD).

Qué le vendes al cliente:

  • Un bot de Telegram o una integración con Intercom/Crisp
  • Una base de conocimiento armada a partir de su FAQ
  • Un dashboard de métricas (Deflection Rate, tiempo de respuesta)
  • Documentación para actualizar la base de conocimiento

Ideas clave

  • La atención al cliente con IA no reemplaza al equipo. Lo refuerza: lo típico lo toma la IA, lo complejo se queda con las personas
  • 80/15/5 es una referencia, no una ley: la mayor parte, respuesta automática; una parte menor, borrador de IA para el agente; el resto, escalamiento. Las proporciones cambian en cada empresa
  • RAG con archivos MD es el camino más simple hacia respuestas precisas. La base de conocimiento se actualiza en 30 segundos
  • Tres métricas que le importan al cliente: Deflection Rate (> 75%), First Response Time (< 1 min), CSAT (> 4.2)
  • Un MVP mínimo se arma en unas horas. Como servicio se puede presentar en tres partes: setup, mantenimiento mensual e integraciones a la medida

Siguiente lección

→ Soporte por llamada con IA: Vapi + Bland.ai, llamadas de voz en soporte

Vemos el siguiente nivel: no tickets de texto, sino llamadas de voz. Cómo la IA contesta una llamada, responde con la base de conocimiento y pasa los casos complejos a una persona.

La marca se guarda solo en este navegador y no se envía a ningún sitio. Mi progreso