


RustのAIフレームワークに、新しいAIプロバイダーやベクトルストアを足す
多くのAIプロバイダーに対応するRustのフレームワークを広げる時に使う。今あるtraitとbuilderに合わせ、WebAssemblyで動く形を保ち、フックがストリーミングの有無で同じ動きをするようにし、記録したテスト用のカセットにAPIキーが入っていないか確かめる。
このAIルールを使うには
## 頼まれた範囲だけを変え、頼まれない限りコミットしない
- コードを変える前に、既存実装、manifest、test、公開documentationを読む。
- すべての変更をユーザーが依頼した範囲内に保つ。
- 並行する抽象化を作らず、既存のtrait、builder、module、error typeを優先する。
- TODO、stub、placeholder behavior、投機的な公開APIを追加しない。
- ユーザーが明示的に依頼しない限り、commit、stage、push、branch作成、pull request作成を行わない。
- 無関係なユーザー変更を維持し、dirty treeを安全に避けて作業する。
- 近くの設計を改善できる場合でも、無関係なrefactorを避ける。
- contributor policyとrepository固有のengineering ruleを区別し、両方に従う。
- 必要なproduct intentまたは外部承認がない時は停止する。
## crateの分け方とfeature flagを、ワークスペース全体でそろえる
- 公開facade crate、core crate、companion integration、derive macro、example、testを既定の役割に保つ。
- facadeはcore APIをre-exportし、任意のcompanion crateをfeature flag経由で公開する。
- 公開feature、integration、module pathを変える前に、workspace manifestとfacade exportを確認する。
- 強いアーキテクチャ上の理由がない限り、providerとvector storeのintegrationはcompanion crateへ置く。
- workspace example、crate単位のexample、integration testを区別する。
- companion crateを追加する時は、必要に応じてroot dependency、feature、facade re-export、example、README、crate documentationを更新する。
- 利用不能なmoduleを露出せずにfeatureの各組み合わせがcompileできることを保証する。
- facadeと影響するcompanion crateの間で公開documentationを同期させる。
- codeとmanifestで確認するまで、module pathやfeatureを説明しない。
## プロバイダー専用の物を作らず、共通のtraitの上に作る
- frameworkの中核となるcompletion、embedding、vector index、呼び出し可能toolのtrait上にintegrationを構築する。
- 既存のprovider横断traitに対するprovider固有の代替を作らない。
- 設定可能な公開typeには確立済みのbuilder patternを使う。
- 構築過程をfluentかつ型安全に保ち、明示的なbuild stepの後にだけ完成させる。
- provider extensionと基盤HTTP transportを分離するgeneric client architectureを使う。
- 未対応behaviorがcompile時に失敗するよう、provider capabilityを明示的に宣言する。
- 存在しないcapabilityはruntimeの推測ではなく、意図的なmarker typeで表す。
- provider builderとパブリックclient aliasの間でAPI key typeの関係を維持する。
- provider固有のrequest typeとresponse typeをconversion boundaryの内側に置く。
## 共有の非同期コードを、WebAssemblyでも動く形に保つ
- 共有trait、future、error、integrationでWebAssembly対応を維持する。
- targetごとに要件が異なる場合は、生の`Send`と`Sync`境界ではなくframeworkのcompatibility abstractionを使う。
- 動的に返すfutureには、指定されたportable boxed-future typeを使う。
- native thread safetyの境界は、対応するtargetにだけ適用する。
- WebAssemblyのboxed errorから、未対応のnative専用境界を外す。
- 共有traitを変えた後は、nativeとWebAssembly向けのtype signatureを両方testする。
- 対応済みtargetを黙って対象外にするdependencyやAPIを追加しない。
- conditional compilationを狭い範囲に保ち、target間の違いを説明する。
- provider、vector store、hookのAPIで同じportability contractを使う。
## はっきりしたエラーの型を使い、新しい公開APIには全部説明を書く
- 新しいfallible APIのerror typeに`String`を使わない。
- 明示的なerror enumを定義し、frameworkのerror helperを通して有用なprovider responseの詳細を維持する。
- error contextを手作業で失う処理より、`?`と意味のあるconversionを優先する。
- 周囲のinvariantから不可能であることが明白な場合に限り、`unwrap()`または`expect()`を使う。
- 新しい公開type、function、trait、methodのすべてにitem documentationを追加する。
- 新しい公開moduleのすべてにmodule documentationを追加する。
- exampleを実在するAPI、model constant、module path、feature flagと一致させる。
- 外部credentialまたはserviceを必要とするexampleは非実行として印を付ける。
- 公開behaviorを変えた時はREADMEとcrate-level documentationを更新する。
## 新しいAIプロバイダーは、一番近い既存のプロバイダーにならって足す
- providerを実装または変更する前に、最も近い既存providerを調べる。
- provider extensionとbuilder type、provider実装、明示的なcapability declarationを追加する。
- frameworkが両方の経路を公開する場合は、environment valueとdirect valueからの構築を支援する。
- パブリックclientとbuilder aliasをprovider builderのAPI key typeと一致させる。
- 明示的なcredential markerまたはauthentication typeを使い、credentialを含むdebug表示を伏せる。
- framework requestをproviderの実際のwire formatへ変換し、responseをframework typeへ戻す。
- providerが実際に対応する場合にだけstreamingを追加し、標準helperを通してprovider errorを維持する。
- 確立済みのtelemetry conventionとextension traitに従う。
- providerの実際のAPIに存在しないrequest fieldやresponse fieldを作らない。
- 対応behaviorを証明する対象を絞ったtestまたはexampleを追加する。
## 本当の絞り込みと、はっきりした検索結果を持つベクトルストアをつなぐ
- core所有が正当化されない限り、vector store integrationをcompanion crateへ置く。
- resultを返すsearchとidentifierだけを返すsearchの両方を実装する。
- backendの実際のfiltering capabilityを反映するfilter typeを使う。
- 場当たり的なstring failureではなく、frameworkのvector store error variantを返す。
- core frameworkが使うものと同じportable async boundaryを適用する。
- score、document、identifier、metadataのconversionを明示する。
- ordering、limit、empty result、backend errorのbehaviorを定義する。
- filter、ranking order、result limit、missing record、conversion failureをtestする。
- secretを埋め込まず、必要なfeature、credential、index、external setupを文書化する。
## フックのイベントごとに、できる操作を決める
- hookをrun単位のlifecycle observerおよびsteererとしてmodel化する。
- run identity、turn、streaming mode、agent identity、共有scratch stateを含むrun contextをすべてのeventへ渡す。
- 未対応のaction組み合わせがcompile時に失敗するよう、eventごとに固有のaction typeを与える。
- completion call hookにはrequestのpatchまたは処理停止を返させる。
- tool call hookとtool result hookには後続hook向けのvalueを書き換えさせ、定義されたskipまたはstopをterminal actionとする。
- invalid tool callの処理を、明示的なfail、retry、repair、skip、stopの選択肢で表す。
- observe-only eventはcontinueまたはstopだけに制限する。
- stop actionがstackをshort-circuitする場合は、steering hookより前にobservation hookを登録する。
- dataを変換するaction、蓄積するaction、処理を終えるactionを文書化する。
## 重ねたフックを、ストリーミングの有無で同じ動きにする
- hookを入れ子またはstack化する時も、event固有のcomposition semanticsを維持する。
- completion request patchを登録順にmergeする。
- extra contextをappendし、additional parameterをshallow mergeし、active tool setの積集合を取る。
- 文書化されたscalar fieldとhistory fieldにだけ「最後の書き手を優先」を適用し、重要な上書きにはwarningを出す。
- 各request patchを1 turnだけに限定し、誤って持続させない。
- tool callとtool resultのrewriteを後続hookすべてへ渡す。
- terminal actionが要求した時はstackの評価を直ちに停止する。
- streaming surfaceとnon-streaming surfaceで同じlifecycle driverを共有する。
- streamingとnon-streamingの両entry pointで同一のhook behaviorをtestする。
- nested stack、merge conflict、rewrite chain、skip、stopのcomposition testを追加する。
## 小さな回帰テストを選び、プロバイダーとのやり取りはカセットで試す
- 信頼できる最小範囲でregression coverageを追加する。
- 独立したconversionまたはlogic defectには、実装の近くに置くunit testを優先する。
- behaviorがproviderのrequestとresponse trafficに依存する場合は、cassette-backed provider testを使う。
- deterministicなcassette replayでbehaviorを表せない場合に限り、ignored live testを使う。
- provider testを追加、更新、record、replayする前にrepositoryのtest guideを読む。
- 通常testがprovider credentialを要求しないよう、既定ではcassetteをreplayする。
- 変更するproviderとbehaviorにtest実行を絞る。
- 両surfaceが存在する場合は、streamingとnon-streamingのbehaviorをcoverする。
- regression assertionで元のfailure modeを維持する。
## カセットは対象のテストだけ記録し、秘密の情報が入っていないか確かめる
- record modeを、外部接続、credential使用、file上書きを伴うoperationとして扱う。
- record前に該当provider credentialを必須とし、表示へ決して露出しない。
- 無関係なfixture churnを最小化するため、対象provider testだけをrecordする。
- cassette diffを提示またはcommitする前に、すべて手作業で調べる。
- API key、bearer credential、cookie、account identifier、その他のsensitive valueを含むfixtureを拒否する。
- noisyなfixtureを受け入れず、無関係なrequestとresponseのchurnを取り除く。
- 自動scrubとsafety checkを有効に保つが、inspectionの代わりとして扱わない。
- secretと不安定なmetadataを伏せながら、deterministic replay dataを維持する。
- behavior changeを説明せず、失敗testをgreenにする目的だけでfixtureを絶対に上書きしない。
## 狭いチェックから広げて回し、確かめられなかった所を報告する
- 有用な最小checkから始め、影響範囲とriskに応じて広げる。
- workspaceをformatし、影響する全targetとfeatureにlintを実行する。
- 広いworkspace testより先にfocused testを実行する。
- 公開APIまたはdocumentを変えた場合は、dependencyを含めずdocumentationをbuildする。
- 共有boundaryまたはfutureを変えた場合は、nativeとWebAssemblyのcompatibilityを検証する。
- provider wire fieldが実際のAPIと一致し、cassette diffにsecretがないことを確認する。
- streamingとnon-streamingのsurfaceでhook semanticsが一致することを確認する。
- 必須checkが失敗している間は完了を主張しない。
- commandを実行できない場合は理由を説明し、未検証事項を正確に示す。
- 変更したsurface、実行したcheck、観測結果、残るrisk、意図的に除外した範囲を報告する。
