Saltar al contenido principal
  • un autoresponder para cada mensaje de texto que tus comandos, triggers y flows no manejaron, con historial por chat;
  • ctx.tools.ai, un cliente que llamas desde tus propios handlers, flows y conversaciones;
  • opcionalmente, herramientas, memoria a largo plazo y RAG y streaming.
Funciona con Anthropic, OpenAI, Gemini, Ollama (local o en la nube) y nueve servicios compatibles con OpenAI, en todos los canales.
Paquete separadoA diferencia de slashCommandsPlugin (incluido en el core), @botmesh/ai es un paquete propio, así que un bot sin IA no arrastra dependencias de IA. Los SDK de cada proveedor son opcionales y solo se cargan si se usan.

Quickstart (autoresponder)

Instala el plugin y el SDK de tu proveedor (aquí Anthropic, el de por defecto):
Esto registra:
  • Un autoresponder catch-all (bot.onMessage) que contesta cualquier mensaje de texto que ningún comando, trigger o conversación haya consumido. Guarda en el store del bot una ventana de historial por chat (20 mensajes por defecto, context.maxMessages).
  • Un comando /reset que borra el historial de IA del chat y cancela su conversación activa (se desactiva con resetCommand: false).
Tus handlers siempre van primero: el autoresponder solo ve lo que sobra. lang: 'es' hace que los textos que escribe el plugin (errores, confirmaciones) salgan en español (ver Idioma de las respuestas integradas).

Política de IA de WhatsApp (desde el 15-01-2026)

Los asistentes de IA de propósito general no están permitidos en la WhatsApp Business PlatformDesde el 15 de enero de 2026, los términos de Meta para la WhatsApp Business Platform (sección AI Providers) prohíben los asistentes de IA de propósito general: chatbots abiertos estilo ChatGPT cuya funcionalidad principal es la propia IA. Sigue estando permitido usar IA al servicio del negocio (atención al cliente, ventas, reservas, seguimiento de pedidos).
Qué significa para aiPlugin:
  • Escribe una persona acotada a tu negocio (qué vendes, horarios, políticas) que rechace con amabilidad lo que no tenga que ver con él, como en el quickstart.
  • Ofrece una salida hacia una persona (handoff) y no presentes el bot como un asistente “para todo”.
  • La regla viene de los términos de la Cloud API oficial (@botmesh/meta). Baileys es un cliente no oficial de WhatsApp Web y también está sujeto a los términos de WhatsApp.
Es una guía práctica, no asesoría legal: revisa los términos vigentes antes de salir a producción.

Elegir proveedor

Todos los proveedores admiten herramientas (en Ollama y los presets, si el modelo las admite) y streaming.
Los modelos por defecto no entran en semverCada proveedor retira modelos a su ritmo, así que los modelos por defecto siguen a los actuales y pueden cambiar en una versión menor. Fija model si la elección te importa (coste, latencia, estilo de respuesta).

Anthropic (por defecto)

baseUrl apunta el SDK a un gateway o proxy. Para chats de alto volumen, un modelo más pequeño (claude-sonnet-5-5, claude-haiku-5-5) es más barato y rápido.

OpenAI

baseUrl apunta a cualquier servidor compatible con OpenAI (vLLM, LM Studio, un gateway) o a Azure OpenAI. El tope de tokens de salida se envía como max_completion_tokens a OpenAI y a Azure OpenAI (api.openai.com, *.openai.azure.com, *.cognitiveservices.azure.com o *.services.ai.azure.com bajo /openai), y como max_tokens a cualquier otro baseUrl, que es lo que esos servidores esperan. maxTokensField fuerza la elección, por ejemplo para un proxy delante de OpenAI:

Gemini

Instala @google/genai. Google declaró obsoleto el SDK anterior, @google/generative-ai; se sigue usando si falta @google/genai, con un DeprecationWarning la primera vez. baseUrl pasa a ser el httpOptions.baseUrl del SDK.
Para Vertex AI, o cualquier opción del cliente que aiPlugin no expone, crea el proveedor tú mismo y pásalo como instance:

Ollama (local o en la nube)

Ollama ejecuta modelos abiertos en tu propia máquina. Sin SDK y sin API key: el plugin habla con su API HTTP mediante fetch.
  • Las herramientas necesitan un modelo entrenado para ellas (Llama 3.1+, Qwen 3, Mistral y los demás marcados tools en la biblioteca de Ollama).
  • Sin tope de salida por defecto: maxTokens se envía como options.num_predict solo si lo fijas, para que un modelo que piensa no se corte a mitad del razonamiento.
  • Modelos en la nube a través de tu Ollama local: inicia sesión una vez (ollama signin) y usa el nombre de un modelo en la nube (los que terminan en -cloud, como gpt-oss:120b-cloud) con la misma configuración. Ollama reenvía la petición a su nube; tu bot sigue hablando con localhost.
  • La API en la nube de Ollama directamente (sin servidor local), o un Ollama detrás de un proxy con autenticación: pon apiKey y se envía como Authorization: Bearer <clave> (nunca va al log ni a un mensaje de error):

Presets

DeepSeek, Qwen, GLM, Kimi, Groq, OpenRouter, Mistral, Grok y Together son compatibles con OpenAI: el plugin los alcanza con el SDK openai y su propia URL base, así que openai es el único paquete que hay que instalar. Cada preset trae un modelo por defecto y lee su clave de su variable de entorno habitual:
apiKey, model y baseUrl sobrescriben los valores del preset.

Versiones de SDK soportadas

Los SDK son dependencias peer opcionales; instala solo el que necesite tu proveedor. Los tests usan cada SDK en los dos extremos de su rango contra una imitación local de la API HTTP: respuestas simples, llamadas a herramientas con su resultado y streaming.

Usar la IA desde tu código: ctx.tools.ai

El plugin también hace bot.provide('ai', client), así que ctx.tools.ai está disponible en cualquier handler, flow o conversación. Desactiva el autoresponder (autoReply: false) si solo quieres esto:
ctx.tools.ai es un AIClient: Tipado (TypeScript). Importar @botmesh/ai amplía la interfaz DefaultTools del core, así que ctx.tools.ai (y bot.injectTools.ai fuera de los handlers) es un AIClient sin casts. El tipo no comprueba que el plugin esté instalado: sin bot.plug(aiPlugin(...)), o después de desenchufarlo, ctx.tools.ai es undefined en tiempo de ejecución.

Pensamiento (Anthropic)

Los modelos Claude piensan antes de responder. @botmesh/ai elige la petición adecuada para el modelo que configures (thinking: 'auto', el valor por defecto):
  • 'adaptive' envía siempre pensamiento adaptativo; 'off' no envía nunca el parámetro (los modelos que siempre piensan, como Claude Opus 5.5, lo siguen haciendo: baja effort en su lugar).
  • { budgetTokens } (un entero positivo; cualquier otro valor lanza un RangeError al construir la configuración) activa el pensamiento con presupuesto (budget_tokens, elevado al mínimo de 1024) en los modelos sin pensamiento adaptativo (Haiku 4.5, Sonnet 4.5, Opus 4.5 y anteriores). Los modelos adaptativos reciben pensamiento adaptativo: Opus / Sonnet 4.6 aún aceptan presupuestos pero los marcan como obsoletos, y Opus 4.7+, Sonnet 5+, Haiku 5+, Fable y Mythos los rechazan. Si maxTokens no deja sitio para la respuesta, pasa a ser budgetTokens + maxTokens.
  • Si aun así la API rechaza la petición con un 400 que menciona thinking (o effort), la llamada se reintenta una vez sin ese parámetro y el modelo queda recordado, así que las siguientes llamadas ya no lo envían. anthropicModelSupport(model) expone las reglas.

Herramientas, memoria, RAG y streaming

Cada uno tiene su guía; así se ven en la configuración del plugin. Herramientas: el modelo llama a tus funciones (buscar en el catálogo, registrar un pedido), con validación de argumentos, timeouts, confirmación humana para las herramientas confirm: true y eventos de auditoría.
Memoria y RAG: memory recuerda hechos de cada usuario entre conversaciones (se guardan en ctx.user); rag responde a partir de tus documentos con citas numeradas, con cualquier retriever, como createRag() de @botmesh/rag.
Streaming: stream: true envía la respuesta del autoresponder mientras se genera: borradores nativos en Telegram, ediciones en vivo en Discord y trozos por frases con “escribiendo…” en WhatsApp. Los turnos con herramientas no se transmiten.

Formato por canal

Los modelos escriben Markdown (**negrita**, # título, [enlace](url), - elemento), pero la mayoría de las apps de chat no lo muestran. El autoresponder convierte cada respuesta (también en streaming) al formato del chat, ctx.textFormat: el textFormatFor(chatId) del provider cuando sirve varios canales y, si no, su capabilities.textFormat. Nunca se tocan los bloques ni los spans de código, ni las palabras con snake_case o un 2 * 3. El historial guarda el texto original del modelo. format: 'whatsapp' (o cualquier formato) fija uno; format: false envía el texto tal cual. formatMarkdown(text, format) y formatMarkdownStream(source, format) se exportan para tus propios handlers. Con 'html' solo las URLs http(s), mailto: y tel: se convierten en enlaces <a href>: cualquier otra (un enlace javascript: que podría colar una inyección de prompt) queda como texto.

Notas de voz

El autoresponder solo contesta mensajes de texto. Con el plugin @botmesh/voice, las notas de voz se transcriben antes de enrutarse (usa autoTranscribe: { asText: true } para que la IA las conteste) y autoReplyWithVoice puede devolver la respuesta como audio.

Idioma de las respuestas integradas

Los textos que el plugin escribe por ti (las respuestas de error y de rechazo, la pregunta de confirmación de herramientas y sus botones Sí/No, la línea de fuentes) siguen el idioma del chat, ctx.lang. Vienen en inglés y en español; el inglés es el de por defecto. Un bot solo en español fija lang: 'es' en el Bot:
Cambia cualquier texto con bot.locale() bajo las claves ai. (ai.error, ai.refusal, ai.tools.confirm…, listadas en AI_MESSAGES), o fija errorMessage, refusalMessage y maxStepsMessage en el plugin. Ver i18n.

/reset y slashCommandsPlugin

aiPlugin registra su propio /reset: limpia el contexto de IA del chat y cancela la conversación activa. El registro de comandos es “último plug() gana”, así que si también usas slashCommandsPlugin (que trae su propio /reset para cancelar conversaciones), instala aiPlugin después para que su /reset prevalezca:

Coste y latencia

Cada turno del autoresponder es una llamada de pago al modelo (más, con herramientas). Para gastar y esperar menos:
  • Responde lo obvio sin IA. Los comandos, los triggers de bot.when() y los flows van antes que el autoresponder; las preguntas frecuentes, menús y saludos que resuelvas ahí nunca llegan al modelo.
  • Elige el modelo según la tarea. Para atención al cliente suele bastar un modelo pequeño; dale a la extracción de memoria en segundo plano uno más barato (memory: { autoExtract: { model: '...' } }).
  • Limita la salida. maxTokens acota cada respuesta (incluye los tokens de pensamiento de los modelos de razonamiento); en Anthropic, effort: 'low' piensa menos.
  • Envía menos historial. Cada turno reenvía la ventana del chat: baja context.maxMessages para intercambios cortos. Con RAG, menos pasajes y más pequeños (topK, chunkSize) acortan el prompt.
  • Acota el bucle de herramientas. Cada paso es una llamada al modelo: mantén maxSteps bajo y describe bien las herramientas para que el modelo acierte a la primera.
  • No hables de más en grupos. groupMode: 'mentions' en el Bot hace que solo responda en grupos cuando lo mencionan.
  • Que se sienta más rápido. stream: true muestra la respuesta mientras se escribe; humanizeTyping (activo por defecto) muestra “escribiendo…” mientras tanto.
  • Protege tu cuota. El rate limiting limita cuántos mensajes puede colar un chat, y timeoutMs acota cada llamada.
  • Mide. Con trazas, cada llamada al modelo es un span con su modelo y su consumo de tokens.

Errores y solución de problemas

aiPlugin nunca tumba el proceso por un proveedor mal configurado (SDK sin instalar, proveedor desconocido, API key ausente): install es síncrono y resuelve el proveedor de forma perezosa. Al arrancar, un warmup registra el problema como error, y desde ahí cada mensaje recibe la respuesta errorMessage en lugar de una contestación. Una configuración que nunca puede funcionar falla al construirla: aiPlugin() lanza un RangeError / TypeError que nombra la opción ante un timeout que no es un número finito de ms (un NaN de Number(process.env.X) saltaría a 1 ms), un maxTokens que no es un entero positivo, un context.maxMessages negativo o NaN, límites de memory inválidos, un toolConfirm.timeout que no se puede leer o una herramienta sin handler. bot.stop() aborta todas las llamadas al modelo en curso: las respuestas automáticas, la extracción de memoria en segundo plano y las llamadas que hacen tus handlers con ctx.tools.ai, que rechazan con un AbortError. Un proveedor lento nunca mantiene vivo el proceso. Una llamada que supera timeoutMs rechaza con AITimeoutError.

Config completa de aiPlugin(config)

Los textos por defecto son los ingleses; un chat en español recibe los integrados en español (ver Idioma de las respuestas integradas).

Testing

MockAIProvider (de @botmesh/ai/testing) implementa AIProvider con respuestas fijas o programables y sin red. Enchúfalo al plugin real con instance:
Sin bot, AIClient recibe el proveedor y un ContextManager sobre cualquier ConvStore (el store del bot es uno):
reply también puede ser una función de la petición que devuelva { toolCalls } para simular llamadas a herramientas (ver Testing en la guía de herramientas), y mock.calls registra cada petición que envió el cliente. El paquete tiene tres entradas: Un AIProvider propio que haga streaming debe respetar GenerateInput.onStreamEnd: llamarlo una vez, tras el último fragmento, solo si el stream termina con normalidad (no ante un error, un aborto o un break temprano), con un AIStreamEndInfo (stopReason, usage si se conoce). Comprueba signal.aborted antes de llamarlo: si no, un cliente que ignora la señal terminaría “con normalidad” tras un aborto.

Limitaciones

  • El historial es una ventana fija de mensajes (context.maxMessages); los turnos antiguos se descartan, no se resumen.
  • Una persona por plugin: un bot que necesite varias las elige en sus propios handlers con ctx.tools.ai.generate({ system, messages }).
  • El autoresponder solo contesta texto (ver Notas de voz).

Siguientes pasos