(input, ctx) に対してpureに実行します。
Packの概要
各Packは、関連するToolをいくつかまとめています。Capabilities 列は、各Toolが自身のctx で要求できるものを示します。capabilityが存在するのは、contractがそれを宣言した場合だけです(原則 T2、構造上強制されます)。? は、効果を持つ入力がそれを要求する場合にだけToolが宣言するcapabilityを示します。
| Pack | Tools | Capabilities | Status |
|---|---|---|---|
std/http | http.get, http.post, http.request, http.paginate | net, secrets? | ✅ 実装済み |
std/transform | transform, jq, template | なし(pure) | ✅ 実装済み |
std/shell | shell.run | spawn, fs? | ✅ 実装済み |
std/fs | fs.read, fs.write, fs.glob | fs | ✅ 実装済み |
std/browser | browser.tool(決定論的リプレイ) | browser, secrets? | ✅ 実装済み。下の注記を参照 |
std/control | noop, assert, delay, gate, uuid | clock?, random? | ✅ 実装済み |
std/model | model.complete | net, secrets | ✅ 実装済み |
std/browser は、完全に実装済みのTool factoryとして browserTool()(Recordingからの決定論的なPlaywrightリプレイ)を提供します。browser.record は @browserflow/browser にあるオーサリング時のキャプチャ関数です。そのライブPlaywright driverはthrowするstubです。現在の決定論的な継ぎ目では、browserflow record --ops <file> で事前に解決済みのoperationsを渡してください。http.post、transform のような裸のkindで参照されます。これは、pinされたstd pack内の互換性がある最新バージョンへ解決されます(references & versioning)。daily-price-watch Flowは、そのうち4つを使います。価格を調べる std/browser Tool、それを整形する std/transform 手順、そしてsecret webhookへ条件付きでpostする std/http です。
身につけておくべき3つのルール
capability表には、これらのPackを相手にオーサリングするとき重要になる3つの挙動が隠れています。どれも原則から直接導かれ、どれもFlowの再現性を保つものです。std/http と secrets? capability
std/http はデフォルトでは net のみです。認証情報付きURL、認証ヘッダー、トークン本文のような、シークレットを含む入力がbindされたそのときだけ secrets を追加します。これが、secret. 参照が secret_leak diagnosticに引っかからず http.post に流れ込める理由です。bindされたシークレット入力が secrets capabilityを宣言し、それによって境界ルールに違反するのではなく、境界ルールを満たします(validation & diagnostics)。
daily-price-watchでは、notify 手順が secret.webhook をURLとしてbindします。
http.post は net のみです。このcapabilityは需要駆動です。設定で secrets を有効化するのではなく、シークレットをbindすることで得ます。
secret. 参照は、secrets capabilityを持つToolの内部でのみ解決されます。参照はFlowSpecとJournalを通って運ばれますが、平文の値は実行時に注入され、どちらにも書き込まれません。シークレット — 境界 を参照してください。std/control の uuid - 新しいidの認可された生成元
std/control の uuid は、新しい識別子を生成する唯一の認可された方法です。seedされた random capabilityから値を引き、その出力をJournalに記録します。そのため、生成されたidはリプレイ時にも安定します。新しい値ではなく、記録済みの値が返されます。
これこそが、expression language に uuid() built-inがない理由です。非決定性が存在してよい場所は、記録されるToolという、ただ1か所だけです。expressionの内部には決して置きません。expressionは小さく、pureで、副作用のないサブ言語です。もし uuid() がexpression関数だったなら、static checkerは、expressionが非決定性を隠していないことをもはや証明できません。
std/model - 意思決定者ではなくeffect
モデル呼び出しは完全に正当なToolですが、std/model の model.complete は他と同じeffectです。model.complete は3つの入力を取ります。endpoint(provider URL)、request(passthrough JSON object。messages、model、paramsなど、providerが期待する任意のbody形状)、そして secret 入力として宣言された任意の authorization 値です。そして、注入された net capability経由で、request を endpoint にPOSTします。
リクエストとレスポンスは記録される
Toolは
fetch を直接呼び出しません。呼び出すのは ctx.net.fetch だけです。Riderがそのcapabilityをwrapするため、http 呼び出しやbrowser extractionと同じように、リクエストとそのレスポンスの両方がJournalに書き込まれます。authorization の値は、他のシークレット入力とまったく同じように、journalingの前にredactされます。すべてのリクエストは冪等性キーを持つ
model.complete は ctx.idempotencyKey から Idempotency-Key ヘッダーを付与します。これはrun id、step id、手順の入力から導出されたdigestです。そのため、providerに対するcompletionがretryされても、二重課金されるのではなくdedupeできます。それ自体がcontrol flowを動かすことはない
モデル出力はデータです。branch predicateや
when: 条件は、記録済みの値に対するpure expressionです。次にどの手順が実行されるかについて、モデルに決定権はありません。request を特定vendorのschemaへ整形せず、返ってきたものをそのまま { status, response } として記録します(response はpassthrough JSONです)。provider固有のrequest/response整形はcallerの責務です。通常は上流の transform 手順で行います。
このPackのスローガンは、AIはpinできるデータソースであり、意思決定者ではない、です。これはオーサリング時のAI支援(authoring with AI at the edges)と一貫しています。モデルはFlowを書いたりToolを記録したりするのを助けることがありますが、compileされpinされた後のFlowは、記録される手順として明示的に model.complete を呼び出さない限り、loop内にモデルを置かずに実行されます。
表の残りを読む
std/transform - なし(pure)
std/transform - なし(pure)
transform、jq、template はcapabilityを宣言しません。これらはnetwork、filesystem、clock、randomnessに到達できません。呼び出せる ctx.net や ctx.fs などは文字通り存在しません。これによってエンジンはtransform手順をpureで再実行可能なものとして扱えます。transform はexpression(with.expr)を持ち、jq はjq programを適用し、template はtemplateをrenderします。いずれも決定論的なJSON-to-JSONまたはJSON-to-stringです。std/shell - spawn, fs?
std/shell - spawn, fs?
shell.run はsubprocessを起動するために spawn を宣言し、入力が読み書き対象のpathにbindされた場合だけ fs を宣言します。すべてのeffectful toolと同じく、観測可能な結果が記録されるため、その手順は再spawnせずにリプレイされます。std/fs - fs
std/fs - fs
fs.read、fs.write、fs.glob は fs を宣言します。読み取りはJournalに記録されるため、Flowはofflineでリプレイできます。path入力は、他の入力と同じように境界で型付けされvalidateされます。std/browser - browser, net
std/browser - browser, net
browser.tool は、記録済みの決定論的なPlaywright sessionをリプレイします。browser.record はdemonstrationからそれを作成します。どちらも browser(および net)を宣言します。このPackには専用ページがあります。下を参照してください。browser automationは主要なTool kindであり、「一度作成する」(record)と「ずっと実行する」(tool)に分かれているためです。std/control - clock?, delay, gate, assert, noop
std/control - clock?, delay, gate, assert, noop
noop は何もしない手順です(downstream referenceを持たない no: branch armは、compile時にdead noopとして省略されます)。assert はinvariantを検査します。delay は待機します。注入されたclockが必要なときに clock を宣言します。gate はguardします。uuid は random から記録済みidを生成します。clock と random の ? は、それらを必要とするToolだけが宣言することを反映しています。関連
カスタムTool
defineTool で独自のToolを作成します。標準Packが実装しているものと同じcontractです。ブラウザーTool
一度記録し、ずっとリプレイする。
std/browser がdemonstrationを決定論的なPlaywrightへ変換する仕組み。シークレット — 境界
secret. 参照が漏洩するのではなく、std/http の境界ルールを満たす理由。ToolとPack
完全なTool contract、versioning、そしてPackがbundleされ複製される仕組み。