


AIエージェントの記憶サービスを、ユーザーごとの記憶を混ぜずに作る
AIエージェントの長期記憶サービスを作る・広げる時に使う。メモリバンクごとに記憶を分け、取り込み・検索・推論を分けたうえで、データベースのマイグレーション、APIクライアントの更新、別のモデルに判定させるテスト、テナントやバンクごとの設定までそろえて進める。
このAIルールを使うには
## 記憶の種類を分け、バンク間でデータを混ぜない
取り込み、検索、推論では保存された知識の種類をそれぞれ異なる形で扱うため、その区別を維持する。
### メモリを明示的にモデル化する
- 世界事実は一般的な知識を表す。
- 経験事実は、ユーザーまたはエージェントに帰属する個人的な出来事を表す。
- メンタルモデルは、複数の事実から統合・合成したパターンをまとめる。
- 各メモリバンクに、背景コンテキストと、懐疑性、字義どおりに解釈する傾向、共感性などの気質特性を1–5の尺度で持たせられるようにする。
- 気質は内省的な推論だけに適用し、事実の想起を変化させない。
### バンクを分離する
- 各バンクを、単一のユーザーまたはエージェントに対応する独立したメモリストアとして扱う。
- 保存、検索、推論、テストのすべてでバンク間の漏えいを防ぐ。
- 各APIリクエストで、厳密に1つのバンクだけを操作する。
- 複数バンクのワークフローは、サーバーの境界を弱めるのではなく、クライアント側でオーケストレーションする。
## 取り込み・検索・推論を分け、複数の検索方法を合わせる
モデル、埋め込み、エンティティ、ランキングについては安定した抽象化を共有しながら、三つの主要操作を分離する。
### 操作の意味を維持する
- Retainはコンテンツを取り込み、事実、エンティティ、関係を抽出する。
- Recallは気質特性を適用せずに既存のメモリを検索する。
- Reflectは、メモリと統合済みメンタルモデルを使って、気質を考慮した推論を行う。
- オーケストレーションを、事実抽出、エンティティ正規化、リンク管理、プロバイダーアダプターから分離しておく。
### 検索をパイプラインとして構築する
- 相互補完的な4つの検索戦略、すなわちセマンティック検索、語彙的BM25検索、グラフ検索、時間検索を並列実行する。
- クロスエンコーダーで再ランキングする前に、相互順位融合で順位付き結果を統合する。
- グラフ展開を検索インターフェースの背後に置き、別の戦略へ交換できる状態を保つ。
- 狭い責務のラッパーを通じて、ローカルおよびリモートのモデル、埋め込み、再ランキングの各プロバイダーをサポートする。
### API契約を同期する
- エンドポイントを変更したら、OpenAPI仕様を再生成する。
- 仕様変更後は、Python、TypeScript、Rustを含む、サポート対象のすべてのクライアントSDKを再生成する。
- APIパラメーターを変更するときは、プロキシルート、リクエスト値の抽出、SDK呼び出し、クライアント型、利用側のインターフェースを一緒に更新する。
## PostgreSQLとOracleの両方で動くマイグレーションを書く
方言間の違いを明示しながら、サポート対象のすべてのデータベースで単一のマイグレーション履歴を使用する。
### リビジョンを定義する
- 各マイグレーションに一意の12文字の十六進リビジョン識別子を付け、`down_revision`で直前のリビジョンを指定する。
- アップグレードとダウングレードの動作を対にして維持する。
- 稼働中の接続から実装を選択する、単一の方言ルーターを通じて処理を振り分ける。
- ディスパッチャーがない場合は、マイグレーション形状テストを失敗させる。
### 方言ごとに実装する
- PostgreSQLとOracle互換データベース向けに、それぞれ個別のアップグレード関数とダウングレード関数を用意する。
- テナントスキーマが設定されている場合、生のPostgreSQL SQLではそのスキーマを明示的に修飾する。
- 特殊なベクトル、テキスト、パーティション操作など、マイグレーションフレームワークで表現できないデータベース形式に限り、ドライバーレベルの実行を使用する。
- マイグレーションが本当に1つの方言だけに適用される場合は、もう一方のスロットを意図的に省略する。見落としで空のまま残してはならない。
- レビューでは方言間のスキーマドリフトを考慮する。
### 実行を検証する
- ベーススキーマとすべてのテナントスキーマに対してマイグレーションを実行する。
- 集中的な検証のため、名前を指定した1つのテナントスキーマだけを対象とする実行にも対応する。
- サービス起動時にマイグレーションを自動実行する場合は、サポート対象のすべての方言とテナント範囲に対して起動をテストする。
## pushの前に、必須のチェックとコードレビューを通す
実装を変更するたびに正規のツールチェーンを使用し、リリースを阻む欠陥と助言的なクリーンアップを区別する。
### 必須チェックを実行する
- PythonまたはTypeScriptを変更したら、フォーマット、lint、型チェックを実行する。
- 未使用のimportと変数、孤立したクライアントファイル、パッケージ一覧にない依存関係を、リリースを阻む失敗として扱う。
- 関数全体が未使用の場合や、コンポーネントライブラリが意図的に広くexportしている場合は、プロジェクト方針で必須に格上げされない限り助言事項として扱う。
- リポジトリの標準コマンドで、ユニットテスト、API統合テスト、関連するベンチマークを実行する。
- 必要に応じてAPI仕様とクライアントを再生成し、コミットされていない生成物の差分が残っていないことを確認する。
### 公開前にレビューする
- 実装後に構造化されたコードレビューを行う。
- テスト不足、デッドコード、型安全性、データ分離、生成ファイルのずれに関するすべての要修正指摘を直す。
- 要修正指摘を解決するまで、コードをpushしたり変更リクエストを作成したりしない。
- 未リリースのchangelogエントリを手動で追加せず、リリースプロセスにバージョン付きエントリを生成させる。
## 決まった処理は直接テストし、モデルの答えは別のモデルに判定させる
動作が機械的に決定されるか、モデルの解釈に依存するかに応じてアサーションを選ぶ。
### 決定的な仕組みを直接テストする
- 件数、必須フィールド、プロンプト組み立て、抑制ロジック、注入された部分文字列は、直接アサーションで検証する。
- モックモデルまたは純粋関数を使い、ほとんどのテストを高速に保つ。
- モデル動作テストには、その前後にある決定的な準備処理を対象とするユニットテストを組み合わせる。
### モデルの意味的な動作を判定する
1. 単一プロバイダーのコアモデルジョブ用にセマンティックテストをマークし、プロバイダーマトリックス用マーカーはプロバイダー横断の受け入れテストにだけ使用する。
2. モックではなく、実際に設定されたパイプラインを呼び出す。
3. 評価に必要なセマンティックラベルを使ってレスポンスを要約する。
4. 提供されたコンテキスト内の明示的な基準をレスポンスが満たすかどうかを、独立した判定モデルに尋ねる。
5. テスト対象と同じモデル呼び出しで判定してはならない。
- 分類、帰属、次元の保持、指示への追従には、判定モデルによるアサーションを使用する。
- モデルや実行ごとに妥当な差異が生じる動作では、文字列やenum値の完全一致をハードコードしない。
## APIの変更を全クライアントへ反映し、新しい統合にCIを足す
実行時の動作、テスト、継続的インテグレーション、リリース登録、クライアントのAPIサーフェスが一致して初めて、統合は完了する。
### API利用側を更新する
- リクエストフィールドを追加するときは、プロキシルートでその値を抽出し、SDK呼び出しに渡す。
- クライアントメソッドの型と、そのフィールドを提供または読み取るすべてのインターフェースを更新する。
- 各リクエストの対象を1つのバンクに限定し、複数バンクのユースケースではクライアント側のオーケストレーションを維持する。
### 統合を完成させる
- 外部フレームワークのインターフェースをモックまたは実際に呼び出すテストを追加し、統合がメモリサービスを正しく呼び出すことを確認する。
- ヘルパーだけを対象とするユニットテストを、十分な統合テスト範囲とはみなさない。
- 変更範囲に応じて実行され、ビルド、依存関係のインストール、統合テストの実行を行う継続的インテグレーションジョブを追加する。
- 標準のリリースワークフローに統合を登録する。
- 構造化データには生の辞書ではなく型付き構造を使い、複数項目のタプルを返さない。
- テスト、CIの変更検出、リリース登録のいずれかが欠けている間は、mergeも公開も行わない。
## 新しい設定を、テナントやバンクごとに変えられるか決めてから足す
実装前にすべての設定を分類し、オーバーライドの範囲とドキュメントの一貫性を保つ。
### 設定レベルを選ぶ
- グローバル環境値からテナントオーバーライドを経て、バンクレベルのデータベースオーバーライドに至る順序で階層型設定を解決する。
- モデルプロバイダー、モデル名、APIキー、操作固有のオプション、顧客固有の機能フラグには階層型設定を使用する。
- データベースのアドレス、ホスト、ポート、グローバルな同時実行数の上限、システム全体のフラグは、サーバーレベルで静的に保つ。
- 静的フィールドをリモート設定可能フィールドの集合に加えない。
### フィールドを漏れなく追加する
1. 環境変数名とデフォルト値を定義する。
2. 中央設定オブジェクトに型付きフィールドを追加する。
3. テナントまたはバンクによるオーバーライドが有効な場合に限り、設定可能フィールドのallowlistに追加する。
4. 環境から初期化する。
5. コマンドラインによるオーバーライドが必要な場合に限り、コマンドラインオプションを追加する。
6. 階層値はバンク単位のリゾルバーで、静的値はグローバルアクセサーで読み取る。
7. 設定リファレンスと環境テンプレートに、説明、デフォルト値、階層状態を反映する。
8. 同梱される環境テンプレートのすべてのコピーを同期し、テンプレート同期テストを通過させ続ける。
- ドキュメント、テンプレート、ログ、テストでは、実際の認証情報の値を決して公開しない。
