Definir un agente y consumirlo¶
Traducción automática del original francés.
Tres objetos para colocar, en este orden. Ninguno es opcional: omitir el segundo crea un agente cuyo gasto no puede ser imputado a nadie.
flowchart TD
e["1 · Équipe<br/>budget commun, un projet"] --> c["2 · Clé virtuelle<br/>une par agent = imputation"]
c --> a["3 · Agent<br/>carte + limites + contrat"]
a --> u["Consommation<br/>script · workflow · chat · autre agent"]
1. El equipo¶
Gestiona el presupuesto de un proyecto y agrupa sus claves. Un agente solo no necesita equipo; dos agentes del mismo proyecto, si — de lo contrario — sus presupuestos no se suman en ningún lugar.
curl -sX POST "$PROXY/team/new" -H "Authorization: Bearer $CLE_ADMIN" \
-H 'Content-Type: application/json' \
-d '{"team_alias":"<projet>","max_budget":0.5,"budget_duration":"1d"}'
2. La clave virtual — una por agente¶
Es todo el mecanismo de imputación. Sin ella, todos gastan bajo el mismo nombre y el panel de control muestra números que no responden a ninguna pregunta.
curl -sX POST "$PROXY/key/generate" -H "Authorization: Bearer $CLE_ADMIN" \
-H 'Content-Type: application/json' \
-d '{"key_alias":"<projet>-<agent>","team_id":"<id>",
"models":["<alias1>","<alias2>"],
"max_budget":0.5,"budget_duration":"1d",
"metadata":{"role":"agent-projet","agent":"<agent>"}}'
La clave se guarda en el cofre, inmediatamente, nunca en un archivo del repositorio: ver Leer un secreto del cofre.
Un presupuesto corta
Alcanzado, la clave se rechaza. Para un agente de fondo cuyo resultado nadie revisa, esto proporciona un servicio sin sentido, cuyo trabajo ha terminado. Ajustar por encima del pico medido, y configurar la alerta de presupuesto (Configurar una notificación).
3. El agente¶
curl -sX POST "$PROXY/v1/agents" -H "Authorization: Bearer $CLE_ADMIN" \
-H 'Content-Type: application/json' -d '{
"agent_name": "<nom>",
"agent_card_params": {
"protocolVersion": "0.3",
"name": "<Nom affiché>",
"description": "<ce qu'"'"'il fait, et ce qu'"'"'il ne fera jamais>",
"url": "http://<hôte>:<port>/a2a/<nom>",
"version": "1.0.0",
"defaultInputModes": ["text/plain"],
"defaultOutputModes": ["text/markdown"],
"skills": [{
"id": "<verbe>", "name": "<verbe — ce qu'"'"'il établit>",
"description": "<ce que cette compétence rend, et ce qu'"'"'elle ne rend pas>",
"tags": ["<axe>"], "examples": ["<appel typique>"]
}]
},
"object_permission": { "models": ["<alias1>","<alias2>"] },
"rpm_limit": 6
}'
| Campo | Lo que decide |
|---|---|
agent_name |
El identificador estable. Es el que se empareja, nunca el agent_id: un agente recreado desde la interfaz cambia de ID y mantiene su nombre |
agent_card_params |
La tarjeta A2A para los llamantes — nombre, descripción, habilidades, formatos |
agent_card_params.url |
dónde va a escribir el proxy. Si no hay nada detrás, la tarjeta miente |
object_permission.models |
La lista blanca de modelos. Segundo filtro, distinto del de la clave |
rpm_limit, tpm_limit |
El caudal. session_* limita una conversación, no al agente entero |
Consumir el agente¶
Cuatro vías, desde la más universal a la más específica.
A. Como un modelo ordinario — la vía que sirve al 90% de los casos¶
curl -sX POST "$PROXY/v1/chat/completions" -H "Authorization: Bearer $CLE" \
-H 'Content-Type: application/json' \
-d '{"model":"a2a/<nom>","messages":[{"role":"user","content":"<consigne>"}]}'
El proxy reconoce el prefijo a2a/, resuelve el agente en su registro, lo traduce a message/send y vuelve a traducir la respuesta. Todo lo que ya se comunica con un modelo, se comunica con el agente: la interfaz de chat lo ve en su lista, el motor de flujos de trabajo también, y un script de tres líneas no necesita hacer nada especial.
Con una advertencia que no es teórica: esta resolución lee el registro en memoria del proxy. Después de un reinicio, falla para todos, siempre y cuando los agentes no se hayan reconfigurado —ver la trampa nº4.
B. En A2A, para un cliente que conoce el protocolo¶
curl -s "$PROXY/a2a/<id>/.well-known/agent-card.json" -H "Authorization: Bearer $CLE"
curl -sX POST "$PROXY/a2a/<id>/message/send" -H "Authorization: Bearer $CLE" \
-H 'Content-Type: application/json' \
-d '{"jsonrpc":"2.0","id":"1","method":"message/send","params":{"message":{
"role":"user","parts":[{"kind":"text","text":"<consigne>"}],"messageId":"<uuid>"}}}'
Se utiliza cuando se quiere el ciclo de tarea: una tarea que dura, un seguimiento del estado, artefactos entregados a lo largo del proceso.
C. Desde un script del parque, sin pasar por A2A¶
Este es el caso de un agente que es un programa de línea de comandos en una máquina, no un servidor. El script lee su definición en el registro, y luego lanza el ejecutable:
agent, provenance, contrat = charge(AGENT_DE_SECOURS) # GET /v1/agents
print(f"définition : {provenance}") # litellm | cache | code
Tres cosas a tener en cuenta, y todas son costosas de redescubrir:
- La lectura se realiza con la clave del agente mismo. El registro responde a una clave ordinaria: ninguna clave de administración necesita estar presente en la máquina que ejecuta.
- Un caché local y la origen mostrada. Un agente que funciona en su copia de seguridad, produce la misma salida, en el mismo momento, por el mismo coste: esta línea es la única diferencia visible entre "actualizado" y "proxy silencioso desde hace tres días".
- Una etiqueta de imputación en la llamada (
x-litellm-tags), de lo contrario, el gasto se atribuye a la clave, pero no al trabajo.
D. Entre agentes¶
Un agente registrado puede llamar a otro por los mismos medios, con su clave. Es aquí donde object_permission.agents sirve: para delimitar quién puede llamar a quién.
Los peligros, todos medidos¶
1. La interfaz de administración elimina litellm_params
El formulario reescribe este campo utilizando únicamente sus propios campos. Cualquier dato que se guarde en él — un contrato de ejecución, metadatos personalizados — desaparece al primer usuario que abra el agente y lo guarde. Sin mensaje, sin rastro, aparte de lo que se indica en updated_by.
Medido: 21 campos establecidos a las 07:52, eliminados a las 08:13 del mismo día. La tarjeta, los límites y la lista de modelos, en cambio, permanecieron intactos.
A tener en cuenta: lo que debe durar va en la tarjeta. Lo que se guarda en litellm_params necesita una fuente externa y una acción que lo valide.
2. La tarjeta elimina lo que no conoce, devolviendo 200
capabilities.extensions — el lugar normativo para los metadatos personalizados — se filtra al guardar. Solo streaming, pushNotifications y stateTransitionHistory sobreviven. La creación tiene éxito, la tarjeta vuelve sin nada, y no se indica nada.
3. La salida no es una entrada válida
Intentar leer una tarjeta y volver a enviarla tal cual falla con el código 422, en campos de seguridad que el propio proxy ha creado. Basta con eliminar estos campos del cuerpo: se regeneran. Cualquier operación de leer / modificar / reescribir debe eliminarlos.
4. El registro vive en memoria: un reinicio del proxy lo vacía
La lista de agentes no se vuelve a leer desde la base al inicio: solo contiene los agentes creados o modificados desde que el proceso está en funcionamiento. Después de un reinicio, siguen existiendo — se pueden acceder a ellos aún mediante su identificador — pero la lista está vacía, y sobre todo, a2a/<nom> deja de funcionar: la resolución lee el mismo registro. Todo lo que consumía al agente como modelo se detiene, sin ninguna advertencia, mientras que el proxy sigue healthy y los modelos responden.
Medido dos veces en la misma hora, el 13 de septiembre de 2026, en seis agentes.
Tres acciones, en este orden: mantener el identificador de cada agente en su definición (cuando la lista es olvidativa, es el único elemento que queda); hacer que la condición sea controlada en lugar de simplemente indicar "ausente", lo que intentaría crear uno y haría que "ya existe"; y reaplicar — una simple operación de escritura lo devuelve al registro.
El reinicio se automatiza sin riesgo porque el desacuerdo es falso: la base y la fuente dicen lo mismo, no hay nada que arbitrar. Pero solo en este caso — tan pronto como surge una discrepancia de otra naturaleza, se detecta y no se hace nada, para evitar que se interrumpa el trabajo de otra persona.
5. Una modificación parcial reemplaza
Un PATCH no fusiona: los campos ausentes del cuerpo se pierden. Siempre se debe devolver el objeto completo. Y un contrato mutilado se comporta exactamente como un contrato sano, ya que lo que falta vuelve a las variables por defecto del código — de ahí la importancia de un consumidor que anuncia lo que realmente ha leído.
Comprobaciones¶
# La définition telle que le registre la sert, et rien d'autre
curl -s "$PROXY/v1/agents" -H "Authorization: Bearer $CLE" | jq '.[] | {agent_name, rpm_limit}'
# Ce que l'exécutant a réellement chargé, à son dernier run
<commande de l'agent> 2>&1 >/dev/null | head -1
# Qui a modifié l'agent en dernier — un humain dans l'UI s'y voit
curl -s "$PROXY/v1/agents/<id>" -H "Authorization: Bearer $CLE_ADMIN" \
| jq '{updated_at, updated_by}'
Dernière vérification de cette page : 2026-09-13