run 関数を記述します。それ以外のすべて、つまりwireの型チェック、ランタイム検証、挙動を固定するcontent digestは、自動的に導出されます。
Toolのコントラクト
Tool は、ToolContract(そのインターフェース)と run 実装を組み合わせます。run は (input, ctx) の組に対して純粋です。すべての効果は ctx に注入されたcapabilityを通じて流れ、ambientな経路は使いません。そして contract.output に一致する値を返します。
ToolCtx
Riderはrun に ToolCtx を渡します。重要なのは、何が任意なのかです。net、fs、browser、secrets は、コントラクトが対応するcapabilityを宣言した場合にのみ存在します。常に存在するメンバーである clock、random、env、idempotencyKey、logger、signal は、すべてのToolが使用できる決定論的な基盤です。
2つの不変条件
コントラクトが持つ2つの性質によって、Toolは合成可能かつ再現可能になります。Capability-gated
capabilityは、コントラクトがそれを宣言した場合にのみ
ctx 上に存在します。transform Toolは文字通りネットワークへ到達できません。呼び出すべき ctx.net が存在しないためです。これは原則 T2 であり、構築上強制されます。型付きI/O
contract.inputs と contract.output はスキーマです。コンパイラはオーサリング時にすべてのwireを型チェックし、Riderは実行境界で検証します。transform 手順には ctx.net がないため、純粋で自由に再実行できます。一方で、browser Toolの search-price@1.2.0 と、シークレットなwebhookへの条件付き http.post はcapabilityを保持し、その効果が記録されます。
Toolはファクトリである
Toolはファクトリとして作成されます。createHttpGet(): Tool のような関数がオブジェクトを返します。クラスはなく、共有される可変状態もありません。これによりToolは、どこでも同一に、安全にインスタンス化して実行でき、呼び出し間で何も漏れません。
slugifyの例
コントラクトは十分に小さいため、有用なToolも数行で書けます。capabilityを宣言しない、純粋なtransform Toolの例です。
defineToolはコントラクトを導出する
defineTool は、Zodスキーマから ToolContract を導出します。これには InputDecl[] と、コントラクトのcontent digestが含まれるため、データ型とコントラクトがドリフトすることはありません。これがこのプロジェクトの標準スタイルです。データ型はZodで表し、静的型は z.infer で復元します。スキーマは一度だけ書きます。run に渡される型付き入力、境界での検証、そしてToolのアイデンティティを固定するコントラクトは、すべて同じソースから得られます。
具体的には、上記の inputs マップは、システムの他の部分が推論対象にする宣言済みスロットの配列になります。
Toolの種類
すべてのコントラクトは1つのkind を宣言します。kindは、コンパイラとRiderに対してToolの形を伝えます。たとえば、デフォルトのリトライポリシーはkindから導出されます。
| Kind | 内容 |
|---|---|
browser | 決定論的なPlaywrightオートメーション。一度記録され、モデルなしでリプレイされます。中心的なkindです。 |
http | HTTPリクエストTool(net、さらにシークレット入力がバインドされている場合は secrets)。 |
shell | コマンド呼び出し(spawn、必要に応じて fs)。 |
transform | 純粋なJSON-to-JSON関数。capabilityはありません。 |
code | capabilityによってサンドボックス化されたescape hatchです。 |
model | 宣言され、記録されるLLM呼び出し。オーサリング時の利便性であり、ランタイム上の判断ではありません。 |
composite | Toolとして再公開されたFlow。Flowをネストする仕組みです。 |
search-price@1.2.0 の記録とリプレイについては ブラウザーTool を参照してください。
コントラクトdigest
Toolのコントラクトdigestは、そのinputs、output、または capabilities が変わるたびに変化します。Flowは各Tool依存関係を name@version refと解決済みの contract_digest の両方で固定するため、Flowのアイデンティティは単なるバージョンラベルではなく、Toolの挙動を閉じ込めます。
slugify を 1.0.0 から 1.0.1 に上げながら、こっそり output スキーマを変えた場合、コントラクトdigestは、それを要求するどのFlowにも固定されているdigestと一致しなくなります。その不一致は、ランタイムではなくコンパイル時に表面化します。
capabilityが条件付きで現れるのはいつですか?
capabilityが条件付きで現れるのはいつですか?
一部の標準Toolは、効果を持つ入力がそれを要求する場合にのみcapabilityを宣言します。
std/http は、シークレットを含む入力(認証情報付きURL、authヘッダー、トークン本文)がバインドされたときだけ、正確に secrets を追加します。これにより、http.post に流れ込む secret. 参照は、secret_leak を発生させるのではなく境界ルールを満たします。シークレット入力がバインドされていない場合、http は net のみです。自分のToolも同じ原則に従います。Toolが実際に使う場合にcapabilityを宣言してください。シークレット入力はどのように自分のToolへ届きますか?
シークレット入力はどのように自分のToolへ届きますか?
secret: true と宣言されたスロットは、FlowSpec、Journal、ログ行、公開済みPackのいずれにも現れません。secrets capabilityを持つToolが、ctx に注入された secrets providerを通じて解決する瞬間まで、これは参照として運ばれます。その解決を行うToolは効果を記録する層でもあるため、何かが書き込まれる前にシークレットをredactする層でもあります。シークレット — 境界 を参照してください。Flow全体をToolとしてネストできますか?
Flow全体をToolとしてネストできますか?
はい。それが
composite kindです。composite Toolは内側のFlowからコントラクトを継承します。宣言される capabilities は、内側のFlowが呼び出すすべてのToolのcapabilityの和集合です(ネストは効果を隠しません)。また、外側のcompositeにある secret: true 入力は、参照として内側のFlowの対応するシークレット入力へ渡されるため、どのネスト深度でも境界は保たれます。関連
スキーマリファレンス
完全な
ToolContract、InputDecl、Require スキーマを1か所にまとめています。標準Pack
first-partyのリファレンスToolです。
std/http、std/transform、std/browser などがあります。Pack — デプロイ可能なSDK
Toolを、バージョン付きのcontent-addressed artifactにバンドルして公開します。