Sugerencias con IA
Rellena un ticket con IA a partir de texto, audio o imágenes, recibe el resultado por consulta periódica o en tiempo real y registra qué aceptó la persona.
Actualizado el . 5 min de lectura
Incluido en los planes Profesional y Avanzado. Ver precios
Las sugerencias con IA proponen el título, la descripción, la categoría y la prioridad de un ticket a partir de lo que cuenta la persona: un texto, una nota de voz o capturas de pantalla. Tu aplicación muestra la propuesta, la persona la acepta o la corrige, y con eso abres el ticket. La generación es una tarea en segundo plano: la pides, y recoges el resultado consultando su estado o escuchándolo en tiempo real. Funciona con la clave de tu aplicación externa, siempre desde tu servidor.
De la descripción de la persona al ticket propuesto
Participan: Persona, Tu aplicación, UTILIA OS.
De Persona a Tu aplicación:
Cuenta el problema
Por escrito, con una nota de voz o con capturas.De Tu aplicación a UTILIA OS:
POST /api/external/v1/tickets/ai-suggest
Encola la tarea con el texto, el audio o las imágenes.De UTILIA OS a Tu aplicación:
jobId (respuesta)
De Tu aplicación a UTILIA OS:
GET /api/external/v1/tickets/ai-suggest/:jobId/stream
Abre la conexión de eventos.De UTILIA OS a Tu aplicación:
Eventos progress y completed (respuesta)
completed trae suggestions, la transcripción si hubo audio y analyticsEventId.De Tu aplicación a Persona:
Muestra la propuesta
De Persona a Tu aplicación:
La acepta, la corrige o la descarta
De Tu aplicación a UTILIA OS:
POST /api/external/v1/tickets/ai-track-action
trackAiAction registra la decisión con analyticsEventId.
Antes de empezar
Todos los métodos de IA exigen el scope support:tickets:ai, además del aplicativo Tickets de soporte y la capacidad «Tickets propios» en el plan. Si mandas audio, primero se transcribe y las sugerencias se generan sobre esa transcripción; para transcribir sin pedir sugerencias, mira Transcripción de audio.
Qué envías y qué recibes
| JavaScript | Python | Qué es |
|---|---|---|
userId | user_id | Obligatorio: el externalId de la persona. |
text | text | El problema contado por escrito. Hace falta text o audio. |
audioBase64, audioFilename | audio_base64, audio_filename | Nota de voz en base64 y su nombre con extensión (webm, mp4, mp3 o wav). |
existingFields | existing_fields | Lo que ya está escrito en el formulario (title, description, category, priority) y el contexto (currentUrl, userAgent, appVersion), para afinar la propuesta. |
images | images | Hasta 5 capturas en base64, cada una con base64, filename y mimeType (mime_type). |
El resultado trae suggestions (con title, description, category y priority; cualquiera puede venir vacío), transcription si mandaste audio, y analyticsEventId (analytics_event_id), que usarás después para registrar qué hizo la persona con la propuesta.
Tres formas de recoger el resultado
| Forma | JavaScript | Python | Cuándo |
|---|---|---|---|
| Todo en uno, consultando | getSuggestions(datos, opciones?) | get_suggestions(datos, opciones, on_progress=...) | La más sencilla. |
| En tiempo real | streamSuggestions(datos, opciones?) | stream_suggestions(datos, opciones) o stream_suggestions_handle(datos, opciones) | Progreso inmediato y opción de cancelar. |
| Paso a paso | requestSuggestions(datos) y getSuggestionsStatus(jobId) | request_suggestions(datos) y get_suggestions_status(job_id) | Si quieres controlar tú cada consulta. |
Todo en uno
getSuggestions encola la tarea y consulta su estado hasta que termina. Opciones: intervalo entre consultas (1 s por defecto), espera máxima (60 s) y función de progreso. En JavaScript van en milisegundos (pollInterval, maxWait, onProgress); en Python, en segundos (GetSuggestionsOptions(poll_interval, max_wait)) y el progreso como on_progress. Si la tarea falla, no se encuentra o se agota la espera, lanza un error normal (Error en JavaScript, RuntimeError en Python) con el motivo.
const result = await sdk.ai.getSuggestions(
{
userId: 'usr-456',
text: 'No puedo descargar las facturas del mes de enero',
existingFields: { currentUrl: 'https://app.ejemplo.com/facturas', appVersion: '2.1.0' },
},
{ pollInterval: 500, maxWait: 30000, onProgress: (p) => console.log(`${p}%`) },
);
console.log(result?.suggestions?.title);
console.log(result?.suggestions?.category); // BUG, CONSULTA o SUGERENCIAEn tiempo real
Abre una conexión de eventos (Server-Sent Events) y recibe el progreso y el resultado en cuanto están listos, sin consultas repetidas. Opciones: función de progreso, función para errores no fatales y tiempo máximo (120 s por defecto; en milisegundos en JavaScript y en segundos en Python).
- JavaScript:
streamSuggestionsdevuelve un objeto conresult(una promesa con el resultado) yabort()para cancelar. NecesitaEventSource: en Node.js, instala un polyfill comoeventsourcey asígnalo aglobalThis.EventSource. - Python:
stream_suggestionsdevuelve directamente el resultado. Si necesitas poder cancelar,stream_suggestions_handledevuelve un objeto conresult(una tarea que esperas conawait) yabort().
const handle = await sdk.ai.streamSuggestions(
{
userId: 'usr-456',
text: 'El botón de pago no responde',
images: [{ base64: capturaBase64, filename: 'captura.png', mimeType: 'image/png' }],
},
{ onProgress: (p) => console.log(`${p}%`), timeout: 60000 },
);
// handle.abort() si la persona cancela
const result = await handle.result;
console.log(result?.suggestions?.title);Desde la 4.2.0, la clave de tu aplicación no viaja en la dirección de la conexión: en JavaScript, como EventSource no admite cabeceras, el SDK pide antes un pase de un solo uso atado a ese trabajo y abre la conexión con él; en Python, la clave va en la cabecera X-Api-Key. Aun así, la clave es un secreto de servidor: usa estos métodos desde tu servidor, nunca desde el navegador de tus usuarios. Si necesitas abrir la conexión por tu cuenta, pide el pase con createStreamToken.
Si consumes los eventos sin el SDK
El servidor envía todos los eventos con el tipo genérico message, sin línea event:. El tipo real (progress, completed, failed o heartbeat) va dentro del JSON, en el campo event. Escuchar addEventListener('completed', ...) no recibe nada nunca. El formato exacto está en Endpoints de sugerencias con IA.
Paso a paso
requestSuggestions encola la tarea y devuelve su identificador: { jobId } en JavaScript y el identificador como texto en Python. getSuggestionsStatus devuelve el estado: pending, processing, completed (con el resultado en result), failed (con el motivo en error) o not_found, y el progreso de 0 a 100.
const { jobId } = await sdk.ai.requestSuggestions({ userId: 'usr-456', text: 'No carga el panel' });
let status = await sdk.ai.getSuggestionsStatus(jobId);
while (status.status === 'pending' || status.status === 'processing') {
await new Promise((r) => setTimeout(r, 1000));
status = await sdk.ai.getSuggestionsStatus(jobId);
}
if (status.status === 'completed') console.log(status.result?.suggestions);Registrar qué hizo la persona
Después de enseñar la propuesta, registra con trackAiAction (track_ai_action) si la aceptó, la corrigió o la descartó. Así tu organización puede medir si las sugerencias ayudan.
| JavaScript | Python | Qué es |
|---|---|---|
eventId | event_id | El analyticsEventId del resultado. Obligatorio. |
userAction | user_action | ACCEPTED_ALL, ACCEPTED_PARTIAL, REJECTED, MODIFIED o IGNORED. Obligatorio. |
fieldsAccepted, fieldsModified, fieldsRejected | fields_accepted, fields_modified, fields_rejected | Qué campos aceptó, cambió o rechazó. |
timeToDecisionMs | time_to_decision_ms | Milisegundos desde que vio la propuesta hasta que decidió. |
source | source | SDK_EXTERNAL o WIDGET. |
await sdk.ai.trackAiAction({
eventId: result!.analyticsEventId!,
userAction: 'ACCEPTED_PARTIAL',
fieldsAccepted: ['title', 'description'],
fieldsModified: ['priority'],
timeToDecisionMs: Date.now() - shownAt,
source: 'SDK_EXTERNAL',
});Avisar de fallos del cliente
reportError (report_error) envía a UTILIA OS un fallo de tu integración de IA o de tu formulario por la telemetría ligera, con gravedad baja y componente sdk si no indicas otro. Hace un solo intento y no lanza nunca: si el aviso falla, se descarta en silencio. Acepta message (obligatorio) y, si quieres, stack, code, component, endpoint, method, requestId, url, userAgent y, desde la 4.6.0, release y environment.
También acepta context (en JavaScript, un objeto o un texto, que viaja como detail), pero esa ruta no lo guarda. Si necesitas contexto, agrupación o la respuesta de cada error, usa sdk.errors.capture (Capturar los errores de tu aplicación).
await sdk.ai.reportError({
message: 'No se pudieron generar las sugerencias',
code: 'SUGGESTIONS_FAILED',
component: 'formulario-soporte',
});