콘텐츠 대표 이미지 - 파이썬 + OpenAI API로자동 요약 시스템 구축하기
🤖 AI/머신러닝 개발

파이썬 + OpenAI API로
자동 요약 시스템 구축하기

긴 글도 AI가 순삭! 실전 코드부터 아키텍처까지 완전 정복 🚀
자동 요약 시스템 동작 흐름 📄 텍스트 입력 뉴스, 논문, 문서 웹페이지, PDF 유튜브 자막 등 긴 원문 텍스트 🐍 파이썬 처리 텍스트 전처리 청크 분할 프롬프트 구성 API 호출 관리 🤖 OpenAI API GPT-4o / GPT-4 GPT-3.5-turbo 요약 생성 핵심 추출 ✅ 요약 결과 핵심 요약문 불릿 포인트 키워드 추출 다국어 번역 요약 🔑 API 키 하나면 누구나 만들 수 있는 나만의 AI 요약 시스템!
🙋 야, 이거 진짜 필요하지 않아?

솔직히 말해서... 요즘 정보가 너무 많잖아ㅋㅋㅋ
뉴스 기사 하나 읽으려면 스크롤이 끝도 없고, 논문은 영어에 전문용어 범벅이고,
유튜브 영상은 1시간짜리인데 핵심은 5분이고... 😩

그래서 오늘은 파이썬(Python)이랑 OpenAI API를 조합해서
자동 요약 시스템을 직접 만드는 방법을 완전 실전 위주로 파헤쳐볼 거야!

코드 복붙만 해도 돌아가는 수준으로 설명할 테니까 걱정 ㄴㄴ 😎
AI/머신러닝 개발 입문자부터 실무자까지 다 유용할 내용이니까 끝까지 봐줘~


🧩 자동 요약 시스템이 뭔데?

자동 요약(Automatic Summarization)은 크게 두 가지 방식으로 나뉘어:

① 추출적 요약 (Extractive Summarization)
원문에서 중요한 문장을 그대로 뽑아내는 방식.
예전 NLP 방식에서 많이 썼고, 원문 문장을 그대로 가져오니까 왜곡이 없다는 장점이 있어.

② 생성적 요약 (Abstractive Summarization)
원문을 이해하고 새로운 문장으로 재생성하는 방식.
GPT 같은 LLM이 바로 이 방식이야. 훨씬 자연스럽고 핵심을 잘 잡아줌!

우리가 오늘 만들 건 당연히 생성적 요약 방식이고,
OpenAI의 GPT 모델을 API로 호출해서 구현할 거야. 🔥

💡 OpenAI API란? OpenAI가 제공하는 REST API로, GPT-4o, GPT-4, GPT-3.5-turbo 등의 모델을
HTTP 요청으로 호출할 수 있어. 파이썬 SDK도 공식 지원해서 코드 몇 줄이면 연동 완료!

⚙️ 환경 세팅부터 하자 (진짜 5분이면 됨)
1️⃣ 필요한 것들

준비물 체크리스트야. 다 있으면 바로 시작 가능!

1
Python 3.8 이상 — 공식 사이트(python.org)에서 설치. 3.10 이상 추천!
2
OpenAI API 키 — platform.openai.com에서 회원가입 후 발급. 처음엔 무료 크레딧 줌!
3
openai 파이썬 패키지 — pip으로 설치 (아래 코드 참고)
4
텍스트 에디터 or IDE — VS Code, PyCharm, Jupyter Notebook 다 OK!
2️⃣ 패키지 설치

터미널(cmd/PowerShell/bash)에서 아래 명령어 실행해줘:

# 기본 openai 패키지 설치
pip install openai

# 추가로 유용한 패키지들도 같이 설치하자
pip install python-dotenv  # 환경변수 관리용
pip install tiktoken       # 토큰 수 계산용
pip install requests       # HTTP 요청용 (웹 크롤링 등)
pip install beautifulsoup4 # HTML 파싱용
3️⃣ API 키 안전하게 관리하기

API 키를 코드에 직접 박아넣으면 절대 안 돼!! 😱
깃허브에 올리는 순간 키 탈취당하고 요금 폭탄 맞을 수 있거든.
.env 파일을 사용해서 환경변수로 관리하자:

# .env 파일 생성 (프로젝트 루트에)
OPENAI_API_KEY=sk-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
# .gitignore 파일에 반드시 추가!
.env
__pycache__/
*.pyc
# Python 코드에서 불러오기
import os
from dotenv import load_dotenv

load_dotenv()  # .env 파일 로드

api_key = os.getenv("OPENAI_API_KEY")
print(f"API 키 로드 완료: {api_key[:10]}...")  # 앞 10자리만 확인
⚠️ 주의! .env 파일은 절대 깃허브에 올리지 마세요.
.gitignore에 추가하는 거 잊으면 진짜 큰일 나요ㅋㅋ 실제로 매년 수천 명이 이 실수로 요금 폭탄 맞음...

🚀 기본 요약 코드 — 일단 돌려봐!

환경 세팅 끝났으면 바로 코드 써보자.
가장 기본적인 텍스트 요약 코드야. 복붙하면 바로 돌아감!

import os
from openai import OpenAI
from dotenv import load_dotenv

# 환경변수 로드
load_dotenv()

# OpenAI 클라이언트 초기화
client = OpenAI(api_key=os.getenv("OPENAI_API_KEY"))

def summarize_text(text: str, max_length: int = 300) -> str:
    """
    텍스트를 요약하는 기본 함수
    
    Args:
        text: 요약할 원문 텍스트
        max_length: 요약문 최대 글자 수 (기본 300자)
    
    Returns:
        요약된 텍스트 문자열
    """
    
    # 시스템 프롬프트 설정
    system_prompt = """당신은 전문 텍스트 요약 AI입니다.
주어진 텍스트를 핵심 내용만 추출하여 명확하고 간결하게 요약해주세요.
요약 시 다음 원칙을 따르세요:
1. 핵심 정보를 빠짐없이 포함
2. 불필요한 반복 제거
3. 자연스러운 한국어 문장 사용
4. 원문의 사실 관계를 정확하게 유지"""

    # 사용자 프롬프트 구성
    user_prompt = f"""다음 텍스트를 {max_length}자 이내로 요약해주세요:

---
{text}
---

요약:"""

    # API 호출
    response = client.chat.completions.create(
        model="gpt-4o-mini",  # 비용 효율적인 모델
        messages=[
            {"role": "system", "content": system_prompt},
            {"role": "user", "content": user_prompt}
        ],
        max_tokens=500,
        temperature=0.3  # 낮을수록 일관된 출력
    )
    
    # 결과 추출
    summary = response.choices[0].message.content.strip()
    return summary


# 테스트 실행
if __name__ == "__main__":
    sample_text = """
    인공지능(AI) 기술이 빠르게 발전하면서 다양한 산업 분야에서 혁신이 일어나고 있다.
    특히 자연어 처리(NLP) 분야에서는 GPT, BERT 등 대형 언어 모델이 등장하면서
    텍스트 이해와 생성 능력이 인간 수준에 근접하고 있다. 이러한 기술은 의료, 법률,
    금융, 교육 등 전문 분야에서도 활발히 적용되고 있으며, 특히 문서 요약, 번역,
    질의응답 시스템 등에서 두드러진 성과를 보이고 있다. 전문가들은 향후 5년 내에
    AI가 대부분의 반복적인 지식 작업을 자동화할 것으로 전망하고 있으며, 이에 따른
    직업 시장의 변화와 새로운 역할의 등장에 대한 논의도 활발해지고 있다.
    """
    
    result = summarize_text(sample_text, max_length=150)
    print("=== 요약 결과 ===")
    print(result)
💡 gpt-4o-mini 모델 추천 이유 GPT-4o-mini는 GPT-4 수준의 성능을 훨씬 저렴한 비용으로 제공해.
요약 작업에는 충분히 강력하고, 비용도 GPT-4 대비 약 15배 저렴함!
(2024년 기준 입력 $0.15/1M 토큰, 출력 $0.60/1M 토큰)

🎯 프롬프트 엔지니어링 — 여기서 퀄리티가 갈림!

솔직히 API 호출 자체는 쉬워.
근데 좋은 요약을 뽑아내려면 프롬프트를 잘 짜야 해!
프롬프트 엔지니어링이 전체 퀄리티의 70%를 결정한다고 봐도 과언이 아님ㅋㅋ

📝 목적별 프롬프트 템플릿
class SummaryPrompts:
    """다양한 요약 목적에 맞는 프롬프트 모음"""
    
    @staticmethod
    def news_summary(text: str) -> dict:
        """뉴스 기사 요약용"""
        return {
            "system": "당신은 뉴스 편집 전문가입니다. 기사의 핵심 사실을 5W1H(누가, 언제, 어디서, 무엇을, 왜, 어떻게) 기준으로 요약합니다.",
            "user": f"다음 뉴스 기사를 3문장으로 요약해주세요. 가장 중요한 사실부터 서술하세요:\n\n{text}"
        }
    
    @staticmethod
    def academic_summary(text: str) -> dict:
        """학술 논문/보고서 요약용"""
        return {
            "system": "당신은 학술 문서 분석 전문가입니다. 연구의 목적, 방법론, 주요 결과, 결론을 체계적으로 요약합니다.",
            "user": f"""다음 학술 텍스트를 아래 형식으로 요약해주세요:

【연구 목적】
【연구 방법】
【주요 결과】
【결론 및 시사점】

텍스트:
{text}"""
        }
    
    @staticmethod
    def meeting_summary(text: str) -> dict:
        """회의록 요약용"""
        return {
            "system": "당신은 비즈니스 회의 전문 서기입니다. 회의의 핵심 결정사항, 액션 아이템, 담당자를 명확히 정리합니다.",
            "user": f"""다음 회의 내용을 요약해주세요:

📌 주요 논의 사항:
✅ 결정된 사항:
📋 액션 아이템 (담당자 - 기한):
❓ 미결 사항:

회의 내용:
{text}"""
        }
    
    @staticmethod
    def bullet_summary(text: str, num_points: int = 5) -> dict:
        """불릿 포인트 요약용"""
        return {
            "system": "당신은 정보를 명확하고 간결한 핵심 포인트로 정리하는 전문가입니다.",
            "user": f"다음 텍스트의 핵심 내용을 {num_points}개의 불릿 포인트로 정리해주세요. 각 포인트는 한 문장으로 작성하세요:\n\n{text}"
        }
    
    @staticmethod
    def keyword_extract(text: str) -> dict:
        """키워드 추출용"""
        return {
            "system": "당신은 텍스트 분석 전문가입니다. 텍스트에서 가장 중요한 키워드와 핵심 개념을 추출합니다.",
            "user": f"다음 텍스트에서 핵심 키워드 10개를 추출하고, 각 키워드의 중요도를 1-5점으로 평가해주세요. JSON 형식으로 출력하세요:\n\n{text}"
        }
def smart_summarize(text: str, mode: str = "general") -> str:
    """
    목적에 맞는 스마트 요약 함수
    
    mode 옵션:
    - "general": 일반 요약
    - "news": 뉴스 기사
    - "academic": 학술 문서
    - "meeting": 회의록
    - "bullet": 불릿 포인트
    - "keyword": 키워드 추출
    """
    
    prompts = SummaryPrompts()
    
    mode_map = {
        "news": prompts.news_summary,
        "academic": prompts.academic_summary,
        "meeting": prompts.meeting_summary,
        "bullet": prompts.bullet_summary,
        "keyword": prompts.keyword_extract,
    }
    
    if mode in mode_map:
        prompt_data = mode_map[mode](text)
    else:
        # 기본 일반 요약
        prompt_data = {
            "system": "당신은 전문 텍스트 요약 AI입니다.",
            "user": f"다음 텍스트를 핵심 내용 중심으로 간결하게 요약해주세요:\n\n{text}"
        }
    
    response = client.chat.completions.create(
        model="gpt-4o-mini",
        messages=[
            {"role": "system", "content": prompt_data["system"]},
            {"role": "user", "content": prompt_data["user"]}
        ],
        temperature=0.3,
        max_tokens=800
    )
    
    return response.choices[0].message.content.strip()

# 사용 예시
result = smart_summarize(my_text, mode="news")
print(result)

📏 긴 텍스트 처리 — 토큰 한계 극복하기

GPT 모델에는 컨텍스트 윈도우(Context Window)라는 한계가 있어.
쉽게 말해서 한 번에 처리할 수 있는 텍스트 양에 제한이 있다는 거야.

GPT-4o-mini 기준으로 128K 토큰이 한계인데,
한국어 기준으로 대략 1토큰 ≈ 0.5~1글자 정도야.
그래도 엄청 긴 문서는 분할 처리가 필요해!

모델 컨텍스트 윈도우 한국어 기준 처리량
gpt-3.5-turbo 16K 토큰 약 8,000~16,000자
gpt-4o-mini 128K 토큰 약 64,000~128,000자
gpt-4o 128K 토큰 약 64,000~128,000자
gpt-4-turbo 128K 토큰 약 64,000~128,000자
🔪 청크 분할 + 계층적 요약 전략

정말 긴 문서(책, 긴 보고서 등)는 Map-Reduce 방식으로 처리해!
① 문서를 청크로 분할 → ② 각 청크 요약 → ③ 요약들을 다시 요약

📄 원문 (초장문)
→
✂️ 청크 분할
→
🤖 각 청크 요약
→
🔗 요약 합치기
→
✅ 최종 요약
import tiktoken

def count_tokens(text: str, model: str = "gpt-4o-mini") -> int:
    """텍스트의 토큰 수를 계산하는 함수"""
    try:
        encoding = tiktoken.encoding_for_model(model)
    except KeyError:
        encoding = tiktoken.get_encoding("cl100k_base")
    return len(encoding.encode(text))


def split_text_into_chunks(text: str, max_tokens: int = 3000) -> list[str]:
    """
    텍스트를 토큰 기준으로 청크 분할
    문단 단위로 분할하여 문맥 유지
    """
    paragraphs = text.split('\n\n')  # 문단 단위로 분할
    chunks = []
    current_chunk = []
    current_tokens = 0
    
    for para in paragraphs:
        para_tokens = count_tokens(para)
        
        if current_tokens + para_tokens > max_tokens and current_chunk:
            # 현재 청크 저장하고 새 청크 시작
            chunks.append('\n\n'.join(current_chunk))
            current_chunk = [para]
            current_tokens = para_tokens
        else:
            current_chunk.append(para)
            current_tokens += para_tokens
    
    # 마지막 청크 추가
    if current_chunk:
        chunks.append('\n\n'.join(current_chunk))
    
    return chunks


def hierarchical_summarize(text: str, final_max_tokens: int = 500) -> str:
    """
    계층적 요약 (Map-Reduce 방식)
    긴 텍스트를 청크로 나눠 요약 후 최종 요약 생성
    """
    
    total_tokens = count_tokens(text)
    print(f"📊 원문 토큰 수: {total_tokens:,}")
    
    # 토큰이 적으면 바로 요약
    if total_tokens < 3000:
        print("✅ 직접 요약 처리")
        return summarize_text(text)
    
    # 청크 분할
    chunks = split_text_into_chunks(text, max_tokens=3000)
    print(f"✂️ {len(chunks)}개 청크로 분할 완료")
    
    # 각 청크 요약 (Map 단계)
    chunk_summaries = []
    for i, chunk in enumerate(chunks):
        print(f"🔄 청크 {i+1}/{len(chunks)} 요약 중...")
        summary = summarize_text(chunk, max_length=200)
        chunk_summaries.append(summary)
    
    # 청크 요약들을 합쳐서 최종 요약 (Reduce 단계)
    combined_summaries = '\n\n'.join(chunk_summaries)
    
    print("🎯 최종 요약 생성 중...")
    
    final_prompt = f"""다음은 긴 문서를 여러 부분으로 나눠 요약한 내용들입니다.
이 요약들을 종합하여 전체 문서의 핵심 내용을 하나의 완결된 요약문으로 작성해주세요.
중복 내용은 제거하고, 가장 중요한 정보를 중심으로 정리해주세요.

부분 요약들:
{combined_summaries}

최종 종합 요약:"""
    
    response = client.chat.completions.create(
        model="gpt-4o-mini",
        messages=[
            {"role": "system", "content": "당신은 여러 요약문을 하나의 완결된 요약으로 통합하는 전문가입니다."},
            {"role": "user", "content": final_prompt}
        ],
        max_tokens=final_max_tokens,
        temperature=0.3
    )
    
    return response.choices[0].message.content.strip()


# 사용 예시
with open("long_document.txt", "r", encoding="utf-8") as f:
    long_text = f.read()

final_summary = hierarchical_summarize(long_text)
print("\n=== 최종 요약 ===")
print(final_summary)

🌐 웹 페이지 자동 요약 — 실전 활용!

URL 하나만 넣으면 웹 페이지 내용을 자동으로 가져와서 요약해주는 기능이야.
뉴스 기사, 블로그 포스트 등에 바로 적용 가능! 😎

import requests
from bs4 import BeautifulSoup
import re

def fetch_webpage_text(url: str) -> str:
    """
    웹 페이지에서 텍스트 추출
    
    Args:
        url: 크롤링할 웹 페이지 URL
    
    Returns:
        추출된 텍스트
    """
    headers = {
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
    }
    
    try:
        response = requests.get(url, headers=headers, timeout=10)
        response.raise_for_status()
        response.encoding = response.apparent_encoding  # 인코딩 자동 감지
        
        soup = BeautifulSoup(response.text, 'html.parser')
        
        # 불필요한 태그 제거
        for tag in soup(['script', 'style', 'nav', 'footer', 'header', 
                         'aside', 'advertisement', 'iframe']):
            tag.decompose()
        
        # 본문 텍스트 추출 (article, main, p 태그 우선)
        article = soup.find('article') or soup.find('main') or soup.find('body')
        
        if article:
            text = article.get_text(separator='\n', strip=True)
        else:
            text = soup.get_text(separator='\n', strip=True)
        
        # 텍스트 정제
        lines = [line.strip() for line in text.split('\n') if line.strip()]
        text = '\n'.join(lines)
        
        # 연속 공백 제거
        text = re.sub(r'\n{3,}', '\n\n', text)
        
        return text
        
    except requests.RequestException as e:
        raise Exception(f"웹 페이지 로드 실패: {e}")


def summarize_url(url: str, mode: str = "news") -> dict:
    """
    URL을 입력받아 웹 페이지 내용을 요약
    
    Returns:
        {
            "url": 원본 URL,
            "text_length": 원문 글자 수,
            "summary": 요약 결과
        }
    """
    print(f"🌐 URL 로딩 중: {url}")
    text = fetch_webpage_text(url)
    
    print(f"📝 텍스트 추출 완료: {len(text):,}자")
    
    # 너무 짧으면 요약 불필요
    if len(text) < 100:
        return {"url": url, "text_length": len(text), "summary": "텍스트가 너무 짧아 요약이 불필요합니다."}
    
    # 요약 실행
    summary = smart_summarize(text, mode=mode)
    
    return {
        "url": url,
        "text_length": len(text),
        "summary": summary
    }


# 사용 예시
result = summarize_url("https://example-news.com/article/12345", mode="news")
print(f"원문 길이: {result['text_length']:,}자")
print(f"\n요약:\n{result['summary']}")
💡 robots.txt 확인 필수! 웹 크롤링 전에 해당 사이트의 robots.txt를 확인하고 크롤링 허용 여부를 체크해야 해.
상업적 목적의 크롤링은 사이트 이용약관도 꼭 확인하자!

⚡ 비동기 처리로 속도 UP! — asyncio 활용

여러 문서를 동시에 요약해야 할 때는 비동기(async) 처리가 필수야!
동기 방식으로 10개 문서 처리하면 10초 걸릴 거 비동기로 하면 2~3초면 됨ㅋㅋ

import asyncio
from openai import AsyncOpenAI

# 비동기 클라이언트 초기화
async_client = AsyncOpenAI(api_key=os.getenv("OPENAI_API_KEY"))

async def async_summarize(text: str, session_id: str = "") -> dict:
    """비동기 요약 함수"""
    
    response = await async_client.chat.completions.create(
        model="gpt-4o-mini",
        messages=[
            {"role": "system", "content": "당신은 전문 텍스트 요약 AI입니다."},
            {"role": "user", "content": f"다음 텍스트를 200자 이내로 요약해주세요:\n\n{text}"}
        ],
        max_tokens=400,
        temperature=0.3
    )
    
    return {
        "id": session_id,
        "summary": response.choices[0].message.content.strip()
    }


async def batch_summarize(texts: list[dict]) -> list[dict]:
    """
    여러 텍스트를 동시에 요약 (배치 처리)
    
    Args:
        texts: [{"id": "1", "text": "..."}, ...] 형태의 리스트
    
    Returns:
        요약 결과 리스트
    """
    
    # 동시 요청 수 제한 (Rate Limit 방지)
    semaphore = asyncio.Semaphore(5)  # 최대 5개 동시 요청
    
    async def limited_summarize(item):
        async with semaphore:
            return await async_summarize(item["text"], item["id"])
    
    # 모든 요약 작업을 동시 실행
    tasks = [limited_summarize(item) for item in texts]
    results = await asyncio.gather(*tasks, return_exceptions=True)
    
    # 에러 처리
    processed_results = []
    for i, result in enumerate(results):
        if isinstance(result, Exception):
            processed_results.append({
                "id": texts[i]["id"],
                "summary": f"오류 발생: {str(result)}"
            })
        else:
            processed_results.append(result)
    
    return processed_results


# 실행 예시
async def main():
    documents = [
        {"id": "doc1", "text": "첫 번째 문서 내용..."},
        {"id": "doc2", "text": "두 번째 문서 내용..."},
        {"id": "doc3", "text": "세 번째 문서 내용..."},
        # ... 더 많은 문서들
    ]
    
    print(f"📦 {len(documents)}개 문서 동시 요약 시작...")
    import time
    start = time.time()
    
    results = await batch_summarize(documents)
    
    elapsed = time.time() - start
    print(f"⏱️ 처리 완료: {elapsed:.2f}초")
    
    for result in results:
        print(f"\n[{result['id']}] {result['summary']}")

# 실행
asyncio.run(main())

🛡️ 에러 처리 & 재시도 로직 — 프로답게!

API 호출은 네트워크 상황, Rate Limit, 서버 오류 등으로 실패할 수 있어.
실전 서비스라면 재시도(Retry) 로직은 필수야!

import time
import random
from openai import RateLimitError, APIError, APIConnectionError

def robust_summarize(text: str, max_retries: int = 3) -> str:
    """
    에러 처리와 재시도 로직이 포함된 견고한 요약 함수
    Exponential Backoff 전략 사용
    """
    
    for attempt in range(max_retries):
        try:
            response = client.chat.completions.create(
                model="gpt-4o-mini",
                messages=[
                    {"role": "system", "content": "당신은 전문 텍스트 요약 AI입니다."},
                    {"role": "user", "content": f"다음 텍스트를 요약해주세요:\n\n{text}"}
                ],
                max_tokens=500,
                temperature=0.3,
                timeout=30  # 30초 타임아웃
            )
            
            return response.choices[0].message.content.strip()
            
        except RateLimitError as e:
            # Rate Limit 초과 시 대기 후 재시도
            wait_time = (2 ** attempt) + random.uniform(0, 1)
            print(f"⚠️ Rate Limit 초과. {wait_time:.1f}초 후 재시도... (시도 {attempt+1}/{max_retries})")
            time.sleep(wait_time)
            
        except APIConnectionError as e:
            # 연결 오류 시 재시도
            wait_time = 2 ** attempt
            print(f"🔌 연결 오류. {wait_time}초 후 재시도... (시도 {attempt+1}/{max_retries})")
            time.sleep(wait_time)
            
        except APIError as e:
            # 서버 오류 (5xx) 시 재시도
            if e.status_code >= 500:
                wait_time = 2 ** attempt
                print(f"🚨 서버 오류 ({e.status_code}). {wait_time}초 후 재시도...")
                time.sleep(wait_time)
            else:
                # 4xx 오류는 재시도 불필요
                raise Exception(f"API 오류: {e.status_code} - {e.message}")
                
        except Exception as e:
            print(f"❌ 예상치 못한 오류: {e}")
            if attempt == max_retries - 1:
                raise
    
    raise Exception(f"최대 재시도 횟수({max_retries}) 초과")


# 비용 추적 기능 추가
class CostTracker:
    """API 사용 비용 추적 클래스"""
    
    # 2024년 기준 가격 (USD per 1M tokens)
    PRICES = {
        "gpt-4o-mini": {"input": 0.15, "output": 0.60},
        "gpt-4o": {"input": 5.00, "output": 15.00},
        "gpt-3.5-turbo": {"input": 0.50, "output": 1.50},
    }
    
    def __init__(self):
        self.total_input_tokens = 0
        self.total_output_tokens = 0
        self.total_cost_usd = 0.0
        self.request_count = 0
    
    def track(self, response, model: str = "gpt-4o-mini"):
        """API 응답에서 토큰 사용량 추적"""
        usage = response.usage
        input_tokens = usage.prompt_tokens
        output_tokens = usage.completion_tokens
        
        self.total_input_tokens += input_tokens
        self.total_output_tokens += output_tokens
        self.request_count += 1
        
        if model in self.PRICES:
            cost = (input_tokens * self.PRICES[model]["input"] / 1_000_000 +
                   output_tokens * self.PRICES[model]["output"] / 1_000_000)
            self.total_cost_usd += cost
    
    def report(self):
        """비용 리포트 출력"""
        print(f"\n💰 API 사용 현황")
        print(f"   총 요청 수: {self.request_count:,}회")
        print(f"   입력 토큰: {self.total_input_tokens:,}")
        print(f"   출력 토큰: {self.total_output_tokens:,}")
        print(f"   예상 비용: ${self.total_cost_usd:.4f} USD")
        print(f"   (약 {self.total_cost_usd * 1350:.0f}원)")  # 환율 1350원 기준

# 전역 비용 추적기
tracker = CostTracker()

🏗️ 완성형 자동 요약 시스템 아키텍처 입력 레이어 웹 URL PDF 파일 텍스트 파일 직접 입력 유튜브 자막 API 연동 처리 레이어 (Python) 텍스트 전처리 정제 · 인코딩 토큰 계산 tiktoken 활용 청크 분할 Map-Reduce 프롬프트 구성 목적별 템플릿 에러 처리 재시도 로직 🤖 OpenAI API (GPT-4o-mini) 요약 생성 · 키워드 추출 · 다국어 처리 출력: 요약문 · JSON · 불릿포인트 · 키워드 · 번역 요약
🖥️ 완성형 CLI 요약 도구 만들기

지금까지 배운 걸 다 합쳐서 실제로 쓸 수 있는 CLI 도구를 만들어보자!
터미널에서 명령어 하나로 요약 가능한 도구야 😎

#!/usr/bin/env python3
"""
auto_summarizer.py
파이썬 + OpenAI API 자동 요약 시스템
사용법: python auto_summarizer.py [옵션]
"""

import os
import sys
import argparse
import json
from pathlib import Path
from openai import OpenAI
from dotenv import load_dotenv

load_dotenv()
client = OpenAI(api_key=os.getenv("OPENAI_API_KEY"))


def read_input(source: str) -> str:
    """다양한 소스에서 텍스트 읽기"""
    
    # URL인 경우
    if source.startswith(('http://', 'https://')):
        return fetch_webpage_text(source)  # 위에서 정의한 함수
    
    # 파일인 경우
    path = Path(source)
    if path.exists():
        suffix = path.suffix.lower()
        
        if suffix == '.txt':
            return path.read_text(encoding='utf-8')
        
        elif suffix == '.pdf':
            try:
                import pypdf
                reader = pypdf.PdfReader(str(path))
                text = ""
                for page in reader.pages:
                    text += page.extract_text() + "\n"
                return text
            except ImportError:
                print("PDF 처리를 위해 pypdf 설치 필요: pip install pypdf")
                sys.exit(1)
        
        elif suffix in ['.md', '.rst']:
            return path.read_text(encoding='utf-8')
        
        else:
            print(f"지원하지 않는 파일 형식: {suffix}")
            sys.exit(1)
    
    # 직접 텍스트인 경우
    return source


def format_output(summary: str, output_format: str, original_length: int) -> str:
    """출력 형식 포맷팅"""
    
    if output_format == "json":
        result = {
            "summary": summary,
            "original_length": original_length,
            "summary_length": len(summary),
            "compression_ratio": f"{(1 - len(summary)/original_length)*100:.1f}%"
        }
        return json.dumps(result, ensure_ascii=False, indent=2)
    
    elif output_format == "markdown":
        return f"""# 📋 요약 결과

**원문 길이:** {original_length:,}자  
**요약 길이:** {len(summary):,}자  
**압축률:** {(1 - len(summary)/original_length)*100:.1f}%

---

## 요약

{summary}
"""
    
    else:  # plain text
        return f"""
{'='*50}
📋 요약 결과
{'='*50}
원문: {original_length:,}자 → 요약: {len(summary):,}자 ({(1 - len(summary)/original_length)*100:.1f}% 압축)
{'='*50}

{summary}

{'='*50}
"""


def main():
    parser = argparse.ArgumentParser(
        description='🤖 AI 자동 요약 시스템',
        formatter_class=argparse.RawDescriptionHelpFormatter,
        epilog="""
사용 예시:
  python auto_summarizer.py "https://news.example.com/article"
  python auto_summarizer.py document.txt --mode academic
  python auto_summarizer.py document.pdf --format json --output result.json
  python auto_summarizer.py "긴 텍스트 내용..." --mode bullet --points 5
        """
    )
    
    parser.add_argument('source', help='요약할 소스 (URL, 파일 경로, 또는 텍스트)')
    parser.add_argument('--mode', choices=['general', 'news', 'academic', 'meeting', 'bullet', 'keyword'],
                       default='general', help='요약 모드 (기본: general)')
    parser.add_argument('--format', choices=['text', 'json', 'markdown'],
                       default='text', help='출력 형식 (기본: text)')
    parser.add_argument('--output', '-o', help='결과 저장 파일 경로')
    parser.add_argument('--model', default='gpt-4o-mini', help='사용할 GPT 모델')
    parser.add_argument('--points', type=int, default=5, help='불릿 포인트 수 (bullet 모드)')
    parser.add_argument('--max-length', type=int, default=300, help='요약 최대 글자 수')
    
    args = parser.parse_args()
    
    print(f"🚀 요약 시작...")
    print(f"   모드: {args.mode} | 모델: {args.model}")
    
    # 텍스트 읽기
    text = read_input(args.source)
    original_length = len(text)
    print(f"   원문 길이: {original_length:,}자")
    
    # 요약 실행
    if original_length > 10000:
        print("   📦 긴 문서 감지 - 계층적 요약 적용")
        summary = hierarchical_summarize(text)
    else:
        summary = smart_summarize(text, mode=args.mode)
    
    # 출력 포맷팅
    output = format_output(summary, args.format, original_length)
    
    # 결과 출력/저장
    if args.output:
        Path(args.output).write_text(output, encoding='utf-8')
        print(f"✅ 결과 저장 완료: {args.output}")
    else:
        print(output)


if __name__ == "__main__":
    main()
# 실제 사용 예시들

# 뉴스 기사 URL 요약
python auto_summarizer.py "https://www.bbc.com/news/article" --mode news

# PDF 파일 학술 요약 (JSON 출력)
python auto_summarizer.py research.pdf --mode academic --format json -o result.json

# 텍스트 파일 불릿 포인트 요약
python auto_summarizer.py report.txt --mode bullet --points 7

# 마크다운 형식으로 저장
python auto_summarizer.py document.txt --format markdown -o summary.md

🌍 다국어 요약 & 번역 요약

영어 문서를 한국어로 요약하거나, 한국어 문서를 영어로 요약하는 것도 GPT로 쉽게 가능해!
이게 진짜 강력한 기능 중 하나야 ㅋㅋㅋ

def multilingual_summarize(text: str, 
                           source_lang: str = "auto",
                           target_lang: str = "ko") -> dict:
    """
    다국어 요약 함수
    
    Args:
        text: 원문 텍스트
        source_lang: 원문 언어 ("auto", "en", "ko", "ja", "zh" 등)
        target_lang: 요약 출력 언어 ("ko", "en", "ja" 등)
    
    Returns:
        {"original_lang": ..., "summary": ..., "keywords": [...]}
    """
    
    lang_names = {
        "ko": "한국어", "en": "영어", "ja": "일본어",
        "zh": "중국어", "fr": "프랑스어", "de": "독일어"
    }
    
    target_lang_name = lang_names.get(target_lang, target_lang)
    
    system_prompt = f"""당신은 다국어 텍스트 분석 및 요약 전문가입니다.
주어진 텍스트를 분석하고 {target_lang_name}로 요약해주세요.
원문이 어떤 언어이든 상관없이 {target_lang_name}로 핵심 내용을 요약합니다."""

    user_prompt = f"""다음 텍스트를 {target_lang_name}로 요약해주세요.
아래 JSON 형식으로 출력해주세요:

{{
  "detected_language": "감지된 원문 언어",
  "summary": "{target_lang_name} 요약 (200자 이내)",
  "key_points": ["핵심 포인트 1", "핵심 포인트 2", "핵심 포인트 3"],
  "keywords": ["키워드1", "키워드2", "키워드3", "키워드4", "키워드5"]
}}

텍스트:
{text}"""

    response = client.chat.completions.create(
        model="gpt-4o-mini",
        messages=[
            {"role": "system", "content": system_prompt},
            {"role": "user", "content": user_prompt}
        ],
        response_format={"type": "json_object"},  # JSON 모드 활성화!
        temperature=0.3,
        max_tokens=600
    )
    
    result = json.loads(response.choices[0].message.content)
    return result


# 사용 예시: 영어 뉴스를 한국어로 요약
english_article = """
Artificial intelligence is transforming the way we work and live. 
Recent advances in large language models have enabled machines to 
understand and generate human-like text with unprecedented accuracy...
"""

result = multilingual_summarize(english_article, target_lang="ko")
print(f"감지된 언어: {result['detected_language']}")
print(f"\n한국어 요약:\n{result['summary']}")
print(f"\n핵심 포인트:")
for point in result['key_points']:
    print(f"  • {point}")
print(f"\n키워드: {', '.join(result['keywords'])}")
💡 response_format={"type": "json_object"} 꿀팁! 이 옵션을 쓰면 GPT가 반드시 유효한 JSON을 반환해줘.
구조화된 데이터가 필요할 때 진짜 유용함! 단, 시스템 프롬프트나 유저 프롬프트에 "JSON" 언급이 있어야 작동해.

📊 실전 활용 사례들 — 이런 데 쓰면 대박!
🙋 야 이거 실제로 어디에 써먹을 수 있어?

ㅋㅋㅋ 진짜 활용처가 무궁무진해! 들어봐~
1
📰 뉴스 모니터링 시스템
특정 키워드 관련 뉴스를 자동 수집하고 요약해서 매일 아침 이메일로 받기.
기업 PR팀, 투자자, 리서처들이 많이 씀!
2
📚 논문 리뷰 자동화
arXiv, PubMed 등에서 논문 PDF를 받아서 연구 목적/방법/결과를 자동 요약.
연구자들의 문헌 검토 시간을 80% 이상 단축 가능!
3
🎥 유튜브 영상 요약
YouTube API로 자막 추출 후 GPT로 요약.
1시간짜리 강의도 5분 만에 핵심 파악 가능!
4
💼 회의록 자동 정리
음성 인식(Whisper API)으로 회의 녹음을 텍스트로 변환 후 GPT로 요약.
Whisper + GPT 조합이면 완전 자동화 가능!
5
🛒 고객 리뷰 분석
수천 개의 상품 리뷰를 요약하고 긍/부정 핵심 포인트 추출.
이커머스, 앱 서비스 등에서 UX 개선에 활용!
6
⚖️ 법률 문서 요약
계약서, 약관, 판례 등 복잡한 법률 문서를 쉬운 말로 요약.
단, 법적 효력이 있는 판단은 반드시 전문가 검토 필요!

이런 서비스들을 직접 만들어서 재능넷 같은 플랫폼에서 프리랜서로 제공하면
꽤 괜찮은 수익을 올릴 수 있어! AI 개발 재능을 거래하는 시장이 점점 커지고 있거든 😎


💰 비용 최적화 전략 — 돈 아끼는 법!

API 비용이 걱정된다면? 걱정 ㄴㄴ! 최적화 방법이 있어.

전략 방법 절감 효과
🎯 모델 선택 gpt-4o-mini 우선 사용, 복잡한 경우만 gpt-4o 최대 97% 절감
✂️ 텍스트 전처리 불필요한 공백, HTML 태그 제거 후 API 호출 10~30% 절감
💾 캐싱 동일 텍스트 재요약 방지 (Redis, SQLite 활용) 중복 요청 100% 절감
📏 프롬프트 최적화 시스템 프롬프트 간결하게 유지 5~15% 절감
🔢 배치 처리 Batch API 사용 (비동기, 50% 할인) 50% 절감
import hashlib
import sqlite3
import json

class SummaryCache:
    """SQLite 기반 요약 결과 캐싱"""
    
    def __init__(self, db_path: str = "summary_cache.db"):
        self.conn = sqlite3.connect(db_path, check_same_thread=False)
        self._init_db()
    
    def _init_db(self):
        self.conn.execute("""
            CREATE TABLE IF NOT EXISTS cache (
                text_hash TEXT PRIMARY KEY,
                mode TEXT,
                summary TEXT,
                created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
            )
        """)
        self.conn.commit()
    
    def _hash(self, text: str, mode: str) -> str:
        """텍스트 + 모드의 해시값 생성"""
        content = f"{mode}:{text}"
        return hashlib.sha256(content.encode()).hexdigest()
    
    def get(self, text: str, mode: str) -> str | None:
        """캐시에서 요약 결과 조회"""
        key = self._hash(text, mode)
        cursor = self.conn.execute(
            "SELECT summary FROM cache WHERE text_hash = ?", (key,)
        )
        row = cursor.fetchone()
        return row[0] if row else None
    
    def set(self, text: str, mode: str, summary: str):
        """요약 결과를 캐시에 저장"""
        key = self._hash(text, mode)
        self.conn.execute(
            "INSERT OR REPLACE INTO cache (text_hash, mode, summary) VALUES (?, ?, ?)",
            (key, mode, summary)
        )
        self.conn.commit()


# 캐시 적용된 요약 함수
cache = SummaryCache()

def cached_summarize(text: str, mode: str = "general") -> tuple[str, bool]:
    """
    캐시를 활용한 요약 함수
    
    Returns:
        (summary, from_cache) - 요약 결과와 캐시 여부
    """
    # 캐시 확인
    cached = cache.get(text, mode)
    if cached:
        print("⚡ 캐시에서 결과 로드 (API 호출 없음!)")
        return cached, True
    
    # API 호출
    summary = smart_summarize(text, mode=mode)
    
    # 캐시 저장
    cache.set(text, mode, summary)
    
    return summary, False

🔧 고급 기능 — Streaming 응답

긴 요약을 생성할 때 결과가 다 나올 때까지 기다리면 UX가 별로잖아.
스트리밍(Streaming)을 쓰면 ChatGPT처럼 글자가 실시간으로 출력돼! 😎

def stream_summarize(text: str) -> str:
    """
    스트리밍 방식으로 요약 결과를 실시간 출력
    ChatGPT처럼 글자가 하나씩 나타나는 효과!
    """
    
    print("📝 요약 생성 중...\n")
    print("-" * 40)
    
    full_response = ""
    
    # stream=True로 스트리밍 활성화
    with client.chat.completions.create(
        model="gpt-4o-mini",
        messages=[
            {"role": "system", "content": "당신은 전문 텍스트 요약 AI입니다."},
            {"role": "user", "content": f"다음 텍스트를 요약해주세요:\n\n{text}"}
        ],
        max_tokens=500,
        temperature=0.3,
        stream=True  # 스트리밍 활성화!
    ) as stream:
        for chunk in stream:
            if chunk.choices[0].delta.content is not None:
                content = chunk.choices[0].delta.content
                print(content, end="", flush=True)  # 실시간 출력
                full_response += content
    
    print("\n" + "-" * 40)
    return full_response


# Flask/FastAPI 웹 서버에서 스트리밍 응답 예시
from flask import Flask, Response, request, stream_with_context

app = Flask(__name__)

@app.route('/summarize/stream', methods=['POST'])
def summarize_stream():
    """스트리밍 요약 API 엔드포인트"""
    data = request.json
    text = data.get('text', '')
    
    def generate():
        with client.chat.completions.create(
            model="gpt-4o-mini",
            messages=[
                {"role": "system", "content": "당신은 전문 텍스트 요약 AI입니다."},
                {"role": "user", "content": f"다음 텍스트를 요약해주세요:\n\n{text}"}
            ],
            stream=True
        ) as stream:
            for chunk in stream:
                if chunk.choices[0].delta.content:
                    yield f"data: {chunk.choices[0].delta.content}\n\n"
        yield "data: [DONE]\n\n"
    
    return Response(
        stream_with_context(generate()),
        mimetype='text/event-stream',
        headers={
            'Cache-Control': 'no-cache',
            'X-Accel-Buffering': 'no'
        }
    )

if __name__ == '__main__':
    app.run(debug=True, port=5000)

🎓 마무리 — 이것만 기억해!
🙋 오늘 배운 거 너무 많은데... 핵심만 정리해줄 수 있어?

ㅋㅋㅋ 알겠어! 진짜 핵심만 정리해줄게~
🔑 📝 🚀 💡 ⚡

① 환경 세팅: pip install openai + .env로 API 키 관리 (절대 코드에 직접 넣지 말 것!)

② 프롬프트가 핵심: 목적에 맞는 시스템 프롬프트 + 유저 프롬프트 조합이 퀄리티를 결정함

③ 긴 텍스트: tiktoken으로 토큰 계산 → 청크 분할 → Map-Reduce 계층적 요약

④ 비용 최적화: gpt-4o-mini 우선 사용 + 캐싱 + 텍스트 전처리로 비용 최소화

⑤ 실전 기능: 비동기 처리, 에러 재시도, 스트리밍, 다국어 지원으로 프로급 시스템 완성

⑥ 활용: 뉴스 모니터링, 논문 리뷰, 회의록 정리, 고객 리뷰 분석 등 무궁무진!

이 정도면 진짜 실전에서 쓸 수 있는 자동 요약 시스템을 만들 수 있어!
처음엔 기본 코드부터 시작해서 조금씩 기능을 추가해나가는 게 베스트야.

AI 개발 관련 재능을 키워서 재능넷에서 프리랜서로 활동하거나,
자신만의 SaaS 서비스로 발전시켜보는 것도 완전 현실적인 목표야! 💪

OpenAI API는 계속 업데이트되고 있으니까 공식 문서(platform.openai.com/docs)도
주기적으로 체크하는 습관을 들이면 더 좋아. 새 모델이나 기능이 자주 추가되거든!

자, 이제 직접 만들어봐~ 코드 짜다가 막히면 GPT한테 물어보면 되잖아ㅋㅋㅋ 🎉

🐍 🤖 ✨ 🚀 💻
댓글 작성

이 글에 대한 여러분의 생각을 들려주세요

댓글 0
댓글을 불러오는 중입니다.

아직 댓글이 없습니다.