파이썬으로 AI 문서 요약과 번역 봇 만들기 - 긴 문서도 통째로
지난 편에서 짧은 글 하나를 AI한테 넘겨서 요약해봤잖아요. "핵심만 뽑아줘" 한마디에 세 줄로 정리해주는 거, 처음 봤을 땐 좀 신기하죠.
근데 실무에선 좀 다르잖아요. 요약하고 싶은 건 딱 세 줄짜리 메모가 아니라, 회의록 10장이거나 영문 보고서 30장이거든요. 그 긴 걸 어떻게 AI한테 먹이지? 지난 편 마지막에 "긴 문서는 다음 편에서" 하고 넘어갔던 게 바로 이 얘기예요.
이번엔 그 약속을 지킬 거예요. 그리고 하나 더. 지금까지 따로따로 배웠던 재료들이 여기서 딱 합쳐지거든요. 파일에서 글자 뽑는 법, AI한테 요약 시키는 법, 결과를 워드로 저장하는 법. 이 셋을 하나로 이으면 "문서만 넣으면 요약본이 툭 나오는 봇"이 완성돼요.
요약·번역을 함수로 만들어두기 (한 번 만들면 재사용)
먼저 지난 편에서 했던 OpenAI 호출을, 이번엔 함수라는 부품으로 만들어둘 거예요.
왜 굳이 함수로? 이게 이 글 전체를 관통하는 핵심이라서요. 한 번 잘 만들어두면 txt에도 갖다 쓰고, PDF에도 갖다 쓰고, 회의록에도 그냥 재사용하거든요. 믹서기랑 똑같아요. 재료가 사과든 당근이든, 넣기 좋게 손질만 해서 넣으면 믹서기는 항상 같은 방식으로 갈아주잖아요.
import os
from openai import OpenAI
from dotenv import load_dotenv
load_dotenv()
client = OpenAI(api_key=os.getenv("OPENAI_API_KEY")) # 지난 편과 똑같은 준비
def summarize(text): # 글을 넣으면 요약해주는 부품
r = client.chat.completions.create(
model="gpt-4o-mini", # 저렴하고 빠른 모델 (이름은 바뀔 수 있어요)
messages=[
{"role": "system", "content": "너는 긴 글을 핵심만 3줄로 요약하는 비서야. 존댓말로 답해."},
{"role": "user", "content": text}
],
temperature=0.2 # 요약은 낮게 (들쭉날쭉하지 않게)
)
return r.choices[0].message.content
def translate(text, target="영어"): # 번역 방향을 골라 쓰는 부품
r = client.chat.completions.create(
model="gpt-4o-mini",
messages=[
{"role": "system",
"content": f"다음 글을 자연스러운 {target}로 번역해. 직역 티 내지 말고, 전문용어와 고유명사는 그대로 둬."},
{"role": "user", "content": text}
],
temperature=0.3
)
return r.choices[0].message.content
보시면 summarize랑 translate가 뼈대는 거의 똑같아요. system 프롬프트 한 줄만 바꿨을 뿐이거든요. 같은 뼈대가 지시문만 갈아끼우면 요약가도 되고 번역가도 되는 거예요.
txt·PDF에서 텍스트 꺼내 AI에게 넘기기
부품을 만들었으니 이제 재료를 준비할 차례예요. 먼저 가장 쉬운 txt 파일부터요.
with open("회의록.txt", "r", encoding="utf-8") as f: # 파일 읽기 (앞 편에서 배웠죠)
text = f.read() # 파일 전체를 문자열 하나로
print(summarize(text)) # 재료를 문자열로만 만들면 뒤는 함수가 알아서
encoding="utf-8"은 한글이 깨지지 않게 하는 거예요. 이거 빼먹으면 가끔 글자가 와장창 깨지더라고요.
이번엔 PDF예요. 지난 시리즈에서 pdfplumber로 PDF 글자 뽑는 거, 이미 해봤잖아요. 그때 뽑아둔 글자를 이제 AI한테 넘기기만 하면 돼요.
import pdfplumber # PDF에서 글자 뽑는 도구
text = ""
with pdfplumber.open("보고서.pdf") as pdf: # PDF 열기
for page in pdf.pages: # 페이지를 하나씩 돌면서
text += page.extract_text() or "" # 글자를 뽑아 이어붙이기 (없으면 빈칸)
print(summarize(text)) # PDF든 txt든, 문자열로 만들었으니 같은 함수 재사용
여기서 잠깐, 두 코드의 마지막 줄 보이세요? print(summarize(text)). 완전히 똑같아요.
이게 부품 만들어두는 재미예요. 입력이 txt든 PDF든, 결국 문자열 하나로 만들어놓으면 그다음은 다 똑같거든요. 워드도 마찬가지고요. 형식이 뭐든 "문자열로 만드는 데까지"만 신경 쓰면 요약은 함수가 알아서 해줘요.
참고로 스캔한 이미지 PDF는 extract_text()가 빈 문자열을 뱉어요. 사진처럼 찍힌 PDF는 글자가 아니라 그림이라서 그래요. 텍스트를 복사할 수 있는 PDF인지 먼저 확인하는 게 좋아요.
긴 문서는 왜 통째로 안 들어갈까 - 토큰 한도와 청킹
자, 여기가 이 글의 진짜 알맹이예요.
회의록 30장을 통째로 넣고 실행하면, 이런 에러를 만나실 거예요.
openai.BadRequestError: maximum context length exceeded
"최대 길이를 넘었다"는 뜻이에요. 처음 이 에러 보면 좀 당황스러우실 거예요. 웹 챗지피티엔 긴 글도 잘만 붙여넣었는데 코드에선 왜 안 되지? 싶거든요.
이유는 이래요. AI한테도 한 번에 읽을 수 있는 분량의 한계가 있어요. 이걸 토큰 한도라고 불러요. 웹에서는 뒤에서 알아서 잘라서 넣어주는데, 코드에서는 내가 직접 잘라줘야 하는 거죠.
그래서 나온 발상이 청킹이에요. 어려운 말 같지만 별거 없어요.
두꺼운 책을 떠올려보세요. 500쪽짜리를 한 번에 다 외울 순 없잖아요. 그래서 우리는 챕터별로 읽고, 챕터마다 짧은 메모를 남겨요. 다 읽고 나면 그 메모들만 모아서 훑어보고 전체 줄거리를 만들죠.
AI도 똑같이 시키는 거예요.
긴 문서를 토막 내서 → 각 토막을 따로 요약하고 → 그 요약들을 다시 요약.
한 입에 못 삼키면 잘라서 먹는다. 이게 청킹이에요.
AI 문서 요약, 긴 문서용 요약기 만들기
말로 하면 어려워 보이는데, 코드로는 몇 줄이에요.
def summarize_long(text, chunk_size=3000): # 긴 글도 처리하는 요약기
# (1) 긴 글을 chunk_size 글자씩 토막 내기 (청킹)
chunks = [text[i:i+chunk_size] for i in range(0, len(text), chunk_size)]
# (2) 짧으면 굳이 나눌 것 없이 한 번에 요약하고 끝
if len(chunks) == 1:
return summarize(text)
# (3) 토막마다 따로 요약하기 (챕터별 메모 만들기)
partial = []
for c in chunks:
partial.append(summarize(c)) # 각 토막의 요약을 차곡차곡 모음
# (4) 토막 요약들을 이어붙여 한 번 더 요약 (전체 줄거리)
combined = "\n".join(partial)
return summarize(combined) # 재요약
주석에 붙여둔 (1)~(4)를 아까 책 비유랑 나란히 놓아볼게요.
- (1) 책을 챕터로 나누기 → 글자 수로 토막 내기
- (2) 얇은 책이면 그냥 통째로 읽기 → 짧으면 한 번에 요약
- (3) 챕터마다 메모 남기기 → 토막마다 따로 요약
- (4) 메모들 모아 줄거리 만들기 → 요약들을 다시 요약
딱 1대1로 맞아떨어지죠. 여기까지 이해되면 이 글의 8할은 끝난 거예요.
chunk_size=3000은 3000자씩 자르라는 뜻이에요. 토막을 더 정밀하게 세고 싶으면 토큰 단위로 자르는 방법도 있는데, 입문 단계에선 글자 수로 충분해요. 문단(\n\n) 기준으로 자르면 문장이 중간에 뚝 끊기지 않아서 더 매끄럽긴 한데, 그건 나중에 다듬어도 되고요.
청킹은 요금 할인 쿠폰이 아니에요
여기서 많이들 오해하는 게 하나 있어요. "긴 걸 잘라서 넣으니까 돈이 덜 들겠지?"
아니에요. 오히려 반대예요.
청킹은 한도를 넘겨서 아예 실패하는 걸 막아주는 우회로지, 요금을 깎아주는 게 아니거든요. 토막이 5개면 요약 호출을 5번, 거기에 재요약 1번까지 해서 오히려 호출 수가 늘어나요. 시간도 조금 더 걸리고요.
그렇다고 겁먹을 정도는 아니에요. 저렴한 모델 쓰면 문서 하나 요약하는 데 여전히 몇 원에서 몇십 원 수준이거든요. 다만 30장짜리를 곧장 돌리기 전에, 2~3장으로 리허설 한 번 해보는 걸 추천해요. 지난 편에서 걸어둔 사용량 한도도 그대로 있는지 확인하고요.
참고로 토막을 너무 빠르게 연달아 호출하면 속도 제한(RateLimitError)에 걸릴 때가 있어요. 그럴 땐 토막 사이에 time.sleep(1) 한 줄 넣어서 잠깐 숨 돌리게 해주면 돼요.
파이썬 번역 봇, 자연스럽게 번역하는 프롬프트 만들기
번역도 원리는 같아요. 아까 만든 translate 함수 그대로 쓰면 되고요.
with open("영문보고서.txt", "r", encoding="utf-8") as f:
eng = f.read()
print(translate(eng, target="한국어")) # 방향만 바꾸면 영→한도 그대로
근데 번역은 그냥 "번역해줘"만 하면 좀 애매해요. 어떤 말투로 할지, 전문용어는 어떻게 처리할지 안 정해주면 결과가 들쭉날쭉하거든요.
자동번역기랑 전문 통역사의 차이를 생각하면 쉬워요. 자동번역기는 단어를 기계적으로 바꾸지만, 통역사는 "이건 비즈니스 이메일이니 격식 있게, 이 제품명은 원어 그대로" 같은 맥락을 챙기잖아요. 그 맥락을 프롬프트로 미리 일러주면, API 번역이 통역사처럼 굴어요.
그래서 아까 translate의 system 프롬프트에 "직역 티 내지 말고, 전문용어와 고유명사는 그대로 둬"를 넣어둔 거예요. 여기에 "정중한 비즈니스 이메일체로" 한 줄만 더 붙여도 결과가 확 달라져요.
프롬프트가 구체적일수록 결과가 쓸모 있어져요
이건 요약도 마찬가지예요. 같은 문서, 같은 모델이어도 어떻게 시키느냐로 결과가 갈리거든요.
회의록으로 예를 들어볼게요. 그냥 "요약해줘" 하면 두루뭉술한 문단이 나와요. 근데 형식을 딱 지정해주면?
def summarize_meeting(text):
r = client.chat.completions.create(
model="gpt-4o-mini",
messages=[
{"role": "system", "content":
"너는 회의록 정리 담당이야. 다음 형식으로만 정리해:\n"
"【핵심 요약】 3줄\n【결정사항】 불릿\n【담당자별 할 일】 이름: 할 일"},
{"role": "user", "content": text}
],
temperature=0.2
)
return r.choices[0].message.content
# 실행 결과 예시:
# 【핵심 요약】
# - 신제품 출시일을 9월로 확정함
# - 마케팅 예산을 300만 원 증액하기로 함
# - 디자인 시안은 다음 주까지 재검토
# 【결정사항】
# - 출시일: 9월 15일
# - 예산: 총 1,300만 원
# 【담당자별 할 일】
# - 김대리: 시안 수정
# - 박과장: 예산안 재작성
"바빠서 다 못 읽어, 결정할 것만 뽑아줘" 하고 유능한 비서한테 시키는 거랑 같아요. 지시가 구체적일수록 결과물이 바로 쓸 만해지죠. 이게 프롬프트를 잘 쓰는 감각이에요.
결과를 파일로 저장하기
화면에 print만 하면 결과가 스르륵 사라지잖아요. 파일로 저장하면 완성품이 남아요.
result = summarize_long(text) # 긴 문서 요약 결과
# (a) txt로 저장 - 가장 간단
with open("요약결과.txt", "w", encoding="utf-8") as f:
f.write(result)
# (b) 워드로 저장 - 앞 편에서 배운 python-docx 연결
from docx import Document
doc = Document()
doc.add_heading("문서 요약본", level=1)
doc.add_paragraph(result)
doc.save("요약결과.docx") # 상사에게 바로 보낼 워드 완성
open(..., "w")에서 "w"가 "쓰기 모드"예요. 여기까지 오면, 문서를 넣어서 요약하고 워드로 딱 떨궈서 바로 보내는 흐름이 완성돼요.
그럴듯하게 틀리는 함정 하나
마지막으로 꼭 짚고 넘어갈 게 있어요.
AI 요약이랑 번역은 결과가 너무 매끄러워서, 다 맞는 줄 알기 쉬워요. 근데 가끔 원문에 없는 내용을 슬쩍 지어내거나, 번역하면서 숫자나 날짜를 바꿔놓을 때가 있거든요. 이걸 환각이라고 불러요.
에러는 빨간 글씨로 티라도 나잖아요. 환각은 조용해서 더 위험해요.
그래서 습관 하나만 들이세요. 숫자, 날짜, 고유명사, 계약 조건처럼 틀리면 안 되는 건 사람이 원문이랑 대조. AI는 똑똑한 인턴이지 최종 결재자가 아니에요. 이거 하나만 지켜도 사고 날 일이 확 줄어요.
직접 해보기
머리로만 읽으면 안 남아요. 짧게라도 손으로 해보세요.
- 본인 회의록이나 메일 하나를 txt로 저장해서
summarize()로 3줄 요약해보기. - 같은 문서를 system 프롬프트만 "결정사항과 할 일만 나눠서"로 바꿔 다시 돌려보고, 결과가 어떻게 달라지는지 비교하기.
- 짧은 영문 문단 하나를
translate(text, "한국어")로 번역해보고, "정중한 비즈니스체로" 지시를 넣었을 때와 비교하기. - (도전) 좀 긴 문서를
summarize_long으로 돌려보고, 안 잘랐을 때 나던 한도 초과 에러가 사라지는지 확인하기.
힌트를 드리면, 2번에서 형식을 지정한 쪽이 훨씬 바로 쓸 만할 거예요. "아, 프롬프트를 이렇게 쓰는 거구나" 하는 감이 그때 와요.
이제 문서만 넣으면 요약하고 번역해주는 도구가 손에 생겼어요. 근데 하나 아쉬운 게 있죠. 매번 직접 실행 버튼을 눌러야 하잖아요. 이걸 "매일 아침 9시에 알아서" 돌게 만들면 그게 진짜 자동화예요. 내가 잠든 사이 알아서 도는 봇, 그 방법을 다음 편에서 다룰 거예요.
제 글이 도움이 되셨다면 댓글 & 공감 부탁드려요 😀
'Application > Python' 카테고리의 다른 글
| [Python] 24. 파이썬 로깅과 에러 알림: 봇이 죽지 않게 지키기 (0) | 2026.08.13 |
|---|---|
| [Python] 23. 파이썬 스케줄링: schedule 모듈로 정기 실행하기 (0) | 2026.08.10 |
| [Python] 21. 파이썬으로 ChatGPT API 호출하기 - AI에게 텍스트 처리 자동으로 시키기 (0) | 2026.08.04 |
| [Python] 20. 파이썬 뉴스 봇 만들기 - 매일 아침 관심 뉴스 요약 메일 받기 (1) | 2026.08.03 |
| [Python] 19. 파이썬 알림 봇 만들기 - 슬랙과 카카오톡으로 실시간 알림 받기 (1) | 2026.07.31 |