- 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.
Quickstart (autoresponder)
Instala el plugin y el SDK de tu proveedor (aquí Anthropic, el de por defecto):- 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
/resetque borra el historial de IA del chat y cancela su conversación activa (se desactiva conresetCommand: false).
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)
Qué significa paraaiPlugin:
- Escribe una
personaacotada 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.
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.
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 mediantefetch.
- 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:
maxTokensse envía comooptions.num_predictsolo 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, comogpt-oss:120b-cloud) con la misma configuración. Ollama reenvía la petición a su nube; tu bot sigue hablando conlocalhost. -
La API en la nube de Ollama directamente (sin servidor local), o un
Ollama detrás de un proxy con autenticación: pon
apiKeyy se envía comoAuthorization: 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 SDKopenai 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: bajaefforten su lugar).{ budgetTokens }(un entero positivo; cualquier otro valor lanza unRangeErroral 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. SimaxTokensno deja sitio para la respuesta, pasa a serbudgetTokens + maxTokens.- Si aun así la API rechaza la petición con un 400 que menciona
thinking(oeffort), 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 herramientasconfirm: true y eventos
de auditoría.
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.
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:
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.
maxTokensacota 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.maxMessagespara 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
maxStepsbajo y describe bien las herramientas para que el modelo acierte a la primera. - No hables de más en grupos.
groupMode: 'mentions'en elBothace que solo responda en grupos cuando lo mencionan. - Que se sienta más rápido.
stream: truemuestra 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
timeoutMsacota 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:
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
- Herramientas de IA — deja que el modelo llame a funciones de tu bot
- Memoria y RAG — hechos por usuario y respuestas con tus documentos
- Respuestas en streaming —
ctx.stream()por canal - Voz — transcribe notas de voz y responde con audio
- Handoff — pasa el chat a una persona
- Plugins — cómo funciona
bot.plug()en general