def detect_pii(self, text: str) -> Dict[str, Any]:
"""
텍스트에서 PII를 탐지 (MCP Tool용)
Args:
text (str): 분석할 텍스트
Returns:
Dict[str, Any]: MCP Tool 응답 형식
"""
try:
start_time = time.time()
# Provider에 따른 langextract 호출
if self.provider_type == "vllm":
# vLLM Provider 사용
result = lx.extract(
text_or_documents=text,
prompt_description=self.prompt,
examples=self.examples,
model=self.provider, # 커스텀 Provider 인스턴스 사용
use_schema_constraints=False
)
else:
# OpenAI Provider 사용 (기본)
os.environ["OPENAI_BASE_URL"] = "https://api.openai.com/v1"
result = lx.extract(
text_or_documents=text,
prompt_description=self.prompt,
examples=self.examples,
model_id=self.model_id,
api_key=self.api_key,
fence_output=True
)
# 결과를 PIIItem 리스트로 변환
pii_items = []
logger.info(f"탐지된 extraction 수: {len(result.extractions)}")
for i, extraction in enumerate(result.extractions):
logger.info(f"Extraction {i+1}: class='{extraction.extraction_class}', text='{extraction.extraction_text}'")
# char_interval이 없으면 텍스트에서 직접 위치 찾기
start_pos = 0
end_pos = 0
if extraction.char_interval:
start_pos = extraction.char_interval.start_pos
end_pos = extraction.char_interval.end_pos
logger.info(f" char_interval 사용: {start_pos}-{end_pos}")
else:
# 텍스트에서 직접 위치 찾기 (대소문자 구분 없이)
search_text = extraction.extraction_text
start_pos = text.find(search_text)
# 대소문자 구분 없이 찾기
if start_pos == -1:
start_pos = text.lower().find(search_text.lower())
if start_pos != -1:
end_pos = start_pos + len(search_text)
logger.info(f" 텍스트에서 직접 찾음: {start_pos}-{end_pos}")
# 실제 찾은 텍스트와 원본이 일치하는지 확인
actual_found = text[start_pos:end_pos]
if actual_found != search_text:
logger.warning(f" 대소문자 차이: 찾은='{actual_found}', 원본='{search_text}'")
else:
start_pos = -1
end_pos = -1
logger.warning(f" 텍스트에서 찾을 수 없음: '{extraction.extraction_text}'")
mapped_type = self._map_extraction_class(extraction.extraction_class)
logger.info(f" 매핑된 타입: '{extraction.extraction_class}' -> '{mapped_type}'")
pii_items.append(PIIItem(
type=mapped_type,
value=extraction.extraction_text,
confidence=0.9, # langextract는 confidence를 제공하지 않으므로 기본값
start_pos=start_pos,
end_pos=end_pos
))
processing_time = time.time() - start_time
return {
"success": True,
"pii_items": [asdict(item) for item in pii_items],
"count": len(pii_items),
"processing_time": processing_time,
"summary": self._get_pii_summary(pii_items)
}
except Exception as e:
return {
"success": False,
"error": str(e),
"pii_items": [],
"count": 0,
"processing_time": 0,
"summary": {}
}