AI Connect/How to create/Une flotte d’agents d’IA
Une flotte d’agents d’IA
Exécutez de nombreux agents d'IA autonomes depuis une seule application, en modélisant chaque agent comme un utilisateur propre du AI Connect SDK, afin qu'il ne charge que les connecteurs dont sa tâche a besoin, avec des identifiants, une consommation et une révocation instantanée par agent. Aucune autre plateforme ne donne à chaque agent d'un essaim sa propre identité isolée.
La prochaine plateforme que vous construirez n'est pas un agent, c'est un essaim : un agent de triage qui lit les tickets, un agent de recherche qui extrait le contexte, un agent d'exploitation qui exécute des runbooks, un agent de conformité qui bloque les actions non sûres. Les systèmes d'IA modernes y sont déjà. Ce qui n'a jamais existé, c'est l'infrastructure pour faire tourner cet essaim en sécurité : jusqu'ici, chaque agent partageait vos identifiants maîtres, ou vous construisiez un système de permissions à la main.
Le AI Connect SDK donne à chaque agent ce qu'aucune plateforme n'a jamais donné à un agent : sa propre identité en tant qu'utilisateur, avec ses propres connecteurs, ses propres identifiants, sa propre consommation et son propre interrupteur d'arrêt, adossé à des milliers de connexions IA dès le premier jour. Donner à chaque agent l'union de tous les outils est un risque pour la sécurité et les coûts : le modèle se comporte de façon imprévisible, une boucle incontrôlée peut épuiser le budget, et vous ne pouvez pas attribuer chaque action à un agent. La réponse habituelle de l'industrie est une matrice de permissions vissée sur un compte partagé. Le SDK le résout comme personne d'autre, parce qu'il le résout de la même manière que pour les humains : chaque agent est un utilisateur. Un external_id par agent, et capabilities() ne renvoie que ce à quoi cet agent est autorisé à accéder. Un programme autonome devient un citoyen gouverné de votre plateforme. Cette phrase n'a jamais été vraie pour un produit d'intégration auparavant. C'est la différence structurelle entre des agents qui partagent votre accès et des agents qui possèdent le leur.
Le bénéfice de « agent = utilisateur »
| Préoccupation | Comment « agent = utilisateur » y remédie |
|---|---|
| Portée | Lexternal_id de chaque agent ne connecte que ses propres connecteurs, l’agent de triage ne peut pas exécuter pagerduty__page. |
| Coût | Chaque connexion est mesurée et révocable via son propre jeton, si bien que la consommation et l’arrêt sont par agent. |
| Audit | Vous savez toujours à quel agent appartient l’action que vous observez, grâce à external_id. |
| Rayon d’impact | Un agent au comportement anormal reste à un seul disconnect() d’être révoqué, sans impact sur les autres agents. |
| Cycle de vie | Créez un nouveau worker en émettant un nouvel id ; désactivez-le en le déconnectant. Aucun déploiement de configuration. |
Ce sont des agents d’IA, et ils agissent eux-mêmes comme utilisateurs du SDK. Il n’y a aucun humain dans la boucle lorsque ops-agent se connecte à votre outil de gestion d’incidents à 3 h 00. Lexternal_id opaque est exactement ce qui permet à un acteur non humain de posséder des identifiants isolés.
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. Donnez à chaque agent une identité stable
Dérivez l’id du rôle de l’agent et, si vous mettez des workers à l’échelle, de son instance. Préfixez-le pour qu’un id d’agent ne puisse jamais entrer en collision avec un humain ou un département.
type AgentRole = 'triage' | 'research' | 'ops' | 'compliance';
const agentUserId = (role: AgentRole, instance = 'primary') =>
`agent-${role}-${instance}`;
// "agent-triage-primary", "agent-ops-worker-07"2. Définissez ce que chaque agent est autorisé à connecter
Ce registre est votre politique de moindre privilège. Gardez-le déclaratif afin de pouvoir raisonner sur toute la flotte à la fois.
// 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. Approvisionnez un agent
Lors du déploiement, connectez les comptes de l’agent. ensure() qualifie l’acteur comme agent afin que votre tableau de bord de gouvernance puisse distinguer la flotte des humains.
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();
// les agents sans interface n’ont pas de consentement dans un navigateur : fournissez
// les identifiants api_key/token depuis votre coffre de secrets via connector.credentials.set()
}
return user.connectors(); // [{ slug, status }, …]
}Les agents s’exécutent sans interface ; préférez donc les connecteurs qui acceptent un jeton ou une clé statique via credentials.set(). Les connecteurs OAuth exigent le consentement d’un humain au moins une fois, faites-le lors de l’onboarding, puis l’agent réutilise la connexion résultante.
4. Exécutez la boucle d’un agent
Chaque agent charge ses propres capacités délimitées. Il ne peut pas voir les outils d’un autre agent parce qu’ils résident sous un external_id différent.
// 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: `Vous êtes l’agent ${role}. Accomplissez la tâche en utilisant uniquement vos outils.` },
{ 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 'Tâche interrompue : budget d’étapes atteint.';
}5. Assurez le relais entre agents en préservant la portée
Un agent de triage escalade vers l’agent d’exploitation en l’appelant avec une nouvelle tâche. L’agent d’exploitation s’exécute sous son propre id et connecte ses propres outils, le relais n’élargit jamais ce que l’un ou l’autre peut faire.
async function orchestrate(ticket: string) {
const triage = await runAgent('triage', `Classez et routez : ${ticket}`);
if (triage?.includes('INFRA')) {
// l’agent d’exploitation utilise pagerduty/kubernetes/github — des outils que le triage ne voit jamais
return runAgent('ops', `Mitigez : ${ticket}`);
}
return runAgent('research', `Rassemblez le contexte sur : ${ticket}`);
}Parce que external_id est opaque, vous pouvez passer à l’échelle : exécutez 20 workers d’exploitation comme agent-ops-worker-01 … agent-ops-worker-20. Chacun a sa propre connexion et son propre compteur, si bien qu’une boucle incontrôlée dans un worker n’affecte que ce worker.
6. L’agent de conformité / gouverneur
Modélisez également un agent de politique. Il connecte des outils en lecture seule, inspecte les actions d’un autre agent et peut déconnecter un connecteur pour révoquer un agent, la gouvernance exprimée comme de simples appels SDK.
async function quarantine(role: AgentRole, instance: string, slug: string) {
await vinkius.user(agentUserId(role, instance)).connector(slug).disconnect();
}7. Python ou CrewAI ? Faites le pont avec un JSON Schema neutre
Le SDK est uniquement en TypeScript aujourd’hui, mais les capacités sont portables. Dans une pile Python, exposez le jeu d’outils via l’adaptateur neutre sous forme de données, ou appelez directement le runtime de la même connexion.
import { toJSONSchemaTools } from '@vinkius/connect/json-schema';
const definitions = toJSONSchemaTools(await vinkius.user('agent-ops-primary').capabilities());
// remettez `definitions` à votre framework d’agents Python ; celui-ci rappelle une seule routeEn interne : pourquoi un seul agent au comportement anormal ne peut pas compromettre la flotte
L’isolation dont vous héritez n’est pas seulement organisationnelle, elle est appliquée par le data plane, et c’est le véritable avantage derrière une flotte autonome.
*Chaque connexion possède son propre jeton `vk_live_`.* L’énumération et l’exécution des outils vont directement au runtime de cette connexion, où chaque appel est mesuré et révocable via son propre jeton*, jamais par le biais de l’API de l’application. Ainsi, la consommation de chaque agent et l’arrêt de chaque agent sont indépendants de ceux de tous les autres agents, par construction.
La révocation est à défaillance sécurisée. Le SDK émet exactement un jeton par connexion lors de connect() et n’en émet jamais implicitement pendant l’exécution. Cela signifie que disconnect() stoppe l’accès immédiatement : un agent détenteur d’un jeton révoqué ne peut pas en obtenir un nouveau par lui-même. Servez-vous-en comme coupure d’urgence :
// l’agent gouverneur arrête un worker anomale en un seul appel — immédiat et définitif
await vinkius.user('agent-ops-worker-07').connector('pagerduty').disconnect();Bornez chaque étape. Un agent peut accorder à un outil lent plus de temps qu’un humain ne le ferait, mais vous plafonnez chaque appel avec timeoutMs (composé avec un AbortSignal). Une exécution incontrôlée reçoit une date limite stricte au lieu d’un budget sans borne :
const result = await capabilities
.findCapability('kubernetes__scale_deployment')!
.execute(
{ deployment: 'ingress', replicas: 6 },
{ idempotencyKey: `ops:${task.id}:scale`, timeoutMs: 10_000 },
);Tracez chaque agent séparément. Les hooks se déclenchent par tentative HTTP avec un requestId expurgé, de sorte que vous attribuez chaque appel, chaque retry et chaque échec à un seul external_id sans construire votre propre système de tracing. Les champs d’identifiants et le segment de chemin vk_live_* sont masqués avant que votre hook ne les reçoive.
Passez des types, pas du texte libre. Lorsqu’un agent transmet des données à un autre, préférez result.structuredContent (l’objet structuré du connecteur) à la sérialisation de content : plus précis, plus économe en jetons et non ambigu.
Liste de contrôle pour la production
- [ ] Préfixez chaque id d’agent (
agent-…) pour que les agents n’entrent jamais en collision avec des humains ou des départements. - [ ] Encodez le moindre privilège dans le registre
AGENTS, un agent, un ensemble étroit de connecteurs. - [ ] Donnez aux agents mutables un
idempotencyKeypar tâche afin qu’une boucle rejouée ne puisse pas s’exécuter deux fois. - [ ] Fixez un budget d’étapes strict par boucle d’agent ; renvoyez
isErrorau modèle au lieu de lever une exception. - [ ] Qualifiez les acteurs avec
user.ensure({ kind: 'agent', role })pour que la gouvernance puisse filtrer la flotte. - [ ] Intégrez
disconnect()dans le chemin de votre gouverneur, la révocation d'urgence est un appel par agent.
Vous exécutez désormais une flotte où chaque agent possède exactement les outils dont sa tâche a besoin, où la consommation de chaque agent est indépendante, et où un agent au comportement anormal est neutralisé sans affecter les autres. La plupart des équipes gouvernent leurs agents avec des documents de politique et les doigts croisés. Vous gouvernez les vôtres avec un système d'identités, et c'est cet écart qui permet à votre flotte de dépasser dix agents alors que la leur ne peut pas dépasser deux en sécurité.
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.
