From 571d740930fd6ce4049a5814c039477233e57b3d Mon Sep 17 00:00:00 2001 From: bodapatisaikrishna Date: Wed, 16 Sep 2026 16:41:13 +0530 Subject: [PATCH] fix(analyzer): add Korean context terms to KrRrnRecognizer (#2212) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit KrRrnRecognizer.CONTEXT only contained English keywords ('Korean RRN', 'RRN', etc.), so context enhancement never fired for Korean-language documents where RRNs appear. All other Korean recognizers already define native Korean context terms. Adds '주민등록번호', '주민번호', '주민등록증', '주민등록', '신분증', and '본인인증' to KrRrnRecognizer.CONTEXT and adds unit tests covering context presence and score enhancement. Fixes #2212. --- .../korea/kr_rrn_recognizer.py | 6 + .../tests/test_kr_rrn_recognizer.py | 105 +++++++++++++++++- 2 files changed, 107 insertions(+), 4 deletions(-) diff --git a/presidio-analyzer/presidio_analyzer/predefined_recognizers/country_specific/korea/kr_rrn_recognizer.py b/presidio-analyzer/presidio_analyzer/predefined_recognizers/country_specific/korea/kr_rrn_recognizer.py index 77ebab08d1..9c7c5a3c59 100644 --- a/presidio-analyzer/presidio_analyzer/predefined_recognizers/country_specific/korea/kr_rrn_recognizer.py +++ b/presidio-analyzer/presidio_analyzer/predefined_recognizers/country_specific/korea/kr_rrn_recognizer.py @@ -43,6 +43,12 @@ class KrRrnRecognizer(PatternRecognizer): ] CONTEXT = [ + "주민등록번호", + "주민번호", + "주민등록증", + "주민등록", + "신분증", + "본인인증", "Korean RRN", "Korean Resident Registration Number", "Resident Registration Number", diff --git a/presidio-analyzer/tests/test_kr_rrn_recognizer.py b/presidio-analyzer/tests/test_kr_rrn_recognizer.py index 3a1719e079..de5b8c1228 100644 --- a/presidio-analyzer/tests/test_kr_rrn_recognizer.py +++ b/presidio-analyzer/tests/test_kr_rrn_recognizer.py @@ -1,16 +1,21 @@ import pytest +from presidio_analyzer.predefined_recognizers import KrRrnRecognizer from tests import assert_result_within_score_range -from presidio_analyzer.predefined_recognizers import KrRrnRecognizer + @pytest.fixture(scope="module") def recognizer(): + """Return KrRrnRecognizer instance.""" return KrRrnRecognizer() + @pytest.fixture(scope="module") def entities(): + """Return supported entity list.""" return ["KR_RRN"] + @pytest.mark.parametrize( "text, expected_len, expected_positions, expected_score_ranges", [ @@ -20,15 +25,19 @@ def entities(): ("000505-3637892", 1, ((0, 14),), ((0.5, 0.5),), ), ("0005053637892", 1, ((0, 13),), ((0.5, 0.5),), ), ("His Korean RRN is 960121-1234567", 1, ((18, 32),), ((0.5, 0.5),), ), - + ("주민등록번호: 960121-1234567", 1, ((8, 22),), ((0.5, 0.5),), ), + ("신분증 확인: 960121-1234567", 1, ((8, 22),), ((0.5, 0.5),), ), + ("본인인증 960121-1234567", 1, ((5, 19),), ((0.5, 0.5),), ), + # Valid RRNs, strong match by validate_result() ("960121-1021413", 1, ((0, 14),), ((1.0, 1.0),), ), ("9601211021413", 1, ((0, 13),), ((1.0, 1.0),), ), ("050912-2000019", 1, ((0, 14),), ((1.0, 1.0),), ), ("0509122000019", 1, ((0, 13),), ((1.0, 1.0),), ), ("His RRN is 9601211021413", 1, ((11, 24),), ((1.0, 1.0),), ), - - # Invalid RRNs + ("주민번호는 9601211021413입니다", 1, ((6, 19),), ((1.0, 1.0),), ), + + # Invalid RRNs ("001332-1234567", 0, (), (),), ("0013321234567", 0, (), (),), ("960121+1021413", 0, (), (),), @@ -48,6 +57,7 @@ def test_when_all_rrns_then_succeed( entities, max_score, ): + """Test recognition and validation of Korean RRN across valid and invalid inputs.""" results = recognizer.analyze(text, entities) assert len(results) == expected_len for res, (st_pos, fn_pos), (st_score, fn_score) in zip( @@ -58,3 +68,90 @@ def test_when_all_rrns_then_succeed( assert_result_within_score_range( res, entities[0], st_pos, fn_pos, st_score, fn_score ) + + +def test_korean_context_keywords_in_rrn_recognizer(recognizer): + """Test that Korean context keywords are present in KrRrnRecognizer.context.""" + expected_korean_keywords = [ + "주민등록번호", + "주민번호", + "주민등록증", + "주민등록", + "신분증", + "본인인증", + ] + for keyword in expected_korean_keywords: + assert keyword in recognizer.context + + +@pytest.mark.parametrize( + "context_word", + ["주민등록번호", "주민번호", "주민등록증", "주민등록", "신분증", "본인인증"], +) +def test_when_korean_context_used_then_score_enhanced(recognizer, context_word): + """Test that Korean context keywords enhance confidence score from 0.5 to 0.85.""" + from presidio_analyzer.context_aware_enhancers import LemmaContextAwareEnhancer + from presidio_analyzer.nlp_engine import NlpArtifacts + + text = f"{context_word} 960121-1234567" + results = recognizer.analyze(text, ["KR_RRN"]) + assert len(results) == 1 + assert results[0].score == 0.5 + + tokens = [context_word, "960121-1234567"] + tokens_indices = [0, len(context_word) + 1] + artifacts = NlpArtifacts( + entities=[], + tokens=tokens, + tokens_indices=tokens_indices, + lemmas=tokens, + nlp_engine=None, + language="ko", + ) + artifacts.keywords = [context_word, "960121-1234567"] + + enhancer = LemmaContextAwareEnhancer() + enhanced_results = enhancer.enhance_using_context( + text, results, artifacts, [recognizer] + ) + + assert len(enhanced_results) == 1 + assert enhanced_results[0].score == 0.85 + assert ( + enhanced_results[0].analysis_explanation.supportive_context_word + == context_word + ) + + +def test_when_no_korean_context_then_score_not_enhanced(recognizer): + """Test that unrelated context does not enhance confidence score.""" + from presidio_analyzer.context_aware_enhancers import LemmaContextAwareEnhancer + from presidio_analyzer.nlp_engine import NlpArtifacts + + text = "random text 960121-1234567" + results = recognizer.analyze(text, ["KR_RRN"]) + assert len(results) == 1 + assert results[0].score == 0.5 + + tokens = ["random", "text", "960121-1234567"] + tokens_indices = [0, 7, 12] + artifacts = NlpArtifacts( + entities=[], + tokens=tokens, + tokens_indices=tokens_indices, + lemmas=tokens, + nlp_engine=None, + language="ko", + ) + artifacts.keywords = ["random", "text", "960121-1234567"] + + enhancer = LemmaContextAwareEnhancer() + enhanced_results = enhancer.enhance_using_context( + text, results, artifacts, [recognizer] + ) + + assert len(enhanced_results) == 1 + assert enhanced_results[0].score == 0.5 + assert ( + enhanced_results[0].analysis_explanation.supportive_context_word == "" + )