You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Presidio中如何构建可动态传入拒绝列表的识别器?

问题描述

加载Presidio分析引擎要花不少时间,我需要给每份文档过滤不同的特定名称,但发现analyze函数只支持传allow_list,没法直接用deny_list。想做一个每次调用能传不同拒绝列表的识别器,还不用每次重新加载分析引擎(避免重复加载NLP模型)。

试了用my_analyzer_first.registry.remove_recognizer方法移除临时添加的识别器,但没效果:跑了代码之后,哪怕调用了移除方法,"Bob"还是被识别成PATIENT,对比移除前后的my_analyzer_first.registry,内容根本没变。

附上测试代码:

from presidio_analyzer import PatternRecognizer, EntityRecognizer, RecognizerResult, AnalyzerEngine, nlp_engine
from presidio_analyzer.nlp_engine import NlpArtifacts
import time

my_analyzer_first = AnalyzerEngine(
        supported_languages=["en"], default_score_threshold=0.5
    )

def on_the_fly_without_loading(text : str, deny_list : list[str]):
    denylist_recognizer = PatternRecognizer(supported_entity="[PATIENT]", deny_list=deny_list)
    my_analyzer_first.registry.add_recognizer(denylist_recognizer)
    entities = my_analyzer_first.analyze(text=text, language="en")
    my_analyzer_first.registry.remove_recognizer(denylist_recognizer)
    return entities


def sanity_check(text : str):
    entities = my_analyzer_first.analyze(text=text, language="en")
    return entities

if __name__=="__main__":
    text = 4 * "I went to the zoo and said hello to Bob the tiger."
    deny_list = ["Bob"]

    print(on_the_fly_without_loading(text, deny_list))
    print(sanity_check(text))

预期只有第一次调用会过滤"Bob",结果每次调用都过滤了。

问题原因

每次调用on_the_fly_without_loading都会新建一个PatternRecognizer实例,调用remove_recognizer(denylist_recognizer)时,Presidio的注册表没法正确匹配这个新实例(比如实例的唯一标识没被正确识别),导致识别器根本没被移除,后面调用sanity_check时它还在生效。

解决方案

方法1:用识别器名称精准移除

给临时创建的拒绝列表识别器指定一个唯一名称,移除时用名称而非实例对象操作,保证能准确删掉:

修改后的代码:

from presidio_analyzer import PatternRecognizer, AnalyzerEngine

my_analyzer_first = AnalyzerEngine(
        supported_languages=["en"], default_score_threshold=0.5
    )

# 定义临时识别器的唯一名称
TEMP_RECOGNIZER_NAME = "temp_denylist_recognizer"

def on_the_fly_without_loading(text : str, deny_list : list[str]):
    # 创建带唯一名称的拒绝列表识别器
    denylist_recognizer = PatternRecognizer(
        supported_entity="PATIENT", 
        deny_list=deny_list,
        name=TEMP_RECOGNIZER_NAME
    )
    my_analyzer_first.registry.add_recognizer(denylist_recognizer)
    entities = my_analyzer_first.analyze(text=text, language="en")
    # 通过名称移除识别器
    my_analyzer_first.registry.remove_recognizer(TEMP_RECOGNIZER_NAME)
    return entities


def sanity_check(text : str):
    entities = my_analyzer_first.analyze(text=text, language="en")
    return entities

if __name__=="__main__":
    text = 4 * "I went to the zoo and said hello to Bob the tiger."
    deny_list = ["Bob"]

    print("带拒绝列表调用结果:", on_the_fly_without_loading(text, deny_list))
    print("无拒绝列表调用结果:", sanity_check(text))

方法2:复用单个识别器,动态更新拒绝列表

创建一个全局的拒绝列表识别器,每次调用前更新它的deny_list,不用频繁加删识别器:

from presidio_analyzer import PatternRecognizer, AnalyzerEngine

my_analyzer_first = AnalyzerEngine(
        supported_languages=["en"], default_score_threshold=0.5
    )

# 创建可复用的拒绝列表识别器,初始为空
denylist_recognizer = PatternRecognizer(
    supported_entity="PATIENT", 
    deny_list=[],
    name="reusable_denylist_recognizer"
)
# 一次性添加到注册表
my_analyzer_first.registry.add_recognizer(denylist_recognizer)

def on_the_fly_without_loading(text : str, deny_list : list[str]):
    # 动态更新拒绝列表
    denylist_recognizer.deny_list = deny_list
    entities = my_analyzer_first.analyze(text=text, language="en")
    # 可选:调用后清空,避免影响后续无拒绝列表的调用
    denylist_recognizer.deny_list = []
    return entities


def sanity_check(text : str):
    entities = my_analyzer_first.analyze(text=text, language="en")
    return entities

if __name__=="__main__":
    text = 4 * "I went to the zoo and said hello to Bob the tiger."
    deny_list = ["Bob"]

    print("带拒绝列表调用结果:", on_the_fly_without_loading(text, deny_list))
    print("无拒绝列表调用结果:", sanity_check(text))

方法3:识别后手动过滤结果

如果不想折腾注册表,也可以拿到识别结果后,手动把拒绝列表里的实体删掉:

from presidio_analyzer import AnalyzerEngine

my_analyzer_first = AnalyzerEngine(
        supported_languages=["en"], default_score_threshold=0.5
    )

def on_the_fly_without_loading(text : str, deny_list : list[str]):
    entities = my_analyzer_first.analyze(text=text, language="en")
    # 过滤掉拒绝列表中的实体
    filtered_entities = [
        ent for ent in entities 
        if text[ent.start:ent.end] not in deny_list
    ]
    return filtered_entities


def sanity_check(text : str):
    entities = my_analyzer_first.analyze(text=text, language="en")
    return entities

if __name__=="__main__":
    text = 4 * "I went to the zoo and said hello to Bob the tiger."
    deny_list = ["Bob"]

    print("带拒绝列表调用结果:", on_the_fly_without_loading(text, deny_list))
    print("无拒绝列表调用结果:", sanity_check(text))

内容的提问来源于stack exchange,提问作者Pink Flying Elephant

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.23 15:05:03