AI Connect/How to create/Una flota de agentes de IA
Una flota de agentes de IA
Ejecute muchos agentes de IA autónomos desde una sola aplicación, modelando cada agente como un usuario propio del AI Connect SDK, de modo que cargue solo los conectores que su tarea necesita, con credenciales, gasto y revocación instantánea por agente. Ninguna otra plataforma da a cada agente de un enjambre su propia identidad aislada.
La próxima plataforma que construirá no es un agente, es un enjambre: un agente de triaje que lee tickets, un agente de investigación que extrae contexto, un agente de operaciones que ejecuta runbooks, un agente de cumplimiento que bloquea acciones inseguras. Los sistemas de IA modernos ya están ahí. Lo que nunca ha existido es la infraestructura para ejecutar ese enjambre con seguridad: hasta hoy, todo agente o compartía sus credenciales maestras, o usted construía un sistema de permisos a mano.
El AI Connect SDK le da a cada agente lo que ninguna plataforma le ha dado jamás a un agente: su propia identidad como usuario, con sus propios conectores, credenciales, gasto e interruptor de apagado, respaldado por miles de conexiones de IA desde el primer día. Dar a cada agente la unión de todas las herramientas es un riesgo de seguridad y de coste: el modelo se comporta de forma imprevisible, un bucle descontrolado puede agotar el presupuesto y no puede atribuir cada acción a un agente. La respuesta habitual de la industria es una matriz de permisos atornillada a una cuenta compartida. El SDK lo resuelve como nadie más lo hace, porque lo resuelve de la misma manera que resuelve a los humanos: cada agente es un usuario. Un external_id por agente, y capabilities() devuelve solo aquello a lo que ese agente tiene derecho a acceder. Un programa autónomo se convierte en un ciudadano gobernado de su plataforma. Esa frase nunca fue cierta para un producto de integraciones antes. Es la diferencia estructural entre agentes que comparten su acceso y agentes que poseen el suyo.
La ventaja de "agente = usuario"
| Preocupación | Cómo la resuelve "agente = usuario" |
|---|---|
| Alcance | El external_id de cada agente conecta solo sus propios conectores, el agente de triaje no puede ejecutar pagerduty__page. |
| Coste | Cada conexión se mide y es revocable mediante su propio token, de modo que el gasto y la terminación son por agente. |
| Auditoría | Siempre sabe a qué agente pertenece la acción que está observando, mediante external_id. |
| Radio de impacto | Un agente con un comportamiento anómalo queda a un solo disconnect() de ser revocado, sin afectar a los demás agentes. |
| Ciclo de vida | Cree un nuevo worker emitiendo un nuevo id; desactívelo desconectándolo. Sin despliegues de configuración. |
Estos son agentes de IA, y ellos mismos actúan como usuarios del SDK. No hay ningún humano en el bucle cuando ops-agent se conecta a su herramienta de incidentes a las 03:00. El external_id opaco es exactamente lo que permite que un actor no humano sea propietario de credenciales aisladas.
vinkius.user('alice_123').capabilities({ include: ['github'] })GET /apps/vk_app_xxx/users/alice_123/tools?connector=githubconst capabilities = await vinkius
.user('alice_123')
.capabilities({ include: ['github'] });CapabilitySet (6)
github__list_issues read-only
github__create_issue POST /repos/{owner}/{repo}/issues
github__list_pull_requests read-only
github__search_code read-only
...1. Dé a cada agente una identidad estable
Derive el id del rol del agente y, si escala workers, de su instancia. Añada un prefijo para que un id de agente jamás colisione con un humano o un departamento.
type AgentRole = 'triage' | 'research' | 'ops' | 'compliance';
const agentUserId = (role: AgentRole, instance = 'primary') =>
`agent-${role}-${instance}`;
// "agent-triage-primary", "agent-ops-worker-07"2. Defina lo que a cada agente se le permite conectar
Este registro es su política de mínimo privilegio. Manténgalo declarativo para poder razonar sobre toda la flota a la vez.
// server/fleet.ts
export const AGENTS: Record<AgentRole, { model: string; connectors: string[] }> = {
triage: { model: '[MODEL_ID]', connectors: ['zendesk', 'linear'] },
research: { model: '[MODEL_ID]', connectors: ['drive', 'confluence', 'search'] },
ops: { model: '[MODEL_ID]', connectors: ['pagerduty', 'kubernetes', 'github'] },
compliance: { model: '[MODEL_ID]', connectors: ['audit-log', 's3'] },
};3. Aprovisione un agente
En el despliegue, conecte las cuentas del agente. ensure() marca el actor como agente para que su panel de gobernanza pueda filtrar la flota de los humanos.
import { vinkius } from './vinkius';
import { AGENTS, type AgentRole, agentUserId } from './fleet';
async function provisionAgent(role: AgentRole, instance = 'primary') {
const user = vinkius.user(agentUserId(role, instance));
await user.ensure({ kind: 'agent', role });
for (const slug of AGENTS[role].connectors) {
const connector = user.connector(slug);
await connector.connect();
// los agentes sin interfaz no tienen consentimiento en el navegador: suministre
// credenciales api_key/token desde su almacén de secretos mediante connector.credentials.set()
}
return user.connectors(); // [{ slug, status }, …]
}Los agentes se ejecutan sin interfaz, así que prefiera conectores que acepten un token o clave estáticos mediante credentials.set(). Los conectores OAuth necesitan el consentimiento de un humano al menos una vez, hágalo durante el onboarding, y luego el agente reutiliza la conexión resultante.
4. Ejecute el bucle de un agente
Cada agente carga sus propias capacidades con alcance propio. No puede ver las herramientas de otro agente porque estas residen bajo un external_id diferente.
// server/run-agent.ts
import OpenAI from 'openai';
import { vinkius } from './vinkius';
import { toOpenAITools, runOpenAIToolCall } from '@vinkius/connect/openai';
import { AGENTS, type AgentRole, agentUserId } from './fleet';
const openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY! });
export async function runAgent(role: AgentRole, task: string, instance = 'primary') {
const user = vinkius.user(agentUserId(role, instance));
const capabilities = await user.capabilities({ include: AGENTS[role].connectors });
const messages: object[] = [
{ role: 'system', content: `Usted es el agente ${role}. Complete la tarea usando solo sus herramientas.` },
{ role: 'user', content: task },
];
for (let step = 0; step < 12; step++) {
const completion = await openai.chat.completions.create({
model: AGENTS[role].model,
messages: messages as never,
tools: toOpenAITools(capabilities),
});
const msg = completion.choices[0].message;
messages.push(msg as object);
if (!msg.tool_calls?.length) return msg.content;
for (const call of msg.tool_calls) {
const result = await runOpenAIToolCall(capabilities, call);
messages.push({
role: 'tool',
tool_call_id: call.id,
content: JSON.stringify({ content: result.content, isError: result.isError }),
});
}
}
return 'Tarea interrumpida: presupuesto de pasos alcanzado.';
}5. Transfiera entre agentes conservando el alcance
Un agente de triaje escala al agente de operaciones llamándolo con una nueva tarea. El agente de operaciones se ejecuta bajo su propio id y conecta sus propias herramientas, la transferencia nunca amplía lo que cualquiera de los dos puede hacer.
async function orchestrate(ticket: string) {
const triage = await runAgent('triage', `Clasifique y enrute: ${ticket}`);
if (triage?.includes('INFRA')) {
// el agente de operaciones usa pagerduty/kubernetes/github — herramientas que el triaje nunca ve
return runAgent('ops', `Mitigue: ${ticket}`);
}
return runAgent('research', `Reúna contexto sobre: ${ticket}`);
}Dado que external_id es opaco, puede escalar horizontalmente: ejecute 20 workers de operaciones como agent-ops-worker-01 … agent-ops-worker-20. Cada uno tiene su propia conexión y su propia medición, de modo que un bucle descontrolado en un worker solo afecta a ese worker.
6. El agente de cumplimiento / gobernador
Modele también un agente de política. Conecta herramientas de solo lectura, inspecciona las acciones de otro agente y puede desconectar un conector para revocar un agente, gobernanza expresada como llamadas ordinarias al SDK.
async function quarantine(role: AgentRole, instance: string, slug: string) {
await vinkius.user(agentUserId(role, instance)).connector(slug).disconnect();
}7. ¿Python o CrewAI? Tienda un puente con JSON Schema neutro
El SDK es solo de TypeScript por ahora, pero las capacidades son portables. En una pila de Python, exponga el conjunto de herramientas a través del adaptador neutro como datos, o llame directamente al runtime de la misma conexión.
import { toJSONSchemaTools } from '@vinkius/connect/json-schema';
const definitions = toJSONSchemaTools(await vinkius.user('agent-ops-primary').capabilities());
// entregue `definitions` a su framework de agentes de Python; este llama de vuelta a una única ruta suyaPor dentro: por qué un agente con comportamiento anómalo no puede comprometer la flota
El aislamiento que hereda no es solo organizacional, lo aplica el data plane, y es la verdadera ventaja que respalda una flota autónoma.
*Cada conexión posee su propio token `vk_live_`.* El listado y la ejecución de herramientas se dirigen directamente al runtime de esa conexión, donde cada llamada se mide y es revocable mediante su propio token*, nunca a través de la API de la aplicación. Así, el gasto de cada agente y la terminación de cada agente son independientes de los de cualquier otro agente, por construcción.
La revocación falla en modo cerrado. El SDK emite exactamente un token por conexión en connect() y nunca emite uno implícitamente durante la ejecución. Eso significa que disconnect() detiene el acceso de inmediato: un agente con un token revocado no puede obtener un reemplazo por sí solo. Úselo como corte de emergencia:
// el agente gobernador detiene un worker anómalo en una sola llamada — inmediato y definitivo
await vinkius.user('agent-ops-worker-07').connector('pagerduty').disconnect();Limite cada paso. Un agente puede conceder a una herramienta lenta más tiempo del que le concedería un humano, pero usted acota cada llamada con timeoutMs (compuesto con una AbortSignal). Una ejecución descontrolada pasa a tener un plazo límite en lugar de un presupuesto ilimitado:
const result = await capabilities
.findCapability('kubernetes__scale_deployment')!
.execute(
{ deployment: 'ingress', replicas: 6 },
{ idempotencyKey: `ops:${task.id}:scale`, timeoutMs: 10_000 },
);Rastree cada agente por separado. Los hooks se disparan por intento HTTP con un requestId ya redactado, de modo que puede atribuir cada llamada, reintento y fallo a un único external_id sin construir su propio sistema de trazado. Los campos de credenciales y el segmento de ruta vk_live_* se enmascaran antes de que su hook los reciba.
Pase tipos, no texto libre. Cuando un agente transmite datos a otro, prefiera result.structuredContent (el objeto estructurado del conector) frente a serializar content: es más preciso, más eficiente en tokens y no es ambiguo.
Lista de comprobación para producción
- [ ] Prefije cada id de agente (
agent-…) para que los agentes nunca colisionen con humanos ni departamentos. - [ ] Codifique el mínimo privilegio en el registro
AGENTS, un agente, un conjunto estrecho de conectores. - [ ] Dé a los agentes mutables un
idempotencyKeypor tarea para que un bucle reintentado no ejecute dos veces. - [ ] Defina un presupuesto estricto de pasos por bucle de agente; devuelva
isErroral modelo en lugar de lanzar una excepción. - [ ] Etique los actores con
user.ensure({ kind: 'agent', role })para que la gobernanza pueda filtrar la flota. - [ ] Incorpore
disconnect()a la ruta de su gobernador, la revocación de emergencia es una llamada por agente.
Ahora ejecuta una flota en la que cada agente posee exactamente las herramientas que su tarea necesita, el gasto de cada agente es independiente y un agente con comportamiento anómalo queda neutralizado sin afectar a los demás. La mayoría de los equipos gobiernan sus agentes con documentos de políticas y los dedos cruzados. Usted gobierna los suyos con un sistema de identidades, y esa brecha es la razón por la que su flota puede crecer más allá de diez agentes mientras la de ellos no puede crecer con seguridad más allá de dos.
What you just got
Not a pitch: the properties this build inherits automatically.
Connections and capabilities resolve only inside one external_id. No cross-actor leakage is possible, and you wrote none of that enforcement.
Your server stores secrets and can read back which fields are configured, never the values. Not your code, the model, or a dashboard can exfiltrate them.
Every connection owns a vk_live_* token, so cost and revocation are per connection. One call to disconnect() is a complete, auditable stop.
One CapabilitySet converts to OpenAI, Anthropic, Gemini, Vercel AI SDK, LangChain, LlamaIndex, Workers AI or neutral JSON Schema. Only the last line changes.
idempotencyKey, timeoutMs and AbortSignal per call; automatic full-jitter retries on transient failures; typed VinkiusError branches. No bespoke harness.
Give each agent only its connectors, cap each loop with timeoutMs, revoke one misbehaving agent in one call. The others keep running.
Give it to your AI agent
An Agent Skill (SKILL.md) for this build. Preview the first lines below, then copy or download it into your repo under .claude/skills/: Claude Code, Cursor or any Agent-Skills-compatible agent follows it to implement this pattern correctly.
