camunda-feel について
Camunda 8 開発者向けに、BPMN、DMN、フォームで使う FEEL 式の作成、デバッグ、評価方法を案内します。クラスターエンジンとローカルエンジンの違いも扱います。
主な機能
- 01FEEL 式の作成と確認
- 02c8ctl による評価
- 03変数コンテキストでのテスト
- 04BPMN、DMN、フォームへの対応
- 05クラスターとローカルのエンジン比較
活用シーン
- 01BPMN のゲートウェイ条件とマッピングの検証
- 02DMN の判定式のデバッグ
- 03フォームの検証や条件付き表示の確認
- 04変数、型変換、日付の問題の調査
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ camunda/ skills --skill camunda-feelインストール後、Claude Code で/camunda-feelを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
portability.jsonreferences/common-patterns.mdfunction-reference.md詳細
Camunda FEEL Expressions
Camunda 8 の BPMN プロセス、DMN の意思決定、フォームで使用される FEEL 式を作成、デバッグ、評価します。
前提条件
- c8ctl CLI がインストールされ、設定されていること(
c8ctl add profile)—c8ctl feel evaluateを提供します - デフォルトのクラスターエンジンによる評価には Camunda 8.9 以降のクラスターが必要です(
POST /v2/expression/evaluationを使用します)
相互参照
- camunda-bpmn:FEEL 式が BPMN の条件または I/O マッピングに含まれる場合に使用します
- camunda-dmn:FEEL が DMN 内の入力式、入力エントリー(一項テスト)、出力エントリー、リテラル式に含まれる場合に使用します
- camunda-forms:FEEL 式でフォームの検証または条件付き表示を制御する場合に使用します
- camunda-ai-agents:FEEL 式がプロンプト、
fromAi()のパラメーター宣言、または AI エージェントプロセスにおけるtoolCallResultの整形に使われる場合に使用します
手順
Camunda における FEEL
FEEL は次の用途で使用されます。
- ゲートウェイ条件:変数の値に基づいてプロセスフローを分岐します
- 入力/出力マッピング:プロセススコープ間で変数を変換します
- タイマー定義:期間、日付、繰り返しを定義します
- DMN 意思決定テーブル:入力/出力ルールを定義します
- フォーム検証:ユーザー入力を検証します
BPMN XML のすべての FEEL 式には、先頭に = を付ける必要があります。
<bpmn:conditionExpression xsi:type="bpmn:tFormalExpression">=amount > 1000</bpmn:conditionExpression>
式の評価
FEEL 式を検証してデバッグするには、c8ctl feel evaluate を使用します。デフォルトでは、設定済みクラスターの Scala FEEL エンジンに対して実行されます。これは Zeebe が実行時に使用するエンジンと同じなので、結果は本番環境の動作と正確に一致します。
# Simple expression
c8ctl feel evaluate '1 + 2'
# Expression with individual variables (leading = optional)
c8ctl feel evaluate '=amount * 1.15' --var amount=100
# Multiple variables
c8ctl feel evaluate 'a + b' --var a=1 --var b=2
# JSON values for complex types
c8ctl feel evaluate 'sum(items)' --var 'items=[1,2,3]'
# Bulk variables as a single JSON object
c8ctl feel evaluate 'orderTotal > 1000 and customer.tier = "premium"' \
--vars '{"orderTotal": 1500, "customer": {"tier": "premium"}}'
# Dot-path nesting on the CLI
c8ctl feel evaluate 'customer.name' --var customer.name=Alice
デバッグのワークフロー:
- 式を記述します
- 想定される変数コンテキストを特定します
c8ctl feel evaluateを使ってクラスターエンジンに対して評価し、検証します- 評価に失敗した場合は、エラーメッセージに基づいて修正し、再試行します
オフライン評価(--engine local)
c8ctl feel evaluate --engine local は、feelin JavaScript エンジンを使って式をローカルで評価します。クラスターを利用できない場合に便利です。明示的に依頼された場合、またはクラスターに接続できず、かつユーザーがフォールバックを確認した場合にのみ使用してください。 確認なしにフォールバックしないでください。
feelin の動作は、本番環境で Zeebe が実行する Scala FEEL エンジンとは異なります。型の強制、関数のサポート、日付/時刻の処理にわずかな違いがあるため、ローカルで成功した式がクラスターで失敗する(またはその逆)ことがあります。--engine local で得た結果を利用する前に、必ずクラスターに対して再検証してください。
c8ctl feel evaluate '=amount * 1.15' --var amount=100 --engine local
具体的な差異:today() は異なる型を返します。 クラスターエンジンでは、today() は date を返します(例:2026-05-12)。--engine local(feelin)では、ローカルタイムゾーンの午前0時を示す日時を返します(例:2026-05-12T00:00:00.000+02:00)。これにより、後続の比較で問題が発生します。
# cluster engine — passes
c8ctl feel evaluate 'today() = date("2026-05-12")' # → true
# local engine — fails silently
c8ctl feel evaluate 'today() = date("2026-05-12")' --engine local # → false
後続の関数で date 型の引数が必要な場合、ローカルの結果は型エラーを引き起こすこともありますが、そのエラーはクラスターでは発生しません。
基本構文
データ型:数値(1、1.5)、文字列("hello")、ブール値(true/false)、null、日付(date("2024-01-15"))、時刻(time("14:30:00"))、日時(date and time("2024-01-15T14:30:00"))、期間(duration("P1D"))、リスト([1, 2, 3])、コンテキスト({name: "Alice"})、範囲([1..10])
演算子:+、-、*、/、**、=、!=、<、>、<=、>=、and、or、not()、between x and y、in
If-Then-Else(すべての if に else が必要です):
if score >= 80 then "A" else if score >= 60 then "B" else "C"
For ループ:
for x in [1, 2, 3] return x * 2
量化子:
every x in items satisfies x.price > 0
some x in items satisfies x.status = "urgent"
リスト操作:
- 1 始まりのインデックス:
list[1]、負のインデックス:list[-1] - フィルター:
items[price > 100] - 射影:
items.nameは各項目から name を抽出します
コンテキスト操作:
- プロパティへのアクセス:
customer.name get value(ctx, "key")、get entries(ctx)context put(ctx, "key", value)、context merge(ctx1, ctx2)
一般的なパターンと例
例 — ゲートウェイ条件:
=orderTotal > 1000 and customer.tier = "premium"
変換を伴う入力マッピング:
="https://api.example.com/users/" + string(userId)
string() ラッパーは必須であり、単なるスタイル上の指定ではありません。FEEL は算術演算で型を自動変換しません。userId が数値の場合、"prefix-" + userId はエラーではなく、Can't add 'N' to '"prefix-"' という警告とともに、暗黙のうちに null と評価されます。ルールの全容とデバッグのヒントについては、references/common-patterns.md § 型変換の落とし穴を参照してください。
結果式(API レスポンスから抽出):
={user: response.body, status: response.statusCode}
エラー式(失敗時に BPMN エラーをスロー):
=if response.statusCode >= 400 then bpmnError("HTTP_ERROR", string(response.statusCode)) else null
タイマー期間(FEEL が必須):
="PT" + string(delayHours) + "H"
null 安全なアクセス:
=if customer != null then customer.name else "Unknown"
トラブルシューティング
- FEEL_RESOLUTION_ERROR — 式で参照されている変数が、評価時に変数コンテキストに存在しません。実行時に想定される変数を指定して
c8ctl feel evaluate '<expr>' --vars '<json>'を実行し、確認してください。 - 式が予期せず
nullを返す — よくある原因:+による連結での型の不一致(上記のstring()のルールを参照);0 始まりのインデックス指定(events[0]ではなくevents[1]を使用);存在しないヘルパーの呼び出し(first()は存在しません);コンテキストのエントリー間のカンマ抜け。c8ctl feel evaluateは、実際の null 結果をテキストモードで<null>と表示し、FEEL の文字列"null"(こちらはnullと表示されます)と区別します。
参考資料
詳細なリファレンス資料については、references/ にある以下のファイルを参照してください。
- function-reference.md — FEEL 組み込み関数の完全な一覧(文字列、数値、リスト、コンテキスト、日付/時刻、ブール値)
- common-patterns.md — 日付の算術演算、リストのフィルタリング、複数エントリーのコンテキストパターン、エージェント型 AI 向けの fromAi()