
문서의 텍스트와 메타데이터를 추출하는 초기 단계입니다. Spring AI에서는 주로 PDFBox 또는 Tika 기반의 래퍼를 사용합니다.
Apache Tika는 천 가지 이상의 파일 형식을 파싱할 수 있는 통합 인터페이스입니다.
PDF 파싱 시에는 내부적으로 PDFBox를 사용하며, OCR을 위해 Tesseract와 같은 외부 엔진을 연결할 수 있습니다.
Tika + Tesseract OCR 설정
첨부된 이미지 코드는 Tika를 사용하여 PDF를 파싱하고 Tesseract OCR을 설정하는 방법을 보여줍니다.
val tika = AutoDetectParser() // Tika 자동 감지 파서
val ctx = ParseContext() // 파싱 컨텍스트
// 1. PDF 파서 설정: OCR 전략을 AUTO로 설정
ctx.set(PDFParserConfig::class.java, PDFParserConfig().apply {
ocrStrategy = PDFParserConfig.OCR_STRATEGY.AUTO
})
// 2. Tesseract OCR 설정: 실행 파일 경로와 인식 언어 지정
ctx.set(TesseractOCRConfig::class.java, TesseractOCRConfig().apply {
tesseractPath = "/usr/bin" // Tesseract 실행 파일 경로
language = "eng+kor" // 영어와 한국어 동시 인식 설정
})
val handler = BodyContentHandler(-1)
val metadata = Metadata()
// Tika를 사용하여 파일 파싱 실행
tika.parse(FileInputStream(file), handler, metadata, ctx)
val content = handler.toString()
// 파싱된 텍스트와 메타데이터를 Document 객체로 변환
val doc = Document(content, metadata.map { it.key to it.value }.toMap())
| 파서 유형 | 주요 특징 | 한계점 및 주의사항 |
|---|---|---|
| PDFBox 래퍼 | 상하단 마진 설정을 통해 스캔 범위 제어 가능. | OCR 기능 미제공, 동시성 취약, 초기 로딩 시 전체 메타데이터 생성으로 인한 속도 저하. |
| Tika 래퍼 | 다양한 파일 포맷 지원. | PDFBox 옵션 미지원. Spring AI 기본 리더에서는 OCR 옵션 노출이 제한적. |
Spring AI의 기본 리더가 OCR 옵션을 제공하지 않을 경우, 직접 Tesseract를 활용하여 이미지 전처리와 OCR을 수행해야 합니다.
이미지 전처리 및 Tesseract 연동
// 이미지 전처리 함수 (감마 보정, 샤프닝, 오츠 이진화 포함)
internal fun preprocessImage(originalImage: BufferedImage): BufferedImage {
// ... (이진화, 샤프닝, 감마 보정 로직)
val binaryImage = otsuThreshold(sharpened) // 오츠 알고리즘으로 이진화
return binaryImage
}
class Tess internal constructor(path: String, lang: String) {
private val tess = Tesseract().also { /* 데이터 경로 및 언어 설정 */ }
/**
* 전처리된 이미지에서 텍스트를 추출
*/
fun extractText(image: BufferedImage, isPreprocess: Boolean = true): String {
val img = if (isPreprocess) preprocessImage(image) else image
return tess.doOCR(img) // Tesseract OCR 실행
}
/**
* 이미지에서 단어/문자 단위의 경계 정보와 정확도를 추출
*/
fun extractCharacters(image: BufferedImage, level: OCRLevel = OCRLevel.SYMBOL, isPreprocess: Boolean = true): OCRList {
val img = if (isPreprocess) preprocessImage(image) else image
val list = tess.getWords(img, level.ordinal).fold(OCRList()) { acc, it ->
// ... (OCR 결과 (좌표, 정확도 등)를 OCRWord 객체로 저장하는 로직)
acc
}
return list
}
}
추출된 문서를 LLM의 컨텍스트 창에 맞게 청크로 나누는 과정입니다.
| 유형 | 설명 | 주의사항 |
|---|---|---|
| TokenTextSplitter | 토큰 기반으로 텍스트를 나눕니다. | |
Spring AI에서 DocumentTransformer의 실질적인 구현체로 사용됩니다. | 오버랩(Overlap)을 지원하지 않아 문맥 단절의 위험이 있습니다. | |
| 커스텀 Splitter | 문서의 구조에 맞춰 분할 규칙을 직접 정의합니다. | 범용 Splitter의 한계를 극복하고 의미론적인 청크를 생성하여 RAG 품질을 향상시킵니다. |
| LLM을 통한 Split | LLM에게 문서의 의미를 분석하게 하여 문맥적으로 완성된 단위로 청크를 만듭니다. | 비용과 속도가 증가하나, 최적의 의미론적 청크를 생성할 수 있습니다. |
습관:
split이후 출력된 청크를 반드시 확인하여 만족스러운 문맥을 포함하고 있는지 검토해야 합니다.
검색의 정확도와 포괄성을 높이기 위해 질의나 저장된 데이터를 조작하는 고급 기법입니다.
사용자의 불완전한 질의를 LLM을 이용해 명확하고 검색에 최적화된 질의로 변환합니다.
| Transformer | 기능 | 목적 |
|---|---|---|
| CompressionQueryTransformer | 비사실 기반의 질문을 사실 기반의 질문으로 변환합니다. | 검색 결과의 사실 기반 청크와의 불일치를 최소화합니다. |
| RewriteQueryTransformer | 하나의 질문으로부터 확장된 질문이나 관련된 질문을 여러 개 생성합니다. | 다중 검색을 통해 포괄적인 결과를 얻고 이를 통합하여 답변합니다. |
저장 시점에 하나의 원본 청크에 대해 다양한 임베딩을 생성하여 검색 가능성을 극대화합니다.
LLM이 외부 함수를 호출하여 답변에 필요한 최신 정보나 계산 결과를 얻는 메커니즘입니다.
공급자 의존성: 도구 호출 가능 여부는 사용하는 LLM 공급자에 따라 결정되며, 미지원 모델은 도구 호출 요청을 무시합니다.
기능이 비슷한 도구가 여러 개 있을 때, LLM이 혼란을 겪는 문제를 해결하기 위한 전략입니다.
| 전략 | 설명 | 모델 적합성 |
|---|---|---|
| 세분화 전략 | 기능을 제한적이고 독립적인 작은 도구로 분할. | 인자를 적게 받고 도구 수를 늘릴 때 유리한 모델. |
| 위임 전략 | 도구를 에이전트로 보고 큰 작업을 위임하여 도구 수를 줄임. | 인자를 많이 받고 도구 수를 줄일 때 유리한 모델. |
| 결과 구조 단순화 | 도구의 반환 결과를 LLM이 이해하기 쉬운 단순한 구조로 제공. | 복잡한 구조보다 단순한 구조가 LLM의 파싱과 활용에 유리. |