Crear una habilidad bajo demanda
Empaqueta una capacidad que el modelo recupera solo cuando es relevante; créala y conéctala a un agente.
list_skillscreate_skillupdate_agenttest_skill_triggerPrincipio — mantén el número de habilidades pequeño. Adjunta solo lo que el trabajo de este agente necesita (≈5 o menos). El modelo selecciona habilidades a partir de su
descriptionde una línea; cuanto más adjuntes, más a menudo cargará la incorrecta o ninguna. Si dos habilidades se superponen en cuándo, fusionalas. Más en Principios de diseño.
Una skill (habilidad) es un paquete de capacidades cargado bajo demanda: la system prompt solo lleva su description
("cuándo usar"); el modelo extrae las instructions completas solo cuando juzga que la habilidad es relevante. Por lo tanto,
la description debe ser corta y decir cuándo, o el modelo no sabrá buscarla.
list_skills() # what already exists
create_skill(
name="refund-policy",
description="Use when the user asks about refunds, cancellations or chargebacks.",
instructions="The full procedure: eligibility windows, how to word the outcome, when to escalate …",
)
update_agent(name="Support", add_skills=["refund-policy"]) # attach it (incremental — keeps existing skills)Mantén los procedimientos en instructions (obtenidos al cargar), no en el soul (alma) del agente (que está en el prompt
en cada turno). Una description de una línea que diga cuándo es lo que hace que la habilidad sea descubrible; una vaga
significa que el modelo nunca la extrae.
Las herramientas pueden viajar con la habilidad
Una habilidad puede llevar sus propias vinculaciones de herramientas — create_skill(tools=[…]) o
update_skill(name, add_tools=[…]), de modo que una habilidad se envía como una capacidad autocontenida: procedimiento más
las herramientas que necesita, adjuntas en un solo movimiento.
Las herramientas propias de una habilidad solo aparecen después de que la habilidad se haya cargado. No están en el conjunto de herramientas al inicio del turno; cargar la habilidad las coloca allí. Por lo tanto, el orden está fijo: leer el procedimiento, luego obtener las herramientas; el modelo no puede saltar a las herramientas e improvisar.
Este orden se aplica en el código, no se pide en el prompt, porque pedirlo no funcionó. Medido el 2026-08-22, en "investiga a los competidores de la empresa X" con el procedimiento y las herramientas entregados juntos: el modelo llamó a la búsqueda web tres veces y nunca cargó la habilidad. Inventó nombres de competidores desde la memoria, buscó esos nombres inventados, no encontró nada (no existen), e informó los nombres inventados como respuesta — mientras que el paso uno del procedimiento que nunca leyó decía "primero establece qué vende realmente esta empresa". Un procedimiento que es opcional no se lee.
Dos cosas que saber antes de confiar en ello:
- Las herramientas de la habilidad no aparecen en la lista
toolspropia del agente.get_agentmuestra solo las herramientas directamente adjuntas al agente; la pestaña Tools de la consola muestra las vinculadas a la habilidad como una línea de solo lectura "vía habilidades" separada. Al verificar "¿está habilitada la herramienta X?", revisa ambos lugares — o simplemente llama a la herramienta en un chat de prueba. - Las herramientas que el agente ya tiene no se ven afectadas. Si el agente en sí lleva
web_search, sigue disponible todo el turno; solo las herramientas que llegan debido a una habilidad esperan a que esa habilidad se cargue. - Prefiere vincular una herramienta a la habilidad cuando solo tiene sentido dentro de ese procedimiento (una
búsqueda de reembolso dentro de la habilidad de reembolso); vincula a la agente cuando es generalmente útil entre turnos
(
save_contact,web_search).
Vincular una herramienta es disponibilidad, no política — las instrucciones deben decir cuándo llamarla
Marcar una herramienta en una habilidad (o agente) solo la hace callable (llamable). El modelo ve el esquema de la herramienta y
su descripción de una línea en cada turno, por lo que puede usarla de manera oportunista — un visitante ofrece voluntariamente un
correo electrónico y save_contact se dispara. Pero cualquier comportamiento que deba ocurrir de manera confiable debe estar escrito,
y cada parte de él tiene un único lugar correcto:
| Lo que estás especificando | Dónde va |
|---|---|
| Cuándo cargar esta habilidad | la description de la habilidad (en el prompt en cada turno) |
| El procedimiento: en qué paso llamar a qué herramienta, con qué argumentos | las instructions de la habilidad — nombra la herramienta explícitamente ("después de que el interlocutor confirme el interés, llama a save_contact con correo electrónico y teléfono; luego llama a schedule_followup para 1 día hábil después") |
| Un comportamiento que debe dirigir toda la conversación (captura siempre los leads, nunca cites tarifas) | el soul / task del agente |
El modo de fallo cuando las instrucciones no nombran la herramienta: todo parece configurado — la herramienta está marcada, la habilidad se carga — y el agente aún así nunca la llama, o la llama con argumentos adivinados. No hay errores. Escribe el procedimiento como si estuvieras dando una charla a un nuevo empleado: el paso, el nombre de la herramienta, los argumentos y cómo se ve el "hecho".
Llamadas obligatorias a herramientas: el activador debe vivir en description, no solo en instructions
Las instructions son visibles para el modelo solo después de que llama a load_skill — y los modelos frecuentemente
responden directamente sin cargar la habilidad. Por lo tanto, una regla como "cuando el usuario proporciona un número de teléfono,
debes llamar a save_contact" escrita solo en instructions es invisible exactamente cuando importa:
la herramienta se llama silenciosamente nunca, y el modelo incluso puede afirmar que guardó el contacto. Esto sucedió
en producción — un agente respondió varios mensajes con datos de contacto consecutivos, "confirmó" que los detalles
quedaban registrados, y no existía ningún registro.
La solución es una frase en la description (que sí está en el system prompt en cada turno):
update_skill(
name="consultative-sales",
description="Consultative sales: understand the case, recommend products, arrange expert callbacks. "
"When the user provides a phone number or email, call save_contact BEFORE answering anything else.",
)Mantén el procedimiento detallado en instructions; coloca el activador de cualquier llamada obligatoria en
description.
Dos reglas relacionadas:
- Nunca escribas los valores de retorno que la herramienta no produce — retransmite los reales tal cual.
save_contactyschedule_followupintegrados devuelven un código de referencia real en caso de éxito (formatoAB2C-D3EF, almacenado en el servidor y visible para el inquilino en la página de detalles del usuario final). Instruye al modelo para que retransmita el código del resultado de la herramienta tal cual — nunca para inventar uno o reformatearlo como "#12345". Para cualquier otra herramienta, verifica que realmente devuelva un ID antes de escribir uno: un ID prometido pero ausente será fabricado. create_skill/update_skill/get_skillahora devuelven una listawarningsque marca exactamente estos dos patrones (trigger_hidden_in_instructions,promised_tool_return_id). Las advertencias son orientativas — el guardado tiene éxito — pero trátalas como un linter: corrige, no ignores.
Ejemplos trabajados en instructions aumentan mediblemente el cumplimiento de llamadas a herramientas
Hicimos benchmarks en backends de producción (6 mensajes de captura de leads de dificultad creciente — números enterrados en preguntas largas, grupos de dígitos con espacios, correcciones — muestreados por condición). Con la habilidad cargada, una orden plana de "debes llamar a X" alcanzó 3–4/6; añadir un bloque corto de ejemplos trabajados llevó a ambos modelos probados a 6/6. Mover la sección de la orden alrededor no hizo nada — la posición no importa, los ejemplos sí.
Un bloque de ejemplo efectivo tiene cuatro tipos de entradas, cada una de una línea:
### Worked examples (follow exactly)
1. User: "I'd like to know about X, my phone is 13800138000"
→ call save_contact(phone="13800138000") first, then answer about X.
2. User: "email me the offer: li@example.com"
→ call save_contact(email="li@example.com").
3. COUNTER-EXAMPLE (forbidden): user gives a phone number and you reply
"I've noted it down" WITHOUT calling the tool — claiming success without
the call is the worst failure.
4. User: "sorry, wrong number — it's 13633334444"
→ call save_contact again with the corrected value.
5. Numbers may contain spaces ("138 0013 9000") — still a phone number;
strip the spaces and call save_contact(phone="13800139000").El contra-ejemplo (3) y el caso límite de formato (5) cierran la mayoría de los fallos restantes — los modelos fallan en el reconocimiento ("¿es este un número de teléfono?") y en la honestidad bajo presión (responder primero una pregunta de dominio rico y afirmar que el guardado ocurrió) más que en la voluntad. Mantenlo en ~5 entradas; usa la sintaxis exacta de la llamada con argumentos realistas.
Prueba el activador antes de enviar — no cuentes cadáveres en producción
Si una habilidad realmente se dispara es medible, así que mídela. test_skill_trigger simula tus
mensajes contra el ensamblaje de prompt de producción, los esquemas de herramientas y el enrutamiento del modelo, e informa
lo que el modelo decidió — las herramientas nunca se ejecutan, no se almacena nada, los tokens cuentan hacia tu cuota (límites: 5 mensajes × 5 muestras).
test_skill_trigger(
agent="advisor",
messages=[
"My phone is 555 0123, call me back", # easy
"long question about the product … oh and my number is 555 0123", # buried
"555 0123 — that's me", # implicit
"sorry, wrong number, it's 555 9999", # correction
],
expect_tool="save_contact",
samples=3,
loaded=true, # simulate post-load_skill → tests instructions quality
) # loaded=false (default) → first turn, tests the description triggerLee el resultado así:
hit_ratepor debajo de ~90% en mensajes realistas → fortalece el activador (description) o añade ejemplos trabajados (instructions), luego vuelve a probar.claimed_without_call> 0 es el peor fallo — el modelo le dijo al usuario "¡tenido en cuenta!" sin llamar a la herramienta. Añade el contra-ejemplo del bloque anterior.- Prueba ambos modos:
loaded=falseprueba que la descripción sola se activa en el primer turno;loaded=trueprueba que las instrucciones cargadas no lo diluyen (las instrucciones largas lo diluyen mediblemente — eso es lo que los ejemplos trabajados compensan).
El resultado también lleva una lista advice: cuando las muestras fallan o mienten, te dice exactamente cuál
solución aplicar (activador en la descripción, añadir el bloque de ejemplos trabajados, añadir un contra-ejemplo o
caso límite de formato) con los números de benchmark detrás de cada recomendación — aplícala y vuelve a probar.
El ciclo completo: create_skill → corregir cualquier warnings (lint estático) → test_skill_trigger (comprobación de realidad dinámica) → aplicar su advice → volver a probar hasta que la tasa de aciertos se mantenga.