对llm-guard模型护栏实现敏感词过滤和敏感主题检测的源码分析?包括研究问题和具体实现原理。
LLM Guard敏感词过滤系统实现分析 Ban Substrings Scanner采用两种匹配方式: STR模式:简单字符串包含检查 WORD模式:正则表达式单词边界匹配 支持大小写敏感配置和敏感词替换功能 Ban Topics Scanner基于零样本分类技术: 使用预训练Transformer模型(如RoBERTa)进行主题分类 通过概率分数与人工设定阈值比较判断敏感内容 支持替换为中文模
问题一 1.Ban Substrings Scanner 是如何实现的,对用户的输入是如何处理的。 2.Ban Topics Scanner 的实现原理,使用的模型是否可以替换为中文模型,对阈值的判断是模型自主实现吗?
LLM Guard敏感词主题过滤实现
基于我对LLM Guard项目代码的分析,我来详细回答您的两个问题:
- Ban Substrings Scanner 实现原理
llm_guard/input_scanners
class MatchType(Enum):
STR = "str"
WORD = "word"
def match(self, text: str, substring: str) -> bool:
if self == MatchType.STR:
return substring in text
if self == MatchType.WORD:
return re.search(r"\b" + re.escape(substring) + r"\b", text) is not None
Ban Substrings Scanner 的核心实现特点:
匹配方式
- STR模式:简单的字符串包含检查 (substring in text)
- WORD模式:使用正则表达式进行单词边界匹配 (\b 边界符)
输入处理流程
llm_guard/input_scanners
def scan(self, prompt: str) -> tuple[str, bool, float]:
sanitized_prompt = prompt
matched_substrings = []
missing_substrings = []
for s in self._substrings:
if self._case_sensitive is False:
s, prompt = s.lower(), prompt.lower()
if self._match_type.match(prompt, s):
matched_substrings.append(s)
else:
missing_substrings.append(s)
配置选项
- case_sensitive: 控制大小写敏感性
- redact: 是否用 [REDACTED] 替换敏感词
- contains_all: 是否需要匹配所有子串(默认匹配任意一个)
- match_type: 匹配类型(字符串或单词级别)
- Ban Topics Scanner 实现原理
llm_guard/input_scanners
class BanTopics(Scanner):
def __init__(
self,
topics: list[str],
*,
threshold: float = 0.6,
model: Model | None = None,
use_onnx: bool = False,
):
if model is None:
model = MODEL_ROBERTA_BASE_C_V2
self._classifier = pipeline(
task="zero-shot-classification",
model=tf_model,
tokenizer=tf_tokenizer,
**model.pipeline_kwargs,
)
核心技术原理
Ban Topics Scanner 使用 Zero-Shot Classification(零样本分类) 技术:
- 模型架构:基于 Transformer 的预训练模型(如 RoBERTa、DeBERTa)
- 分类机制:无需针对特定主题训练,直接使用预训练模型进行主题分类
- 阈值判断:模型输出概率分数,通过阈值判断是否包含禁止主题
扫描流程
llm_guard/input_scanners
def scan(self, prompt: str) -> tuple[str, bool, float]:
output_model = self._classifier(prompt, self._topics, multi_label=False)
label_score = dict(zip(output_model["labels"], output_model["scores"]))
max_score = round(max(output_model["scores"]) if output_model["scores"] else 0, 2)
if max_score > self._threshold:
return prompt, False, calculate_risk_score(max_score, self._threshold)
可用的预训练模型
项目提供了多个预训练模型选择:
llm_guard/input_scanners
# 多语言模型 - 支持100+语言,包括中文
MODEL_BGE_M3_V2 = Model(
path="MoritzLaurer/bge-m3-zeroshot-v2.0",
# 基于 XLM-RoBERTa,支持多语言
)
# 英文专用模型
MODEL_ROBERTA_BASE_C_V2 = Model(
path="MoritzLaurer/roberta-base-zeroshot-v2.0-c",
)
关于具体问题
- 是否可以替换为中文模型?
可以! 有两种方式:
方式一:使用内置多语言模型
from llm_guard.input_scanners.ban_topics import BanTopics, MODEL_BGE_M3_V2
scanner = BanTopics(
topics=["暴力", "政治", "宗教"], # 中文主题
threshold=0.6,
model=MODEL_BGE_M3_V2 # 多语言模型,支持中文
)
方式二:自定义中文模型
llm_guard
@dataclasses.dataclass
class Model:
path: str
subfolder: str = ""
revision: str | None = None
onnx_path: str | None = None
pipeline_kwargs: dict = dataclasses.field(default_factory=dict)
- 阈值判断是模型自主实现吗?
不是完全自主的,而是结合了模型输出和人工设定的阈值:
- 模型输出:Zero-Shot分类模型输出每个主题的概率分数(0-1之间)
- 阈值比较:人工设定阈值(如0.6),当最高概率超过阈值时触发拦截
- 风险评分:使用 calculate_risk_score 函数计算最终风险分数
llm_guard
@dataclasses.dataclass
class Model:
path: str
subfolder: str = ""
revision: str | None = None
onnx_path: str | None = None
pipeline_kwargs: dict = dataclasses.field(default_factory=dict)
总结:
模型负责生成主题分类的概率分数
阈值判断是基于人工设定的规则
最终的风险评分结合了模型输出和阈值设置
这种设计允许用户根据具体应用场景调整敏感度,在准确性和误报率之间找到平衡。
问题二: 那具体的敏感词过滤的实现逻辑是什么?
详细分析Ban Substrings Scanner的具体实现逻辑。根据代码分析,它的实现比您想象的要简单直接:
Ban Substrings Scanner 的实际实现逻辑
llm_guard/input_scanners
def scan(self, prompt: str) -> tuple[str, bool, float]:
sanitized_prompt = prompt
matched_substrings = []
missing_substrings = []
for s in self._substrings:
if self._case_sensitive is False:
s, prompt = s.lower(), prompt.lower()
if self._match_type.match(prompt, s):
matched_substrings.append(s)
else:
missing_substrings.append(s)
关键发现:它并不进行分词! 而是采用以下逻辑:
- 遍历敏感词列表
- 对敏感词列表中的每个词进行逐一检查
- 不是对输入文本分词,而是直接在原文中查找每个敏感词
- 两种匹配模式
llm_guard/input_scanners
class MatchType(Enum):
STR = "str"
WORD = "word"
def match(self, text: str, substring: str) -> bool:
if self == MatchType.STR:
return substring in text # 简单字符串包含检查
if self == MatchType.WORD:
return re.search(r"\b" + re.escape(substring) + r"\b", text) is not None
STR模式:
# 示例
text = "这是一个测试文本"
substring = "测试"
result = substring in text # True
WORD模式:
# 示例
text = "This is a test message"
substring = "test"
pattern = r"\btest\b" # 单词边界匹配
result = re.search(pattern, text) is not None # True
# 但如果是
text = "This is a testing message"
result = re.search(r"\btest\b", text) is not None # False,因为是"testing"
- 具体执行流程
让我用一个具体例子说明:
# 假设用户输入
user_input = "我想了解一些政治话题和暴力内容"
# 敏感词列表
banned_words = ["政治", "暴力", "色情"]
# 执行过程:
for word in banned_words:
if word in user_input: # STR模式
# "政治" in "我想了解一些政治话题和暴力内容" -> True
# "暴力" in "我想了解一些政治话题和暴力内容" -> True
# "色情" in "我想了解一些政治话题和暴力内容" -> False
matched_words.append(word)
优缺点分析
优点:
- 简单高效:直接字符串匹配,性能好
- 精确控制:可以精确指定要过滤的词汇
- 支持正则:WORD模式支持单词边界匹配
缺点: - 容易绕过:
- “政 治” (加空格)
- “政*治” (加特殊字符)
- “zhengzhi” (拼音)
误杀问题: - “政治学” 会匹配 “政治”
- “暴力美学” 会匹配 “暴力”
- 无语义理解:不理解上下文含义
更高级的过滤方案
如果需要更智能的过滤,项目还提供了基于AI模型的方案:
Ban Topics Scanner:使用零样本分类,理解语义
Toxicity Scanner:检测有毒内容
Ban Code Scanner:检测代码内容
这些扫描器可以组合使用,形成多层防护:
总结:Ban Substrings Scanner 采用的是直接字符串匹配而非分词匹配,这使得它简单高效但也存在一定局限性。在实际应用中,通常会结合多种扫描器来提高过滤效果。
更多推荐
所有评论(0)