- AI ⚙️
生成AIのガードレールとは?入力・出力の検証設計と対策一覧
生成AIのガードレールとは、モデルの前後に置いて、入力と出力を決めた基準で検査する層のことです。 モデル自体を作り変えるのではなく、アプリ側のコードで「通す・直す・止める・やり直す」を決めます。
この記事では、検査を置く場所を入力・出力・ツール実行の3か所に分け、リスクごとに「どこで・何を使って」検査するかを一覧にしました。後半では、マスク処理・スキーマ検証・再試行と代替応答までをつないだPythonのコードを、実行結果と一緒に載せています。プロンプトインジェクションへの対策と、JSONを出力させる方式の比較は範囲が広いため、ここではそれ以外の検査項目と全体の設計に絞ります。
確認環境:Ubuntu 24.04 / Python 3.11.15 / jsonschema 4.26.0 / Mermaid 12.1.0(流れ図の描画を確認)。確認日 2026-10-06。コードはモデルを呼び出さず、決まった文字列を返す代役の関数で検査の動きを確かめています。
この記事の結論
- ガードレールはモデルの前後に置く検査の層。プロンプトの書き方とは別に、アプリのコードとして用意する。
- 検査の場所は入力・出力・ツール実行の3か所。リスクごとに、どこで受け止めるかを先に決める。
- 手段はルールベース検査 → 分類モデル → 別モデルによる判定の順に、速いものから並べる。
- 不合格のときの動きは直す・やり直す・代替応答を返すの3つ。再試行の回数には上限を付ける。
ガードレールの対策一覧(リスク別の早見表)
| リスク | 起きること | 検査する場所 | 主な実装手段 | 不合格のときの動き |
|---|---|---|---|---|
| 個人情報の混入 | メールアドレスや電話番号がモデルへ送られる、または回答に出る | 入力・出力 | 正規表現でのマスク、固有表現を見つける分類モデル | 置き換えて通す |
| 話題の逸脱 | 用途と関係のない質問に答えてしまう | 入力 | キーワードの許可リスト、話題の分類モデル | モデルを呼ばずに案内文を返す |
| 形式崩れ | JSONが壊れる、必須項目が欠ける、余分な項目が付く | 出力 | JSONの読み込み、JSON Schemaでの検証 | 理由を添えて再試行 |
| 根拠の無い回答 | 渡していない資料や、資料に無い内容を答える | 出力 | 出典IDの照合、引用文の照合、別モデルによる判定 | 再試行し、合格しなければ「分からない」と返す |
| 過剰な長さ | 入力が長すぎて費用が増える、回答が画面に収まらない | 入力・出力 | 文字数・トークン数の上限、最大出力トークンの指定 | 入力は受け付けず案内、出力は再試行 |
| 有害表現 | 攻撃的な言葉や不適切な内容が回答に入る | 入力・出力 | 語のリスト、有害表現の分類モデル、別モデルによる判定 | 代替応答を返す |
| ツールの誤った呼び出し | 許可していない操作、範囲外の引数で実行される | ツール実行 | ツールの許可リスト、引数のスキーマ検証、人の承認 | 実行せず理由を返す |
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「生成AIのガードレールとは?入力・出力の検証設計と対策一覧」
ガードレールを置く3か所
flowchart LR
U[利用者の入力] --> I[入力の検査]
I -->|通す| M[モデル]
I -->|止める| G[案内文]
M --> O[出力の検査]
O -->|合格| A[回答]
O -->|不合格| R{再試行の上限内か}
R -->|はい| M
R -->|いいえ| F[代替応答]
M -. ツール呼び出し .-> T[ツール実行の検査]
T -->|許可| X[ツール]
X -. 結果 .-> M
| 場所 | 役割 | 検査の例 | ここで止める利点 |
|---|---|---|---|
| 入力 | モデルへ渡す前に整える・止める | 長さの上限、個人情報のマスク、話題の判定 | モデルを呼ばないので速く、費用もかからない |
| 出力 | 利用者や後続の処理へ渡す前に確かめる | 形式、長さ、出典の照合、使わない語 | 画面や後続のプログラムに渡る内容をそろえられる |
| ツール実行 | モデルが選んだ操作を実行する前に確かめる | 許可リスト、引数の型と範囲、人の承認 | データの変更や外部への送信を、実行の手前で止められる |
OWASPが公開している「Top 10 for LLM Applications 2025」には、機微な情報の開示(LLM02:2025)、出力の不適切な取り扱い(LLM05:2025)、過剰な権限(LLM06:2025)、誤情報(LLM09:2025)、際限のない消費(LLM10:2025)が並んでいます。上の3か所は、この5項目をそれぞれ受け止める位置に対応します。
似た手段との違い:システムプロンプト・ルール・分類モデル・別モデル判定
ガードレールとして使える手段は4つあります。システムプロンプトでの指示はモデルへのお願いで、残りの3つはモデルの外で行う検査という点が大きな違いです。
| 手段 | 仕組み | 速度 | すり抜けやすさ | 向く検査 |
|---|---|---|---|---|
| システムプロンプトでの指示 | 「〜には答えない」「JSONだけを返す」とモデルに伝える | 追加の時間なし | 高い(従うかどうかはモデル次第) | 口調、答える範囲の基本方針 |
| ルールベース検査 | 正規表現、文字数、スキーマ、許可リストで機械的に判定 | とても速い(下の計測で1件あたり数十マイクロ秒) | 形が決まったものは低い、言い換えには弱い | 形式、長さ、決まった形の個人情報、ツールの引数 |
| 分類モデル | 文を「有害/無害」「対象/対象外」などに分ける小さなモデル | 速い | 中くらい(学習した範囲に強い) | 有害表現、話題の判定、固有表現 |
| 別モデルによる判定 | もう1つのモデルに「この回答は資料に沿っているか」と採点させる | 遅い(モデル呼び出しが1回増える) | 低め(意味まで見られる)、ただし判定自体も揺れる | 根拠の有無、文脈に合うか、トーン |
どれを使うかの判断基準
| 条件 | 選ぶもの |
|---|---|
| 形が決まっている(JSON、文字数、メールアドレス、ID) | ルールベース検査 |
| 言い換えが多く、語のリストでは拾いきれない | 分類モデル |
| 意味を読まないと判定できない(資料と回答の食い違いなど) | 別モデルによる判定 |
| 応答までの時間を延ばしたくない | ルールベース検査を必ず通し、重い検査は対象を絞る |
| データの変更や外部送信を伴う | ツール実行の検査に加えて人の承認 |
| 方針を伝えたいだけで、守られなくても影響が小さい | システムプロンプトでの指示 |
速い検査を前に、重い検査を後ろに置くと、ほとんどの入力は最初の段で判定が終わります。システムプロンプトでの指示は残しつつ、守られなかった場合に受け止める検査を外側に用意する、という組み合わせが基本です。
正規表現で個人情報をマスクする
入力と出力の両方で使えるマスク処理です。メールアドレス・電話番号・郵便番号を [MAIL] のような印に置き換え、置き換えた件数も返します。
# mask.py
import re
import unicodedata
# 検査の順番が大事:長くて形がはっきりしたものから先に置き換える
PATTERNS = [
("MAIL", re.compile(r"[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}")),
("TEL", re.compile(r"(?<!\d)0\d{1,4}-\d{1,4}-\d{4}(?!\d)")),
("TEL", re.compile(r"(?<!\d)0[789]0\d{8}(?!\d)")),
("ZIP", re.compile(r"(?<!\d)\d{3}-\d{4}(?!\d)")),
]
# NFKC では半角ハイフンにならない横棒を、数字の直後にある場合だけそろえる
DASHES = re.compile(r"(?<=\d)[ー‐−–—―](?=\d)")
def mask(text: str) -> tuple[str, dict[str, int]]:
"""個人情報らしい文字列を [種類] に置き換え、置き換えた件数も返す"""
text = unicodedata.normalize("NFKC", text) # 全角の数字や記号を半角にそろえる
text = DASHES.sub("-", text)
counts: dict[str, int] = {}
for label, pattern in PATTERNS:
text, n = pattern.subn(f"[{label}]", text)
if n:
counts[label] = counts.get(label, 0) + n
return text, counts
if __name__ == "__main__":
samples = [
"山田太郎です。連絡は taro.yamada@example.com か 090-0000-0000 へ。",
"住所は〒100-0000、電話は03-0000-0000です。",
"携帯は09000000000、注文番号は 2026-1006-0001 です。",
"電話は 03ー0000ー0000 または 03−0000−0000 です。",
"備品の貸出期間を教えてください。",
]
for s in samples:
masked, counts = mask(s)
print(masked)
print(" ->", counts)
$ python3 mask.py
山田太郎です。連絡は [MAIL] か [TEL] へ。
-> {'MAIL': 1, 'TEL': 1}
住所は〒[ZIP]、電話は[TEL]です。
-> {'TEL': 1, 'ZIP': 1}
携帯は[TEL]、注文番号は 2026-1006-0001 です。
-> {'TEL': 1}
電話は [TEL] または [TEL] です。
-> {'TEL': 2}
備品の貸出期間を教えてください。
-> {}
コードの中で効いている点は3つです。
| 工夫 | 入れなかった場合の動き(実行して確認) |
|---|---|
unicodedata.normalize("NFKC", ...) で全角を半角にそろえる |
「03-0000-0000」は置き換え0件で素通りする |
| 数字にはさまれた横棒(ー、‐、−など)を半角ハイフンにそろえる | NFKCをかけても「03ー0000ー0000」「03−0000−0000」の横棒はそのまま残り、電話番号の型に合わない |
(?<!\d) と (?!\d) で前後が数字でないことを確かめる |
郵便番号の型だけだと、注文番号「2026-1006-0001」が「2[ZIP]-0001」に書き換わる |
正規表現で拾えるのは形が決まったものです。氏名や住所の文章部分は形が一定でないため、固有表現を見つける分類モデルと組み合わせます。
スキーマ検証・再試行・代替応答までをつなぐ
次は全体の流れです。題材は架空の「社内の備品予約システムの問い合わせ窓口」で、回答は answer(120字以内)と sources(根拠にした資料のID)を持つJSONで受け取る想定です。
# guard.py
import json
import re
from jsonschema import Draft202012Validator
from mask import mask
# ---- 設定(題材:社内の備品予約システムの問い合わせ窓口) ----
MAX_INPUT_CHARS = 500
MAX_ANSWER_CHARS = 120
MAX_RETRY = 2
TOPIC_WORDS = ["備品", "予約", "貸出", "返却", "延長", "プロジェクター", "会議室"]
BLOCK_WORDS = ["ばか", "無能"]
FALLBACK = {
"answer": "申し訳ありません。この質問には自動でお答えできませんでした。総務窓口へおつなぎします。",
"sources": [],
"fallback": True,
}
ANSWER_SCHEMA = {
"type": "object",
"properties": {
"answer": {"type": "string", "minLength": 1, "maxLength": MAX_ANSWER_CHARS},
"sources": {
"type": "array",
"items": {"type": "string", "pattern": "^DOC-[0-9]{3}$"},
"minItems": 1,
},
},
"required": ["answer", "sources"],
"additionalProperties": False,
}
VALIDATOR = Draft202012Validator(ANSWER_SCHEMA)
DOCS = {
"DOC-001": "プロジェクターの貸出期間は最長7日。延長は1回まで。",
"DOC-002": "返却は総務カウンターで平日9時から17時まで受け付ける。",
}
# ---- 入力の検査 ----
def check_input(text: str) -> tuple[str | None, list[str]]:
"""通すなら (マスク済みの文, 記録)、止めるなら (None, 記録) を返す"""
notes = []
if len(text) > MAX_INPUT_CHARS:
return None, [f"入力が長すぎる({len(text)}字 > {MAX_INPUT_CHARS}字)"]
masked, counts = mask(text)
if counts:
notes.append(f"個人情報らしい文字列をマスク {counts}")
if not any(w in masked for w in TOPIC_WORDS):
return None, notes + ["対象外の話題"]
return masked, notes
# ---- 出力の検査 ----
def check_output(raw: str) -> tuple[dict | None, list[str]]:
"""問題が無ければ (辞書, [])、あれば (None, 理由の一覧) を返す"""
try:
data = json.loads(raw)
except json.JSONDecodeError as e:
return None, [f"JSONとして読めない: {e.msg}"]
errors = [
f"{'/'.join(map(str, e.path)) or '(全体)'}: {e.message}"
for e in VALIDATOR.iter_errors(data)
]
if errors:
return None, errors
unknown = [s for s in data["sources"] if s not in DOCS]
if unknown:
errors.append(f"渡していない資料を根拠にしている: {unknown}")
hit = [w for w in BLOCK_WORDS if w in data["answer"]]
if hit:
errors.append(f"使わない語が入っている: {hit}")
if errors:
return None, errors
data["answer"], _ = mask(data["answer"]) # 出力側でも念のためマスク
return data, []
# ---- 全体の流れ:入力検査 → モデル → 出力検査 → 再試行 → 代替応答 ----
def answer(question: str, call_model) -> dict:
masked, notes = check_input(question)
for n in notes:
print(" [入力]", n)
if masked is None:
return {"answer": "備品の予約・貸出・返却についてお尋ねください。", "sources": [], "fallback": True}
feedback = ""
for attempt in range(1, MAX_RETRY + 2):
raw = call_model(masked, feedback)
data, errors = check_output(raw)
if data is not None:
print(f" [出力] {attempt}回目で合格")
return data
print(f" [出力] {attempt}回目は不合格: {errors}")
feedback = "前回の出力は次の理由で使えません。直して出力してください。\n" + "\n".join(errors)
print(" [出力] 再試行の上限に達したので代替応答を返す")
return FALLBACK
# ---- モデルの代わりに、決まった文字列を順に返す関数(検査の動きを見るための代役) ----
def fake_model(replies: list[str]):
it = iter(replies)
def call(question: str, feedback: str) -> str:
return next(it)
return call
if __name__ == "__main__":
cases = [
("1. そのまま合格", "プロジェクターの貸出期間は?",
['{"answer": "最長7日です。延長は1回までできます。", "sources": ["DOC-001"]}']),
("2. 形式崩れ→再試行で合格", "返却はどこでできますか。連絡先は taro@example.com です",
['回答:総務カウンターです。',
'{"answer": "総務カウンターで平日9時から17時まで受け付けます。", "sources": ["DOC-002"]}']),
("3. 根拠の無い回答→再試行で合格", "貸出の延長は何回までですか",
['{"answer": "延長は3回までできます。", "sources": ["DOC-009"]}',
'{"answer": "延長は1回までです。", "sources": ["DOC-001"]}']),
("4. 3回とも不合格→代替応答", "予約の取り消し方法は?",
['{"answer": "", "sources": []}',
'{"answer": "取り消しできます", "source": "DOC-001"}',
'{"answer": "' + "予約一覧から取り消せます。" * 12 + '", "sources": ["DOC-001"]}']),
("5. 対象外の話題(モデルを呼ばない)", "今日の夕食の献立を考えて", []),
]
for title, q, replies in cases:
print(f"=== {title}")
result = answer(q, fake_model(replies))
print(" =>", json.dumps(result, ensure_ascii=False))
$ python3 guard.py
=== 1. そのまま合格
[出力] 1回目で合格
=> {"answer": "最長7日です。延長は1回までできます。", "sources": ["DOC-001"]}
=== 2. 形式崩れ→再試行で合格
[入力] 個人情報らしい文字列をマスク {'MAIL': 1}
[出力] 1回目は不合格: ['JSONとして読めない: Expecting value']
[出力] 2回目で合格
=> {"answer": "総務カウンターで平日9時から17時まで受け付けます。", "sources": ["DOC-002"]}
=== 3. 根拠の無い回答→再試行で合格
[出力] 1回目は不合格: ["渡していない資料を根拠にしている: ['DOC-009']"]
[出力] 2回目で合格
=> {"answer": "延長は1回までです。", "sources": ["DOC-001"]}
=== 4. 3回とも不合格→代替応答
[出力] 1回目は不合格: ["answer: '' should be non-empty", 'sources: [] should be non-empty']
[出力] 2回目は不合格: ["(全体): 'sources' is a required property", "(全体): Additional properties are not allowed ('source' was unexpected)"]
[出力] 3回目は不合格: ["answer: '予約一覧から取り消せます。予約一覧から取り消せます。…(省略)…予約一覧から取り消せます。' is too long"]
[出力] 再試行の上限に達したので代替応答を返す
=> {"answer": "申し訳ありません。この質問には自動でお答えできませんでした。総務窓口へおつなぎします。", "sources": [], "fallback": true}
=== 5. 対象外の話題(モデルを呼ばない)
[入力] 対象外の話題
=> {"answer": "備品の予約・貸出・返却についてお尋ねください。", "sources": [], "fallback": true}
この流れで決めていること
| 決めること | このコードでの値 | 考え方 |
|---|---|---|
| 入力の上限 | 500字 | 用途に対して十分な長さにし、超えたらモデルを呼ばない |
| 回答の上限 | 120字 | 画面の表示枠に合わせ、スキーマの maxLength で検査 |
| 余分な項目 | additionalProperties: False で受け付けない |
項目名の打ち違い(source)を見つけられる |
| 再試行の回数 | 最大2回(合計3回まで) | 回数に上限が無いと、費用と待ち時間が増え続ける |
| 再試行で伝える内容 | 不合格の理由の一覧 | 同じ依頼を繰り返すより、理由を渡すほうが直りやすい |
| 代替応答 | 人の窓口への案内 | 合格しない回答を見せるより、次の手段を示す |
| 記録 | 入力・出力の検査結果を1行ずつ出力 | どの検査で何件止まったかを後から数えられる |
実際のアプリでは fake_model の部分を各社のAPI呼び出しに置き換え、feedback を次のメッセージとして渡します。
ツール実行の検査:許可リストと引数の検証
モデルがツール(関数)を呼び出す構成では、実行の手前にも検査を置きます。許可リストにあるか、引数がスキーマに合うか、人の承認が要る操作かの3点です。
# tool_guard.py
from jsonschema import Draft202012Validator
# 呼び出してよいツールと、引数のスキーマ、人の承認が要るかどうか
TOOLS = {
"search_items": {
"confirm": False,
"schema": {
"type": "object",
"properties": {"keyword": {"type": "string", "minLength": 1, "maxLength": 50}},
"required": ["keyword"],
"additionalProperties": False,
},
},
"cancel_reservation": {
"confirm": True,
"schema": {
"type": "object",
"properties": {"reservation_id": {"type": "integer", "minimum": 1}},
"required": ["reservation_id"],
"additionalProperties": False,
},
},
}
def check_tool_call(name: str, args: dict, approved: bool = False) -> tuple[bool, str]:
tool = TOOLS.get(name)
if tool is None:
return False, f"許可リストに無いツール: {name}"
errors = [e.message for e in Draft202012Validator(tool["schema"]).iter_errors(args)]
if errors:
return False, "引数がスキーマに合わない: " + " / ".join(errors)
if tool["confirm"] and not approved:
return False, "人の承認待ち(変更を伴う操作)"
return True, "実行してよい"
if __name__ == "__main__":
calls = [
("search_items", {"keyword": "プロジェクター"}, False),
("delete_all_items", {}, False),
("search_items", {"keyword": "プロジェクター", "limit": 100000}, False),
("cancel_reservation", {"reservation_id": "12; 13"}, False),
("cancel_reservation", {"reservation_id": 12}, False),
("cancel_reservation", {"reservation_id": 12}, True),
]
for name, args, approved in calls:
ok, reason = check_tool_call(name, args, approved)
print("OK " if ok else "NG ", name, args, "->", reason)
$ python3 tool_guard.py
OK search_items {'keyword': 'プロジェクター'} -> 実行してよい
NG delete_all_items {} -> 許可リストに無いツール: delete_all_items
NG search_items {'keyword': 'プロジェクター', 'limit': 100000} -> 引数がスキーマに合わない: Additional properties are not allowed ('limit' was unexpected)
NG cancel_reservation {'reservation_id': '12; 13'} -> 引数がスキーマに合わない: '12; 13' is not of type 'integer'
NG cancel_reservation {'reservation_id': 12} -> 人の承認待ち(変更を伴う操作)
OK cancel_reservation {'reservation_id': 12} -> 実行してよい
読み取りだけのツールは自動で通し、データを変えるツールは承認を挟む、という分け方にすると、検査の重さを操作の影響に合わせられます。
根拠の無い回答を減らす検査の段階
上のコードの出典ID照合は、最も軽い検査です。確かめられる範囲は段階ごとに違います。
| 段階 | 検査の内容 | 確かめられること | 手段 |
|---|---|---|---|
| 1 | 出典IDが渡した資料に含まれるか | 存在しない資料を挙げていないこと | ルールベース |
| 2 | 回答に付けた引用文が資料の本文にそのまま含まれるか | 引用が資料に実在すること | ルールベース(文字列の一致) |
| 3 | 回答の内容が資料と食い違っていないか | 意味のうえで資料に沿っていること | 別モデルによる判定 |
Anthropicのドキュメント「Reduce hallucinations」では、分からないときは分からないと答えてよいと伝えること、資料からの直接の引用を先に取り出させること、主張ごとに引用を付けさせ、裏づけの無い主張は取り下げさせることが挙げられています。段階2の検査は、この「引用を付けさせる」指示と組み合わせて使えます。
確かめて分かったこと
- NFKCだけでは横棒がそろわない。 全角の数字と「-」は半角になりましたが、長音記号「ー」やマイナス記号「−」は変わらず、電話番号の型に合いませんでした。数字にはさまれた横棒を別に置き換えると拾えます。
- 前後の数字を確かめない型は、別の番号を書き換える。 郵便番号の型だけを当てると、注文番号「2026-1006-0001」の途中が
[ZIP]になりました。 - 出典IDの照合は、内容の食い違いまでは見ない。
{"answer": "延長は3回までできます。", "sources": ["DOC-001"]}をcheck_outputに渡すと、資料の記載(1回まで)と違っていても合格しました。意味の検査は段階3の役割です。 - jsonschemaのエラー文には、不合格になった値がそのまま入る。 長すぎる回答を検査すると、エラー文に回答の全文が含まれました。再試行の依頼や記録に渡す前に、エラー文を一定の長さで切ると扱いやすくなります。
- ルールベース検査は応答時間にほぼ影響しない。 この環境で1万回ずつ計測した平均は、入力の検査が約6マイクロ秒、出力の検査が約39マイクロ秒でした。
- 入力で止めた場合はモデルを1度も呼ばない。 対象外の話題の例では、代役の関数に応答を1つも用意していなくても最後まで動きました。
設計するときの確認表
| 確認項目 | 決める内容 |
|---|---|
| 守る対象 | 誰に何を見せないか、何を実行させないか |
| 検査の場所 | リスクごとに入力・出力・ツール実行のどこで受け止めるか |
| 不合格の動き | 直す・再試行・代替応答・人へ引き継ぐ、のどれか |
| 上限 | 入力の長さ、出力の長さ、再試行の回数、ツール呼び出しの回数 |
| 記録 | 検査名・結果・件数(マスク前の個人情報は記録に残さない) |
| 見直し | 止めすぎた例とすり抜けた例を集め、ルールと語のリストを更新する周期 |
| 試験 | 合格する例と不合格になる例を用意し、検査を変えるたびに流す |
よくある質問
Q. 生成AIのガードレールとは何ですか? A. モデルの前後に置いて、入力と出力を決めた基準で検査する層です。入力では長さ・個人情報・話題を、出力では形式・長さ・根拠・表現を確かめ、ツールを使う構成では実行の手前にも検査を置きます。
Q. ガードレールとシステムプロンプトの違いは? A. システムプロンプトはモデルへの指示で、ガードレールはモデルの外で行う検査です。指示は守られない場合があるため、守られなかったときに受け止める検査を外側に用意します。
Q. ガードレールとプロンプトインジェクション対策の違いは? A. プロンプトインジェクション対策は、ガードレールが扱うリスクの1つです。ガードレールはそれに加えて、個人情報の混入、形式崩れ、根拠の無い回答、過剰な長さ、有害表現なども対象にします。
Q. 出力がスキーマに合わないときはどうすればよいですか? A. 不合格の理由を添えて再試行し、上限の回数まで合格しなければ代替応答を返します。この記事のコードでは最大2回の再試行のあと、人の窓口へ案内する文を返しています。
Q. ガードレールを入れると応答は遅くなりますか? A. ルールベース検査だけなら、ほぼ変わりません。別モデルによる判定はモデル呼び出しが1回増えるため、対象を絞って使います。
まとめ
- ガードレールはモデルの前後に置く検査の層で、アプリのコードとして用意する。
- 置く場所は入力・出力・ツール実行の3か所。リスク別の一覧表で、受け止める場所を先に決める。
- 手段はルールベース検査・分類モデル・別モデルによる判定。速いものを前に置く。
- マスク処理はNFKCでの正規化と横棒の統一、前後の数字の確認を入れると取りこぼしと書き換えすぎが減る。
- 出力はスキーマ検証 → 理由を添えた再試行 → 代替応答の順で扱い、回数に上限を付ける。
- 出典IDの照合は軽い検査。内容の食い違いは別モデルによる判定で確かめる。
参考・出典
- OWASP Top 10 for LLM Applications 2025(OWASP GenAI Security Project)https://genai.owasp.org/llm-top-10/
- Reduce hallucinations(Claude Platform Docs)https://platform.claude.com/docs/en/test-and-evaluate/strengthen-guardrails/reduce-hallucinations
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「生成AIのガードレールとは?入力・出力の検証設計と対策一覧」
株式会社RJC ― SI事業・SES事業・AI駆動開発。RJCは一緒に成長を楽しめる会社です。
WE ARE HIRING
RJCで一緒に開発しながら、
成長を楽しみませんか?
RJCは、Web・モバイル・AIを活用した開発プロジェクトで、テックリードやPM・PMOも活躍するシステム開発会社です。会社を知る、待遇を確かめる、話を聞いてみる。気になるところ見てみてください!
- 127日年間休日
- 12時間平均残業時間
- 毎日ガチャ遊びココロも大切にする福利厚生。アマギフなどの賞品ラインナップ!
ほかにも、チケットレストラン、書籍読み放題、2年ごとの慰労報奨(休暇 or 金一封)、11期連続の黒字決算。