问题一 1.Ban Substrings Scanner 是如何实现的,对用户的输入是如何处理的。 2.Ban Topics Scanner 的实现原理,使用的模型是否可以替换为中文模型,对阈值的判断是模型自主实现吗?

LLM Guard敏感词主题过滤实现

基于我对LLM Guard项目代码的分析,我来详细回答您的两个问题:

  1. 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: 匹配类型(字符串或单词级别)
  1. 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",
)

关于具体问题

  1. 是否可以替换为中文模型?
    可以! 有两种方式:

方式一:使用内置多语言模型

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)
  1. 阈值判断是模型自主实现吗?
    不是完全自主的,而是结合了模型输出和人工设定的阈值:
  • 模型输出: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)

关键发现:它并不进行分词! 而是采用以下逻辑:

  1. 遍历敏感词列表
  • 对敏感词列表中的每个词进行逐一检查
  • 不是对输入文本分词,而是直接在原文中查找每个敏感词
  1. 两种匹配模式
    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"
  1. 具体执行流程
    让我用一个具体例子说明:
# 假设用户输入
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 采用的是直接字符串匹配而非分词匹配,这使得它简单高效但也存在一定局限性。在实际应用中,通常会结合多种扫描器来提高过滤效果。

Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐