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}})