AIエージェントのリモートトリガー — スマホからPCのAIを起動する
約5分で読めます
外出先からスマホ一つでPCのAIエージェントを起動し、タスクを実行させる。Discord Webhookとファイル同期を組み合わせたリモートトリガーシステムにより、物理的にPCの前にいなくても自律的なAIワークフローを開始できる。本記事では、リモートトリガーの設計から実装、セキュリティ対策まで実践的に解説する。
リモートトリガーの全体アーキテクチャ
リモートトリガーシステムは、外部からの信号をPCが検知し、Claude Codeを自動起動する仕組みである。信号の伝達経路は複数のパターンが考えられる。
- Discord経由: スマホでDiscordにメッセージ送信 → Bot検知 → PC上のスクリプトが起動
- OneDrive経由: スマホでファイルを作成 → クラウド同期 → PC上のWatcherが検知
- GitHub経由: スマホでIssueを作成 → Webhookが発火 → PC上のリスナーが起動
// リモートトリガーの基本インターフェース
interface TriggerSignal {
source: "discord" | "onedrive" | "github" | "webhook";
timestamp: string;
taskType: string;
payload: Record<string, unknown>;
priority: "low" | "normal" | "high" | "urgent";
requester: string;
}
interface TriggerHandler {
watch(): Promise<void>;
onSignal(signal: TriggerSignal): Promise<void>;
acknowledge(signalId: string): Promise<void>;
}
Discord Bot経由のトリガー実装
Discord Botを使ったリモートトリガーが最も手軽で信頼性が高い。スマホのDiscordアプリから特定のチャンネルにコマンドを送信するだけで、PCのAIエージェントが起動する。
import { Client, GatewayIntentBits, Message } from "discord.js";
import { execSync, spawn } from "child_process";
const client = new Client({
intents: [
GatewayIntentBits.Guilds,
GatewayIntentBits.GuildMessages,
GatewayIntentBits.MessageContent,
],
});
const TRIGGER_CHANNEL = "ai-trigger";
const ALLOWED_USERS = ["your-discord-user-id"];
client.on("messageCreate", async (message: Message) => {
// セキュリティチェック
if (message.channel.name !== TRIGGER_CHANNEL) return;
if (!ALLOWED_USERS.includes(message.author.id)) return;
if (message.author.bot) return;
// コマンドパース
const content = message.content.trim();
if (!content.startsWith("!ai")) return;
const task = content.slice(4).trim();
await message.reply("タスクを受信しました: " + task);
try {
// Claude Codeを非対話モードで実行
const process = spawn("claude", ["-p", task], {
cwd: "/path/to/project",
stdio: "pipe",
});
let output = "";
process.stdout.on("data", (data) => { output += data.toString(); });
process.on("close", async (code) => {
const summary = output.length > 1900
? output.slice(0, 1900) + "...(truncated)"
: output;
await message.reply("完了 (exit: " + code + ")\n```\n" + summary + "\n```");
});
} catch (error) {
await message.reply("エラーが発生しました: " + String(error));
}
});
client.login(process.env.DISCORD_BOT_TOKEN);
コマンド体系の設計
const COMMANDS: Record<string, {
description: string;
handler: (args: string) => Promise<string>;
}> = {
"!ai run": {
description: "Claude Codeでタスクを実行",
handler: async (task) => {
return execSync("claude -p " + JSON.stringify(task)).toString();
},
},
"!ai status": {
description: "現在の稼働状態を確認",
handler: async () => {
const status = JSON.parse(
require("fs").readFileSync("runtime_status.json", "utf-8")
);
return "Status: " + status.state + "\nUptime: " + status.uptime;
},
},
"!ai wake": {
description: "スリープ状態のPCを起動",
handler: async () => {
return "Wake signal sent";
},
},
};
OneDrive/ファイル同期経由のトリガー
クラウドストレージのファイル同期を利用したトリガーは、Discord Botが動作していない場合のフォールバックとして有用である。
import { watch } from "fs";
import { readFile, unlink } from "fs/promises";
import { join } from "path";
const TRIGGER_DIR = "C:/Users/user/OneDrive/ai-triggers";
const SIGNAL_FILE = "wakeup_signal.json";
async function watchForTriggers(): Promise<void> {
console.log("Watching for triggers in:", TRIGGER_DIR);
watch(TRIGGER_DIR, async (eventType, filename) => {
if (filename !== SIGNAL_FILE || eventType !== "change") return;
const filePath = join(TRIGGER_DIR, SIGNAL_FILE);
try {
const content = await readFile(filePath, "utf-8");
const signal: TriggerSignal = JSON.parse(content);
console.log("Trigger received:", signal.taskType);
// シグナルファイルを削除(処理済みマーク)
await unlink(filePath);
// タスクを実行
await executeTask(signal);
} catch (error) {
console.error("Trigger processing failed:", error);
}
});
}
シグナルファイルのフォーマット
スマホからOneDriveに保存するシグナルファイルの形式を定義する。
{
"source": "onedrive",
"timestamp": "2026-04-10T14:30:00Z",
"taskType": "code-review",
"payload": {
"repository": "my-project",
"branch": "feature/new-api",
"instruction": "PRの全変更をレビューして"
},
"priority": "normal",
"requester": "haruk"
}
セキュリティ対策
リモートトリガーはセキュリティリスクを伴うため、以下の対策を必ず実装する。
認証と認可
interface SecurityConfig {
allowedUsers: string[];
requireToken: boolean;
maxRequestsPerHour: number;
allowedTaskTypes: string[];
blockedCommands: RegExp[];
}
const securityConfig: SecurityConfig = {
allowedUsers: ["user-id-1"],
requireToken: true,
maxRequestsPerHour: 10,
allowedTaskTypes: ["code-review", "test-run", "status-check"],
blockedCommands: [
/rm\s+-rf/,
/sudo/,
/curl.*\|.*sh/,
],
};
function validateTrigger(
signal: TriggerSignal,
config: SecurityConfig
): { valid: boolean; reason?: string } {
if (!config.allowedUsers.includes(signal.requester)) {
return { valid: false, reason: "Unauthorized user" };
}
if (!config.allowedTaskTypes.includes(signal.taskType)) {
return { valid: false, reason: "Task type not allowed" };
}
return { valid: true };
}
レート制限
class RateLimiter {
private requests: Map<string, number[]> = new Map();
isAllowed(userId: string, maxPerHour: number): boolean {
const now = Date.now();
const hourAgo = now - 60 * 60 * 1000;
const userRequests = this.requests.get(userId) || [];
const recentRequests = userRequests.filter((t) => t > hourAgo);
if (recentRequests.length >= maxPerHour) {
return false;
}
recentRequests.push(now);
this.requests.set(userId, recentRequests);
return true;
}
}
Watchdog統合
リモートトリガーとWatchdogパターンを組み合わせることで、エージェントの死活監視とリモート再起動を実現する。
interface WatchdogConfig {
heartbeatIntervalMs: number;
maxMissedHeartbeats: number;
autoRestart: boolean;
notifyOnRestart: boolean;
}
class AgentWatchdog {
private lastHeartbeat: number = Date.now();
private missedCount: number = 0;
constructor(private config: WatchdogConfig) {
setInterval(() => this.check(), config.heartbeatIntervalMs);
}
heartbeat(): void {
this.lastHeartbeat = Date.now();
this.missedCount = 0;
}
private async check(): Promise<void> {
const elapsed = Date.now() - this.lastHeartbeat;
if (elapsed > this.config.heartbeatIntervalMs * 1.5) {
this.missedCount++;
console.warn("Missed heartbeat:", this.missedCount);
if (this.missedCount >= this.config.maxMissedHeartbeats) {
if (this.config.autoRestart) {
await this.restart();
}
}
}
}
private async restart(): Promise<void> {
console.log("Restarting agent...");
// エージェント再起動ロジック
}
}
トリガー方式の比較
| 方式 | レイテンシ | 信頼性 | セットアップ | コスト |
|---|---|---|---|---|
| Discord Bot | 1-3秒 | 高 | 中 | 無料 |
| OneDrive同期 | 5-30秒 | 中 | 低 | 無料 |
| GitHub Webhook | 1-5秒 | 高 | 中 | 無料 |
| 直接SSH | 即座 | 最高 | 高 | VPN費用 |
| Tailscale | 1-2秒 | 高 | 中 | 無料〜 |
ベストプラクティス
- 複数経路を用意する: メイン経路(Discord)とフォールバック経路(OneDrive)を設ける
- 認証を必ず実装する: 誰でもトリガーできる状態は絶対に避ける
- タスクの範囲を制限する: リモートから実行できるタスクタイプを明確に定義する
- 実行結果を通知する: タスク完了時に同じ経路で結果を返す
関連記事
- マルチPC協調 - 複数PCでのエージェント連携
- Watchdogパターンで24時間自動運転 - 死活監視と自動復旧
- AIエージェントの権限設計 - 安全な自動実行の権限管理
- Discord Bot × AI自動化 - Discord経由のAI活用
A
Agentive 編集部
AIエージェントを実際に使い倒す個人開発者。サイト制作の自動化を実践しながら、その知見を発信しています。