TypeScriptでセルフ実行するAIタスクを構築する方法(クーロン不要)

Dev.to / 2026/4/3

💬 オピニオンDeveloper Stack & InfrastructureTools & Practical Usage

要点

  • この記事では、cronジョブや手作りのNodeスクリプトに頼って、再試行・ログ出力・エラーハンドリングを行いながら、1時間ごとにAI駆動のワークフロー(例:サポートチケットの要約)をスケジューリングすることのつらさを論じています。
  • 代替として、外部のcronスケジューリングなしで実行を管理できるようにする「セルフ実行」型のAIタスクをTypeScriptで構築する方法を提示します。
  • チュートリアルでは、「旧来のやり方」を、失敗の監視・リトライ処理・可観測性といった運用上の懸念を含むものとして位置づけ、新しいパターンがそれらを単純化することを狙います。
  • 実装に向けた具体的なメンタルモデルとコード中心の方針(TypeScript+AI呼び出し+ワークフロートリガー)を示し、自動化された反復可能なAIタスク実行を行うための道筋を提供します。
  • 全体としての要点は、継続的に実行されるAIジョブについて、運用負荷を減らしつつ信頼性と保守性を高めるオーケストレーションのパターンを選ぶことです。
  • この記事の中心はAIモデルそのものではなく、AIタスクのスケジューリングを取り巻くインフラ設計であり、エンジニアリングのワークフロー改善に重点を置いています。

TypeScriptで自己実行するAIタスクを構築する方法(クーロン不要)

サポートチケットを要約するAIがあります。次は、それを毎時間実行させる必要があります。

本当にcronジョブを設定しますか?Nodeスクリプトを書きますか?エラーを処理しますか?リトライを設定しますか?ログを作りますか?失敗を監視しますか?

より良い方法があります。

旧来の方法:Cron+スクリプト+つらさ

スケジュールされたAIタスクを従来の方法で作ると、こんな感じになります:

// scripts/ticket-summarizer.ts
import { OpenAI } from 'openai';
import { writeFileSync } from 'fs';

const openai = new OpenAI({ apiKey: process.env.OPENAI_KEY });

async function summarizeTickets() {
  try {
    const tickets = await fetchOpenTickets();

    const response = await openai.chat.completions.create({
      model: 'gpt-4o-mini',
      messages: [{ role: 'user', content: `Summarize: ${tickets}` }]
    });

    await postToSlack(response.choices[0].message.content);
    writeFileSync('/var/log/ai-runs.json', JSON.stringify({ success: true, time: Date.now() }));
  } catch (err) {
    // 指数バックオフ付きのリトライロジック?
    // 誰かにアラートする?
    // 部分的な失敗はどうする?
    console.error('Failed:', err);
    process.exit(1);
  }
}

summarizeTickets();

そしてcrontabを追加します:

0 * * * * * cd /app && npx ts-node scripts/ticket-summarizer.ts 2>&1 | tee -a /var/log/cron.log

でも待ってください—必要なのは:

  • 指数バックオフ付きのリトライロジック
  • 一時的な失敗と恒久的な失敗を区別するエラーハンドリング
  • ジョブが本当に動いているかを確認する方法
  • 3回連続で失敗したときの監視
  • crontabを編集せずに一時停止する手段
  • 再起動時に消えないログ

あなたがやりたかったのは、AIタスクを毎時間実行することだけでした。なのに、いまインフラを管理しています。

NeuroLinkのやり方:TaskManager

NeuroLink v9.41.0ではTaskManagerが導入されました。これは、SDKに直接組み込まれたスケジュール済みの自己実行型AIタスクです。cron不要。別ワーカー不要。インフラの悩み不要。

import { NeuroLink } from '@juspay/neurolink';

const neurolink = new NeuroLink();

返却形式: {"translated": "翻訳されたHTML"}// それだけです。関数を1回呼び出すだけ。
const task = await neurolink.tasks.create({
  name: 'ticket-summarizer',
  prompt: 'これらのサポートチケットを要約し、緊急の問題を強調してください',
  schedule: {
    type: 'cron',
    expression: '0 * * * *', // 毎時間
    timezone: 'America/New_York'
  },
  mode: 'isolated',
  provider: 'openai',
  model: 'gpt-4o-mini', // 要約用の低コストモデル

  // 組み込みのリトライ(再試行)ロジック
  retry: {
    maxAttempts: 3,
    backoffMs: [30000, 60000, 300000] // 30秒、1分、5分
  },

  // 結果のコールバック
  onSuccess: (result) => {
    if (result.output?.includes('URGENT')) {
      sendPagerDutyAlert(result.output);
    }
  },
  onError: (err) => {
    console.error(`Run ${err.runId} failed:`, err.error);
  }
});

console.log(`Task scheduled: ${task.id}`);
// タスクは自動的に実行されます。プロセスは稼働したままになります。

それだけです。タスク:

  • ✅ 毎時間自動で実行されます
  • ✅ 一時的な失敗(レート制限、タイムアウト)に対して再試行します
  • ✅ 実行のたびにタイムスタンプ付きでログを残します
  • ✅ プロセスの再起動にも耐えます(Redisバックエンドを使用)
  • ✅ API経由で一時停止、再開、削除ができます
  • ✅ 結果をコールバックに送信します

Three Practical Examples

ここでは、実際に動く自己実行型AIタスクを3つ作りましょう。

1. 時間ごとのサポートチケット要約

import { NeuroLink } from '@juspay/neurolink';

const neurolink = new NeuroLink({
  tasks: {
    backend: 'bullmq', // 本番用:Redisで支えられています
    redis: { url: process.env.REDIS_URL }
  }
});

const ticketTask = await neurolink.tasks.create({
  name: 'hourly-ticket-summary',
  prompt: `過去1時間のオープンなサポートチケットを取得します。
要約してください:
- 合計数と優先度の内訳
- よくあるテーマと繰り返し発生する問題
- すぐにエスカレーションが必要なチケット

Slackでそのまま使えるメッセージとしてフォーマットしてください。`,

  schedule: {
    type: 'cron',
    expression: '0 * * * *', // 毎時の頭に実行
    timezone: 'America/New_York'
  },mode: 'isolated', // 各実行ごとに新しいコンテキスト
  provider: 'openai',
  model: 'gpt-4o-mini', // 要約のための費用対効果が高い

  // ツールを有効にして、AIがチケットを自分で取得できるようにする
  tools: true,

  onSuccess: async (result) => {
    await fetch(process.env.SLACK_WEBHOOK, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({
        text: ` チケット要約
${result.output}`
      })
    });
  }
});

なぜこれが機能するのか:

  • isolated モードは、各実行が独立していることを意味します。つまり、コンテキストの混入(汚染)がありません
  • gpt-4o-mini は、高頻度の実行に対してコストを低く抑えます
  • ツールを有効化しているため、AIがあなたのチケットAPIを直接呼び出せます

2. デイリー・コードレビュー・ボット(継続モード)

ここからが面白いところです。継続モードでは、AIが過去の実行を記憶できます。

const codeReviewBot = await neurolink.tasks.create({
  name: 'daily-code-review',
  prompt: `昨日マージされたPRをレビューしてください。各PRについて:
1. 潜在的なバグやセキュリティ上の問題を特定する
2. 追加されていないテストやドキュメントがないか確認する
3. PR間でのパターンをメモする(例:繰り返されるミス)

過去のレビューと比較してください。同じ問題が再発していますか?`,

  schedule: {
    type: 'cron',
    expression: '0 9 * * 1-5' // 平日の9:00
  },

  mode: 'continuation', //  AIが昨日のレビューを記憶する
  provider: 'anthropic',
  model: 'claude-sonnet-4-6',

  maxRuns: 30, // 1か月後に自動停止

  onSuccess: (result) => {
    // チームの #code-reviews チャンネルに投稿する
    postToSlack(result.output);

    // 致命的な問題が見つかった場合はエスカレーション
    if (result.output?.includes('CRITICAL')) {
      notifyTechLead(result.output);
    }
  }
});

継続モードはどのように機能するか:

  • 実行1:「3つのPRが見えます。PR #42 には、潜在的なヌルポインタ問題があります。」
  • 実行2:「PR #45 もヌルポインタ問題があります。今週2回目です。optional chaining に関するチーム研修をおすすめします。」
  • 実行3:「今日はヌルポインタ問題はありません。前回までのパターンは解消されたようです。」

あなた側でDBコードを書くことなく、AIが時間の経過とともに理解を積み上げます。

3. 週次レポートジェネレーター

const weeklyReport = await neurolink.tasks.create({
  name: 'weekly-executive-summary',
  prompt: `リーダーシップ向けのエグゼクティブサマリーを作成してください:

含める指標:
- APIの稼働率とエラー率(モニタリングから取得)
- サポートチケットの件数と解決までの時間
- デプロイ頻度とインシデント数
- 主要な顧客フィードバックのテーマ

フォーマット:
- 1ページのエグゼクティブサマリー
- 補足データ付きで3つの重要な洞察
- 来週に向けた2つの提案`,

返却形式: {"translated": "翻訳されたHTML"}schedule: {
    type: 'cron',
    expression: '0 8 * * 1' // 毎週月曜の8 AM
  },

  mode: 'isolated',
  provider: 'openai',
  model: 'gpt-4o', // 役員向けレポートの品質を向上

  timeout: 300_000, // 5分(レポート生成には時間がかかります)

  onSuccess: async (result) => {
    // Notionに保存
    await saveToNotion({
      title: `Weekly Report - ${new Date().toISOString()}`,
      content: result.output
    });

    // 経営陣にメール送信
    await sendEmail({
      to: 'exec-team@company.com',
      subject: 'Weekly Executive Summary',
      body: result.output
    });
  },

  onError: (err) => {
    // レポート生成に失敗した場合にアラート
    if (!err.willRetry) {
      pagerDuty.trigger({
        severity: 'warning',
        summary: 'Weekly report failed after all retries'
      });
    }
  }
});

TaskManagerの機能を理解する

実行モード

モード 挙動 向いている用途
isolated 実行のたびに新しいAIコンテキスト 単発タスク、モニタリング、レポート
continuation 前回の実行内容をAIが保持 トレンド分析、段階的なワークフロー

スケジュールの種類

// Cron(最も柔軟)
schedule: { type: 'cron', expression: '0 9 * * 1-5', timezone: 'America/New_York' }

// Interval(シンプル)
schedule: { type: 'interval', every: 5 * 60 * 1000 } // 5分ごと

// ワンショット(特定の時刻に1回実行)
schedule: { type: 'once', at: '2026-04-01T14:00:00Z' }

内蔵のリトライ(再試行)ロジック

TaskManagerはエラーを自動的に分類します:

一時的(再試行されます):

  • レート制限が超過しました
  • ネットワークのタイムアウト
  • 5xxのサーバーエラー

恒久的(タスク失敗):

  • 無効なAPIキー
  • モデルが見つかりません
  • 不正な設定

デフォルトのリトライ:指数バックオフ付きで3回(30秒 → 1分 → 5分)

タスクの管理

// すべてのタスクを一覧表示
const tasks = await neurolink.tasks.list();
const active = await neurolink.tasks.list({ status: 'active' });

// スケジュール外で今すぐ実行
await neurolink.tasks.run('task_abc123');

返却形式: {"translated": "翻訳されたHTML"}// 一時停止して再開
await neurolink.tasks.pause('task_abc123');
await neurolink.tasks.resume('task_abc123');

// 更新
await neurolink.tasks.update(' task_abc123', {
  prompt: '新しいプロンプトのテキスト',
  schedule: { type: 'interval', every: 10 * 60 * 1000 }
});

// 削除
await neurolink.tasks.delete(' task_abc123');

// 実行履歴の表示
const runs = await neurolink.tasks.runs(' task_abc123', { limit: 20 });

本番環境での考慮事項

バックエンドを選ぶ

BullMQ(Redis)— 本番環境

const neurolink = new NeuroLink({
  tasks: {
    backend: 'bullmq',
    redis: { url: process.env.REDIS_URL },
    maxConcurrentRuns: 5 // 同時実行数を制限
  }
});
  • ✅ 再起動に耐える
  • ✅ 複数プロセスで安全
  • ✅ ファイルI/Oなし(コンテナ向け)

NodeTimeout — 開発

const neurolink = new NeuroLink({
  tasks: { backend: 'node-timeout' } // 依存関係なし
});
  • ✅ Redis不要
  • ✅ 人が読めるJSONファイル
  • ⚠️ 再起動時にタイマーは失われる(タスクはディスクから自動的に再スケジュールされる)

監視

// イベントを購読する
neurolink.on('task:started', (task, runId) => {
  metrics.increment('task.started', { task: task.name });
});

neurolink.on('task:completed', (result) => {
  metrics.timing('task.duration', result.durationMs);
  console.log(`✅ ${result.taskId}: ${result.output?.slice(0, 100)}`);
});neurolink.on('task:failed', (error) => {
  metrics.increment('task.failed', { task: error.taskId });
  console.error(`❌ ${error.taskId}: ${error.error}`);
});

デーモンとして実行

# PM2 を使用
pm2 start src/tasks.ts --name ai-tasks

# systemd を使用
# (完全な systemd ユニットファイルについては NeuroLink ドキュメントを参照)

# もしくは Node プロセスを単に動かし続ける
npx ts-node src/tasks.ts

CLI の代替手段

コードを書きたくないですか? CLI を使いましょう:

# タスクを作成
neurolink task create \
  --name "hourly-ticket-summary" \
  --prompt "Summarize open support tickets" \
  --cron "0 * * * *" \
  --provider openai \
  --model gpt-4o-mini

# タスクを管理
neurolink task list
neurolink task pause <task-id>
neurolink task resume <task-id>
neurolink task logs <task-id> --limit 50

何が違うのか?

機能 従来の Cron + スクリプト NeuroLink TaskManager
セットアップ 5 つ以上のファイル、インフラ SDK の呼び出し 1 回
リトライ 自分で書く バックオフ付きで組み込み
エラーハンドリング 手動の分類 一時エラー vs 恒久エラーを自動判定
監視 カスタムログ イベント + 実行履歴
コンテキストのメモリ データベース + コード mode: 'continuation'
スケーリング より多くのインフラ Redis バックエンド
AI が自己スケジュールできる 不可能 組み込みのツール

はじめに

# インストール
npm install @juspay/neurolink

# セットアップウィザードを実行
npx @juspay/neurolink setup

# 最初のスケジュール済みタスクを作成
import { NeuroLink } from '@juspay/neurolink';

const neurolink = new NeuroLink();

await neurolink.tasks.create({
  name: 'hello-world',
  prompt: 'Say hello and mention what time it is',
  schedule: { type: 'interval', every: 60000 },
  onSuccess: (r) => console.log(r.output)
});

// プロセスを生かし続ける
setInterval(() => {}, 1000);

結論

AI タスクをスケジュール実行するために、cron ジョブ、ワーカキュー、そしてカスタムのリトライロジックは不要です。TaskManager は次を提供します:

  • Cron、interval、ワンショットのスケジューリング
  • 指数バックオフ付きの組み込みリトライ
  • 分離または継続(continuation)実行モード
  • Redis またはファイルベースの永続化
  • 監視のためのイベントとコールバック
  • CLI および SDK インターフェース

すべてを 1 つの TypeScript SDK に集約。

インフラ管理をやめましょう。自走する AI の構築を始めましょう。

→ GitHub | → ドキュメント | → npm

NeuroLink は Juspay の TypeScript-first AI SDK—13 のプロバイダー、100+ のモデル、一つに統一された API。