例で見る
例は七つあります。五つには、それぞれ Temporal 版、AWS 版、pydantic-graph 版の三つがあります。流れはどれも同じで、タスクの呼び出し方と、外からの通知の受け取り方だけを、プラットフォームのやり方に合わせています。dandori の主なプラットフォームは Temporal なので、最初に読むなら Temporal 版がおすすめです。残りの二つ、請求と支払いは一つだけ書いてあり、どのプラットフォームでもそのまま動きます。Temporal 版と請求と支払いは dandori doc で図にもしてあり、シナリオごとに実行の通るところが光るページで見られます(ワークフローを図にする)。
どの版にも、同じディレクトリに日本語版(<名前>.ja.flow。引当と発送の子は arrange_delivery.ja.flow)があります。API の記述が決めている名前(Stripe のフィールドと状態、倉庫の .proto、SNS と SQS の API)のほかは、すべて日本語の名前で書いています。引当と発送の日本語版は、JSON での名前を日本語にした別のサービス(fulfillment.ja.proto)を実装します。支払いの日本語版が呼ぶ銀行も、JSON での名前を日本語にした payout.ja.proto です。日本語の規則は、各例の rules/ に英語の規則と並べて置いてあります。このサイトの図は、日本語版から描いています。
| 例 | Temporal 版 | AWS 版(Step Functions、Lambda durable functions) | pydantic-graph 版 | 図 |
|---|---|---|---|---|
| ホテルの予約。カードに与信を取り、チェックアウトの日に確定する。Stripe の OpenAPI の記述と照らし合わせる | temporal・日本語 | aws・日本語 | pydantic-graph・日本語 | ページ |
| 倉庫のシステムの注文。催促し、出荷し、届ける | temporal・日本語 | aws・日本語 | pydantic-graph・日本語 | ページ |
注文の明細を並べて引き当て、梱包し、配達する。.proto のサービスを実装するワークフローで(サービスを実装する)、倉庫は Connect で呼び、レスポンスの型はその .proto から作り、配達はすべてのプラットフォーム向けに一度だけ書いた子の .flow(arrange_delivery・日本語) |
temporal・日本語 | aws・日本語 | pydantic-graph・日本語 | ページ |
| 問い合わせの種類を Jev が選び(Jev が確信を持てないときはエージェントが読んだ種類を使う)、規則が振り分け、エージェントが返事の下書きを書く | temporal・日本語 | aws・日本語 | pydantic-graph・日本語 | ページ |
| 申し込みの審査。Jev が採点し、規則がその確信度を量って、足りなければ人の承認に回す。Argo Workflows 版・日本語もある | temporal・日本語 | aws・日本語(Lambda durable functions) | pydantic-graph・日本語 | ページ |
| 請求。注文の品を支払期日まで在庫から押さえ、支払われたら出荷し、支払われなければ戻す。在庫は chobo の帳簿、期日は koyomi の日付(日付と帳簿) | invoice・日本語。どのプラットフォームでも同じもの | 同じ | 同じ | ページ |
売り手への支払い。銀行の API が口座に ID で指して送金し、モデルが知らせを下書きする。銀行の .proto は口座の番号と名義に秘密の印を付けていて、フローはどちらも運ばないので、秘密の値の検査は何も言わない |
payout・日本語。どのプラットフォームでも同じもの | 同じ | 同じ | ページ |
版ごとの違い
Temporal 版は、いちばん多くの機能を使っています。HTTP の API への呼び出しは生成したアクティビティが受け持ち(認証は Transport で付けるので、connection は要りません)、それ以外は自分で書くアクティビティです。ワークフローの外からの通知は event で受け取ります。通知は、ワークフローの ID を宛先にして送られてきます(ホテルでは Stripe の Webhook、注文では運送会社から)。応答が後から返ってくる呼び出しは callback のままで、応答は生成したクライアントから Update で送ります。ホテルと注文は、ワークフローがキャンセルされると、押さえていたものを手放します(on cancel)。引当と発送、審査は、ほかのタスクキューに仕事を送ります。引当と発送は、子の .flow が翌日便の車を取れなかったとき(子の fail NoVan。タスクはこのエラーを宣言しています)、通常便で頼み直します。問い合わせは規則をローカルアクティビティとして呼び、注文の催促のループは、履歴が伸びると新しい実行に引き継ぎます。Jev は、審査では専用のタスクキューのワーカーから、問い合わせではワークフローのワーカーから呼びます。
AWS 版では、タスクが呼ぶのは Lambda 関数、EventBridge の接続を通した HTTP の API、SNS と SQS で、コールバックにはタスクトークンを渡します。注文は、急ぎの規則を Connect のサービスとして、これも接続を通して呼びます。ほかの版は、規則のコードをワークフローと一緒に出します(規則をサービスとして呼ぶ)。Step Functions でも Lambda durable functions でも、同じものが動きます。ほかのプラットフォーム向けに生成したコードも同じ呼び出しをするので、AWS 版は五つのプラットフォームすべてに向けてビルドできます。Jev は HTTP Task で呼び、TypeSafe の API キーは接続に置きます。例外は審査です。承認を求めることとお知らせは自分で書くコードなので、Step Functions では動かせず、AWS では Lambda durable functions だけで動きます。
Argo Workflows 版の審査では、承認を求めることとお知らせは自分のイメージのコンテナで動き(image)、採点と規則は、生成した caller のイメージで動きます。ほかの例は、AWS 版がそのまま Argo でも動きます。そこでも、生成した caller のイメージが呼び出しを受け持ちます。
pydantic-graph 版では、グラフは入力を受け取った Python のプロセスの中で動きます。HTTP の API、エージェント、Jev への呼び出しは生成した関数が受け持ち、規則も同じプロセスの中で動きます。それ以外は自分で書く関数です。コールバックへの応答も、同じプロセスの中で返します(Deps.callbacks)。待っているあいだも、プロセスは生きていなければなりません。プロセスが落ちれば、その実行も失われます。
各例のディレクトリのすぐ下には、それぞれの版のディレクトリと並べて、どのプラットフォームでもそのまま動くフローだけを置いています。引当と発送の子がそれで、呼び出しはどれも HTTP なので、dandori がどのプラットフォーム向けにも呼び出しのコードを生成できます。規則(rules/)と API の記述(specs/)は、版のあいだで共有しています。請求には版がありません。呼び出しは HTTP の API と日付と帳簿の操作で、dandori がどのプラットフォーム向けにも書けるので、一つのフローを、日付のファイル、そのカレンダー、帳簿(dates/、calendars/、books/)と一緒にディレクトリに置いています。
テスト用のフロー
tests/flows のフローは、言語の細かいところまで試すためのものです。json に入れたリスト、並列の中の並列、あらゆる型の応答を返すエージェント、あらゆる種類の質問と確信度を扱う Jev、タイムアウトする呼び出し、ローカルの規則、Connect のゼロ値、イベント、ゼロ値を省いたリクエストを受け取るサービスの実装、日付と帳簿(一つのファイルの二つの日付をローカルアクティビティで呼ぶ、now、すぐに確定する振替、押さえた分の一部の確定)などを扱います。名前はわざと日本語で書いてあり、ASCII でない名前が、五つのプラットフォームで識別子やキー、URL のパスとしてそのまま通ることを確かめています。