Metadata-Version: 2.4
Name: rag-lab-bsg
Version: 0.1.4
Summary: Bronze / Silver / Gold 텍스트 처리 실습 라이브러리
Author-email: Park Un Woo <uwpark@simplatform.com>
License: MIT
Keywords: bronze-silver-gold,langchain,rag,text-splitter
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
Requires-Python: >=3.10
Requires-Dist: langchain-community>=0.3.0
Requires-Dist: langchain-text-splitters>=0.3.0
Requires-Dist: pypdf>=4.0.0
Provides-Extra: notebook
Requires-Dist: ipykernel>=6.0; extra == 'notebook'
Requires-Dist: jupyterlab>=4.0; extra == 'notebook'
Description-Content-Type: text/markdown

# rag_lab

Bronze / Silver / Gold 3단계로 텍스트를 처리하는 RAG 실습용 라이브러리.

## 설치

```bash
pip install rag-lab-bsg
```

설치 패키지명은 `rag-lab-bsg` 이지만, 임포트 이름은 `rag_lab` 입니다.

노트북 실습용으로 로컬에서 개발 모드로 설치하려면 프로젝트 루트(`code/`)에서 다음을 한 번만 실행합니다.

```bash
uv pip install -e ".[notebook]"
```

`-e` 로 설치하면 `rag_lab/` 코드를 고칠 때마다 다시 설치할 필요가 없습니다.

## 단계 요약

| 단계 | 함수 | 입력 | 출력 | 핵심 동작 |
|---|---|---|---|---|
| Bronze | `bronze.to_text(input_path)` | PDF / TXT / MD | `data/dummy/bronze.txt` | 원본 보존, 페이지 사이는 빈 줄로만 구분 |
| Silver | `silver.split(input_path)` | bronze.txt | `data/dummy/silver.jsonl` | `CharacterTextSplitter` — 의미 무시, 글자 수로 분할 |
| Gold   | `gold.split(source)`         | silver.jsonl **또는** 문자열 | `data/dummy/gold.jsonl` | `RecursiveCharacterTextSplitter` — 단락 → 줄 → 종결 → 어절 |

각 단계의 함수는

- 다음 단계 입력으로 쓸 산출물(.txt / .jsonl) 경로를 반환하고
- 호출 결과를 화면에도 짧게 출력합니다.

## 요구 사항

- Python 3.10+
- `langchain-community`, `langchain-text-splitters`, `pypdf`

---

# 실습 가이드 — Bronze → Silver → Gold

`practice.ipynb` 의 실행 흐름을 그대로 옮긴 단계별 실습 가이드입니다. `rag_lab` 라이브러리를 import 해서 세 단계를 직접 실행해 봅니다.

## 사전 준비

```python
from pathlib import Path

from rag_lab import bronze, silver, gold
```

## 1. Bronze — 원본 그대로 .txt 로 저장

`data/input/` 에 PDF 를 넣어 두면 그 PDF 를 쓰고, 없으면 아래 코드에서 샘플 텍스트 파일을 만들어 그것을 입력으로 씁니다.

```python
INPUT_DIR = Path("data/input")
pdfs = sorted(INPUT_DIR.glob("*.pdf")) if INPUT_DIR.exists() else []

if pdfs:
    input_path = pdfs[0]
else:
    input_path = Path("data/dummy/_sample.txt")
    input_path.parent.mkdir(parents=True, exist_ok=True)
    input_path.write_text(
        "임플란트 시술 매뉴얼 (샘플)\n\n"
        "1. 개요\n임플란트는 치아가 결손된 부위에 인공 치근을 식립하여 자연치와 유사한 기능을 회복하는 치료법이다. "
        "시술은 진단, 식립, 보철 단계로 구분되며 각 단계마다 정밀한 계획이 필요하다.\n\n"
        "2. 적응증\n- 단일 치아 결손\n- 부분 무치악\n- 완전 무치악\n"
        "환자의 골량과 골질, 전신 건강 상태를 종합적으로 평가한 후 시술 여부를 결정한다.\n\n"
        "3. 시술 절차\n3.1 진단 단계\n구강 검사, 파노라마 방사선 촬영, CBCT 영상을 통해 식립 위치를 결정한다. "
        "보철 계획을 먼저 수립한 뒤 역으로 식립 위치를 정하는 보철 주도형 진단이 권장된다.\n"
        "3.2 식립 단계\n국소마취 후 점막을 절개하고 드릴링하여 픽스처를 식립한다. 일차 안정성이 확보되어야 한다.\n"
        "3.3 보철 단계\n골 유착 기간(일반적으로 3~6개월) 이후 인상 채득, 지대주 연결, 최종 보철물 장착으로 마무리된다.\n",
        encoding="utf-8",
    )

bronze_txt = bronze.to_text(input_path)
bronze_txt
```

**실행 결과 예시:**

```
[Bronze] 누비슨 플랫폼 활용 부정 방지 전략.pdf → data/dummy/bronze.txt  (1,848자, PDF, 3페이지 합본)

PosixPath('data/dummy/bronze.txt')
```

생성된 텍스트는 다음과 같이 확인합니다.

```python
print(bronze_txt.read_text(encoding="utf-8"))
```

## 2. Silver — 의미와 무관하게 글자 수로 단순 분할

`chunk_size=500` 으로 잘라서 다음 단계(Gold)에 넘길 기준 산출물을 만듭니다.

```python
silver_jsonl = silver.split(bronze_txt, chunk_size=500, chunk_overlap=30)
silver_chunks = silver.load_chunks(silver_jsonl)

for c in silver_chunks:
    print(f"#{c['chunk_id']} ({c['char_len']}자) \n {c['text']}...")
    print("========================================================================================")
```

**실행 결과 예시:**

```
[Silver] bronze.txt → data/dummy/silver.jsonl  (4청크, 평균 478자, 최대 500자, chunk_size=500, overlap=30)
#0 (499자)
 누비슨 플랫폼 활용한 부정 방지 전략 및 인프라 제안 ...
#1 (484자)
 데이터를수집합니다.    육안으로는확인하기힘든 ...
#2 (500자)
 ion):Edge AI 단계. ...
#3 (430자)
 에업데이트하여차기경주시예측정확도를높이는학습 ...
```

> `CharacterTextSplitter` 는 우선 separator(기본 `""`) 로 잘라 낸 뒤에야 `chunk_size` 를 적용하므로, separator 가 비어 있으면 글자 수에서 딱 끊깁니다. 한국어 문장 중간이 어색하게 잘리는 빈도를 직접 눈으로 확인하는 것이 이 단계의 학습 포인트입니다.

## 3. Gold — 의미 단위로 재귀 분할

`gold.split` 은 두 가지 입력을 모두 받습니다.

- **(a) Silver 의 .jsonl 경로** — 청크들을 다시 이어붙여 원본 텍스트로 복원한 뒤 분할
- **(b) raw 텍스트 문자열** — `silver_chunks[0]['text']` 처럼 청크 한 개만 따로 넘기는 경우

`separators` 우선순위(단락 → 줄 → 종결 → 어절) 대로 잘라 내므로, 같은 `chunk_size` 라도 Silver 보다 단락/문장 경계가 살아 있을 확률이 높습니다.

여기서는 **Silver 의 첫 번째 청크 하나만** 다시 의미 단위로 잘게 잘라 봅니다.

```python
gold_jsonl = gold.split(silver_chunks[0]['text'], chunk_size=100, chunk_overlap=30)
gold_chunks = gold.load_chunks(gold_jsonl)

for c in gold_chunks:
    print(f"#{c['chunk_id']} ({c['char_len']}자) \n {c['text']}...")
    print("========================================================================================")
```

**실행 결과 예시:**

```
[Gold] <text 499자> → data/dummy/gold.jsonl  (8청크, 평균 60자, chunk_size=100, overlap=30)
#0 (88자)
 누비슨 플랫폼 활용한 부정 방지 전략 및 인프라 제안 ...
#1 (58자)
 단순한'  사후적발'   을넘어 '     사전예방및실시간차단 ' ...
#2 (68자)
 누비슨플랫폼을활용해부정행위를방지하고예측하는구체적인전략을제안합니다 . ...
#3 (44자)
 선수와자전거에부착된센서를통해신체데이터와물리적데이터를실시간으로수집하여분석합니다 . ...
#4 (58자)
 ●   생체데이터모니터링 :  선수의심박수, ...
...
```

> Silver `chunk_size=500` 으로 만든 한 청크(499자)를 Gold `chunk_size=100` 으로 다시 자르면 **8개 청크, 평균 60자**가 나옵니다. 단락 / 줄 / `다. ` / `요. ` 구분자가 우선순위대로 적용되어 의미 경계에서 끊기는 빈도가 올라갑니다.

### Silver vs Gold 비교

청크 개수, 평균/최소/최대 길이를 한눈에 비교해 봅니다.

```python
def stats(chunks):
    lens = [c["char_len"] for c in chunks] or [0]
    return {
        "개수": len(chunks),
        "평균": round(sum(lens) / len(lens), 1),
        "최소": min(lens),
        "최대": max(lens),
    }

print("Silver:", stats(silver_chunks))
print("Gold  :", stats(gold_chunks))

print("\n— Silver #0 ——————————————")
print(silver_chunks[0]["text"])
print("\n— Gold #0 ————————————————")
print(gold_chunks[0]["text"])
```

**실행 결과 예시:**

```
Silver: {'개수': 4, '평균': 478.2, '최소': 430, '최대': 500}
Gold  : {'개수': 8, '평균': 60.5, '최소': 27, '최대': 88}
```

같은 원본 일부(Silver #0, 499자)를 Gold 가 다시 평균 60자 단위로 잘게 쪼개면서, **시작 지점이 자연스러운 단락 경계**(`누비슨 플랫폼 활용한 부정 방지 전략 및 인프라 제안` 으로 시작) 에 맞춰진 것을 확인할 수 있습니다.

## 4. 직접 바꿔 보기

다음을 바꿔 가며 결과 차이를 관찰해 봅니다.

- Silver `chunk_size` 를 100 / 300 / 800 등으로 바꿔 청크 수와 평균 길이가 어떻게 달라지는지
- Gold `separators` 리스트에서 `"다. "`, `"요. "` 를 맨 앞으로 옮기면 한국어 종결 경계가 얼마나 더 잘 살아나는지
- Silver 의 다른 청크(`silver_chunks[1]['text']` 등) 를 Gold 에 넣어 단락별로 어떻게 잘리는지

`separators` 순서를 바꿔서 Gold 결과를 비교하는 예시입니다.

```python
gold.split(
    silver_chunks[0]['text'],
    output_path="data/dummy/_gold_custom.jsonl",
    chunk_size=100,
    chunk_overlap=30,
    separators=["다. ", "요. ", "\n\n", "\n", ". ", " ", ""],
)
```
