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
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:
- Clasificador: decide a dónde enviar
- RAG sobre la base de conocimiento: encuentra la respuesta exacta en los documentos
- 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:
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 cuentaEjemplo de billing.md:
## 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 billing@company.com 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
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
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.
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 |
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:
- Editas el archivo MD en
knowledge-base/ - Reinicias el servidor (o agregas hot-reload)
- 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 Claudepython-telegram-bot: si usas Telegramflask: 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