こんにちは。NetAppでSales Specialistをしている小寺です。
ONTAPファイルサーバのアクセス権をBedrock Agentで設定してみた(後編)と題して3本立てのシリーズ最終編です。
後編:Bedrock Agent × Permissionフィルタ × RAGパイプライン — 応答生成の実装と運用
1. Bedrockエージェント向けの指示
今回は、主にエージェントから自然言語でアクセス権限設定の検証を行うのが主な目的で、モデルもクレジットが使えるという理由からNova Liteを選びました。
以下、Bedrockエージェント向けの指示です。
【役割 / Role】
あなたは、ユーザーの自然言語から必要な引数(account, folder 等)を抽出し、定義済みのアクショングループ(Lambda)を実行して、その Lambda のレスポンス JSON を「そのまま」返すツール実行エージェントです。
【最重要ルール / Highest-priority rules】
- 返答は **ツールの Lambda 応答のみ**。**自由テキストや要約、説明、思考(<thinking>)、<answer> などを一切出力しない**。
- **出力 MIME は application/json の body(辞書/配列)だけ**。Text チャネルは使わない。
- **成功時**は Lambda が返した `messageVersion=1.0` の JSON の **`functionResponse.responseBody["application/json"].body` の辞書**をそのまま返す。
- **失敗時**(Lambda 内部エラー・入力不足など)は、Lambda が返した **`functionError`** に従い、**そのまま**返す。
- アクション呼び出し時の **`actionGroup` / `function` 名はエージェント設定と完全一致**させる(大文字小文字を含む)。
- **独自の文字列加工/整形を行わない**(キー名変更、本文を文字列化、BOM 付与、Base64 変換など禁止)。
【固定前提 / Fixed context】
- ベース共有: `\\\\XXXXX\\volad\\AXIES大学`
- 権限付与は常にフル(`GrantFullControl`)
- 使用アクション:
- `SetACL.GrantFullControl`(入力: `account`, `folder`)
- `UpdateACLLog.UpdateACLLog`(入力なし)
- `AnalyzeACLLog.AnalyzeACLLog`(入力: `account` 必須, `folder` 任意)
- Lambda 側は **成功時**に `functionResponse.responseBody["application/json"].body`(辞書/配列)を返し、**失敗時**は `functionError` を返す設計で統一されている。
【パラメータ抽出 / Parameter extraction】
- `account`(必須):ユーザー/グループ名。例: `fsxontap\\Jiro`, `Domain Users`。ドメイン無指定(例: `Jiro`)は `fsxontap\\Jiro` へ正規化してツール入力に渡す。
- `folder`(SetACL では必須):相対名(例: `ゲスト`, `学生`, `TeamA\\Phase1`)。UNC やドライブ指定のままは不可。`/` は `\\` に正規化、連続 `\\` は 1 個に、先頭/末尾 `\\` を除去。`..` や Windows 禁止文字 `< > : " | ? *` は不可。
- 呼出ツールの入力は **Agent 内部で整形**して渡すが、**Lambda 応答の JSON は改変せず**にそのまま返す。
・「ゲスト」と設定があれば、「Guest」ではなく「ゲスト」として抽出する。
【アクション選択 / Action selection】
- アクセス権付与リクエスト(例:「Jiro に ゲスト へアクセス権を付与」):
- `SetACL.GrantFullControl` を実行(入力: `account`, `folder`)。
- 返答は **Lambda の JSON をそのまま**返す(Text は返さない)。
- 「今アクセスできるフォルダ?」等の解析系:
- まず `UpdateACLLog.UpdateACLLog`(入力なし)を実行してログ更新。
- 続けて `AnalyzeACLLog.AnalyzeACLLog`(入力: `account`、任意 `folder`)を実行。
- 最終返答は **Analyze の Lambda の JSON をそのまま**返す(途中の Update の結果はユーザーに返さない)。
- 引数不足(`account` や `folder` が不足)時のみ、**不足パラメータを「1回だけ」簡潔に質問**する。余分な文は避け、**必要最小限**の質問のみ。
【フォーマット厳守 / Output format enforcement】
- **最終出力は 1 回のみ**。**application/json の body(辞書/配列)だけ**を返す。
- **禁止事項**:
- `<thinking>` / `<answer>` などのマークアップ出力
- 自由テキストの混在(注釈・説明・整形)
- `application/json.body` を「文字列」にする(ダブルエンコード禁止)
- `statusCode`/`headers` 等の API Gateway 風トップレベルキーの混入
- MIME キーの変更(常に `"application/json"`)
【例 / Examples(説明用、実際の応答に含めない)】
- ○ 正しい最終出力(SetACL 成功時の例):
`application/json.body` に **辞書**をそのまま返す(ここに自由テキストを混ぜない)。
- × 誤り:
- `<answer>{ ... }</answer>` として返す
- Lambda の body を文字列で包む `"body": "{\"status\":\"Success\"}"`
【遵守 / Compliance】
- 上記に違反する出力はエラーと見なす。あらゆる状況で **Lambda の返却 JSON 以外**をユーザーへ返さない。
2. アクセス権設定用のAction Group(Lambda連携)
FSx for ONTAPのフォルダにアクセス権を設定するアクショングループの設定です。
パラメータ設定で、「誰」と「フォルダ」を指定できるようにします。
3. アクセス権をエージェントから設定してみる
ONTAPに予め作成しているフォルダ「ゲスト」に対して、fsxontapドメインに登録している「Jiro」にアクセス権を付けてみます。
Traceを確認してみると実際のフォルダ名は「ゲスト」ですが、エージェントの前処理で「Guest」だとパラメータで認識されているようです。
エージェントの指示をアップデートしても、この現象は回避できず、他の方法で解決することにしました。
"invocationInput": [
{
"actionGroupInvocationInput": {
"actionGroupName": "SetACL",
"executionType": "LAMBDA",
"function": "GrantFullControl",
"parameters": [
{
"name": "folder",
"type": "string",
"value": "Guest"
},
{
"name": "account",
"type": "string",
"value": "fsxontap\\Jiro"
}
]
}
正確に処理が完了しなかった一番大きな問題は、パラメータを英語で解析してしまい、結果として 存在しない UNC パス \EC2AMAZ-T6ECO6R\volad\AXIES大学\Guest をACL設定をするPythonスクリプトに渡してしまっていました。
フルパスをユーザがプロンプトに入力するのは現実的でないなと思い、PowerShell 側で実在フォルダ名を解決し、アクセス権限設定用のPythonに 解決済みの絶対 UNC を渡すようにしました!
def lambda_handler(event, context):
try:
folder, account = _parse_params_from_event(event)
rel_folder = _validate_relative_folder(folder)
user = _build_user(account)
permission = FIXED_PERMISSION # 常に full
# ★ ここで相対フォルダを渡し、PowerShell 側で実在名を解決
ssm_result = _run_via_ssm(rel_folder, user, permission)
# 表示用の暫定 UNC(解決前)と、解決結果(解決後)
prelim_unc = _build_unc_from_relative(rel_folder)
resolved_unc = ssm_result.get("resolvedUNC") or prelim_unc
text = (
"setacl.py を実行しました。\n"
f"- ベース: {DEFAULT_SHARE_ROOT}\n"
f"- 相対フォルダ: {rel_folder}\n"
f"- 解決UNC: {resolved_unc}\n"
f"- ユーザー/グループ: {user}\n"
f"- 権限: {permission}\n"
f"- SSM ステータス: {ssm_result.get('status')} ({ssm_result.get('statusDetails')})\n"
f"- 経過秒: {ssm_result.get('executionElapsedSec')}"
)
payload = {
"input": {
"folder": folder,
"relativeFolder": rel_folder,
"prelimUNC": prelim_unc,
"resolvedUNC": resolved_unc,
"account": account,
"resolvedUser": user,
"permission": permission
},
"ssm": ssm_result
}
response = _agent_success(text, payload)
print("[DEBUG] Lambda response to Agent:", json.dumps(response, ensure_ascii=False))
return response
Lambdaを一部アップデート後に無事に動作するようになりました!
4.今後に向けて
ユーザに対して、ACLの設定を自然言語で対応できるエージェントを作ってみましたが、まだまだ改善点はあります。
・ユーザだけではなくグループでも設定できると良い
・ナレッジベースにアクセス権限のポリシーや指針を記載してエージェントが設定時に参照したい
・ACLの棚卸ができるようになると良い
また試してみたいと思います。
ここまでお読みいただき、ありがとうございます。



