Lección 12 · 5 min · Gratis
Construye un asistente de voz con ElevenLabs y Next.js (Next.js)
Antes de escribir cualquier código, invoca la habilidad /speech-engine para aprender los patrones correctos del SDK de ElevenLabs.
1. package.json
- Añade
@elevenlabs/react,@elevenlabs/elevenlabs-js,openai,dotenvytsx. - Añade scripts:
speech-engine:create(tsx scripts/create-engine.mts),speech-engine:enable-first-message(tsx scripts/enable-first-message.mts),speech-engine:server(tsx server.mts).
2. .env.example
- Documenta
ELEVENLABS_API_KEY,ELEVENLABS_SPEECH_ENGINE_ID,OPENAI_API_KEYyPUBLIC_WS_URL(wss://…/wsde ngrok).
3. scripts/create-engine.mts
- Carga el entorno con
dotenv/config. - Crea un Speech Engine con
elevenlabs.speechEngine.create, usandospeechEngine.wsUrldePUBLIC_WS_URL. - Llama a un ayudante compartido para habilitar
overrides.firstMessageen el nuevo recurso. - Imprime
engineIde instrucciones para el siguiente paso.
4. scripts/enable-first-message.mts
- Actualiza un Speech Engine existente (
ELEVENLABS_SPEECH_ENGINE_ID) para que el cliente pueda estableceroverrides.agent.firstMessage.
5. lib/speech-engine-overrides.ts
- Exporta
enableFirstMessageOverride(client, speechEngineId)usandospeechEngine.updateconoverrides: { firstMessage: true }.
6. lib/assistant.ts
- Ayudantes compartidos de la API de respuestas de OpenAI:
ASSISTANT_INSTRUCTIONS,isChatRole,normalizeChatMessages,transcriptToChatMessages,createAssistantReply(messages, signal)ycreateAssistantStream(messages, signal). - Mapea el rol
agentde Speech Engine aassistantde OpenAI; mantén las respuestas concisas. - Normaliza el historial antes de enviarlo a OpenAI: recorta el contenido, elimina mensajes vacíos, limita el recuento de mensajes y la longitud por mensaje.
7. lib/voice-history.ts
- Añade un pequeño almacén local que permita a la aplicación Next.js entregar el historial de chat tipificado al servidor Speech Engine independiente.
- Almacena el
ChatMessage[]normalizado en.next/cache/voice-history.jsonporhistoryIdaleatorio. - Exporta
createVoiceHistory(messages),linkVoiceHistory(conversationId, historyId)yloadVoiceHistory(conversationId). - Mantén esto solo para demostración y respaldado por archivos; sin base de datos ni capa de autenticación.
8. server.mts
- Servidor HTTP independiente en el puerto 3001 con
speechEngine.attach(SPEECH_ENGINE_ID, httpServer, "/ws", { debug: true, … }). onTranscript: carga cualquier historial de chat tipificado vinculado asession.conversationId, añadetranscriptToChatMessages(transcript), pasaAbortSignalacreateAssistantStreamysession.sendResponse(stream).- Almacena en caché el historial inicial cargado por conversación y reintenta brevemente porque el navegador puede vincular el historial inmediatamente después de que se crea la conversación de voz.
- Registra
onInit,onCloseyonError. RequiereELEVENLABS_SPEECH_ENGINE_IDyOPENAI_API_KEY.
9. app/api/chat/route.ts
- Ruta POST segura para chat tipificado regular.
- Acepta
{ messages }, valida cada mensaje como{ role: "user" | "assistant", content: string }, limita el historial de solicitudes y llama acreateAssistantReply(messages, request.signal). - Devuelve
{ message }; devuelve errores JSON para cargas útiles no válidas o fallos de OpenAI. - Esta ruta y el modo de voz deben usar la misma forma
ChatMessagepara que el contexto pueda moverse entre modos.
10. app/api/voice-history/route.ts
- Ruta POST que acepta el historial de chat tipificado actual antes de que comience el modo de voz.
- Valida y normaliza los mensajes con
isChatRole, llama acreateVoiceHistory(messages)y devuelve{ historyId }. - Devuelve un 400 si no se proporcionan mensajes válidos.
11. app/api/voice-history/link/route.ts
- Ruta POST que acepta
{ historyId, conversationId }del navegador una vez que el cliente de Speech Engine crea una conversación de voz. - Llama a
linkVoiceHistory(conversationId, historyId)y devuelve{ ok: true }, o 404 si el ID del historial es desconocido.
12. app/api/token/route.ts
- Ruta GET segura; nunca expongas
ELEVENLABS_API_KEYal cliente. - Devuelve
{ token }deconversationalAi.conversations.getWebrtcToken({ agentId: ELEVENLABS_SPEECH_ENGINE_ID }). - Maneja la falta de entorno y los errores de la API con respuestas de error JSON.
13. app/page.tsx
- Crea una única interfaz de usuario de chat que funcione primero como un chat de asistente normal, y luego pueda cambiar al modo de voz sin perder el contexto.
- Mantén un array de estado
messagescon{ id, role: "user" | "assistant", content, channel: "chat" | "voice", pending?, error? }. - Ruta de envío de texto: añade el mensaje del usuario, envía el historial normalizado completo a
/api/chat, reemplaza el mensaje pendiente del asistente con la respuesta y mantén el compositor utilizable. - Ruta de inicio de voz: solicita acceso al micrófono, envía el historial de chat actual no pendiente/sin errores a
/api/voice-history, busca/api/tokeny luego inicia la sesión de voz con un saludooverrides.agent.firstMessageen línea. - En
onConversationCreated, vincula elhistoryIdalmacenado avoiceConversation.getId()con/api/voice-history/link; si es útil, también envía una actualización contextual que resuma el chat tipificado reciente. - Ruta de eventos de voz: usa
useConversation({ onMessage })para añadir o actualizar mensajes de transcripción en el mismo arraymessages, mapeando el rolagentdel SDK al rol localassistant. - Mientras la voz esté conectada, permite también mensajes tipificados: añádelos al mismo hilo y llama a
conversation.sendUserMessage(content); evita duplicar ecos deonMessage. - El siguiente chat tipificado después del modo de voz debe incluir mensajes de transcripción de voz en el historial enviado a
/api/chat. - Muestra un hilo de mensajes desplazable, un compositor de texto, un control de micrófono/inicio de voz, un control de parada de voz, estado de conexión, silenciar/activar sonido cuando esté conectado y mensajes de error de chat/voz separados.
- Sigue
DESIGN.md; haz que la página se sienta como una aplicación de chat regular con voz como un modo, no una demostración solo de voz.
Lección del curso «ElevenLabs Examples» de ElevenLabs, publicado con licencia MIT. Traducción y adaptación al español de IA con Clase. IA con Clase no está afiliado a ElevenLabs. Ver el original · Licencia
Esta lección es gratuita. El resto del curso se abre con la Membresía de IA con Clase, que incluye todos los cursos del catálogo. Ver precios