# Agente con Claude — diseño del loop agentic

## El loop básico

```
Cliente manda mensaje
  ↓
Webhook recibe + valida firma + identifica cliente
  ↓
Carga historial conversación (últimos N mensajes, ventana 24h)
  ↓
Construye prompt: system + historial + nuevo mensaje
  ↓
LOOP (max 5 iteraciones):
  ├─ Llama a Claude con tools disponibles
  ├─ Si stop_reason == "end_turn": Claude tiene respuesta de texto final → mandar a cliente, fin
  ├─ Si stop_reason == "tool_use": ejecutar tool, anexar tool_result, próxima iteración
  └─ Si error: fallback a "no pude procesar, llamanos al local"
  ↓
Guardar mensaje del cliente + respuesta del asistente en BD
```

## System prompt — estructura recomendada

```text
Sos el asistente virtual de {NEGOCIO}, atendiendo a {NOMBRE_CLIENTE} por WhatsApp.
Hablás en español argentino, sos amable, directo, conciso.

CAPACIDADES:
- (lista de qué puede hacer)

REGLAS GENERALES:
- No inventes datos, consultá siempre las tools.
- Para X específico, derivar al humano: "Avisamos al equipo".
- Hoy es {FECHA}.

SALUDO INICIAL:
- Detectar "hola", "buenos días", etc.
- Llamar mostrar_opciones(menu_principal) con saludo según hora.

FLUJOS ESPECÍFICOS:
- (uno por cada flujo: consulta producto, pedido, etc.)

REGLAS DE FORMATO:
- Mensajes que van a templates: NO `\n`, NO em-dash, separadores ASCII.
- Mensaje final con emoji apropiado según tipo de entrega.
```

## Tools — JSON Schema típico

```json
[
  {
    "name": "buscar_productos",
    "description": "Busca productos por nombre, código o marca. Devuelve precio segun categoria del cliente.",
    "input_schema": {
      "type": "object",
      "properties": {
        "termino": {"type": "string"}
      },
      "required": ["termino"]
    }
  },
  {
    "name": "consultar_pedidos",
    "description": "Consulta pedidos del cliente en los ultimos 30 dias (todos los estados).",
    "input_schema": {"type": "object", "properties": {}, "required": []}
  },
  {
    "name": "crear_pedido",
    "description": "Crea un pedido nuevo. Llamar SOLO despues que el cliente confirmo el resumen completo.",
    "input_schema": {
      "type": "object",
      "properties": {
        "items":            {"type": "array", "items": {...}},
        "id_tipo_entrega":  {"type": "integer"},
        "nota":             {"type": "string"}
      },
      "required": ["items", "id_tipo_entrega"]
    }
  },
  {
    "name": "mostrar_opciones",
    "description": "Manda un mensaje con botones via template aprobado.",
    "input_schema": {
      "type": "object",
      "properties": {
        "tipo":     {"type": "string", "enum": ["menu_principal", "seleccion_2", "seleccion_3", "tipo_entrega", "confirmacion"]},
        "mensaje":  {"type": "string"},
        "opciones": {"type": "array", "items": {...}}
      },
      "required": ["tipo", "mensaje"]
    }
  }
]
```

## Modelo recomendado

**Claude Sonnet 4** (`claude-sonnet-4-20250514`) — balance ideal precio/calidad para tool-use.
- Para casos críticos donde necesitás máxima precisión: Claude Opus 4.
- Para minimizar costo: Claude Haiku, pero pierde en interpretación de matices del lenguaje natural argentino.

`max_tokens`: 1024 es suficiente para respuestas de WhatsApp.

## Historial conversación

- Tabla `whatsapp_conversaciones (from_number, role, content, created_at)`.
- Cargar **últimos 40 mensajes** de las **últimas 24h**.
- ⚠️ **Reset cuando**:
  1. Cliente manda saludo y el historial tiene > 4 mensajes (volvió después de horas).
  2. Después de `crear_pedido` OK (cerrar conversación).
  3. (Opcional) Después de N horas de inactividad.

Sin reset, el agente acumula contexto viejo y "alucina" mezclando pedidos pasados.

## Manejo de respuestas de botón

Cuando el cliente apreta un botón quick-reply, Twilio manda:

```json
{
  "MessageType":     "interactive",
  "ButtonText":      "💰 Consultar precio",       // título visible (puede traer emojis)
  "ButtonPayload":   "precio",                    // id del botón ← USAR ESTE
  "OriginalRepliedMessageSid": "MM...",
  "Body":            ""                           // a veces vacío, a veces el title
}
```

**Usar `ButtonPayload` como source de verdad**. El `ButtonText` puede traer emojis que rompen el matcheo.

Si no llega ButtonPayload (caso quoted-reply de cliente WhatsApp viejo), parsear el body buscando el title al final con regex que tolere emojis al inicio.

## Errores comunes en el loop

1. **`messages.N: user messages must have non-empty content`**: Claude llamó tool pero el tool_result vino vacío. Asegurar que siempre devuelvas al menos `{"error": "..."}`.
2. **Loop infinito** (cliente apreta botón, agente vuelve al menú): tu detector de ButtonPayload no funcionó, body cae como saludo. Ver `ButtonPayload`.
3. **Agente "salta" pasos del flujo** (ej. arma resumen sin preguntar tipo entrega): system prompt no es suficientemente estricto. Repetir "OBLIGATORIO" y agregar regla "NUNCA hagas X sin Y".

## Resetear historial bien

```sql
DELETE FROM whatsapp_conversaciones WHERE from_number = ?;
DELETE FROM whatsapp_seleccion_pending WHERE from_number = ?;
```

Hacelo después de `crear_pedido` exitoso y al detectar saludo nuevo con historial > 4 msgs.
