はじめに
GenU で RAG を行う際に、部署によってデータアクセスを制限したいケースがあります。これには主に2つの目的があります。セキュリティの観点から部署限定のデータでのみ RAG を提供したい場合と、精度向上の観点から対象部署のデータに絞り込みたい場合です。
GenU には、メタデータフィルターと呼ばれる機能が実装されていて、RAG チャットのデータに関してフィルターをカスタマイズができます。Document に概要が記載されていますが、どんな指定をするのか手順が不明だったので、検証した記事となります!
デフォルトの機能
GenU のメタデータフィルターは、以下の 4 つのフィルターが仕組みとして提供されています。
- User Defined Explicit Filter: このフィルターがデフォルトで有効化されている
- 機能: ユーザーがUIで選択可能なフィルター
- 概要: GenU のユーザーインターフェースでメタデータを指定できる機能。セキュリティではなく、データの精度向上のために利用可能。フィルターの内容をカスタマイズすることで、「営業向けデータ」といったフィルターを、ユーザーが UI 上で指定できる。
- Dynamic Filter
- 機能: ユーザー属性に基づいて自動適用
- 概要: Cognitoユーザーグループ、SAML IdPグループによるフィルタリング。部署ごとに参照させるデータをセキュリティ上分離させたいときなどに利用が可能。デフォルトは無効化されている、このフィルターは利用頻度が高いと考え、今回の記事で取り上げる。
- Implicit Filter
- 機能: AIが自動的にリクエストを解析してフィルター適用
- 概要: Implicit Filterは「AIがユーザーのクエリを分析して、適切なメタデータフィルターを自動生成する機能」となる。例えば、「2023年のAWSサービスについて教えて」と質問をしたら、自動的に year=2023 のフィルターを自動的に適用してくれ、RAG における精度向上を期待できる。セキュリティのためではなく、精度向上のためのフィルター。
- Hidden Static Explicit Filter
- 機能: ユーザーに見えないアプリレベルの権限制御
- 概要: データ分類、テナント制限。GenU のアプリケーションごとに特定のフィルターを固定で適用する。
上記の 4 つのフィルターのうち「User Defined Explicit Filter」はデフォルトで提供されています。以下のように RAG チャットの画面から、検索対象のメタデータを指定できます。
なお、メタデータについては、こちらの記事 も参考にしてください。S3 に格納するファイルに紐づける形で json ファイルを用意することで、ファイルごとにメタデータを付与できます。このメタデータによって、RAG の検索範囲を絞り込むことができます。
フィルターのカスタマイズ
では実際にカスタマイズをしていきましょう。プログラムのソースコードを編集する必要があります。こちらのファイルが編集対象です。
今回の記事では、getDynamicFilters
関数を以下のように編集します。
/*
* This file is used to define filters for the knowledge base. Feel free to uncomment the filters and customize them to suit your needs.
*
* Please refer to the metadata.json in the sample file (packages/cdk/rag-docs/docs) and define the document metadata accordingly.
*/
// Dynamic Filter
// Filter automatically applied by user attribute
// The filter automatically applied by the user attribute is defined here.
export const getDynamicFilters = (
idTokenPayload: CognitoIdTokenPayload
): RetrievalFilter[] => {
const dynamicFilters: RetrievalFilter[] = [];
const groups = idTokenPayload['cognito:groups'];
if (!groups) throw new Error('cognito:groups is not set'); // If the group is not set, it will not be accessible and an error will be thrown
// 管理者は全データアクセス可能(フィルターなし)
if (groups.includes('admin')) {
return dynamicFilters; // 空配列 = フィルターなし
}
// 営業部:営業関連データのみ
if (groups.includes('sales')) {
const salesFilter: RetrievalFilter = {
equals: {
key: 'department',
value: 'sales'
}
};
dynamicFilters.push(salesFilter);
}
// サポート部:サポート関連データのみ
if (groups.includes('support')) {
const supportFilter: RetrievalFilter = {
equals: {
key: 'department',
value: 'support'
}
};
dynamicFilters.push(supportFilter);
}
return dynamicFilters;
};
ポイントを解説すると以下の指定を行っています。プログラムに慣れていない方はハードルがあると思いますが、Claude Code や Amazon Q Developer CLI などで簡単に指定できるはずです。
- Cognito Userpool 上のグループ名によってフィルタリングを行っている。
-
admin
グループに所属している場合は、フィルタリングなし。 -
sales
グループに所属している場合は、department:sales
でフィルタリングを行う。 -
support
グループに所属している場合は、department:support
でフィルタリングを行う。 - グループに何も所属していない場合は、エラーを出す (
cognito:groups is not set
)
Cognito Group の作成
GenU で利用する Cognito 上で Group を手動で作る必要があります。Cognito の画面を開きます。
Group のページを開き、Group を作成します。
sales グループを作成します。
3 グループを作成
それぞれのグループにユーザーを所属します。
sales group の指定。
support グループの指定。
Deploy
これで準備はできたので、次のようなコマンドで GenU の更新をします。SageMaker Editor や手元のターミナルで操作をします。
npm -w packages/cdk run -- cdk deploy --require-approval never --all
S3
S3 上に動作確認用のファイルを格納します。S3 のページを開きます。
以下の 4 つのファイルを格納します。2 つのファイルと、2 つのメタデータファイルです。
メタデータの json ファイルの中身です。
sales-guide.md.metadata.json
{
"metadataAttributes": {
"department": "sales"
}
}
support-manual.md.metadata.json
{
"metadataAttributes": {
"department": "support"
}
}
Sync を押します。
実行テスト
support
グループで実行している様子です。sales 側のデータは見えません。
sales
グループで実行している様子です。support 側のデータは見えません。
まとめ
GenU でメタデータフィルターを実装することで、部署ごとにデータを制限する機能を実現できました。コードの編集は若干ハードルはあると思いますが、この記事を参考にぜひ使ってみて下さい。
なお、今回の記事では取り上げませんでしたが、SAML で IdP 連携している環境でもこのフィルターは利用可能です。
付録 : グループに所属していないとき
こんな感じのエラーになります。