PythonCodeSplitter

PythonCodeSplitter

PythonCodeSplitter는 파이썬 소스 코드 문서를 구문을 인식하는 청크로 나눠요. 파이썬 파일용으로 설계되었으며, 가능하면 import, 함수, 클래스, 메서드 같은 코드 단위를 한데 모아 유지해 주죠.

출처: 문서

본문

항목 내용
파이프라인에서 가장 흔한 위치 인덱싱 파이프라인에서 Converters 다음, Embedders 또는 DocumentWriter 앞
필수 run 변수 documents: 파이썬 소스 코드 문서 목록
출력 변수 documents: 구문을 인식하는 청크로 나뉜 파이썬 소스 코드 문서 목록
API reference PreProcessors
GitHub 링크 python_code_splitter.py
패키지 이름 haystack-ai

개요

PythonCodeSplitter는 각 입력 문서의 content가 유효한 파이썬 소스 코드일 것이라 기대해요. 파이썬의 ast 모듈로 소스를 파싱한 뒤 다음을 위한 정렬된 분할 단위를 만들죠:

  • 모듈 docstring
  • 연속된 import 블록
  • 최상위 함수
  • 클래스 헤더
  • 메서드와 중첩 클래스
  • 나머지 최상위 문장

스플리터는 이 단위들을 소스 순서대로 max_effective_lines 쪽으로 병합해요. 유효 줄 수는 ceil(len(source) / expected_chars_per_line)로 문자 길이에서 계산하므로, 긴 줄은 한 줄보다 많이 계산되죠.

함수와 메서드는 기본 AST 분할로 온전하게 유지돼요. 하나의 구문 단위가 oversized_factor * max_effective_lines보다 크면, 스플리터는 DocumentSplitter를 사용한 줄 기반 2차 분할로 대체해요. 이 과대 청크 폴백이 청크가 겹칠 수 있는 유일한 경우예요. 기본 AST 분할은 오버랩을 추가하지 않죠.

기본적으로 preserve_class_definition=True예요. 청크에 원래 클래스 헤더 없이 클래스 멤버가 포함되면, 스플리터가 클래스 시그니처를 앞에 붙여 청크가 여전히 클래스 맥락을 지니도록 해요.

strip_docstrings=True로 설정하면 함수·메서드·클래스 docstring이 청크 콘텐츠에서 제거되어 meta["docstrings"]에 저장돼요. 모듈 docstring은 그 자체가 최상위 단위이므로 청크 콘텐츠에 남아요.

청크별 메타데이터

각 출력 문서는 아래 메타데이터를 지녀요. 상위 문서의 meta의 모든 필드(split_id 제외)도 함께 전파돼요.

필드 설명
source_id 원본 문서의 ID
split_id 원본 문서 내에서 이 청크의 순차 인덱스
start_line 원본 소스에서 청크의 첫 줄 (1-based). 과대 2차 청크는 원래 단위의 범위를 유지해요
end_line 원본 소스에서 청크의 마지막 줄 (1-based). 과대 2차 청크는 원래 단위의 범위를 유지해요
unit_kinds 이 청크에 포함된 구문 단위 종류 목록. 예: imports, function, class_header, method
include_classes (해당 시) 이 청크에 멤버가 나타나는 클래스 이름의 정렬된 목록
decorators (해당 시) 포함된 함수·메서드·클래스에서 발견된 데코레이터 문자열의 정렬된 목록
docstrings (strip_docstrings=True 시) 소스 순서대로 제거된 docstring 문자열 목록
secondary_split 이 청크가 과대 폴백 스플리터로 생성되었으면 True
secondary_split_index 2차 분할 시퀀스 내에서 이 조각의 인덱스
secondary_split_total 2차 분할이 만든 총 조각 수

None 콘텐츠의 문서는 ValueError를, 문자열이 아닌 콘텐츠는 TypeError를, 유효하지 않은 파이썬 소스는 SyntaxError를 발생시켜요. 빈 문서는 건너뛰어요.

구성

파라미터 타입 기본값 설명
min_effective_lines int 20 청크당 최소 유효 줄 수. 청크가 이 값 미만이면 스플리터가 다음 단위를 계속 병합해요
max_effective_lines int 100 청크당 목표 유효 줄 수. 단위를 이 값 쪽으로 탐욕적으로 병합해요
expected_chars_per_line int 45 ceil(len(source) / expected_chars_per_line)로 유효 줄 수를 추정하는 데 사용하는 문자 수
oversized_factor int 3 과대 구문 단위에 대한 줄 기반 2차 분할을 트리거하는 배수
strip_docstrings bool False 함수·메서드·클래스 docstring을 콘텐츠에서 meta["docstrings"]로 옮겨요
preserve_class_definition bool True 클래스 헤더 없이 클래스 멤버를 담은 청크에 클래스 시그니처를 붙여요
secondary_split_overlap int 5 과대 2차 분할에서만 사용하는 줄 오버랩
secondary_split_length int | None None 과대 2차 분할의 줄 길이. None이면 max_effective_lines 사용

사용법

단독으로 사용하기

import textwrap
from haystack import Document
from haystack.components.preprocessors import PythonCodeSplitter

source = textwrap.dedent(
    '''
    """Math utilities."""
    from math import pi

    class Circle:
        """A circle."""

        def __init__(self, radius: float) -> None:
            self.radius = radius

        def area(self) -> float:
            return pi * self.radius * self.radius
    '''
).lstrip()

splitter = PythonCodeSplitter(
    min_effective_lines=4,
    max_effective_lines=12,
    strip_docstrings=True,
)

result = splitter.run(
    documents=[Document(content=source, meta={"file_name": "geometry.py"})],
)
for chunk in result["documents"]:
    print(
        chunk.meta["start_line"],
        chunk.meta["end_line"],
        chunk.meta.get("include_classes"),
    )

RAG를 위한 docstring 제거

docstring이 장황할 때는 strip_docstrings=True를 설정하세요. docstring 텍스트는 청크 콘텐츠에서 meta["docstrings"]로 옮겨져 저장된 청크를 간결하게 유지해요. 임베더에 meta_fields_to_embed=["docstrings"]를 전달해, docstring 텍스트가 더 이상 청크 콘텐츠에 없어도 검색에 계속 영향을 주도록 하세요.

from haystack import Document
from haystack.components.preprocessors import PythonCodeSplitter

source = '''"""Example module."""
from math import pi

class Circle:
    """A circle defined by its radius."""
    def __init__(self, r: float) -> None:
        """Store the radius."""
        self.r = r

    def area(self) -> float:
        """Return the area of the circle."""
        return pi * self.r * self.r'''

splitter = PythonCodeSplitter(
    min_effective_lines=20,
    max_effective_lines=100,
    strip_docstrings=True,
)
result = splitter.run(
    documents=[Document(content=source, meta={"file_name": "my_module.py"})]
)
for chunk in result["documents"]:
    print(chunk.content)
    print(chunk.meta.get("docstrings"))

파이프라인에서 사용하기

이 파이프라인은 파이썬 파일을 문서로 변환하고, PythonCodeSplitter로 나눈 뒤, 청크를 인메모리 문서 스토어에 써요.

from pathlib import Path
from haystack import Pipeline
from haystack.components.converters.txt import TextFileToDocument
from haystack.components.preprocessors import PythonCodeSplitter
from haystack.components.writers import DocumentWriter
from haystack.document_stores.in_memory import InMemoryDocumentStore

document_store = InMemoryDocumentStore()

p = Pipeline()
p.add_component("converter", TextFileToDocument())
p.add_component("splitter", PythonCodeSplitter(max_effective_lines=80))
p.add_component("writer", DocumentWriter(document_store=document_store))
p.connect("converter.documents", "splitter.documents")
p.connect("splitter.documents", "writer.documents")

files = list(Path("path/to/your/project").glob("**/*.py"))
p.run({"converter": {"sources": files}})

더 알아보기 (Learn more)