Saltar al contenido principal

Documentation Index

Fetch the complete documentation index at: https://docs.mka1.com/llms.txt

Use this file to discover all available pages before exploring further.

La API MKA1 proporciona una interfaz de voz en tiempo real a través de LiveKit. Esta guía cubre cómo obtener un token de sala, conectarse a una sesión de voz, enviar audio y texto como entrada, y capturar las respuestas del agente.

Descripción general

La integración de voz consta de tres componentes principales:
  1. Token de Sala: Un JWT que otorga acceso a una sala de LiveKit
  2. Conexión LiveKit: Comunicación en tiempo real basada en WebRTC
  3. Agente de Voz: Procesa entrada de audio/texto y genera respuestas habladas
El flujo del agente funciona de la siguiente manera:
  • STT (Reconocimiento de Voz a Texto): El audio se transmite vía WebSocket a 16kHz y se transcribe
  • LLM: El texto transcrito es procesado por la API de Respuestas MKA1
  • TTS (Texto a Voz): La salida del LLM se sintetiza a audio a 24kHz
Cada solicitud que el agente de voz envía a la API de Respuestas incluye automáticamente "voice_mode": "true" en el metadata de la solicitud. Esto le permite distinguir respuestas originadas por voz de las basadas en texto al revisar el uso o el historial de respuestas.

Obtener un token de sala

Para iniciar una sesión de voz, primero solicite un token de sala a la API MKA1. El endpoint de token requiere una clave de API y acepta opcionalmente X-On-Behalf-Of para identificar usuarios finales. Consulte Autenticación para más detalles.
mka1 llm speech livekit-token \
  --llm '{"model":"meetkai:functionary-es-mini","reasoning":{"effort":"none"}}' \
  -H 'X-On-Behalf-Of: <end-user-id>'

Parámetros

El cuerpo de la solicitud tiene dos objetos de nivel superior:
{
  "llm": { ... },   // Requerido — configuración del LLM
  "stt": { ... }    // Opcional — ajuste de reconocimiento de voz
}

llm — Configuración de LLM (requerido)

El objeto llm acepta los mismos campos que el cuerpo de la solicitud de la API de Respuestas, menos los campos gestionados por el agente de voz (input, stream, store, background).
CampoRequeridoDescripción
modelModelo LLM a utilizar (por ejemplo, meetkai:functionary-es-mini)
instructionsNoInstrucciones de sistema personalizadas para el agente
previous_response_idNoEncadena esta sesión a una respuesta específica de una sesión anterior
conversationNoContinúa una conversación existente — pase { "id": "conv_abc123..." } o el ID de la conversación como cadena
toolsNoArray de definiciones de herramientas (function, web_search, file_search, etc.)
tool_choiceNoCómo el modelo selecciona herramientas ("auto", "none", "required", o una herramienta específica)
parallel_tool_callsNoPermitir ejecución paralela de herramientas
max_tool_callsNoMáximo de llamadas de herramientas por respuesta (por defecto: 30)
temperatureNoTemperatura de muestreo (por ejemplo, 0.7)
max_output_tokensNoMáximo de tokens en la respuesta
reasoningNoConfiguración de razonamiento (por ejemplo, { "effort": "high" }). Establezca { "effort": "none" } para sesiones de voz para minimizar la latencia — vea la nota abajo.
top_pNoParámetro de muestreo nucleus
presence_penaltyNoPenalización de presencia para repetición de tokens
frequency_penaltyNoPenalización de frecuencia para repetición de tokens
truncationNo"auto" o "disabled" — controla la truncación de contexto
context_managementNoEstrategias de gestión de contexto para truncar la conversación
service_tierNo"auto", "default", "flex", o "priority"
promptNoReferencia a una plantilla de prompt y sus variables
textNoConfiguración de salida de texto (formato, verbosidad)
metadataNoMetadatos clave-valor pasados a la API de Respuestas
No puede especificar ambos previous_response_id y conversation.
Los metadatos del token se incrustan en un JWT, que se pasa como encabezado HTTP. Mantenga el payload total de llm por debajo de ~8 KB — los arrays grandes de tools pueden necesitar ser recortados.
Para sesiones de voz, desactive el razonamiento configurando "reasoning": { "effort": "none" }. El razonamiento añade tiempo de pensamiento antes de que el modelo responda, lo que incrementa la latencia y crea pausas notables en la conversación. Desactivarlo mantiene las respuestas rápidas y naturales.

stt — Configuración de reconocimiento de voz (opcional)

Controla la detección de actividad de voz (VAD) y el comportamiento de finalización en el servidor.
CampoRequeridoDescripción
silence_timeout_msNoMilisegundos de silencio antes de finalizar el habla (100–5000)
initial_silence_timeout_msNoTiempo de espera antes de detectar cualquier habla (1000–30000)

Configuración avanzada

Puede pasar herramientas, instrucciones personalizadas y ajuste de STT en una sola solicitud de token:
mka1 llm speech livekit-token \
  --llm '{
    "model": "meetkai:functionary-es-mini",
    "instructions": "You are a helpful travel assistant. Be concise in voice responses.",
    "temperature": 0.7,
    "tools": [
      { "type": "web_search", "user_location": { "country": "US" } },
      {
        "type": "function",
        "name": "book_flight",
        "description": "Book a flight for the user",
        "parameters": {
          "type": "object",
          "properties": {
            "origin": { "type": "string" },
            "destination": { "type": "string" },
            "date": { "type": "string" }
          },
          "required": ["origin", "destination", "date"]
        }
      }
    ],
    "tool_choice": "auto"
  }' \
  --stt '{"silence_timeout_ms":500,"initial_silence_timeout_ms":10000}'

Respuesta

{
  "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "url": "wss://apigw.mka1.com/api/v1/livekit",
  "roomName": "550e8400-e29b-41d4-a716-446655440000"
}
CampoDescripción
tokenToken de acceso JWT (TTL de 5 minutos) con permisos para unirse, publicar y suscribirse a la sala
urlURL WebSocket de LiveKit para conectarse
roomNameUUID autogenerado para esta sesión
El token incluye metadatos que el agente de voz utiliza para configurar la sesión.

Continuar una sesión

Para continuar desde una respuesta anterior:
mka1 llm speech livekit-token \
  --llm '{"model":"meetkai:functionary-es-mini","previous_response_id":"resp_abc123..."}'
Para continuar una conversación existente:
mka1 llm speech livekit-token \
  --llm '{"model":"meetkai:functionary-es-mini","conversation":{"id":"conv_abc123..."}}'
Al continuar una sesión, la clave de API y el encabezado X-On-Behalf-Of (si se usa) deben coincidir con la sesión original. El agente de voz cifra ambos en el token de sala y los pasa a todos los servicios MKA1 aguas abajo. Si no coinciden, el agente no tendrá acceso al contexto anterior.

Conectarse a una sala

Una vez que tenga un token, use el SDK de LiveKit para conectarse a la sala.
import { Room, RoomEvent, Track } from 'livekit-client';

const room = new Room();

// Conéctese a la sala
await room.connect(session.url, session.token);

console.log('Conectado a la sala:', room.name);

Enviar entrada de audio

El agente acepta entrada de audio a través de la pista de audio de la sala de LiveKit. El audio se procesa a una frecuencia de muestreo de 16kHz.
import { createLocalAudioTrack } from 'livekit-client';

// Cree una pista de audio local desde el micrófono
const audioTrack = await createLocalAudioTrack({
  echoCancellation: true,
  noiseSuppression: true,
  autoGainControl: true
});

// Publique la pista en la sala
await room.localParticipant.publishTrack(audioTrack);

Comportamiento del audio

  • Detección de Actividad de Voz (VAD): El VAD se maneja en el servidor por el agente MKA1, no localmente. El agente detecta automáticamente cuando deja de hablar y comienza el procesamiento.
  • Frecuencia de muestreo: El audio se transmite a 16kHz al servicio de STT.
  • Finalización: El agente utiliza finalización en el servidor para determinar cuándo termina el habla. No hay retraso de finalización local.

Enviar entrada de texto

También puede enviar mensajes de texto directamente al agente sin hablar.
// Envíe un mensaje de texto al agente
const message = JSON.stringify({
  type: 'user_message',
  content: 'What is the capital of France?'
});

await room.localParticipant.publishData(
  new TextEncoder().encode(message),
  { reliable: true, topic: 'lk.chat' }
);

Recibir respuestas del agente

El agente responde de tres maneras:
  1. Salida de audio: Voz sintetizada a través de una pista de audio
  2. Transcripción: Texto de lo que el agente está diciendo (para subtítulos)
  3. Metadatos de respuesta: ID de respuesta e ID de conversación a través del canal de datos

Suscribirse a la salida de audio

import { RoomEvent, Track } from 'livekit-client';

room.on(RoomEvent.TrackSubscribed, (track, publication, participant) => {
  if (track.kind === Track.Kind.Audio && participant.identity !== room.localParticipant.identity) {
    // Esta es la salida de audio del agente
    const audioElement = track.attach();
    document.body.appendChild(audioElement);
  }
});

Recibir transcripciones

El agente publica transcripciones de su discurso. Puede usarlas para subtítulos o registros.
room.on(RoomEvent.TranscriptionReceived, (segments, participant) => {
  for (const segment of segments) {
    console.log(`Agente dijo: ${segment.text}`);
  }
});

Recibir metadatos de respuesta

El agente publica el response_id y conversation_id (si aplica) cuando comienza a generar una respuesta. Guarde el response_id para encadenar futuras sesiones usando previous_response_id.
room.on(RoomEvent.DataReceived, (payload, participant) => {
  if (participant.identity !== room.localParticipant.identity) {
    const data = JSON.parse(new TextDecoder().decode(payload));

    if (data.response_id) {
      console.log('ID de respuesta:', data.response_id);
      console.log('ID de conversación:', data.conversation_id); // presente si se usa una conversación
      // Guarde response_id para encadenar futuras sesiones con previous_response_id
    }
  }
});

Continuidad de la conversación

El agente soporta conversaciones de varios turnos con memoria persistente. Cada respuesta recibe automáticamente un response_id, mientras que las conversaciones deben ser creadas y gestionadas explícitamente a través de la API de Conversaciones. Hay dos formas de continuar una conversación: llm.previous_response_id encadena una nueva sesión a una respuesta específica. El agente recibe el contexto de esa respuesta y todas las respuestas previas en la cadena. Use esto cuando:
  • Desea continuar desde un punto específico en una conversación
  • Está construyendo un flujo de conversación lineal
  • Desea ramificar desde una respuesta específica
llm.conversation referencia una conversación creada vía la API de Conversaciones. Use esto cuando:
  • Necesita gestionar metadatos de la conversación (títulos, etiquetas, etc.)
  • Desea listar o buscar conversaciones pasadas
  • Está construyendo una interfaz de chat con historial persistente de conversación
  • Múltiples clientes necesitan acceder a la misma conversación

Iniciar una nueva sesión

import { Room, RoomEvent } from 'livekit-client';
import { SDK } from '@meetkai/mka1';

const mka1 = new SDK({
  bearerAuth: `Bearer ${YOUR_API_KEY}`,
});

// 1. Obtenga un token para una nueva sesión
const session = await mka1.llm.speech.livekitToken({
  llm: { model: 'meetkai:functionary-es-mini' }
}, { headers: { 'X-On-Behalf-Of': 'user-123' } }); // Opcional

// 2. Conéctese a la sala
const room = new Room();
await room.connect(session.url, session.token);

// 3. Rastree el ID de respuesta cuando el agente responda
let lastResponseId: string;
room.on(RoomEvent.DataReceived, (payload, participant) => {
  const data = JSON.parse(new TextDecoder().decode(payload));
  if (data.response_id) {
    lastResponseId = data.response_id;
  }
});

// 4. Tenga una conversación...
// 5. Desconéctese cuando termine
room.disconnect();

Continuar desde una respuesta anterior

Use previous_response_id para encadenar una nueva sesión a la última respuesta, preservando el contexto de la conversación:
// Use la misma clave de API y X-On-Behalf-Of que la sesión original
const mka1 = new SDK({
  bearerAuth: `Bearer ${YOUR_API_KEY}`,
});

// 1. Obtenga un nuevo token encadenado a la respuesta anterior
const session = await mka1.llm.speech.livekitToken({
  llm: {
    model: 'meetkai:functionary-es-mini',
    previousResponseId: lastResponseId
  }
}, { headers: { 'X-On-Behalf-Of': 'user-123' } }); // Opcional, pero debe coincidir si la sesión original lo usó

// 2. Conéctese a la nueva sala
const room = new Room();
await room.connect(session.url, session.token);

// 3. El agente ahora tiene contexto de la sesión anterior
// Usuario: "¿Qué te pregunté antes?"
// Agente: "Me preguntaste sobre la capital de Francia..."

Continuar desde una conversación

Use conversation_id para continuar una conversación existente creada vía la API de Conversaciones:
// Use la misma clave de API y X-On-Behalf-Of que la sesión original
const mka1 = new SDK({
  bearerAuth: `Bearer ${YOUR_API_KEY}`,
});

// 1. Obtenga un nuevo token con el ID de la conversación
const session = await mka1.llm.speech.livekitToken({
  llm: {
    model: 'meetkai:functionary-es-mini',
    conversation: { id: conversationId }
  }
}, { headers: { 'X-On-Behalf-Of': 'user-123' } }); // Opcional, pero debe coincidir si la sesión original lo usó

// 2. Conéctese a la nueva sala
const room = new Room();
await room.connect(session.url, session.token);

// 3. El agente ahora tiene contexto de todo el historial de la conversación
Al continuar una conversación, la clave de API y el encabezado X-On-Behalf-Of (si se usa) deben coincidir con la sesión original. El contexto está limitado a la identidad autenticada.

Manejo de desconexión

Los tokens expiran después de 5 minutos. Si necesita sesiones más largas, implemente lógica de reconexión:
room.on(RoomEvent.Disconnected, async () => {
  console.log('Desconectado de la sala');

  // Obtenga un nuevo token (continuando desde la última respuesta)
  const newSession = await mka1.llm.speech.livekitToken({
    llm: {
      model: 'meetkai:functionary-es-mini',
      previousResponseId: savedResponseId
    }
  });

  // Reconéctese
  await room.connect(newSession.url, newSession.token);
});

Ejemplo completo

Aquí tiene un ejemplo completo que lo reúne todo:
import { Room, RoomEvent, Track, createLocalAudioTrack } from 'livekit-client';
import { SDK } from '@meetkai/mka1';

async function startVoiceSession(model: string = 'meetkai:functionary-es-mini') {
  const mka1 = new SDK({ bearerAuth: `Bearer ${YOUR_API_KEY}` });

  // Obtenga credenciales de sala
  const session = await mka1.llm.speech.livekitToken({ llm: { model } });

  // Cree y conéctese a la sala
  const room = new Room();

  let lastResponseId: string | undefined;

  // Maneje la salida de audio del agente
  room.on(RoomEvent.TrackSubscribed, (track, publication, participant) => {
    if (track.kind === Track.Kind.Audio) {
      const audio = track.attach();
      document.body.appendChild(audio);
    }
  });

  // Maneje transcripciones
  room.on(RoomEvent.TranscriptionReceived, (segments) => {
    for (const segment of segments) {
      console.log('Agente:', segment.text);
    }
  });

  // Maneje metadatos de respuesta
  room.on(RoomEvent.DataReceived, (payload, participant) => {
    const data = JSON.parse(new TextDecoder().decode(payload));
    if (data.response_id) {
      lastResponseId = data.response_id;
    }
  });

  // Conéctese a la sala
  await room.connect(session.url, session.token);

  // Capture y publique el micrófono
  const audioTrack = await createLocalAudioTrack({
    echoCancellation: true,
    noiseSuppression: true
  });
  await room.localParticipant.publishTrack(audioTrack);

  // El agente lo saludará automáticamente
  // ¡Comience a hablar para interactuar!

  return { room, getLastResponseId: () => lastResponseId };
}

Manejo de errores

Errores del endpoint de token

Estos se devuelven como respuestas HTTP al solicitar un token de sala:
ErrorCausaSolución
400 Bad RequestFalta el parámetro requerido llm.modelIncluya model dentro del objeto llm
400 Bad RequestSe especificaron ambos previous_response_id y conversationUse solo uno, no ambos
401 UnauthorizedClave de API inválida o ausenteVerifique que su clave de API sea válida

Errores en sesión

Durante una sesión de voz activa, el agente publica errores a través del canal de datos de LiveKit. Escúchelos junto con los metadatos de respuesta:
room.on(RoomEvent.DataReceived, (payload, participant) => {
  if (participant.identity === room.localParticipant.identity) return;

  const data = JSON.parse(new TextDecoder().decode(payload));

  if (data.error) {
    console.error(`[${data.error.service}] ${data.error.code}: ${data.error.message}`);
    // data.error.details puede contener información adicional de depuración
  }

  if (data.response_id) {
    lastResponseId = data.response_id;
  }
});
La estructura del payload de error:
{
  "error": {
    "code": "rate_limited",
    "message": "HTTP 429",
    "service": "llm",
    "details": "..."
  }
}
CampoDescripción
codeCódigo de error (vea la tabla abajo)
messageDescripción corta del error
serviceQué parte del flujo falló: llm, stt, o tts
detailsContexto adicional para depuración (opcional)
Códigos de error:
CódigoServicioCausa
invalid_sessionFaltan campos de metadatos requeridos (sub, llm)
auth_errorFalló el descifrado de credenciales del token de sala
session_errorEl agente no pudo iniciar la sesión de voz
invalid_requestllmSolicitud incorrecta a la API de Respuestas (HTTP 400)
auth_errorllmClave de API inválida (HTTP 401)
access_deniedllmPermisos insuficientes (HTTP 403)
rate_limitedllmLímite de tasa excedido (HTTP 429)
service_errorllmError interno del servidor (HTTP 500)
service_unavailablellmServicio upstream no disponible (HTTP 502/503)
timeoutllmTiempo de espera excedido (HTTP 504)
connection_errorllmFalló la conexión a la API de Respuestas
transcription_errorsttFalló el procesamiento de reconocimiento de voz
speech_errorttsFalló la síntesis de texto a voz

Errores de conexión

ProblemaCausaSolución
Tiempo de espera de conexiónProblemas de red o token inválidoObtenga un token nuevo e intente de nuevo
Token expiradoLa sesión superó los 5 minutosObtenga un nuevo token con previous_response_id para continuar

Próximos pasos