AI Connect/How to create/自動化とサービスアカウント

自動化とサービスアカウント

VinkiusについてAIに質問

cron ジョブ、Webhook、CI パイプライン、夜間バッチ処理に AI Connect SDK の独自のユーザーを持たせ、静的認証情報でシステムをヘッドレスに接続し、ジョブ単位での分離と予算で、どこにもブラウザなしで動作させましょう。プロセスは、設定ファイルの中の黙った鍵ではなく、統治されたアクターになります。

最も価値のある AI は、無人で動作します。台帳を照合する夜間ジョブ、新しいリードを CRM にトリアージする Webhook、デプロイの失敗で Issue を開く CI ステップ。今日まで、インフラにはこの仕事のための形が一つしかありませんでした。環境変数ファイルの中の god-key。アイデンティティなし、計量なし、失効なし、そして肩をすくめて終わるセキュリティレビューです。

AI Connect SDK はその形を、コネクティビティプラットフォームがかつて提供したことのないもので置き換えます。すなわち プロセスはユーザーである ということです。独自の external_id、独自のコネクタ、独自の静的認証情報を持ち、初日からの数千の AI 接続に支えられています。各自動化に独自のアイデンティティを与え、一度だけ接続すれば、隔離・計量・監査可能な状態で無期限に能力を呼び出させられます。すべての cron ジョブが、身分証と予算と独自の退職手続きを持つ、責任を負う従業員になります。この一文は、このプラットフォームが現れるまで、市場のどの統合プラットフォームについても書くことができなかったものです。

Agent loop · one turn
One user turn of the quickstart, exactly as the console serves it. Click a step or press Run.
vinkius.user('alice_123').capabilities({ include: ['github'] })
HTTPGET /apps/vk_app_xxx/users/alice_123/tools?connector=github
const 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
  ...
Step 1 of 6
The agent loop, step by step. Press Run and follow one user turn: capabilities load, convert to tools, the model calls github__create_issue, the SDK executes on that user connection and the result feeds back. Every step shows the real SDK call and its HTTP request.
Real systemsledger · CRM · issuescron · nightlyown external_idwebhookown external_idCI pipelineown external_idstatic credentialsfrom your secret managerbrowsernever neededidempotent actions
Processes as users: headless jobs with their own static credentials act on real systems, idempotent and metered, with no browser anywhere.

プロセス-as-ユーザーがもたらすもの

特性無人ジョブで重要な理由
デザイン上ヘッドレス静的トークンのコネクタ (api_key, token) は対話的な同意を必要としません、credentials.set() がフロー全体です。
ブラウザは一切不要fetch と Application キーのみの cron コンテナが SDK 全体を実行します。
ジョブ単位の被害範囲各自動化が独自の接続を持つため、キーが 1 つ露出しても侵害されるのは 1 つのジョブだけであり、全体は侵害しません。
独立した計量接続ごとのトークンにより、夜間照合ジョブのコストが正確にわかります。
決定的な再試行べき等キーにより、繰り返しの呼び出しが正確に一度だけ適用されることが保証されます。

これらのジョブは人間を介在させずに実際の認証情報を保持します。すべてのスニペットをサーバー側に保ち、静的トークンをシークレットマネージャーから取得し(リポジトリやモデルへの入力からは絶対に取得せず)、各自動化に動作可能な最小限のコネクタ集合を与えてください。

1. ひとつの自動化、ひとつの id、ひとつのコネクタ集合

ジョブはそれが行うことに基づいて名前付けし、その名前を接続の所有者アクターとして扱ってください。

typescript
const JOB = 'svc-nightly-reconcile'; // 安定、URL に安全、255 文字未満、/ \ とスペースなし
const JOB_CONNECTORS = ['netsuite', 'stripe', 'sheets'];

2. サービスアカウントのプロビジョニング(設定時に一度だけ)

これはオンボーディング中に一度だけ実行されます。オペレーターまたはブートストラップスクリプトが静的トークンを供給します。以降、ジョブは接続を使用するだけです。

typescript
// scripts/bootstrap-reconcile.ts
import { Vinkius } from '@vinkius/connect';

const vinkius = new Vinkius({
  appId: process.env.VINKIUS_APP_ID!,
  apiKey: process.env.VINKIUS_APP_KEY!, // シークレットマネージャーから
});

async function provision(jobId: string, tokens: Record<string, Record<string, string>>) {
  const user = vinkius.user(jobId);
  await user.ensure({ kind: 'service-account', job: 'nightly-reconcile' });

  for (const [slug, values] of Object.entries(tokens)) {
    const connector = user.connector(slug);
    await connector.connect();                 // 取得または作成、べき等
    await connector.credentials.set(values);   // 書き込み専用。例: { API_KEY: … }
  }

  return Promise.all(
    Object.keys(tokens).map(async (slug) => ({
      slug,
      status: await user.connector(slug).status(), // "ready" を期待
    })),
  );
}

await provision('svc-nightly-reconcile', {
  netsuite: await secrets.read('netsuite.reconcile'),
  stripe: await secrets.read('stripe.reconcile'),
  sheets: await secrets.read('sheets.reconcile'),
});

credentials.set() は保存された値を決して返さず、status() はどのキーが設定されているかだけを報告します。プロセスは、受け取ったものを外部へ持ち出す能力を持たずにコネクタの準備完了を検証できます。認証情報は使用可能であって、読み取り可能ではありません。

3. 無人ジョブそのもの

スケジュールされたプロセスにはブラウザも同意も存在するユーザーも不要です。能力を読み込んで実行します。

typescript
// jobs/nightly-reconcile.ts
import { Vinkius } from '@vinkius/connect';

const vinkius = new Vinkius({
  appId: process.env.VINKIUS_APP_ID!,
  apiKey: process.env.VINKIUS_APP_KEY!,
});

async function run() {
  const job = vinkius.user('svc-nightly-reconcile');
  const capabilities = await job.capabilities({ include: ['netsuite', 'stripe'] });

  const fetchOpen = capabilities.findCapability('stripe__list_invoices');
  const postEntry = capabilities.findCapability('netsuite__create_journal_entry');

  if (!fetchOpen || !postEntry) {
    await alertOps('reconcile: 能力が利用できません、認証情報の有効期限切れですか?');
    return;
  }

  const invoices = await fetchOpen.execute({ status: 'open', limit: 200 });
  if (invoices.isError) throw new Error('stripe の一覧取得に失敗: ' + invoices.content[0]?.text);

  const entry = await postEntry.execute(
    { lines: toJournalLines(invoices) },
    { idempotencyKey: `reconcile:${runDate()}` }, // 1 回の論理実行 = 1 件の仕訳
  );
  if (entry.isError) await alertOps('reconcile の仕訳が拒否されました: ' + entry.content[0]?.text);
}

run().catch(async (error) => {
  await alertOps(`reconcile がクラッシュしました: ${error.message}`);
});

idempotencyKey が自動化の要です。再試行、cron の重複起動、実行中の再デプロイ、そのどれも仕訳を二度計上することはありません。サーバーが同じキーを持つリプレイを排除するからです。キーはビジネスイベントから導出し(実行日、チケット id、Webhook 配信 id)、Date.now() から導出しないでください。

4. Webhook: ひとつのアクター、キーごとにひとつの配信

イベント駆動の自動化では通常、サービスアカウントのユーザーを 1 つ維持しますが、変更を伴う呼び出しはすべて配信 id をキーとし、再試行された Webhook が正確に一度だけ適用されるようにします。

typescript
// POST /webhooks/lead  (検証済み)
async function handleLeadWebhook(payload: { id: string; email: string }) {
  const user = vinkius.user('svc-lead-intake');

  if ((await user.connector('hubspot').status()) !== 'ready') {
    await alertOps('lead-intake の CRM が準備できていません');
    return;
  }

  const caps = await user.capabilities({ include: ['hubspot'] });
  await caps.findCapability('hubspot__create_contact')?.execute(
    { email: payload.email },
    { idempotencyKey: `lead:${payload.id}` }, // 再試行された配信 -> 重複なし
  );
}

5. CI パイプラインと使い捨てランナー

CI ジョブは cron とまったく同じように認証します。同じ Application キー、独自の external_id、環境にプロビジョニングされた静的認証情報。違いは存続期間で、パイプラインの破棄時に使い捨てランナーを disconnect() します。

typescript
async function openIssueOnFailedDeploy(runId: string, repo: string) {
  const caps = await vinkius.user('ci-deploy-bot').capabilities({ include: ['github'] });

  await caps.findCapability('github__create_issue')?.execute(
    { owner: 'acme', repo, title: `Deploy ${runId} に失敗` },
    { idempotencyKey: `deploy:${runId}` },
  );
}

6. クリーンなローテーションと退役

すべてが単一の external_id に紐付いているため、自動化の廃止は決定的です。

typescript
async function decommission(jobId: string) {
  const user = vinkius.user(jobId);
  for (const conn of await user.connectors()) {
    await user.connector(conn.slug).disconnect();
  }
}

認証情報をローテーションするには、新しい値で credentials.set() を再度呼び出します。接続は同じまま、能力参照は壊れません。

無人プロセスのための信頼性と構造化出力

無人プロセスこそ、SDK 組み込みの堅牢性が最も重要になる場面です。3 つの保証が自動的に継承されます。

自動再試行と群れ集中アクセス対策。 べき等な読み取り(および idempotencyKey を伴うすべての書き込み)は自動的に再試行されます。ただし一時的な 429/502/503/504 とネットワークエラーに限定され、サーバーの Retry-After を尊重するフルジッターのバックオフを用います。cron 自身の再試行ループは不要です。

べき等性が安全機構です。 バッチジョブでは失敗と再実行は日常です。安定した idempotencyKey は、再実行が影響を複製したかどうかの曖昧さを排除します。サーバーがそのキーでリプレイを排除するからです。変更を伴う呼び出しにキーを渡せば、SDK は非べき等な POST さえ安全に再試行します。

結果はテキストではなくオブジェクトとして消費します。 コネクタが構造化データを返すとき、result.structuredContent はそれを解析済みの状態で提供し、パイプライン内での脆いテキスト抽出が不要です:

typescript
const invoices = await fetchOpen.execute({ status: 'open', limit: 200 });
const list = invoices.structuredContent as { invoices: Array<{ id: string; amount: number }> };

const total = list.invoices.reduce((sum, i) => sum + i.amount, 0);

長いバッチ実行には、呼び出しごとに適切な timeoutMs と、プロセス終了時に中断される signal を組み合わせ、漸進的デプロイ中にジョブを決定的に終了させてください。

本番チェックリスト

  • [ ] 各自動化に区別でき人間が読める external_id を与えます(特権共有アカウントは使わない)。
  • [ ] すべての静的トークンをシークレットマネージャーから取得します。コミットも入力プロンプトへの掲載も絶対にしてはなりません。
  • [ ] 設定時に一度だけプロビジョニングします。実行時は status() を読むだけで、シークレットを再保存しません。
  • [ ] 変更を伴うすべてのジョブで、ビジネスイベントから導出した安定した idempotencyKey を設定します。
  • [ ] status() !== 'ready'isError で警告し、沈黙の失敗が見えない失敗にならないようにします。
  • [ ] 解体の一部として使い捨てのサービスアカウントを disconnect() します。

これで、各自動化が自身のアイデンティティを所有し、実際のシステムをヘッドレスに接続し、スケジュールまたはイベントに応じて動作します。OAuth の仕組みも、特権共有アカウントの「このトークンの所有者は誰?」というドリフトも一切ありません。あなたの無人エステートは、監査が不満を漏らす対象から、ガバナンスの話が最も強い対象へと変わりました。そこにいるすべてのプロセスが、ついに名前を持ったからです。

What you just got

Not a pitch: the properties this build inherits automatically.

Isolation by construction

Connections and capabilities resolve only inside one external_id. No cross-actor leakage is possible, and you wrote none of that enforcement.

Write-only credentials

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.

Metered, revocable spend

Every connection owns a vk_live_* token, so cost and revocation are per connection. One call to disconnect() is a complete, auditable stop.

Any model runtime

One CapabilitySet converts to OpenAI, Anthropic, Gemini, Vercel AI SDK, LangChain, LlamaIndex, Workers AI or neutral JSON Schema. Only the last line changes.

Production safety built in

idempotencyKey, timeoutMs and AbortSignal per call; automatic full-jitter retries on transient failures; typed VinkiusError branches. No bespoke harness.

Headless and deterministic

A cron container with only fetch and your key runs it. An idempotencyKey from the business event makes a re-run a non-event.

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.

Download SKILL.md6 · Available in your language
What unattended automations inherit, plus the SKILL.md, in your language, for your coding agent.

次のステップ