Resumen
Las aplicaciones pueden definir capacidades de IA que residen dentro del espacio de trabajo — instrucciones de habilidades reutilizables y agentes con prompts de sistema personalizados.defineSkill
Define habilidades para agentes de IA
defineSkill
Define habilidades para agentes de IA
Las habilidades definen instrucciones y capacidades reutilizables que los agentes de IA pueden usar dentro de tu espacio de trabajo. Usa Puntos clave:
defineSkill() para definir habilidades con validación incorporada:src/skills/example-skill.ts
namees una cadena identificadora única de la habilidad (se recomienda kebab-case).labeles el nombre para mostrar, legible para humanos, que aparece en la interfaz de usuario.contentcontiene las instrucciones de la habilidad — este es el texto que usa el agente de IA.icon(opcional) establece el icono mostrado en la interfaz de usuario.description(opcional) proporciona contexto adicional sobre el propósito de la habilidad.
defineAgent
Define agentes de IA con prompts personalizados
defineAgent
Define agentes de IA con prompts personalizados
Los agentes son asistentes de IA que viven dentro de tu espacio de trabajo. Usa Puntos clave:Notas sobre el esquema:
defineAgent() para crear agentes con un prompt de sistema personalizado:src/agents/example-agent.ts
namees una cadena identificadora única del agente (se recomienda kebab-case).labeles el nombre para mostrar que aparece en la interfaz de usuario.promptes el mensaje del sistema que define el comportamiento del agente.description(opcional) proporciona contexto sobre lo que hace el agente.icon(opcional) establece el icono mostrado en la interfaz de usuario.modelId(opcional) reemplaza el modelo de IA predeterminado usado por el agente.responseFormat(opcional) establece la estructura de salida predeterminada del agente. De forma predeterminada es{ type: 'text' }para texto de formato libre. Usa{ type: 'json', schema }para forzar una salida JSON estructurada. Una llamada arunAgent()o un paso de IA de un flujo de trabajo puede solicitar una estructura diferente, que tiene prioridad.roleUniversalIdentifier(opcional) vincula el agente a un rol que define tu aplicación. Ese rol no debe otorgar más que el rol de la aplicación, o la compilación falla; consulta Roles de agente.modelConfiguration(opcional) activa las herramientas nativas del modelo, como lo hace la página de configuración del agente:{ webSearch: { enabled: true }, twitterSearch: { enabled: true } }. Cada herramienta solo funciona con los modelos que la admiten. Omítelo para mantenerlas desactivadas.isSystem(opcional, el valor predeterminado esfalse) oculta el agente de las listas de agentes, como los agentes que Twenty crea para los pasos de IA de flujo de trabajo. Solo cambia lo que se muestra: los agentes de una aplicación solo se pueden cambiar mediante la aplicación, independientemente de lo que indique esta marca.
responseFormat en { type: 'json' } y proporciona un schema:src/agents/structured-agent.ts
- El esquema es un objeto plano: el
typede cada propiedad debe ser un tipo primitivo (string,numberoboolean). Los objetos anidados y los arrays no son compatibles. description(opcional) en cada propiedad guía al modelo sobre qué debe poner allí.required(opcional) enumera las propiedades que el modelo siempre debe devolver.additionalProperties: false(opcional) prohíbe cualquier propiedad que no esté declarada enproperties.
defineMcpServer
Expón un servidor MCP externo a la IA del espacio de trabajo (beta)
defineMcpServer
Expón un servidor MCP externo a la IA del espacio de trabajo (beta)
Usa Puntos clave:
defineMcpServer() para incluir un servidor MCP remoto (HTTP transmisible) con tu aplicación, para que la IA del espacio de trabajo pueda usar sus herramientas sin sincronizar esos datos con Twenty:src/mcp-servers/github.ts
nameestá en snake_case (letras minúsculas, dígitos y guiones bajos, comienza con una letra y tiene como máximo 32 caracteres) y es único en el espacio de trabajo. Sirve de prefijo para los nombres de las herramientas del servidor.urldebe ser una URL HTTP o HTTPS.connectionProvider(opcional) hace referencia a un proveedor de conexión declarado por la misma aplicación. Las llamadas envían el token de acceso de esa conexión como un token Bearer. Omítelo para un servidor público.- La URL no puede cambiar en una versión posterior mientras haya un proveedor de conexión vinculado, por lo que los tokens nunca se envían a otro host.
- Los espacios de trabajo pueden desactivar el servidor; una versión posterior de la aplicación conserva esa elección.
- Requiere
"engines": { "twenty": ">=2.53.0" }.
runAgent
Ejecutar un agente desde una función de lógica
runAgent
Ejecutar un agente desde una función de lógica
runAgent() permite que una función de lógica ejecute uno de los agentes de tu app (con sus skills y tools). Identifica al agente mediante el universalIdentifier que pasaste
a defineAgent() y pasa lo que quieres que procese como input:src/logic-functions/run-enricher.ts
inputes una cadena o una lista de 1 a 100 mensajes ({ role: 'user' | 'assistant', content: string }). Un mensaje deusertambién puede incluir archivos adjuntos (consulta más abajo). Para mantener una conversación, pasa unthreaden lugar de reproducir tú mismo los turnos anteriores.- De forma predeterminada, la llamada espera al agente, que puede leer/actualizar registros
por sí mismo mediante sus propias herramientas —
runAgent()se resuelve una vez que finaliza la ejecución. PasewaitForCompletion: falsepara que se resuelva en cuanto se inicie la ejecución:statusserá entoncesRUNNINGyresult: null, y la respuesta llegará a la conversación dethreadId. Una ejecución que dure más de 15 minutos también se resuelve comoRUNNING. - El agente también puede pausarse hasta que cambie un registro o haya transcurrido algún tiempo.
runAgent()se resuelve entonces consuccess: true,status: 'SUSPENDED'yresult: null; el agente continúa por sí solo más tarde, y su respuesta llega a la conversaciónthreadId. Una llamada a unthreadcuyo agente sigue esperando falla hasta que termina. - Cada llamada se almacena con su entrada y estado, además de su respuesta y uso
cuando se completa, por lo que aparece en las ejecuciones del agente. Sin un
thread, una llamada obtiene una conversación propia:threadIdla denomina, y una llamada posterior nunca la ve. - Una app solo puede ejecutar sus propios agentes.
- El rol predeterminado de la app debe conceder el indicador de permiso
AI; agregaSystemPermissionFlag.AIa suspermissionFlagUniversalIdentifiers(o establececanAccessAllTools: true). Sin esto,runAgent()falla con un error de permisos. - Establece un valor generoso de
timeoutSecondsen la función de lógica: las ejecuciones de agentes pueden tardar varios segundos. statusesCOMPLETEDcon unresultdistinto de nulo cuando la ejecución finaliza sin esperar;SUSPENDEDconsuccess: trueyresult: nullmientras espera;RUNNINGconsuccess: trueyresult: nullcuando la llamada no esperó a la ejecución;FAILEDconsuccess: falseyerrorque contiene el motivo (por ejemplo, cuando el espacio de trabajo se quedó sin créditos de IA durante la ejecución).
src/roles/default-role.ts
Envío de archivos con un mensaje
Un mensajeuser puede incluir attachments, para que el agente vea una captura de pantalla o un
PDF en lugar de solo su nombre. Cada entrada hace referencia a un archivo que tu aplicación ya ha
subido a la carpeta agent-chat:src/logic-functions/describe-screenshot.ts
UPLOAD_FILE en el rol predeterminado de la aplicación,
junto con AI. filename es opcional y solo etiqueta el archivo para el modelo.- Solo los mensajes de
userpueden incluir archivos adjuntos. No se pueden representar en un turno de asistente, por lo querunAgent()los rechaza allí en lugar de omitirlos. - Como máximo, 10 archivos adjuntos por mensaje.
- El
fileIddebe nombrar un archivo completamente cargado en el mismo espacio de trabajo, dentro de la carpetaagent-chat. Cualquier otra cosa hace que la llamada falle en lugar de responder silenciosamente sin el archivo.
Solicitar un resultado estructurado
PasaresponseFormat para obtener una estructura solo para esta llamada. Tiene prioridad sobre
el propio responseFormat del agente, por lo que un agente puede atender a quienes necesitan
resultados diferentes. Utiliza el mismo esquema que defineAgent():src/logic-functions/score-lead.ts
Continuar una conversación
Pasa unthread y Twenty mantiene la conversación por ti: cada llamada envía solo
la nueva entrada, y el agente ve los turnos anteriores, incluidos los resultados que
devolvieron sus herramientas. Usa como clave cualquier identificador estable de tu lado, como un canal de Slack
y una marca de tiempo de hilo:src/logic-functions/reply-in-thread.ts
- Las claves están limitadas a tu aplicación y agente, por lo que dos aplicaciones nunca comparten una
conversación. Solo los tokens de acceso de la aplicación pueden pasar un
thread. inputsolo puede contener mensajes deuser: la conversación ya contiene las respuestas del agente.- Las
additionalInstructionsse proporcionan al modelo solo para esta llamada y no se almacenan, lo que es adecuado para instrucciones por solicitud, como reglas de formato o una fecha límite. También funcionan sin unthread. - Cada ejecución se almacena como un turno con su estado, incluidas las que fallaron. Su uso se almacena cuando el agente regresa, incluso si se quedó sin créditos. Una ejecución fallida no forma parte de la conversación, por lo que la siguiente llamada no la ve.
- Las llamadas en el mismo hilo se ejecutan de una en una: una segunda llamada espera a que termine la primera, por lo que cada una ve los turnos anteriores a ella.
- Cuando los turnos se ejecutan como miembros diferentes (consulta más abajo), cada uno solo ve el texto de los turnos de los demás, nunca los resultados de sus herramientas, ya que se leyeron con los permisos de otra persona.
Ejecución en nombre de un miembro del espacio de trabajo
PasarunAsWorkspaceMemberId cuando la ejecución es activada por una persona — por ejemplo, un bot de chat que responde a un mensaje — para que el agente actúe como ese miembro en lugar de como la aplicación:src/logic-functions/answer-question.ts
- Un token sin usuario asociado puede designar a cualquier miembro. Una función lógica se ejecuta
con uno, y lo mismo ocurre con los tokens generados mediante
client_credentialso a partir de una clave de API. - Un token emitido en nombre de un usuario, como el que recibe un componente de interfaz, solo puede designar al miembro propio de ese usuario.
Siguiendo una conversación
Una aplicación lee las conversaciones que mantiene con sus agentes a través de la misma API que el chat:listConversationMessages() devuelve su historia, y
streamConversation() produce sus eventos en vivo, token por token. Nombra la conversación
por el agente y la clave hilo que pasas a runAgent(); Tú
puedes empezar a seguirlo antes de que la primera ejecución llegue a él.src/logic-functions/follow-conversation.ts
- Los eventos son propios del chat:
stream-chunklleva un SDKUIMessageChunky unseq, y notificaciones comomessage-persisted(un mensaje fue guardado: un miembro, o la respuesta, que lleva su ejecuciónturnId),credits-exhausted(la respuesta se detuvo cuando el espacio de trabajo corrió sin créditos),queue-updatedostream-errordecir cuando para leer la historia de nuevo. Unkeepaliveviene una vez conectado, luego cada pocos segundos: lee la historia en la primera, ya que lo que pasó antes de está ahí. - Para convertir chunks en mensajes como lo hace el chat, pipéalos en
readUIMessageStream()del paqueteai. listConversationMessages()devuelve{ mensajes, streamCatchup }: los mensajes guardados como AI SDKUIMessages, a medida que el chat los construye, y las porciones de una respuesta todavía fluyendo. Muestra esos trozos y luego omite trozos vivos cuyosseqes como máximostreamCatchup.maxSeq, ya que los repiten.runAgent()devuelverunId, el giro que abre la llamada: los mensajes de esa ejecución llevan comometadata.turnId. Una ejecución que espera en turnos posteriores de la conversación.- El stream permanece abierto hasta que abortes su
señaloromper, y arroja si la conexión cae: stream de nuevo para reanudar. - Una aplicación sólo lee sus propias conversaciones. Turna un miembro abierto en otro lugar,
como en el chat, muestra solo su texto en la historia, viva y en la siguiente carrera del agente
: vive, sus otros trozos llegan como trozos
message-metadata, así queseqpermanece recolectado. Turna tu aplicación se ha ejecutado como un miembro (ver arriba) permanecen entera cuando lee por sí mismo. - Requiere
"engines": { "twenty": ">=2.56.0" }.
ai.generate
Llamar a un modelo desde una función lógica, sin un agente
ai.generate
Llamar a un modelo desde una función lógica, sin un agente
ai.generate() llama a un modelo desde una función lógica. A diferencia de
runAgent(), no necesita defineAgent(): la llamada lleva sus propias instrucciones
, y no abre ninguna conversación. Úselo para un trabajo de un solo tiro, como
clasificar, resumir o extraer.inputes una cadena o una lista de mensajes{ role: 'user' | 'assistant', content }.instructions(opcional) añade las instrucciones del sistema de llamada.modelId(opcional) elige el modelo. Sin ella, la llamada utiliza el modelo predeterminado del espacio de trabajo .responseFormat(opcional) funciona como enrunAgent(). Con{ type: 'json', schema }, el esquema se incluye en la propia solicitud al modelo, por lo que una respuesta JSON requiere una solicitud (más pasos de herramientas cuando unscopeconcede herramientas).scope(opcional) permite a la llamada utilizar la base de datos, como se describe a continuación.
{ éxito, texto, objeto, error, runId, uso }:textes la respuesta de un texto, ynullpara una respuesta JSON.objectes el resultado de una respuesta JSON, ynullde otra manera.errorexplica una ejecución fallida, cuandosuccessesfalse. Una ejecución que falla resuelve esta manera en lugar de lanzarla; una llamada que no puede iniciar una ejecución, tal como una con entrada no válida, lanza.runIdidentifica la ejecución registrada, con su modelo, uso y créditos.usagees{ inputTokens, outputTokens }: lo que gastó la ejecución, incluyendo los pasos que terminaron antes de un fallo.
scope, la llamada lee y no escribe registros. Para permitir que use la base de datos
, pase un scope, escrito como los permisos de un rol:onChunk. La llamada todavía resuelve
al mismo resultado, una vez que el último fragmento fue entregado a onChunk. Si el final del streamformat@@0
nunca llega, deja de esperar una vez que el resultado está dentro y 5 s
pasa sin un nuevo trozo, y sigue entregando cada trozo recibido entonces:- La llamada sigue la ejecución a través de la misma suscripción que
streamConversation(): se suscribe primero, espera hasta que la suscripción esté en vivo, luego comienza la ejecución, por lo que no pierde ningún chunk. - Los trozos son los propios del chat: póngalos en
readUIMessageStream()desde el paqueteaipara construir el mensaje. signalaborta la llamada: rechaza con el error de aborto y cierra la transmisión . La ejecución en sí misma continúa, y se registra como de costumbre.- Una vez que el stream está vivo, un fallo solo detiene los pedazos: la llamada
todavía se resuelve con el resultado de la ejecución. que se facturan de cualquier manera. La llamada
se rechaza cuando la suscripción falla antes de activarse, por lo que la ejecución nunca
comienza, o cuando
onChunkgenera una excepción. - Una aplicación sólo sigue sus propias ejecuciones, así que
onChunknecesita un token de aplicación. - Requiere
"engines": { "twenty": ">=2.56.0" }.
ai.generate() requiere la bandera de permiso de IA en el rol predeterminado de la aplicación,
como runAgent().sendInboxMessage
Inicia o continúa una conversación en los chats de los miembros
sendInboxMessage
Inicia o continúa una conversación en los chats de los miembros
sendInboxMessage() publica un mensaje de tu aplicación en los chats de uno o más
miembros del espacio de trabajo. Puede terminar en una llamada a una herramienta a la que el miembro responde desde el chat,
después de lo cual la conversación continúa con el asistente de Twenty. Consulta
Inbox para conocer sus opciones, las funciones auxiliares de menciones
y cómo publicar desde un componente de front-end.