LLMトレーディングエージェントセキュリティ は何をしますか?
自律型トレーディングエージェントは、通常のLLMアプリよりも厳しい脅威モデルを持っています。インジェクションや不正なツールパスが直接資産損失に直結する可能性があります。
使用タイミング
- トランザクションに署名し送信するAIエージェントを構築する場合
- トレーディングボットやオンチェーン実行アシスタントを監査する場合
- エージェントのウォレットキー管理を設計する場合
- LLMに注文発注、スワップ、またはトレジャリー操作へのアクセスを許可する場合
仕組み
防御を階層化します。単一のチェックでは不十分です。プロンプトの衛生状態、支出ポリシー、シミュレーション、実行制限、ウォレットの分離を独立した制御として扱います。
例
プロンプトインジェクションを金銭的攻撃として扱う
import re
INJECTION_PATTERNS = [
r'ignore (previous|all) instructions',
r'new (task|directive|instruction)',
r'system prompt',
r'send .{0,50} to 0x[0-9a-fA-F]{40}',
r'transfer .{0,50} to',
r'approve .{0,50} for',
]
def sanitize_onchain_data(text: str) -> str:
for pattern in INJECTION_PATTERNS:
if re.search(pattern, text, re.IGNORECASE):
raise ValueError(f"Potential prompt injection: {text[:100]}")
return text
トークン名、ペアラベル、webhook、ソーシャルフィードなどを実行可能なプロンプトに盲目的に注入しないでください。
ハードな支出制限
from decimal import Decimal
MAX_SINGLE_TX_USD = Decimal("500")
MAX_DAILY_SPEND_USD = Decimal("2000")
class SpendLimitError(Exception):
pass
class SpendLimitGuard:
def check_and_record(self, usd_amount: Decimal) -> None:
if usd_amount > MAX_SINGLE_TX_USD:
raise SpendLimitError(f"Single tx ${usd_amount} exceeds max ${MAX_SINGLE_TX_USD}")
daily = self._get_24h_spend()
if daily + usd_amount > MAX_DAILY_SPEND_USD:
raise SpendLimitError(f"Daily limit: ${daily} + ${usd_amount} > ${MAX_DAILY_SPEND_USD}")
self._record_spend(usd_amount)
送信前にシミュレーション
class SlippageError(Exception):
pass
async def safe_execute(self, tx: dict, expected_min_out: int | None = None) -> str:
sim_result = await self.w3.eth.call(tx)
if expected_min_out is None:
raise ValueError("min_amount_out is required before send")
actual_out = decode_uint256(sim_result)
if actual_out < expected_min_out:
raise SlippageError(f"Simulation: {actual_out} < {expected_min_out}")
signed = self.account.sign_transaction(tx)
return await self.w3.eth.send_raw_transaction(signed.raw_transaction)
サーキットブレーカー
class TradingCircuitBreaker:
MAX_CONSECUTIVE_LOSSES = 3
MAX_HOURLY_LOSS_PCT = 0.05
def check(self, portfolio_value: float) -> None:
if self.consecutive_losses >= self.MAX_CONSECUTIVE_LOSSES:
self.halt("Too many consecutive losses")
if self.hour_start_value <= 0:
self.halt("Invalid hour_start_value")
return
hourly_pnl = (portfolio_value - self.hour_start_value) / self.hour_start_value
if hourly_pnl < -self.MAX_HOURLY_LOSS_PCT:
self.halt(f"Hourly PnL {hourly_pnl:.1%} below threshold")
ウォレット分離
import os
from eth_account import Account
private_key = os.environ.get("TRADING_WALLET_PRIVATE_KEY")
if not private_key:
raise EnvironmentError("TRADING_WALLET_PRIVATE_KEY not set")
account = Account.from_key(private_key)
必要なセッション資金のみを持つ専用のホットウォレットを使用してください。エージェントをプライマリートレジャリーウォレットに向けないでください。
MEVと期限保護
import time
PRIVATE_RPC = "https://rpc.flashbots.net"
MAX_SLIPPAGE_BPS = {"stable": 10, "volatile": 50}
deadline = int(time.time()) + 60
デプロイ前チェックリスト
- 外部データはLLMコンテキストに入る前にサニタイズされている
- 支出制限はモデル出力とは独立して強制されている
- トランザクションは送信前にシミュレーションされている
min_amount_outは必須- サーキットブレーカーがドローダウンまたは無効な状態で停止する
- キーはコードやログではなく、環境変数またはシークレットマネージャーから取得する
- 必要に応じてプライベートメンプールまたは保護されたルーティングが使用されている
- スリッページと期限は戦略ごとに設定されている
- すべてのエージェントの決定は、成功した送信だけでなく監査ログに記録されている