AIの参照データと出力先の制限
検索・生成・出力の各境界で、許可された資料と宛先を維持する方法を確認できます。
説明
検索で取得した文書をモデルへ渡す構成では、生成後に情報を隠すだけでは遅い場合があります。参照資料の取得時点で利用者の権限を適用し、生成結果の表示先や送信先にも別の制約を設けます。
同名のreportを持つ二つのテナントを使います。取得SQL、HTML表示用の値、送信待ちキューの宛先を調べ、監査記録と実際に参照した文書IDを照合します。
検索・生成・出力の各境界で、許可された資料と宛先を維持する方法を確認できます。
1同名文書と所属を確認する
aliceはred、bobはblueに所属し、各テナントにreportがあります。
from sd15 import *
lab = Retrieval()
print(lab.db.execute("SELECT id,tenant,title同名文書の配置を確認する FROM docs ORDER BY id").fetchall())
print(lab.db.execute("SELECT * FROM members ORDER BY subject").fetchall())タイトルが同じでも別の文書です。モデルへ渡す前の検索条件に所属を含めます。
2取得時点でテナントを絞る
同じ検索を二つの認証主体で実行します。
print(lab.search("alice")所属を含むSQLで取得する)
print(lab.search("bob"))aliceへ7、bobへ8だけが返ります。検索後に混ざった資料をモデルの指示だけで見せ分ける方法に頼りません。
3生成HTMLを文字として表示する値へ変換する
タグを含む固定生成結果をHTML本文へ文字として出す場合です。
generated = '<a href="https://other.example.test">report</a>'
safe = html.escape生成文字列をHTML本文の文字へ変換する(generated,quote=True)
print(safe)山括弧と引用符がエスケープされます。この段階はHTML本文へ文字として埋め込む用途です。URLとして使う場合は別途宛先を検査します。
4送信先を許可された宛先へ限定する
出力先契約はhttps://reports.example.test/inboxだけです。
print(lab.queue送信待ち登録の宛先を検査する("alice","https://other.example.test/inbox",7))
print(lab.queue("alice","https://reports.example.test.other.example.test/inbox",7))
print(lab.db.execute("SELECT COUNT(*) FROM outbox").fetchone())両方destination_rejected、キューは0件です。URLの部分文字列ではなく、scheme・host・pathを解析して照合します。
5許可宛先でも他テナントの文書は出さない
aliceが文書8を送信待ちへ入れようとします。
print(lab.queue("alice","https://reports.example.test/inbox",8他テナントの文書を指定する))
print(lab.db.execute("SELECT COUNT(*) FROM outbox").fetchone())permission_rejectedで0件です。検索時の認可だけで済ませず、出力する文書も現在の所属と照合します。
6許可された文書と宛先をキューへ確定する
alice自身の文書7を同じ許可宛先へ登録します。
print(lab.queue許可された文書を配送待ちへ入れる("alice","https://reports.example.test/inbox",7))
print(lab.db.execute("SELECT * FROM outbox").fetchall())queuedとなり、redの本文だけが1件保存されます。送信完了を示す結果ではなく、許可された配送待ちの状態です。
7監査記録から取得と出力を対応付ける
どの主体がどの文書を取得し、どれを配送待ちへ入れたか読みます。
print(lab.db.execute("SELECT * FROM audit取得対象と出力対象を照合する ORDER BY rowid").fetchall())aliceの7取得、bobの8取得、aliceの7登録が残ります。監査には文書本文を重複保存せず、調査に必要な主体・ID・処理を残しています。
まとめ
モデルへ渡す前に参照資料の権限を適用します。
HTML表示とURL利用では異なる検査を行います。
出力時にも資料と宛先を認可し、状態を記録します。
操作履歴と解説を振り返れます。
Python 3.15 / 固定の公開試料
from sd15 import *
lab = Retrieval()
print(lab.db.execute("SELECT id,tenant,title FROM docs ORDER BY id").fetchall())
print(lab.db.execute("SELECT * FROM members ORDER BY subject").fetchall())[(7, 'red', 'report'), (8, 'blue', 'report')]
[('alice', 'red'), ('bob', 'blue')]print(lab.search("alice"))
print(lab.search("bob"))[(7, 'red public sample')] [(8, 'blue private sample')]
generated = '<a href="https://other.example.test">report</a>'
safe = html.escape(generated,quote=True)
print(safe)<a href="https://other.example.test">report</a>
print(lab.queue("alice","https://other.example.test/inbox",7))
print(lab.queue("alice","https://reports.example.test.other.example.test/inbox",7))
print(lab.db.execute("SELECT COUNT(*) FROM outbox").fetchone())destination_rejected destination_rejected (0,)
print(lab.queue("alice","https://reports.example.test/inbox",8))
print(lab.db.execute("SELECT COUNT(*) FROM outbox").fetchone())permission_rejected (0,)
print(lab.queue("alice","https://reports.example.test/inbox",7))
print(lab.db.execute("SELECT * FROM outbox").fetchall())queued
[('alice', 'https://reports.example.test/inbox', 'red public sample')]print(lab.db.execute("SELECT * FROM audit ORDER BY rowid").fetchall())[('alice', 7, 'retrieved'), ('bob', 8, 'retrieved'), ('alice', 7, 'queued')]演習完了