3/26

web search 로직

  1. 사용자의 입력 값을 받아온다. ⇒ 사용자가 입력한 문자메세지

  2. 문자메세지가 스팸일 경우 이와 비슷한 사례를 웹서치를 통해 조사한다.

  3. 생성형 AI에게 비슷한 사례와 뉴스기사의 게시일, 제목, url 링크를 받아온다.

  4. 생성형 AI가 서치해준 정보를 Json 형식 리스트로 반환하라고 명령한다.

    [
    {"date" : "YYYY-MM-DD", "title" : "기사 제목", "url" : "원문 링크"
    ]
    

3.27 요약 문구 추가

                    "[\\n"
                    "  {\\n"
                    "    \\"date\\": \\"YYYY-MM-DD\\",\\n"
                    "    \\"title\\": \\"기사 제목\\",\\n"
                    "    \\"summary\\": \\"기사 핵심 내용을 2~3문장으로 요약\\",\\n"
                    "    \\"url\\": \\"원문 링크\\"\\n"
                    "  }\\n"
                    "]"
import os
from openai import OpenAI
from dotenv import load_dotenv

load_dotenv()

OPENAI_API_KEY = os.getenv('OPENAI_API_KEY')

# API 연결
client = OpenAI(api_key=OPENAI_API_KEY)

# 테스트 데이터 - 분석 대상이 되는 실제 스미싱 문자 원문
message = '엄마~ 바빠? 엄마 나 급한 일이 좀 생겨서… 지금 98만원만 입금해 줄 수 있어?+C6 '

def get_phishing_news(message) :
    response = client.responses.create(
        model = "gpt-5.2",
        input = [
            {   # 역할 부여
                "role" : "system",
                "content" : "당신은 대한민국 금융감독원 및 경찰청 데이터를 숙지한 '실시간 국내 피싱 사례 분석가'입니다. "
                            "다음 지침을 반드시 따르세요:\\n\\n"
    
                            "1. [국내 타겟 검색]: 사용자의 메시지에서 [기관명, 앱이름, 키워드, 수법]을 추출하되, "
                            "반드시 검색어 뒤에 '국내 사례', '한국 기사', '보안 공지'를 붙여 한국어 결과를 우선적으로 찾으세요. "
                            "(예: 'Hookt 앱 스팸 국내 사례', '코인원 해외IP 로그인 사칭 한국 뉴스')\\n\\n"
    
                            "2. [세부 사례 제공]: 단순히 기관의 메인 홈페이지(kisa.or.kr 등)를 제공하는 것은 금지합니다. "
                            "반드시 한국의 '특정 사건'이나 '실제 피해 사례'가 담긴 세부 기사/블로그/공지사항 URL만 찾으세요.\\n\\n"
    
                            "3. [유형 판단 및 검색]: [원격제어형, 기관사칭형, 결제 요구형, 지인사칭형, 광고성] 중 "
                            "사용자의 메시지가 어디에 해당하는지 먼저 내부적으로 판단한 뒤, 그 유형의 '한국 내 실제 사례'를 3개 검색하세요.\\n\\n"
    
                            "4. [엄격한 형식 준수]: 출력은 반드시 아래 JSON 형식의 배열만 허용하며, 날짜는 'YYYY-MM-DD' 형식을 유지하세요.\\n"
                            "형식: [{'date': 'YYYY-MM-DD', 'title': '기사 제목', 'url': '원문 링크'}]"
            },
            {   # 생성형 AI에게 원문 전달
                "role" : "user",
                "content" : message
            }
        ],

        # 웹서치 API 사용
        tools=[
            {"type" : "web_search"}
        ],
    )
    
    return response.output_text

result = get_phishing_news(message)
print(result)

# 스팸이 아닐 경우 웹서치 기능은 실행하지 않음
# 웹서치 결과 받아오기
result = get_phishing_news(message)

# web_search.py 결과값 대신 사용할 임시 데이터
# result = """
# [
#     {
#         "date": "2026-03-10",
#         "title": "[경고] '엄마 나 폰 고장났어' 지인 사칭 메신저 피싱 급증",
#         "url": "<https://www.naver.com>"
#     },
#     {
#         "date": "2026-02-25",
#         "title": "가족 사칭해 '문화상품권' 요구... 신종 스미싱 수법 주의",
#         "url": "<https://www.google.com>"
#     },
#     {
#         "date": "2026-01-15",
#         "title": "금융감독원, '지인 사칭형' 보이스피싱 피해 경보 발령",
#         "url": "<https://www.fss.or.kr/fss/bbs/B0000188/view.do?nttId=123456>"
#     }
# ]
# """

# 문자열 형태의 JSON을 파이썬 리스트로 변환
web_results = json.loads(result)

for case in web_results : 
    with st.container(border=True):
        col1, col2 = st.columns([1,5])

        with col1 : 
            date_html = f"""
            <div style="
                background-color: #f0f2f6; 
                color: #31333F; 
                padding: 2px 8px; 
                border-radius: 5px; 
                font-size: 0.85rem; 
                font-family: monospace; 
                text-align: center;
                margin-top: 5px;
                border: 1px solid #dcdfe3;
            ">
                {case['date'].replace("-", ".")}
            </div>
            """
            st.markdown(date_html, unsafe_allow_html=True)

        with col2 :
            st.markdown(f"**[{case['title']}]({case['url']})**")

image.png