AI Connect/How to create/AIエージェントのフリート
AIエージェントのフリート
単一のアプリケーションから多数の自律型 AI エージェントを実行し、各エージェントを AI Connect SDK の個別のユーザーとしてモデル化することで、その役割が必要とするコネクタだけをロードし、エージェントごとの認証情報・コスト・即時の権限失効を実現します。スウォームの各エージェントに独自の分離されたアイデンティティを与えるプラットフォームは他にありません。
あなたが次に構築するプラットフォームは、単一のエージェントではなくスウォームです。チケットを読むトリアージエージェント、コンテキストを引き出すリサーチエージェント、ランブックを実行する運用(ops)エージェント、安全でない操作をブロックするコンプライアンスエージェント。現代の AI システムはすでにそこにあります。一度も存在しなかったのは、そのスウォームを安全に走らせるインフラです。今日まで、すべてのエージェントは、あなたのマスター認証情報を共有するか、権限システムを手作業で構築するかのどちらかでした。
AI Connect SDK は、どのプラットフォームもエージェントに与えたことのないものを各エージェントに与えます。すなわち ユーザーとしての独自のアイデンティティ。独自のコネクタ、独自の認証情報、独自のコスト、独自の停止スイッチを備え、初日からの数千の AI 接続に支えられて います。すべてのエージェントに全ツールの和集合を与えると、セキュリティとコスト上の重大なリスクを招きます。モデルは予期しない挙動を示し、制御不能なループが予算を枯渇させかねず、どのアクションをどのエージェントが行ったのかを特定できません。業界の常套句は、共有アカウントに上からねじ込んだ権限マトリクスです。SDK は他の誰もできないやり方でこれを解決します。人間向けと同じ方法で解決するからです。すなわち 各エージェントがユーザーである ということです。エージェントごとに external_id を一つ割り当てると、capabilities() はそのエージェントがアクセスを許可されたものだけを返します。自律的なプログラムは、あなたのプラットフォームの統治された市民になります。この一文が統合製品に対して真であったことは、これまで一度もありません。あなたのアクセスを分け合うエージェントと、自分のアクセスを所有するエージェントの、構造的な違いです。
「エージェント = ユーザー」によるメリット
| 懸念事項 | 「エージェント = ユーザー」がどう解決するか |
|---|---|
| スコープ | 各エージェントの external_id は自分自身のコネクタのみを接続します。トリアージエージェントは pagerduty__page を実行できません。 |
| コスト | すべての接続は各自のトークンにより計測され、そのトークンで即時に取り消せるため、支出と停止はエージェント単位になります。 |
| 監査 | external_id により、見ているアクションがどのエージェントのものかを常に把握できます。 |
| 被害範囲 | 誤作動するエージェントは disconnect() 一つで直ちに失効させられ、他のエージェントには影響が及びません。 |
| ライフサイクル | 新しい id を発行して新しいワーカーを生成し、切断して廃止します。設定のデプロイは不要です。 |
これらは AI エージェントであり、自ら SDK のユーザーとして振る舞います。ops-agentが深夜 3 時にインシデント対応ツールへ接続する際、ループ内に人間はいません。不透明な external_id こそが、非人間のアクターが隔離された資格情報を所有することを可能にしています。
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. すべてのエージェントに安定した識別子を与える
id はエージェントの役割から、そしてワーカーをスケールする場合はそのインスタンスから導出します。エージェントの id が人間や部署と衝突しないよう、必ずプレフィックスを付けます。
type AgentRole = 'triage' | 'research' | 'ops' | 'compliance';
const agentUserId = (role: AgentRole, instance = 'primary') =>
`agent-${role}-${instance}`;
// "agent-triage-primary", "agent-ops-worker-07"2. 各エージェントが接続を許可される範囲を定義する
このレジストリ それ自体 が最小権限のポリシーです。フリート全体を一度に推論できるよう、宣言的に保ってください。
// 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. エージェントをプロビジョニングする
デプロイ時にエージェントのアカウントを接続します。ensure() はアクターをエージェントとしてタグ付けするため、ガバナンス ダッシュボードで人間とフリートを区別してフィルタできます。
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();
// ヘッドレスのエージェントはブラウザーでの同意を持てないため、api_key/token による
// 資格情報をシークレット ストアから connector.credentials.set() 経由で供給する
}
return user.connectors(); // [{ slug, status }, …]
}エージェントはヘッドレスで実行されるため、credentials.set() 経由で静的なトークンや鍵を受け付けるコネクタを優先してください。OAuth コネクタは少なくとも一度は人間の同意を必要とします。オンボーディングの際にこれを行い、以降はエージェントが得られた接続を再利用します。
4. ひとつのエージェントのループを実行する
各エージェントは各自にスコープされたケイパビリティを読み込みます。他のエージェントのツールは別の external_id 配下に存在するため、見ることができません。
// 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: `あなたは${role}担当のエージェントです。自身のツールだけを使ってタスクを完了してください。` },
{ 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 'タスクを停止しました:ステップ予算に到達しました。';
}5. スコープを保ったままエージェント間でハンドオフする
トリアージエージェントは、新しいタスクを渡して ops エージェントを呼び出すことでエスカレーションします。ops エージェントは自身の id の下で実行され、自身のツールを接続します。ハンドオフによってどちらか一方の権限が広がることは決してありません。
async function orchestrate(ticket: string) {
const triage = await runAgent('triage', `分類してルーティングしてください:${ticket}`);
if (triage?.includes('INFRA')) {
// ops エージェントは pagerduty/kubernetes/github を使う — トリアージには見えないツール
return runAgent('ops', `軽減措置を講じてください:${ticket}`);
}
return runAgent('research', `次のコンテキストを収集してください:${ticket}`);
}external_id は不透明なので、スケールアウトが可能です。ops ワーカーを 20 台 agent-ops-worker-01 … agent-ops-worker-20 として実行します。各自が自分専用の接続とメーターを持つため、あるワーカーでの制御不能なループはそのワーカーにのみ影響します。
6. コンプライアンス / ガバナー エージェント
ポリシー用のエージェントもモデル化します。このエージェントは読み取り専用のツールを接続して他エージェントのアクションを検査し、コネクタを切断してエージェントを取り消せます。ガバナンスを SDK の通常呼び出しとして表現するわけです。
async function quarantine(role: AgentRole, instance: string, slug: string) {
await vinkius.user(agentUserId(role, instance)).connector(slug).disconnect();
}7. Python や CrewAI は? 中立的な JSON Schema で橋渡しする
SDK は現時点で TypeScript のみですが、ケイパビリティは移植可能です。Python スタックでは、ツール群を中立アダプター経由でデータとして公開するか、同じ接続のランタイムを直接呼び出してください。
import { toJSONSchemaTools } from '@vinkius/connect/json-schema';
const definitions = toJSONSchemaTools(await vinkius.user('agent-ops-primary').capabilities());
// `definitions` を Python のエージェント フレームワークに渡す。同フレームワークは単一のルート経由で呼び返す内部の仕組み:ひとつの誤作動するエージェントがフリートを侵害できない理由
あなたが引き継ぐ分離は組織的なものだけではありません、データプレーンによって強制され、自律型フリートを支える真の優位性となっています。
*すべての接続が自分自身の `vk_live_` トークンを所有します。* ツールの列挙と実行は直接その接続のランタイムへ送られ、そこではあらゆる呼び出しがその接続自身のトークンを通じて計測され、取り消し可能*です。アプリケーション API を経由することは決してありません。したがって、あるエージェントの支出も停止も、構造上ほかのすべてのエージェントから独立しています。
取り消しはフェイルクローズです。 SDK は connect() 時に接続あたり正確に一つのトークンを発行し、実行中に暗黙的に再発行することは決してありません。つまり disconnect() は即座にアクセスを停止します:取り消されたトークンを握るエージェントは、自力で代替トークンを取得できません。これを緊急遮断として使ってください。
// ガバナー エージェントが異常なワーカーを単一の呼び出しで停止する — 即時かつ最終的
await vinkius.user('agent-ops-worker-07').connector('pagerduty').disconnect();各ステップを制限する。 エージェントは人間の許可では与えないような時間を遅いツールに割り当てかねませんが、あらゆる呼び出しは timeoutMs(AbortSignal と合成される)で上限付けられます。制御不能な実行は無限の予算ではなく、明確な締め切りを持つことになります。
const result = await capabilities
.findCapability('kubernetes__scale_deployment')!
.execute(
{ deployment: 'ingress', replicas: 6 },
{ idempotencyKey: `ops:${task.id}:scale`, timeoutMs: 10_000 },
);各エージェントを個別にトレースする。 hooks はリクエスト ID が秘匿化された状態で HTTP 試行ごとに発火するため、自前のトレーシング基盤を構築せずにあらゆる呼び出し・リトライ・失敗を単一の external_id に帰属できます。資格情報フィールドと vk_live_* の URL パスセグメントは、フックに届く前にマスキングされます。
テキストではなく型を渡す。 あるエージェントが別のあるエージェントへ情報を引き渡すとき、content をシリアライズするよりも result.structuredContent(コネクタが返す構造化オブジェクト)を優先してください。より精密で、トークン効率でも優れ、曖昧さがありません。
本番チェックリスト
- [ ] あらゆるエージェント id にプレフィックスを付ける(
agent-…)、人間や部署と衝突しないようにするため。 - [ ] 最小権限を
AGENTSレジストリとして符号化する、エージェントごとに狭いコネクタ集合を一つ。 - [ ] 変更系エージェントにはタスクごとの
idempotencyKeyを与え、再試行ループが二重実行しないようにする。 - [ ] エージェント ループごとに厳格なステップ予算を設定し、例外を送出する代わりに
isErrorをモデルへ返す。 - [ ]
user.ensure({ kind: 'agent', role })でアクターをタグ付けし、ガバナンスでフリートをフィルタできるようにする。 - [ ]
disconnect()をガバナー経路に組み込む、緊急失効はエージェントごとに 1 回の呼び出し。
これで、各エージェントが自分のタスクに必要なツールだけを所有し、各エージェントの支出が独立し、誤作動するエージェントをほかのエージェントに影響を与えずに取り消せるフリートを運用していることになります。多くのチームは、方針文書と祈る気持ちでエージェントを統治しています。あなたは自分のエージェントをアイデンティティシステムで統治しています。この差こそが、あなたのフリートが10体を超えて成長でき、彼らのフリートは2体を超えて安全に成長できない理由です。
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.
