19장. MLOps 와 LLMOps, 워크플로 자동화
실습 2 — 바뀐 문서만 찾기
원본 시스템이 밤마다 도서관 안내 문서 전체를 library/dayN/ 폴더로 내보낸다고 봅니다. 모든 문서를 다시 임베딩하지 않도록, 내용의 지문으로 바뀐 문서와 조각만 찾습니다. 마지막에는 내보내기가 망가졌는지 보는 검사를 다섯 밤의 폴더에 돌려 봅니다.
1단계. 문서마다 지문 만들기
import hashlib
from pathlib import Path
import pandas as pd
def fingerprint(text):
"""내용의 지문: sha256 의 앞 12글자. 한 글자만 달라도 바뀐다"""
return hashlib.sha256(text.encode("utf-8")).hexdigest()[:12]
def snapshot(day):
"""그 밤에 내보낸 문서 전체: {문서 이름: 글}"""
return {f.stem: f.read_text(encoding="utf-8") for f in sorted(Path(f"library/day{day}").glob("*.txt"))}
docs0 = snapshot(0)
manifest0 = {name: fingerprint(text) for name, text in docs0.items()}
# 결과: 문서 수와 앞의 세 문서의 지문
print(len(manifest0), "개 문서")
for name in list(manifest0)[:3]:
print(name, manifest0[name])10 개 문서
대출과반납 a21b20b3ba69
복사출력 a9c7913c1b83
분실물 f1593e427b332단계. 두 밤의 문서 견주기
def diff(old, new):
"""두 지문 목록을 견줘 문서마다 추가·수정·삭제·그대로를 정한다"""
rows = []
for name in sorted(set(old) | set(new)):
if name not in old:
kind = "추가"
elif name not in new:
kind = "삭제"
elif old[name] != new[name]:
kind = "수정"
else:
kind = "그대로"
rows.append({"문서": name, "변경": kind})
return pd.DataFrame(rows)
manifest1 = {name: fingerprint(text) for name, text in snapshot(1).items()}
d = diff(manifest0, manifest1)
# 결과: 바뀐 문서만, 그리고 변경 종류마다의 수
print(d[d["변경"] != "그대로"].to_string(index=False))
print(d["변경"].value_counts().to_dict()) 문서 변경
대출과반납 수정
상호대차 추가
전자책 추가
휴관일 수정
{'그대로': 8, '수정': 2, '추가': 2}3단계. 조각 단위로 견주기
def split_chunks(docs):
"""문서를 문단 조각으로 나눈다. 첫 문단은 제목이므로 빼고, 조각 앞에 문서 이름을 붙인다"""
out = []
for name, text in docs.items():
paras = [p.strip() for p in text.split("\n\n") if p.strip()]
out += [f"{name}: {p}" for p in paras[1:]]
return out
chunks0 = {fingerprint(c) for c in split_chunks(docs0)}
chunks1 = split_chunks(snapshot(1))
# 새로 임베딩할 조각: 어젯밤 조각의 지문에 없는 것
new = [c for c in chunks1 if fingerprint(c) not in chunks0]
print(f"오늘 조각 {len(chunks1)}개 가운데 새로 임베딩할 조각 {len(new)}개")
for c in new:
print(" -", c[:40])오늘 조각 35개 가운데 새로 임베딩할 조각 7개
- 대출과반납: 책은 한 사람이 7권까지 빌릴 수 있습니다. 대출 기간은 1
- 상호대차: 우리 구의 다른 구립 도서관에 있는 책을 하늘숲 도서관에서 받
- 상호대차: 상호대차는 도서관 홈페이지에서 신청하고, 한 사람이 3권까지
- 상호대차: 신청한 책은 보통 3일에서 5일 뒤에 도착하며, 도착하면 문자
- 전자책: 전자책은 도서관 앱에서 빌려 휴대폰이나 태블릿으로 읽습니다.
- 전자책: 전자책은 한 사람이 5권까지 빌릴 수 있고, 14일이 지나면 저
- 휴관일: 해마다 12월 첫째 주 목요일과 금요일은 장서 점검으로 쉽니다.4단계. 내보내기가 망가졌는지 검사하기
def validate(docs, prev_count):
"""내보내기 검사. 문제 목록이 비어 있으면 통과"""
problems = []
if len(docs) < prev_count * 0.8: # 문서가 갑자기 20% 넘게 줄었다
problems.append(f"문서 수 {prev_count}→{len(docs)}")
empty = [n for n, t in docs.items() if not t.strip()]
if empty:
problems.append(f"빈 파일 {empty}")
# '�'(�)는 글자를 읽지 못해 바꿔 넣은 글자다. 1% 넘게 섞였으면 인코딩이 깨진 것
broken = [n for n, t in docs.items() if t and t.count("�") / len(t) > 0.01]
if broken:
problems.append(f"깨진 글자 {broken}")
return problems
rows, prev = [], len(docs0)
for day in range(5):
docs = snapshot(day)
problems = validate(docs, prev)
rows.append({"밤": day, "문서": len(docs), "검사": "통과" if not problems else "멈춤", "문제": "; ".join(problems)})
if not problems:
prev = len(docs)
# 결과: 다섯 밤의 검사 결과
print(pd.DataFrame(rows).to_string(index=False)) 밤 문서 검사 문제
0 10 통과
1 12 통과
2 12 통과
3 12 멈춤 빈 파일 ['분실물']; 깨진 글자 ['연체', '예약', '프로그램', '회원증']
4 12 통과이 실습이 뜻하는 것
이 실습은 밤마다 통째로 새로 인쇄되어 오는 도서관 안내문 묶음에서 바뀐 것만 골라내는 일과 같습니다. 안내문마다 내용으로 만든 지문 도장을 찍고(1단계), 어젯밤 묶음과 견줘 새로 생긴 것과 고쳐진 것을 고릅니다(2단계). 고쳐진 안내문 안에서도 실제로 바뀐 문단만 골라내고(3단계), 빈 종이나 깨진 글자처럼 인쇄가 망가진 밤을 알아채는 검사를 다섯 밤에 돌려 봅니다(4단계). 여기서 지문 도장이 SHA-256(Secure Hash Algorithm 256비트) 지문, 견주기가 diff(), 바뀐 문단이 새로 임베딩할 조각, 인쇄 검사가 validate() 입니다. 비유와 다른 점은, 사람의 지문과 달리 이 지문은 글자 하나만 바뀌어도 전혀 다른 값이 된다는 것입니다.
| 단계 | 출력 | 뜻 |
|---|---|---|
| 1. 지문 | 10 개 문서, 대출과반납 a21b20b3ba69 | 문서마다 12글자 지문이 생겼습니다. 같은 글이면 늘 같은 지문, 한 글자라도 다르면 다른 지문입니다 |
| 2. 견주기 | 대출과반납·휴관일 수정, 상호대차·전자책 추가 | day0 에서 day1 로 넘어가며 문서 둘이 고쳐지고 둘이 새로 생겼습니다 |
| 2. 합계 | {'그대로': 8, '수정': 2, '추가': 2} | 12개 가운데 8개는 그대로라 다시 처리할 필요가 없습니다 |
| 3. 조각 | 오늘 조각 35개 가운데 새로 임베딩할 조각 7개 | 바뀐 문서는 넷이지만 실제로 새로 만들 조각은 7개뿐입니다. 나머지 28개는 어제 것을 다시 씁니다 |
| 3. 바뀐 문단 | 7권까지, 12월 첫째 주 | 고쳐진 대출과반납과 휴관일에서는 대출 권수 문단과 장서 점검 문단만 새 조각이 되었습니다 |
| 4. 검사 | 밤 0, 1, 2, 4 통과 · 밤 3 멈춤 | 밤 3에는 빈 파일(분실물)과 글자가 깨진 문서 넷이 있어 멈췄습니다 |
한 줄로 줄이면, 밤마다 통째로 오는 문서에서 "무엇이 바뀌었나"를 내용의 지문으로 찾아, 다시 임베딩할 양을 35개에서 7개로 줄이는 실습입니다. 이 지문과 검사는 실습 3의 색인 갱신에 그대로 쓰입니다. 다만 밤 2가 통과했다고 그 밤의 문서가 옳다는 뜻은 아닙니다. 검사는 형식의 고장만 보므로, 글이 멀쩡한 옛 판으로 덮인 밤 2는 프로젝트의 평가 게이트가 잡습니다.
코드 설명
hashlib.sha256(...).hexdigest()[:12]는 내용의 지문입니다. 파일의 시각이 아니라 내용으로 만들기 때문에 복사만 한 파일은 같은 지문이 됩니다.diff()는 두 밤의 지문 목록을 견줘 추가, 수정, 삭제, 그대로를 나눕니다. 밤마다 전체를 내보내는 방식이라 삭제도 알아챌 수 있습니다.- 조각마다 지문을 만들면, 수정된 문서에서도 실제로 바뀐 문단만 새로 임베딩하면 됩니다.
validate()는 문서 수의 급감, 빈 파일, 깨진 글자(�)를 봅니다. 형식의 고장은 잡지만, 글이 멀쩡한 옛 판으로 덮인 것(day2)은 잡지 못합니다. 그것은 프로젝트의 평가 게이트가 맡습니다.