cheoly's language study blog

AI로 자동 요약 & 이메일 본문 작성 시스템, 하루 만에 MVP

프로그래밍/파이썬
반응형
SMALL

업무 보고서(PDF/DOCX)를 자동으로 읽고 핵심만 요약한 뒤, 수신자/상황에 맞춘 이메일 초안을 자동 생성하는 파이프라인을 Python과 LLM으로 구현합니다. 파일 파싱 → 청크 분할 → 요약 → 이메일 템플릿 생성 → 발송 전 검토까지 한 번에.

PDF 보고서가 AI 로봇을 거쳐 이메일로 변환되는 과정을 나타낸 일러스트. 중앙의 AI 로봇 아이콘을 중심으로 양쪽에 보고서와 이메일 아이콘이 배치되어 있으며, 아래에는 ‘AI로 자동 요약 & 이메일 본문 작성 시스템, 하루 만에 MVP’라는 문구가 적혀 있는 이미지.

1) 목표

  • 보고서(회의록/리포트)를 투입하면 요약본이메일 제목/본문 초안이 자동 생성.
  • 사용자는 검토만 하고 전송.
  • 재현 가능한 CLI 스크립트로 배치 실행(스케줄러 연동).

2) 아키텍처 개요

  1. 수집: input/ 폴더의 PDF, DOCX, TXT 로드
  2. 파싱: PDF → 텍스트, DOCX → 텍스트
  3. 전처리: 문단 기준 분할, 길이 제한(토큰/문자) 맞춰 청킹
  4. 요약: LLM으로 청크 요약 → 메타 요약(최종 TL;DR)
  5. 이메일 생성: 수신자/맥락/톤을 조건으로 제목+본문 작성
  6. 출력: output/yyyymmdd_xxx/summary.md, email_draft.md 저장
input/                  # 원본 보고서 위치
output/
  2025-11-11_0930/      # 실행 시각별 결과 폴더
    summary.md
    email_draft.md
config/
  profile.yaml          # 이메일 톤/수신자/금칙어/서명 등

3) 준비물

  • Python 3.10+
  • 패키지: pypdf2(또는 pdfminer.six), python-docx, tqdm, pyyaml, (선택) tenacity 재시도
  • LLM 제공자 SDK (예: OpenAI/Anthropic 등) 또는 로컬 모델(HuggingFace)
    코드는 모델-중립 인터페이스로 제공 (플러그 교체식)
pip install PyPDF2 python-docx tqdm pyyaml tenacity
# (사용 LLM에 맞춰 SDK 추가 설치)

4) 핵심 로직

  • 청킹 전략: 문단 기준 800~1200자(한글) 또는 400~800 토큰 단위
  • 요약 2단계: (1) 청크별 압축 요약 → (2) 청크 요약들을 다시 합쳐 메타 요약
  • 이메일 생성: 요약 + 수신자 역할 + 톤(격식/친근/임원 보고) + CTA(다음 액션) 반영

5) 예시 코드 (모델-중립)

아래는 “LLM 클라이언트” 인터페이스만 갈아끼우면 동작하는 구조예요.
실제 호출 부분은 YourLLM 클래스에서 LLM SDK에 맞게 구현하세요.

 

# file: ai_report_mail_mvp.py
import os, re, glob, datetime, textwrap, yaml
from dataclasses import dataclass
from typing import List
from tqdm import tqdm

# ---------- LLM 인터페이스(여기만 실제 SDK로 교체) ----------
class YourLLM:
    def __init__(self, model_name: str = "your-model"):
        self.model_name = model_name
    def complete(self, prompt: str, max_tokens: int = 1024) -> str:
        # TODO: 여기에 사용 LLM SDK 호출 코드 작성
        # e.g., OpenAI/Anthropic/HF 텍스트 생성
        # return client.generate(prompt, ...)
        raise NotImplementedError("Connect your LLM provider here.")

# ---------- 파일 파서 ----------
def read_txt(path: str) -> str:
    return open(path, "r", encoding="utf-8", errors="ignore").read()

def read_docx(path: str) -> str:
    from docx import Document
    doc = Document(path)
    return "\n".join([p.text for p in doc.paragraphs])

def read_pdf(path: str) -> str:
    from PyPDF2 import PdfReader
    reader = PdfReader(path)
    texts = []
    for page in reader.pages:
        texts.append(page.extract_text() or "")
    return "\n".join(texts)

def load_text(path: str) -> str:
    ext = os.path.splitext(path)[1].lower()
    if ext == ".txt":
        return read_txt(path)
    if ext == ".docx":
        return read_docx(path)
    if ext == ".pdf":
        return read_pdf(path)
    raise ValueError(f"Unsupported file type: {ext}")

# ---------- 전처리/청킹 ----------
def clean_text(s: str) -> str:
    s = s.replace("\r\n", "\n").replace("\r", "\n")
    s = re.sub(r"\n{3,}", "\n\n", s)
    return s.strip()

def split_paragraphs(s: str) -> List[str]:
    paras = [p.strip() for p in s.split("\n\n") if p.strip()]
    return paras

def chunk_by_chars(paras: List[str], max_chars: int = 1200) -> List[str]:
    chunks, buf = [], []
    size = 0
    for p in paras:
        if size + len(p) + 2 > max_chars and buf:
            chunks.append("\n\n".join(buf))
            buf, size = [], 0
        buf.append(p)
        size += len(p) + 2
    if buf:
        chunks.append("\n\n".join(buf))
    return chunks

# ---------- 요약 프롬프트 ----------
SUMMARY_PROMPT = """당신은 간결하고 정확한 전문 비서입니다.
아래 보고서 청크를 5줄 이내 핵심 bullet로 요약하고, 수치/결정/담당자/일정을 보존하세요.
가능하면 '결정사항/리스크/액션아이템' 세 영역으로 나눠주세요.

[청크]
{chunk}
"""

META_SUMMARY_PROMPT = """아래는 보고서의 부분 요약들입니다.
중복을 제거하고 전사에게 공유할 수 있는 최종 요약으로 8줄 이내로 통합하세요.
'핵심 요지', '결정사항', '리스크', '향후 일정/요청사항'을 소제목으로 구분하세요.

[부분 요약들]
{chunk_summaries}
"""

EMAIL_PROMPT = """당신은 한국어 비즈니스 이메일 작성 전문가입니다.
아래 최종 요약을 바탕으로 수신자({recipient_role})에게 보낼 메일 초안을 작성하세요.

조건:
- 제목: 1줄, 50자 이내, 핵심 키워드 포함
- 본문: 6~12줄, 결론 먼저(핵심/결정/요청 → 근거), 불필요한 수식어 금지
- 톤: {tone}
- CTA: 수신자가 취해야 할 다음 행동 2~3개 bullet
- 금칙어: {banned_words}

[최종 요약]
{final_summary}
"""

# ---------- 메인 파이프라인 ----------
@dataclass
class Profile:
    recipient_role: str = "팀장"
    tone: str = "격식 있고 간결하게"
    banned_words: str = "죄송;부탁;최대한;어쨌든"

def summarize_chunks(llm: YourLLM, chunks: List[str]) -> List[str]:
    results = []
    for c in tqdm(chunks, desc="Summarizing chunks"):
        prompt = SUMMARY_PROMPT.format(chunk=c)
        results.append(llm.complete(prompt))
    return results

def meta_summarize(llm: YourLLM, chunk_summaries: List[str]) -> str:
    joined = "\n\n---\n\n".join(chunk_summaries)
    prompt = META_SUMMARY_PROMPT.format(chunk_summaries=joined)
    return llm.complete(prompt, max_tokens=800)

def generate_email(llm: YourLLM, final_summary: str, profile: Profile) -> str:
    prompt = EMAIL_PROMPT.format(
        recipient_role=profile.recipient_role,
        tone=profile.tone,
        banned_words=profile.banned_words,
        final_summary=final_summary,
    )
    return llm.complete(prompt, max_tokens=800)

def ensure_dir(path: str):
    os.makedirs(path, exist_ok=True)

def save_text(path: str, content: str):
    with open(path, "w", encoding="utf-8") as f:
        f.write(content.strip() + "\n")

def main():
    # 1) 설정 로드
    cfg_path = "config/profile.yaml"
    if os.path.exists(cfg_path):
        cfg = yaml.safe_load(open(cfg_path, "r", encoding="utf-8"))
        profile = Profile(**cfg)
    else:
        profile = Profile()

    # 2) 입력 파일 수집
    files = []
    for ext in ("*.pdf", "*.docx", "*.txt"):
        files.extend(glob.glob(os.path.join("input", ext)))
    if not files:
        print("No input files in ./input")
        return

    # 3) LLM 준비 (여기 구현)
    llm = YourLLM(model_name="your-model")

    # 4) 출력 폴더
    stamp = datetime.datetime.now().strftime("%Y-%m-%d_%H%M")
    outdir = os.path.join("output", stamp)
    ensure_dir(outdir)

    # 5) 파일별 처리
    all_chunk_summaries = []
    for fp in files:
        raw = load_text(fp)
        txt = clean_text(raw)
        chunks = chunk_by_chars(split_paragraphs(txt), max_chars=1200)
        chunk_sums = summarize_chunks(llm, chunks)
        # 파일별 요약 저장(옵션)
        save_text(os.path.join(outdir, f"{os.path.basename(fp)}.chunksum.md"),
                  "\n\n---\n\n".join(chunk_sums))
        all_chunk_summaries.extend(chunk_sums)

    # 6) 메타 요약
    final_summary = meta_summarize(llm, all_chunk_summaries)
    save_text(os.path.join(outdir, "summary.md"), final_summary)

    # 7) 이메일 초안 생성
    email_md = generate_email(llm, final_summary, profile)
    save_text(os.path.join(outdir, "email_draft.md"), email_md)

    print(f"Done. See: {outdir}")

if __name__ == "__main__":
    main()

config/profile.yaml 예시

recipient_role: "본부장"
tone: "임원 보고 톤으로 간결하고 단정하게"
banned_words: "죄송;부탁;최대한;아무래도;일단"

6) LLM 연결 힌트

  • 위 YourLLM.complete()에 사용 중인 LLM SDK 호출만 채워 넣으면 됩니다.
  • 토큰 제한이 작은 모델은 max_chars를 낮추세요(예: 800자).

7) 배치/자동화

  • Windows: 작업 스케줄러에서 python ai_report_mail_mvp.py 매일 08:30 실행
  • macOS/Linux: crontab -e
30 8 * * 1-5 /usr/bin/python3 /path/ai_report_mail_mvp.py

8) 품질 팁

  • 금칙어/톤/CTA는 반드시 profile.yaml로 관리(조직별 가이드 반영).
  • 민감정보(인명/금액)는 요약 보존 규칙을 프롬프트에 명시.
  • 요약 정확도 검증을 위해 샘플 원문 → 요약 대조 체크리스트 운용.

9) 확장 아이디어

  • 수신자별 템플릿(영업/개발/임원) 스위치
  • 다국어 이메일(ko→en) 동시 생성
  • 메일 API 연동(초안 자동 업로드까지만, 발송은 사람 확인 후)
반응형
LIST

AI로 자동 보고서 요약 & 이메일 작성 시스템 구상기— 내일은 이걸 실제로 만들어본다!

프로그래밍/파이썬
반응형
SMALL

매일 반복되는 업무 보고서 정리와 이메일 작성.
이 두 가지를 AI가 대신 해준다면 얼마나 편할까?
오늘은 그 아이디어를 정리하고, 내일은 실제 구현에 들어간다.


---

1️⃣ 문제의식

회사나 팀 단위로 일을 하다 보면,
보고서가 쌓이고, 그걸 요약해서 메일로 보내야 할 때가 많다.
그런데 이 과정이 매우 비효율적이다.

같은 내용을 여러 번 복붙

포맷 맞추기, 표현 다듬기 반복

핵심은 한 줄인데 불필요한 내용이 길어짐


결국 시간이 많이 걸리고, 정확성도 떨어진다.


---

2️⃣ 내가 만들려는 시스템

내가 구상한 건 간단하다.

1. PDF나 DOCX 형태의 보고서를 AI가 읽고 요약


2. 그 요약을 바탕으로 이메일 제목과 본문을 자동으로 생성


3. 사용자는 내용 확인 후 보내기만 하면 끝



즉, 사람이 해야 할 건 “확인” 하나뿐이다.
모든 과정은 AI가 자동으로 처리한다.


---

3️⃣ 예상 흐름

input 폴더에 보고서 넣기

AI가 핵심 문장만 추출해 간결하게 요약

“팀장 보고용”, “본부장 보고용” 같은 톤 설정 가능

결과물은 요약문 + 이메일 초안 두 가지 파일로 저장


보고서를 요약하고, 자연스러운 이메일 문체로 정리하는 것까지 한 번에!


---

4️⃣ 기대 효과

보고서 요약 시간 10분 → 10초

메일 작성 스트레스 감소

반복 업무를 AI가 대신하면서 사람은 판단에 집중


특히 팀 단위에서 반복 보고가 많은 곳이라면
이런 자동화 시스템은 업무 효율을 크게 높여줄 것이다.


---

5️⃣ 내일 계획

내일은 실제로 이 시스템을 만들어볼 예정이다.
PDF를 읽고 텍스트로 바꾸는 파트부터,
AI가 문장을 요약하고 이메일 본문을 쓰는 파이프라인까지.

이 블로그에서 단계별 구현기로 나눠서 공유할 예정이니,
관심 있는 분들은 내일 포스트도 꼭 확인해보시길 🚀

반응형
LIST

퇴근 후 30분, 자동화로 하루를 정리하자 — 엑셀·PDF·이메일까지 한 번에!

프로그래밍/파이썬
반응형
SMALL

퇴근 전 반복되는 업무, 예를 들어 파일 정리·보고서 작성·이메일 전송 같은 일들은 생각보다 많은 시간을 잡아먹는다. 하지만 파이썬을 이용하면 이런 일상 업무를 자동화된 루틴으로 바꿀 수 있다. 이번 글에서는 ‘퇴근 후 30분’을 ‘자동화로 3분’으로 단축시키는 실제 방법을 소개한다.

파이썬으로 퇴근 전 자동화 루틴을 완성하는 직장인


매일 퇴근 전, 바탕화면에 흩어진 엑셀 파일을 정리하고
매출 데이터를 PDF로 만들어 보고서를 보내는 일.
이 모든 걸 자동화 루틴으로 전환할 수 있다면 어떨까?

오늘은 ‘퇴근 루틴 자동화’라는 주제로,
실제로 쓸 수 있는 파이썬 코드를 단계별로 다뤄보자.


1️⃣ 폴더 자동 정리 — 날짜별로 파일 분류하기

매일 생성되는 엑셀, PDF 파일을 날짜별로 정리하려면
os, shutil, datetime 모듈을 이용하면 된다.

import os, shutil
from datetime import datetime

base = "C:/Work"
today = datetime.now().strftime("%Y-%m-%d")
target = os.path.join(base, "정리", today)
os.makedirs(target, exist_ok=True)

for file in os.listdir(base):
    if file.endswith((".xlsx", ".pdf")):
        shutil.move(os.path.join(base, file), target)

print("파일 정리 완료:", target)

💡 팁:
이 코드를 daily_cleanup.py로 저장하고
윈도우 “작업 스케줄러”에 등록하면,
퇴근 10분 전에 자동 실행되도록 설정할 수 있다.


2️⃣ 엑셀 데이터에서 자동 보고서 만들기

매일 반복되는 매출 보고서, 수기 편집 대신 코드로 요약하자.

pandas로 데이터를 불러오고,
FPDF로 PDF 보고서를 생성하면 된다.

import pandas as pd
from fpdf import FPDF

# 엑셀 데이터 불러오기
df = pd.read_excel("sales.xlsx")

# 카테고리별 매출 합계
summary = df.groupby("Category")["Amount"].sum().reset_index()

# PDF 생성
pdf = FPDF()
pdf.add_page()
pdf.set_font("Arial", "B", 16)
pdf.cell(0, 10, "Daily Sales Report", ln=True)
pdf.set_font("Arial", "", 12)

for i, row in summary.iterrows():
    pdf.cell(0, 10, f"{row['Category']}: {row['Amount']:,}", ln=True)

pdf.output("sales_report.pdf")
print("PDF 보고서 생성 완료.")

이제 엑셀 파일을 직접 열지 않아도
매일 자동으로 sales_report.pdf가 폴더에 생성된다.


3️⃣ 이메일 자동 전송으로 마무리

마지막 단계는 이메일 전송이다.
보고서를 메일로 자동 발송하면,
“보내기” 버튼조차 누를 필요가 없다.

import smtplib
from email.mime.multipart import MIMEMultipart
from email.mime.base import MIMEBase
from email.mime.text import MIMEText
from email import encoders

sender = "me@company.com"
receiver = "team@company.com"

# 메일 작성
msg = MIMEMultipart()
msg["Subject"] = "오늘의 보고서"
msg["From"] = sender
msg["To"] = receiver

# 본문
body = "팀 여러분, 첨부된 오늘의 보고서를 확인해주세요."
msg.attach(MIMEText(body, "plain"))

# PDF 첨부
filename = "sales_report.pdf"
with open(filename, "rb") as f:
    part = MIMEBase("application", "octet-stream")
    part.set_payload(f.read())
encoders.encode_base64(part)
part.add_header("Content-Disposition", f"attachment; filename={filename}")
msg.attach(part)

# 메일 전송
with smtplib.SMTP("smtp.company.com", 587) as s:
    s.starttls()
    s.login("me@company.com", "password")
    s.send_message(msg)

print("이메일 발송 완료.")

💡 Tip:
회사 내부망이라 SMTP 접근이 어렵다면,
Outlook 자동화 (win32com.client)나 Google Gmail API를 사용하면 된다.


💡 마무리 — 자동화의 진짜 목적은 “여유”

자동화의 핵심은 ‘코드’가 아니라 ‘습관’이다.
매일 하던 일을 자동화하면,
그 시간에 하루를 정리하고 내일을 준비할 수 있다.

파이썬은 결국 “사람의 시간을 되돌려주는 도구”다.
퇴근 전 30분을 3분으로 줄이는 것,
그게 진짜 효율이고, 진짜 여유다.

다음 예고

다음 글에서는 이번 자동화 루틴을 한 단계 더 확장해
AI가 자동으로 보고서 요약을 작성하고, 이메일 본문을 작성해주는 시스템을 만들어볼 예정이다.

반응형
LIST