Saltar a contenido

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:

  1. 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.
  2. 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".
  3. 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