Skip to Content
GuidesAIエージェント統合

AI Agent Integration

Kovaは、AIエージェント(Claude Code、GPT、Gemini等)がウォレット機能を使えるよう設計されています。Agent Skills標準に準拠したスキル集を使用することで、自然言語でウォレット操作が可能になります。

AIエージェント統合の仕組み

AIエージェントとKovaの統合は、Skillsレイヤーを介して行われます:

Agent Skills標準とは

Agent Skills標準は、AIエージェントが外部ツールを呼び出すための標準仕様です。Kovaは、この標準に準拠したスキル集を提供しています。

主な特徴:

  • 自然言語トリガー - ユーザーの発言から自動的にスキルを起動
  • JSON出力 - 構造化されたデータで結果を返却
  • エラーハンドリング - エラーを検出し、適切なメッセージを表示
  • プラットフォーム非依存 - Claude Code、GPT、Gemini等で動作

インストール方法

Claude Code

kova skills コマンドでスキルをインストールできます:

# スキルをインストール kova skills install # インストール済みスキルを確認 kova skills list

GPT/Gemini

各プラットフォームのスキルディレクトリに配置してください。詳細は、各プラットフォームのドキュメントを参照してください。

OpenClaw

kova skills install でインストールしたスキルをシンボリックリンクで参照:

mkdir -p ~/.openclaw/skills cd ~/.openclaw/skills ln -s ~/.kova/skill-repos/base-skills/skills/* .

利用可能なスキル

Kovaは、以下のスキルを提供しています:

1. create-wallet

トリガー:

  • “ウォレットをセットアップして”
  • “kova の初期設定をして”

実行内容:

kova init

ウォレットは kova init の対話フローで作成されます。v0.x はシングルウォレット固定です。

出力例:

{ "ok": true, "data": { "wallet": { "id": "68cf6dba-e5f1-4c3a-9b2e-1234567890ab", "accounts": [ { "chainId": "eip155:8453", "address": "0xB8EC761bf83B4374877e903d217222F2cd5512De" } ] } } }

2. check-balance

トリガー:

  • “残高を確認して”
  • “いくら持ってる?”
  • “my-wallet の Base での USDC 残高を確認して”

実行内容:

kova balance

出力例:

{ "ok": true, "data": { "rows": [ { "chain": "base", "tokenKey": "USDC", "symbol": "USDC", "balance": "100.5", "status": "ok" } ] } }

3. send-tokens

トリガー:

  • “送金して”
  • “XXX USDCを送って”
  • “0x1234…abcd に 10 USDC を送信して”

実行内容:

kova send --name <name> --to <address> --amount <amount> --token <symbol> --broadcast

出力例:

{ "ok": true, "data": { "dryRun": false, "txHash": "0x1234567890abcdef...", "explorerUrl": "https://basescan.org/tx/0x1234567890abcdef..." } }

4. manage-config

トリガー:

  • “デフォルトウォレットを設定して”
  • “設定を変更して”

実行内容:

kova config set <key> <value>

出力例:

{ "ok": true, "data": { "key": "defaultWallet", "value": "my-wallet" } }

エラーハンドリング

スキルは、CLIの出力JSONをパースしてエラーを検出します。

共通エラー

エラーコード原因対処法
WALLET_NOT_FOUNDウォレットが存在しないkova wallet info で確認
INSUFFICIENT_BALANCE残高不足balance で残高確認
INVALID_PARAMSパラメータ不正(無効なアドレス・重複ウォレット名・型不一致など)エラーメッセージで該当箇所を確認
CHAIN_NOT_SUPPORTEDチェーンが未対応サポートチェーン を確認
POLICY_DENIEDポリシーに違反policy list で制限内容を確認
CREDENTIAL_REQUIREDagent 経路で credential 未設定kova init で発行

エラーレスポンス例

{ "ok": false, "error": { "code": "WALLET_NOT_FOUND", "message": "Wallet 'my-wallet' not found. Available wallets: []" } }

スキルは、エラーコードに基づいて適切なメッセージをユーザーに表示します。

TypeScript統合例

AIエージェントをTypeScriptで実装する場合の例:

interface BalanceResult { ok: boolean; data?: { rows: Array<{ chain: string; tokenKey: string; symbol: string; balance: string | null; status: 'ok' | 'empty' | 'error'; }>; }; error?: { code: string; message: string; }; } async function checkBalance(): Promise<BalanceResult> { try { // kova balance は引数なし。全チェーン × 主要トークンのサマリーを返す。 // 非 TTY (パイプ) 経路なので JSON が返る。 const { execFileSync } = require('child_process'); const output = execFileSync('npx', [ '@komlock_lab/kova', 'balance', ], { encoding: 'utf-8' }); return JSON.parse(output); } catch (error) { return { ok: false, error: { code: 'UNKNOWN_ERROR', message: error.message, }, }; } } // 使用例: 全チェーンのサマリーから Base の USDC を取り出す async function main() { const result = await checkBalance(); if (result.ok) { const usdcOnBase = result.data.rows.find( (r) => r.chain === 'base' && r.symbol === 'USDC', ); console.log(`Base USDC 残高: ${usdcOnBase?.balance ?? '0'}`); } else { console.error(`エラー: ${result.error.code} - ${result.error.message}`); } } main();

dry-runとbroadcastの使い分け

送金スキルでは、dry-runとbroadcastの2段階フローを推奨しています。

推奨フロー

async function sendWithApproval(to: string, amount: string, token: string) { const { execFileSync } = require('child_process'); // 1. dry-run(セキュリティ: 配列引数でコマンドインジェクション対策) const dryRunResult = JSON.parse( execFileSync('npx', [ '@komlock_lab/kova', 'send', '--name', 'my-wallet', '--to', to, '--amount', amount, '--token', token, ], { encoding: 'utf-8' }) ); if (!dryRunResult.ok) { console.error(`dry-runエラー: ${dryRunResult.error.message}`); return; } // 2. ユーザー確認 console.log("送金内容を確認してください:"); console.log(JSON.stringify(dryRunResult.data.tx, null, 2)); const approved = await askUserApproval(); // ユーザーの承認を得る関数 if (!approved) { console.log("送金をキャンセルしました"); return; } // 3. broadcast(セキュリティ: 配列引数でコマンドインジェクション対策) const result = JSON.parse( execFileSync('npx', [ '@komlock_lab/kova', 'send', '--name', 'my-wallet', '--to', to, '--amount', amount, '--token', token, '--broadcast', ], { encoding: 'utf-8' }) ); if (result.ok) { console.log(`送金成功: ${result.data.explorerUrl}`); } else { console.error(`送金エラー: ${result.error.message}`); } }

ポリシーの設定

AIエージェントに使わせる場合は、必ずポリシーを設定してください。

ポリシー作成

policy.json:

{ "name": "AIエージェント用ポリシー", "maxAmount": "100", "allowedChains": ["eip155:8453"], "allowedTokens": ["USDC"], "requireApproval": true }
# ポリシー作成 kova policy create --file policy.json # API鍵ローテーション(初回は kova init で自動発行される) kova key rotate --name agent-key # 発行された credential を環境変数に設定 export KOVA_CREDENTIAL=kova_xxxxx

エージェントへの認証情報の渡し方

kova init を実行すると、エージェントキーが ~/.kova/config.json に保存されます。Claude Code などから呼ばれる kova プロセスはこのファイルから credential を直接読み取るため、通常は環境変数を手動で設定する必要はありませんclaude を普段どおり起動するだけで agent モードで動作します。

claude

任意: 環境変数を sanitize して起動する

子プロセスへ渡す環境変数を明示的に制御したい場合は、kova launch ラッパを使えます(任意)。KOVA_CREDENTIAL / KOVA_API_KEY_ID を注入した状態でエージェントを起動します。

kova launch claude --model claude-sonnet-4-5

プログラムから kova を呼び出す場合

スクリプトやサービスから kova を起動する場合は、環境変数で credential を渡します。⚠️ API key をコードにハードコードしないでください。

export KOVA_CREDENTIAL="cred_xxxxxxxxxxxxxxxxxxxxx" export KOVA_API_KEY_ID="ak_1234567890abcdef"
// ❌ BAD: ハードコードは避ける(コミットミスで漏洩リスク) process.env.KOVA_CREDENTIAL = 'cred_xxxxx'; // ✅ GOOD: 環境変数から読み取る const credential = process.env.KOVA_CREDENTIAL; if (!credential) { throw new Error('KOVA_CREDENTIAL not set'); }

API key を環境変数に設定することで、CLIコマンドは自動的にポリシー評価を行います:

const { execFileSync } = require('child_process'); // ポリシー評価付きで送金(環境変数から credential 読み取り) const result = execFileSync('npx', [ '@komlock_lab/kova', 'send', '--name', 'my-wallet', '--to', '0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb', '--amount', '150', '--token', 'USDC', '--broadcast', ], { encoding: 'utf-8' }); // ポリシー違反の場合はエラー // { // "ok": false, // "error": { // "code": "POLICY_DENIED", // "message": "Transaction exceeds policy maxAmount: 100 USDC" // } // }

本番環境での運用

推奨事項

  1. dry-runの徹底 - 必ずdry-runで確認してから--broadcast
  2. ユーザー承認フロー - 高額送金は必ず人間の承認を得る
  3. ポリシー設定 - 必ずポリシーを設定し、最小権限の原則を適用
  4. ログの記録 - すべてのトランザクションをログに記録
  5. 異常検知 - 異常なトランザクションパターンをアラート
  6. プロンプト汚染対策 - 入力のバリデーション、出力の検証を実施

ログ記録の実装例

import * as fs from 'fs'; function logTransaction(tx: any) { const log = { timestamp: new Date().toISOString(), tx, }; fs.appendFileSync( './transaction-log.jsonl', JSON.stringify(log) + '\n' ); } async function sendWithLogging(to: string, amount: string, token: string) { const { execFileSync } = require('child_process'); // セキュリティ: 配列引数でコマンドインジェクション対策 const result = JSON.parse( execFileSync('npx', [ '@komlock_lab/kova', 'send', '--name', 'my-wallet', '--to', to, '--amount', amount, '--token', token, '--broadcast', ], { encoding: 'utf-8' }) ); // ログ記録 logTransaction(result); return result; }

異常検知の実装例

function detectAnomalies(tx: any): boolean { // 高額送金の検出 if (parseFloat(tx.amount) > 1000) { console.warn(`高額送金検出: ${tx.amount} ${tx.token}`); return true; } // 短時間での連続送金の検出 const recentTxs = getRecentTransactions(60); // 直近60秒 if (recentTxs.length > 5) { console.warn(`連続送金検出: ${recentTxs.length}件`); return true; } return false; } async function sendWithAnomalyDetection(to: string, amount: string, token: string) { const tx = { to, amount, token }; if (detectAnomalies(tx)) { // 人間の承認を求める const approved = await askHumanApproval(tx); if (!approved) { return { ok: false, error: { code: 'ANOMALY_DETECTED', message: 'Transaction blocked by anomaly detection' } }; } } // 通常の送金フロー return sendWithApproval(to, amount, token); }

プロンプト汚染の防御

AIエージェントは、悪意のあるプロンプト注入攻撃に脆弱です。以下の対策を推奨します:

入力のバリデーション

function validateAddress(address: string): boolean { // Ethereum アドレス形式チェック const addressRegex = /^0x[a-fA-F0-9]{40}$/; return addressRegex.test(address); } function validateAmount(amount: string): boolean { // 正の数値チェック const num = parseFloat(amount); return num > 0 && !isNaN(num); } async function safeSend(to: string, amount: string, token: string) { // バリデーション if (!validateAddress(to)) { return { ok: false, error: { code: 'INVALID_PARAMS', message: 'Invalid Ethereum address' } }; } if (!validateAmount(amount)) { return { ok: false, error: { code: 'INVALID_PARAMS', message: 'Invalid amount' } }; } // 送金実行 return sendWithApproval(to, amount, token); }

出力の検証

function verifyOutput(output: any): boolean { // JSON構造チェック if (!output.ok || !output.data) { return false; } // トランザクションハッシュ形式チェック if (output.data.txHash && !/^0x[a-fA-F0-9]{64}$/.test(output.data.txHash)) { return false; } return true; }

次のステップ

Last updated on