本文へ移動
AI Dev Lab Japan AI開発ツール、AIコーディングエージェント、M...

Mastra Workflowのsuspend/resumeを本番に入れる前に:snapshot storageと復旧UIの確認ポイント

Mastra Workflowのsuspend/resumeを本番に入れる前に:snapshot storageと復旧UIの確認ポイントの判断ポイントを表す抽象サムネイル

3行まとめ

Visual本番導入で見る3つの要点Mastra Workflowの承認待ちは、停止できるかだけでなく再開と復旧まで見て判断します。
suspend/resume

承認待ちや外部イベント待ちを作り、同じworkflow runへ戻るための入口です。

snapshot storage

どのstepで止まり、どの状態から戻るかを保存する台帳です。

運用確認

runId、resourceId、復旧UI、承認者権限、二重実行防止ログをそろえてから本番へ進めます。

承認ボタンが動くことと、再起動後に安全に戻れることは別の確認項目です。

  • Mastra Workflowのsuspend/resumeは、承認待ちや外部イベント待ちを作れるだけではなく、snapshotを保存して「どこから戻るか」を運用できるかまで見て判断します。
  • 2026年6月15日に@mastra/core 1.42.0@mastra/libsql 1.13.0で最小検証したところ、storageなしの直接workflow実行ではresume時にNo snapshot found for this workflow runで失敗し、LibSQLStore付きのMastraインスタンスに登録するとsuspendedからsuccessへ再開できました。
  • 本番投入前の確認対象は、runIdresourceId、snapshot storage、復旧UI、承認者権限、二重実行防止ログです。承認ボタンだけを作っても、復旧と監査が弱いままだと運用で詰まります。

この記事でわかること

Visual承認付きworkflowの確認順PoCで見落としやすい論点を、本番運用に近い順番で確認します。
  1. 1停止

    suspendで承認待ちのrunを作れるかを確認します。

  2. 2再開

    runIdとstepを指定して同じworkflow runへ戻れるかを確認します。

  3. 3復旧

    サーバー再起動後もsnapshotから再開できるかを確認します。

  4. 4監査

    承認者が見たpayload、承認結果、期限切れ、却下、キャンセルを後から追えるかを確認します。

  5. 5二重実行防止

    同じresumeリクエストが複数回来ても外部APIを一度だけ実行できるかを確認します。

この記事の検証対象は、外部送信前に人間承認を待つworkflowです。

Mastraで人間承認付きのworkflowを作ると、suspend()で一時停止し、後からresume()で続行できるように見えます。PoCではそれだけで十分に見えますが、本番では別の問いが出ます。

  • サーバー再起動後も同じworkflow runを再開できるか
  • どのstepがsuspendedなのかをUIから読めるか
  • 承認者が見たpayloadと、実行されるpayloadが同じか
  • 期限切れ、却下、差し戻し、キャンセルをどう残すか
  • 同じresumeリクエストが二重送信された時に、外部APIを二重実行しないか

この記事では、Mastra公式ドキュメントのsuspend/resume、snapshots、workflow state、workflow state reader、createRun、Run API、libSQL storageを確認したうえで、TypeScriptの最小サンプルを実行しました。対象は外部送信前に人間承認を待つworkflowです。実メール、実DB更新、実GitHub操作は行わず、payloadもダミー値にしています。

関連する人間承認の広い設計は、以前の記事「MCP Elicitationを実装する前に:form・URL・OAuth・human approvalの設計ポイント」も近いです。Mastra固有のAPIより先に、承認対象をどう固定するかを考えたい場合はあわせて読むと整理しやすくなります。

前提知識

Visualsuspend/resumeを支える3層Mastra Workflowの人間承認は、停止処理、snapshot、workflow stateを分けて考えると整理しやすくなります。
suspend/resume

stepの中で停止し、外部入力を受けて同じrunを続行します。

snapshot

step status、output、execution path、suspended metadata、retry attemptsなどを保存します。

workflow state

承認待ちpayload、承認者、承認コメント、実行済みマーカー、外部レスポンスIDを扱う場所を決めます。

snapshotに巨大なpayloadや秘密情報を入れるほど、保存先、閲覧権限、削除ポリシー、監査ログの責務が増えます。

suspend/resumeは「待つ」ためではなく「戻る」ために使う

根拠

Mastraのworkflowでは、stepの中でsuspend()を呼び、外部入力を待ってからresume()できます。ここで重要なのは、停止自体よりも「停止したrunの状態を保存し、後から同じrunへ戻れること」です。

公式のsuspend/resumeドキュメントでは、workflowがsuspendされたときにsnapshotが保存され、step IDを指定してresumeできること、HTTP endpointやevent handler、timerからresumeできることが説明されています。つまり、本番で使うなら、承認UIやジョブキューから「どのrunを再開するか」を特定できなければなりません。

snapshotは実行状態の台帳になる

注意点

Mastraのsnapshotsドキュメントでは、snapshotはworkflow実行状態をserializableに表現したものとして説明されています。step status、output、execution path、suspended metadata、retry attemptsなどを含み、workflow_snapshots tableにrunIdで保存される前提です。

これは便利ですが、同時に設計責務も増えます。snapshotに巨大なpayloadや秘密情報を入れると、保存先、閲覧権限、削除ポリシー、監査ログの問題になります。公式のbest practicesでも、serializability、snapshot sizeの最小化、resume context、monitoring、storage scalingが論点として挙げられています。

workflow stateとstep input/outputは分けて考える

確認項目

Mastraのworkflow stateは、stepのinput/outputとは別に、suspend/resumeのサイクルをまたいで持てる状態です。承認付きworkflowでは、承認待ちpayload、承認者、承認コメント、実行済みマーカー、外部レスポンスIDをどこへ置くかが重要になります。

たとえば「顧客通知ドラフトをレビューして送る」workflowなら、承認UIに表示する通知本文、承認者、承認時刻、送信前のidempotency key、送信後のexternal response IDは、あとで調査できる場所に残す必要があります。単にresumeDataapproved: trueを渡すだけでは、運用ログとして足りません。

結果:storageなしではresumeに失敗し、LibSQL storageありでは再開できた

Visualstorage有無で変わったresume結果同じ承認stepでも、snapshot storageを持つ構成かどうかでresumeの結果が分かれました。
項目内容見方
storageなし最初の実行はsuspendedになりましたが、resume時にNo snapshot found for this workflow runで失敗しました。
LibSQLStoreありMastraインスタンスにworkflowを登録し、file:./mastra-workflow.dbでsuspendedからsuccessへ進みました。

suspendedが返っただけでは復旧可能とは言えません。snapshot storageと復旧経路まで含めて確認します。

ローカル検証で確認したこと

再現条件

2026年6月15日に、ローカルのTypeScript最小サンプルで確認しました。検証用ディレクトリはartifacts/local-verification/langgraph-mastra-human-approval-workflow-state-resumeです。名前にLangGraphが入っていますが、今回の記事ではMastra側の検証結果だけを使います。実行したのは、依存インストール、tsc --noEmit、Mastraサンプル実行の3つです。パッケージの確認値は更新履歴にまとめています。

rtk npm run checktsc --noEmitでPASSしました。npm install時点でlow severityのaudit指摘が2件ありましたが、今回の検証は外部公開アプリではなくローカルサンプルです。本番導入では、自分のプロジェクトでnpm audit、lockfileレビュー、依存更新方針を別途確認してください。

storageなしの失敗ログ

失敗条件

最初に、createWorkflow()で作ったworkflowから直接createRun()し、suspend()後に同じrunでresume()しました。最初の実行はsuspendedになります。

{
  "firstStatus": "suspended",
  "suspended": [
    [
      "approval-step"
    ]
  ],
  "suspendPayload": {
    "approval-step": {
      "reason": "Human approval required before external side effects.",
      "requestId": "REQ-001",
      "details": "Create a GitHub Issue with a sanitized customer request summary."
    }
  }
}

しかし、その後のresume()は次のエラーで失敗しました。

Error: No snapshot found for this workflow run: approval-workflow approval-demo-001

ここが本番設計ではかなり大事です。PoCでsuspendedが返っただけでは、復旧可能とは言えません。snapshotが保存されるstorage、workflowの登録方法、runの再取得方法まで含めて確認しないと、承認UIから戻れないworkflowを作ってしまいます。

LibSQLStore付きの成功ログ

成功条件

次に、@mastra/libsqlLibSQLStoreを使い、Mastraインスタンスへworkflowを登録してから実行しました。storageはローカル検証なのでfile:./mastra-workflow.dbです。

import { Mastra } from "@mastra/core/mastra";
import { createStep, createWorkflow } from "@mastra/core/workflows";
import { LibSQLStore } from "@mastra/libsql";

const mastra = new Mastra({
  storage: new LibSQLStore({
    id: "local-workflow-storage",
    url: "file:./mastra-workflow.db",
  }),
  workflows: {
    approvalWorkflow,
  },
});

const workflow = mastra.getWorkflow("approvalWorkflow");
const run = await workflow.createRun({
  runId: "approval-demo-001",
});

この構成では、同じ承認stepがsuspendedになったあと、resume()successへ進みました。

{
  "resumedStatus": "success",
  "result": {
    "status": "approved",
    "approvalNote": "lead@example.invalid: No secret data in payload."
  }
}

この結果から、本番で見るべきポイントははっきりします。Mastraのsuspend/resumeを使うなら、workflowのコードだけでなく、snapshot storageと復旧経路をセットで設計します。

本番前に決めるべき5つのID

Visual承認台帳で分ける5つのIDrunを再開するID、所属を追うID、承認行為、外部実行、二重実行防止を混ぜずに管理します。
runId

停止したworkflow runを再開するための主キーです。

resourceId

ユーザー、tenant、部署、顧客などの所属識別子です。

approvalId

誰が何を見て承認または却下したかを追う承認行為の識別子です。

actionId

外部API実行や送信処理の単位を表す識別子です。

idempotencyKey

同じ外部実行が重複して走らないようにする識別子です。

DB上の承認台帳とsnapshot storageをrunIdで突き合わせられる形にします。

runIdは再開対象そのもの

評価基準

runIdは、停止したworkflow runを再開するための主キーです。承認UI、通知、管理画面、API endpoint、監査ログのどこからでも同じrunIdへたどれる必要があります。

本番では、ランダムIDを使うだけでなく、次のような台帳を用意します。

フィールド目的
run_idMastra workflow runの識別子
workflow_idどのworkflow定義か
current_step_idどのstepで止まっているか
statussuspendedapprovedrejectedexpiredなど
created_by起票者
assigned_reviewer承認者または承認グループ
expires_at承認期限
last_snapshot_at最後にsnapshotを確認した時刻

runIdをUIだけに埋め込むと、通知メールのリンクが失効したとき、ジョブが再起動したとき、担当者が交代したときに復旧できません。DB上の承認台帳とsnapshot storageを突き合わせられる形にします。

resourceIdはユーザーやtenantを追うために使う

条件

MastraのcreateRunresourceIdを指定できます。公式リファレンスでは、ユーザーやtenantの追跡に使えるものとして扱われています。

マルチテナントSaaSや社内ツールでは、runIdだけでは足りません。承認対象のtenant、部署、顧客、環境を間違えると、別組織のpayloadを承認画面に出す事故につながります。

最低限、次を分けます。

  • runIdはworkflow runの識別子
  • resourceIdはユーザー、tenant、部署、顧客などの所属識別子
  • approvalIdは承認行為そのものの識別子
  • actionIdは外部API実行や送信処理の識別子
  • idempotencyKeyは二重実行を防ぐ識別子

runIdresourceIdを混ぜると、後から監査しづらくなります。特に承認UIを作る場合は、resourceIdに基づくアクセス制御を必ず入れてください。

step idは復旧UIの表示単位になる

表示項目

Mastraのsuspend/resumeでは、resume対象のstepを指定できます。WorkflowStateReaderは、public workflow stateからsuspended steps、resume labels、step payload、step outputを読むためのhelperとして用意されています。

復旧UIでは、ユーザーに単に「承認待ち」と出すだけでは不十分です。少なくとも次を表示できるようにします。

  • workflow名
  • suspended step名
  • stepが待っている入力
  • 承認対象payload
  • 直前stepの出力
  • resume時に渡すschema
  • 期限切れや却下時の遷移先

ここでpayloadを出しすぎると、個人情報や秘密情報の露出につながります。承認UIは便利なdebug画面ではなく、権限付きの業務画面として設計します。

復旧UIに必要な情報

Visual復旧UIで分ける表示・検索・権限承認者が判断する情報と、workflowを正しく続ける情報は分けて扱います。
項目内容見方
表示する依頼者、対象tenant/部署、実行予定の操作、必要最小限の外部API送信先を表示します。
マスクする送信payloadは秘密情報をマスクし、LLMの理由は判断補助として表示します。
表示しないidempotency keyやstorage内部情報は通常の承認画面には出しません。
検索するsuspendedのrun、期限が近いrun、resume失敗が続いているrunを一覧で探せるようにします。
権限を分ける詳細閲覧、承認/却下、期限延長、cancel、restart、timeTravelをロールごとに分けます。

UIに見せたpayloadと実際に実行するpayloadが一致しているかを、再開API側で検証します。

承認者が見る情報と、再開に必要な情報を分ける

境界

承認者が見るべき情報は「判断できる内容」です。再開処理に必要な情報は「workflowを正しく続けるための内容」です。この2つを同じJSONとして雑に保存すると、UIに出してはいけない内部値や、実行に必要な秘密情報が混ざります。

承認UIに出す候補は次の範囲に絞ります。

項目表示方針
依頼者表示する
対象tenant/部署表示する
実行予定の操作表示する
外部APIの送信先必要最小限で表示する
送信payload秘密情報をマスクして表示する
LLMの理由判断補助として表示するが、根拠として過信しない
idempotency key通常は表示しない
storage内部情報表示しない

再開API側では、UIに見せたpayloadと、実際に実行するpayloadが一致しているかを検証します。承認後にLLMへ再推論させると、承認者が見た内容と実行内容がずれる可能性があります。基本は承認対象payloadを固定し、resume後は固定済みpayloadを実行します。

suspended一覧は検索できるようにする

本番運用では、承認待ちrunが増えます。復旧UIは、単一runの詳細だけでなく、一覧検索が必要です。

検索軸は次のように設計します。

  • statusがsuspendedのrun
  • 承認期限が近いrun
  • 特定のreviewerに割り当てられたrun
  • 特定tenantや部署のrun
  • 外部API実行前で止まっているrun
  • resume失敗が続いているrun

Mastraのsnapshotはworkflow実行状態を保存しますが、業務検索に必要な列を全部自動で作ってくれるわけではありません。承認台帳や監査ログテーブルを別に持ち、snapshot storageとrunIdで関連づけるのが現実的です。

cancel、restart、timeTravelを運用手順に含める

MastraのRun APIには、startresumestreamresumeStreamcancelrestarttimeTravelなどがあります。これらは便利ですが、権限を分けて扱うべきです。

たとえば、承認担当者にresume権限は必要でも、restarttimeTravelを許可する必要はないかもしれません。運用者にはcancelが必要でも、一般ユーザーには不要です。

復旧UIを作るときは、ボタンを増やす前にロールごとの権限を決めます。

操作権限の初期値
詳細閲覧起票者、承認者、管理者
承認/却下承認者、管理者
期限延長管理者
cancel管理者または運用担当
restart開発/運用担当だけ
timeTravel原則として検証環境から始める

権限が曖昧な復旧UIは、承認フローを安全にするどころか、危険な再実行ボタンになります。

二重実行を防ぐ設計

Visual外部実行を一度だけにする流れresumeの成功と、外部APIやDB更新が安全に一度だけ走ることは別の問題です。
  1. 1payloadを固定

    承認対象payloadを固定し、request_hashで承認時の内容を確認します。

  2. 2副作用の直前でsuspend

    外部API実行やDB更新の直前で止め、承認後に続行します。

  3. 3resumeDataを検証

    承認、却下、修正を受け取り、runとstepと承認状態を確認します。

  4. 4idempotency keyで実行

    actionIdからidempotencyKeyを作り、再送信や再試行でも同じ処理を重複実行しないようにします。

  5. 5実行済みログを残す

    run_id、step_id、approval_id、external_response_id、result_statusを残します。

却下、cancel、expired、suspendedはそれぞれ別の業務状態として扱います。

resumeできることと安全に実行できることは違う

実行前チェック

resume()が成功しても、外部APIやDB更新が安全に一度だけ実行されるとは限りません。ブラウザの再送信、Webhookの再配信、workerの再起動、ネットワークタイムアウト後の再試行で、同じ処理が複数回走る可能性があります。

外部世界に影響するstepでは、必ずidempotency keyと実行済みログを持ちます。

const actionId = `${workflowRunId}:send-customer-notice`;
const idempotencyKey = `mastra:${actionId}`;

保存するログは、少なくとも次の形にします。

フィールド目的
action_id外部実行単位
run_idworkflow runとの関連
step_id実行step
approval_id承認行為との関連
idempotency_key二重実行防止
request_hash承認対象payloadの固定確認
executed_at実行時刻
external_response_id外部サービス側のID
result_status成功、失敗、再試行待ち

この台帳がないと、resume後に同じメールを2回送ったのか、同じIssueを2回作ったのかを調べられません。

suspendは副作用の直前に置く

人間承認を挟むなら、原則として副作用の直前で止めます。外部APIを呼んだ後にsuspend()しても、承認待ち画面では止まって見えるだけで、外部世界ではすでに実行済みです。

承認付きworkflowの初期形は次の順序にします。

  1. 入力を受け取る
  2. LLMやルールで候補payloadを作る
  3. payloadを固定してhashを取る
  4. suspend()で承認待ちにする
  5. resumeDataで承認、却下、修正を受け取る
  6. 固定済みpayloadとhashを確認する
  7. idempotency keyを使って外部実行する
  8. 外部レスポンスIDを保存する

LLMに再生成させたい場合は、承認前に戻す差し戻しフローとして扱います。承認後にこっそり再生成すると、承認した内容と実行内容が変わります。

却下は失敗ではなく正常な業務結果にできる

Mastraのhuman-in-the-loopドキュメントでは、bail()で拒否を成功終了として扱える流れが説明されています。これは実務では自然です。人間が「この送信はしない」と判断したなら、workflowとしては失敗ではなく、却下という業務結果で終わることがあります。

ただし、却下を成功扱いにする場合も、理由と承認者を残します。

{
  "status": "rejected",
  "reviewer": "lead@example.invalid",
  "reason": "顧客名が本文に含まれていたため差し戻し"
}

失敗、却下、キャンセルを混ぜると、監視も改善も難しくなります。ダッシュボードでは少なくとも次を分けます。

  • failedはシステム失敗
  • rejectedは人間による却下
  • canceledは運用判断による中止
  • expiredは期限切れ
  • suspendedは承認待ち

実務で使うなら

VisualPoCから本番へ進む前の合格条件承認ボタンが動いたことではなく、復旧、権限、監査まで確認できたことを合格条件にします。
項目内容見方
suspend/resume承認対象payloadを保存し、指定runとstepで再開できることを確認します。
storage復旧プロセス再起動後も同じrunを再開できることを確認します。
却下/cancelrejectedとして理由を残し、管理者が安全に中止できることを確認します。
二重実行防止同じresumeが2回来ても外部実行は1回だけになることを確認します。
権限/監査別tenantのrunを読めず、誰が何を見て承認したかを後で説明できることを確認します。
storage方針ローカルPoCはfile、stagingとproductionは永続storageを前提にします。

この表を満たせないうちは、顧客通知、決済、契約、権限変更、GitHub write操作へつなげない方が安全です。

PoCの合格条件を先に書く

合格基準

Mastra Workflowのsuspend/resumeをPoCするときは、「承認ボタンが動いた」では合格にしません。次の項目を合格条件にします。

検証項目合格条件
suspend承認対象payloadが保存され、UIで確認できる
resume指定runとstepで再開できる
storage復旧プロセス再起動後も同じrunを再開できる
却下rejectedとして理由が残る
cancel管理者が安全に中止できる
二重実行防止同じresumeが2回来ても外部実行は1回
権限別tenantのrunを読めない
監査誰が何を見て承認したかを後で説明できる

この表を満たせないうちは、本番の顧客通知、決済、契約、権限変更、GitHub write操作へつなげない方が安全です。

storageはローカルfileから始め、本番では永続ストレージに変える

ローカル検証ではLibSQLStorefile:./mastra-workflow.dbで十分でした。一方、MastraのlibSQL storageリファレンスでは、ローカルfile、:memory:、remote Tursoなどの使い方に加え、serverlessのephemeral filesystemへの注意も説明されています。

本番でfile storageを使うかどうかは、実行環境で判断します。コンテナやserverlessでファイルシステムが揮発するなら、workflow snapshotを失う可能性があります。snapshotを失うと、承認待ちrunを再開できません。

初期方針は次のように分けます。

環境storage方針
ローカルPoCfile:./mastra-workflow.db
CIの短時間検証一時DBでもよいがresume復旧は別検証
staging本番に近い永続storage
productionbackup、監視、容量管理、アクセス制御付きの永続storage

snapshotは「あとで戻るための状態」です。軽いキャッシュのように扱うと、復旧不能な承認待ちが発生します。

WorkflowStateReaderでUIに出す情報を決める

WorkflowStateReaderは、public workflow stateからsuspended stepやpayloadを読むためのhelperです。復旧UIでは、この読み取り結果をそのまま全部出すのではなく、表示用モデルへ変換します。

表示用モデルの例です。

type ApprovalView = {
  runId: string;
  resourceId: string;
  workflowId: string;
  stepId: string;
  title: string;
  maskedPayload: Record<string, unknown>;
  requestedBy: string;
  assignedReviewer: string;
  expiresAt: string;
};

この変換層を置くと、snapshot内部の構造が変わってもUIを守りやすくなります。加えて、秘密情報のマスク、tenant権限、表示項目の監査をこの層で扱えます。

ワークフロー全体の設計をもう少し広く見たい場合は、「AI Agentアプリ開発入門:Workflow・tool calling・guardrails・structured outputの使い分け」も参考になります。Mastraに限らず、workflowとtool callingを混ぜる前に境界を決めるための記事です。

失敗点とハマりどころ

Visual本番レビューで見落としやすい落とし穴suspendedが返るだけでは、承認付きworkflowとして十分とは言えません。
suspendedだけを成功扱いにする

suspend後に別リクエスト、別プロセス、別時間帯からresumeできるかまで確認します。

runIdをURLに載せるだけにする

URLは入口であり、期限切れ、担当者変更、tenant権限、監査ログを支える台帳ではありません。

snapshotに秘密情報を入れすぎる

APIキー、顧客の生データ、private URL、長いLLMコンテキストは影響範囲を大きくします。

resume APIを誰でも叩ける

resume権限は実行権限そのものとして、認証、認可、CSRF、replay対策、hash検証を入れます。

snapshot storageはworkflow runtimeの復旧に使い、業務監査はアプリ側の承認台帳で支えます。

suspendedだけを成功扱いにする

ハマりどころ

今回の検証でいちばん大きな発見は、storageなしでも最初のsuspendedまでは返ったことです。ここだけを見ると「承認待ちは実装できた」と判断してしまいます。

でも、そのあとresume()でsnapshotが見つからず失敗しました。本番のレビューでは、必ず「suspend後に別リクエスト、別プロセス、別時間帯からresumeできるか」まで確認してください。

runIdをURLに載せるだけで台帳を持たない

承認リンクにrunIdを入れるだけでは、期限切れ、担当者変更、tenant権限、監査ログ、再送信防止を扱えません。URLは入口であり、台帳ではありません。

承認台帳には、runの状態、承認対象、承認者、期限、表示payloadのhash、外部実行結果を残します。snapshot storageはworkflow runtimeの復旧に使い、業務監査はアプリ側の台帳で支える、と分けると設計が安定します。

snapshotに秘密情報を入れすぎる

workflowを再開するために便利だからといって、APIキー、顧客の生データ、社内コード、private URL、長いLLMコンテキストをsnapshotに入れるのは危険です。snapshot storageの閲覧権限やバックアップ範囲が広い場合、情報漏えい時の影響が大きくなります。

承認UIに必要な情報はマスクし、再開に必要な秘密値はsecret managerや短命tokenから取得する設計を検討します。snapshotには、可能な限り参照ID、hash、マスク済みpayloadを置きます。

resume APIを誰でも叩ける

resume()はworkflowを先へ進める操作です。外部API実行前で止めているなら、resume権限は実行権限そのものです。

resume endpointには、少なくとも次を入れます。

  • 認証
  • tenant/resourceの認可
  • 承認者ロールの確認
  • CSRFやreplay対策
  • request hashの検証
  • idempotency keyの検証
  • 監査ログ

AIエージェントやMCPの外部操作と同じく、read-onlyから始める発想が役立ちます。MCPまわりの権限設計は「MCP Registryからサーバーを選ぶ前に:OAuth・tool poisoning・allowlistの安全な見方」でも整理しています。

セキュリティ・コスト注意

Visual長期停止runの扱いsnapshot storage、承認台帳、通知ログ、監査ログは、停止期間が長いほど運用負荷になります。
  1. 24時間以内

    通常の承認待ちとして扱います。

  2. 期限超過

    reviewerへ再通知します。

  3. 長期放置

    expiredへ遷移させます。

  4. 顧客影響あり

    管理者レビューへエスカレーションします。

  5. 不要になったrun

    cancelして理由を残します。

料金はLLM呼び出しだけでなく、suspended run数、snapshot平均サイズ、監査ログ量、監視、通知、ジョブキューも分けて見ます。

snapshot storageは監査対象にする

セキュリティ基準

snapshot storageには、workflowの途中状態が入ります。顧客データ、外部API payload、LLM生成物、承認待ちmetadataを含む可能性があります。したがって、通常のアプリDBと同じように、アクセス制御、暗号化、バックアップ、削除ポリシー、監査ログを設計します。

特に注意するのは、開発環境から本番storageへ接続しないこと、stagingのsnapshotに本番データを混ぜないこと、承認UIのdebug表示で秘密情報を出さないことです。

長期停止runは容量と運用負荷になる

承認待ちのrunは、放置すると増えます。snapshot、承認台帳、通知ログ、監査ログが残り続けるため、容量と運用負荷になります。

期限切れポリシーを決めます。

状態推奨する扱い
24時間以内通常の承認待ち
期限超過reviewerへ再通知
長期放置expiredに遷移
顧客影響あり管理者レビューへエスカレーション
不要になったruncancelして理由を残す

期限切れrunをそのまま残すと、後日resumeされて古いpayloadが実行される危険があります。期限を過ぎたrunは、resume前にpayloadの再確認や再作成を要求する設計にします。

料金はLLMだけでなくstorageと監視も見る

Mastraのworkflow自体はアプリケーションコードですが、本番で動かすとstorage、ログ、監視、通知、ジョブキュー、ホスティングのコストが出ます。LLM呼び出しが少なくても、長期停止runや大きなsnapshotが増えればstorageと運用コストが上がります。

コスト見積もりでは、次を別々に見ます。

  • LLM呼び出し回数
  • workflow run数
  • suspended run数
  • snapshot平均サイズ
  • 保持期間
  • 通知回数
  • 監査ログ量
  • 復旧UIの運用担当時間

「AIエージェントだからLLM料金だけ見ればよい」ではありません。承認付きworkflowは、業務システムとしての運用費も持ちます。

更新履歴

Visual今回の確認内容2026年6月15日時点の公式情報とローカル検証に基づく更新です。
公式情報

Mastra公式ドキュメントとnpm情報を確認しました。

検証バージョン

@mastra/core 1.42.0、@mastra/libsql 1.13.0、zod 4.4.3、typescript 6.0.3、tsx 4.22.4で確認しました。

検証結果

storageなしのresume失敗と、LibSQLStore付きMastraインスタンスでのresume成功を確認しました。

利害関係

Mastra、LangChain、OpenAI、GitHubなどからのスポンサー提供や無償提供は受けていません。

公開時点の検証結果として、利用前には自分のプロジェクトの依存関係とstorage構成を確認します。

  • 2026-06-15: Mastra公式ドキュメントとnpm情報を確認。@mastra/core 1.42.0@mastra/libsql 1.13.0zod 4.4.3typescript 6.0.3tsx 4.22.4でローカル検証。storageなしのresume失敗と、LibSQLStore付きMastraインスタンスでのresume成功を確認。
  • 利害関係: この記事はAI Dev Lab Japanの通常検証記事です。Mastra、LangChain、OpenAI、GitHubなどからのスポンサー提供や無償提供は受けていません。

次に読むなら

AI Agentアプリ開発入門

Workflow、tool calling、guardrails、structured outputの全体像から設計したい場合に確認できます。

Mastraのようなagent workflowは、API名よりも運用境界の設計で差が出ます。承認UI、復旧手順、監査ログ、MCP権限設計をチームで見直したい場合は、お問い合わせから相談できます。仕様変更や検証記事の更新を追いたい方は、ニュースレターもどうぞ。

参照した主な情報源

  • Mastra Docs: Suspend and Resume Workflows

https://mastra.ai/docs/workflows/suspend-and-resume

  • Mastra Docs: Snapshots

https://mastra.ai/docs/workflows/snapshots

  • Mastra Docs: Workflow State

https://mastra.ai/docs/workflows/workflow-state

  • Mastra Reference: Workflow State Reader

https://mastra.ai/reference/workflows/workflow-state-reader

  • Mastra Reference: createRun

https://mastra.ai/reference/workflows/workflow-methods/create-run

  • Mastra Reference: Run

https://mastra.ai/reference/workflows/run

  • Mastra Reference: LibSQL Storage

https://mastra.ai/reference/storage/libsql