이 노트북은 언어 파싱을 사용하는 특별한 접근 방식으로 소스 코드 파일을 로드하는 방법을 다룹니다: 코드의 각 최상위 함수와 클래스는 별도의 문서로 로드됩니다. 이미 로드된 함수와 클래스 외부의 나머지 최상위 코드는 별도의 문서로 로드됩니다. 이 접근 방식은 소스 코드에 대한 QA 모델의 정확도를 잠재적으로 향상시킬 수 있습니다. 코드 파싱을 지원하는 언어는 다음과 같습니다:
  • C (*)
  • C++ (*)
  • C# (*)
  • COBOL
  • Elixir
  • Go (*)
  • Java (*)
  • JavaScript (esprima 패키지 필요)
  • Kotlin (*)
  • Lua (*)
  • Perl (*)
  • Python
  • Ruby (*)
  • Rust (*)
  • Scala (*)
  • TypeScript (*)
(*)로 표시된 항목은 tree_sittertree_sitter_languages 패키지가 필요합니다. tree_sitter를 사용하여 추가 언어에 대한 지원을 추가하는 것은 간단하지만, 현재는 LangChain을 수정해야 합니다. 파싱에 사용되는 언어는 구문 기반 분할을 활성화하는 데 필요한 최소 줄 수와 함께 구성할 수 있습니다. 언어가 명시적으로 지정되지 않은 경우, LanguageParser는 파일 확장자가 있으면 이를 통해 언어를 추론합니다.
pip install -qU esprima esprima tree_sitter tree_sitter_languages
import warnings

warnings.filterwarnings("ignore")
from pprint import pprint

from langchain_community.document_loaders.generic import GenericLoader
from langchain_community.document_loaders.parsers import LanguageParser
from langchain_text_splitters import Language
loader = GenericLoader.from_filesystem(
    "./example_data/source_code",
    glob="*",
    suffixes=[".py", ".js"],
    parser=LanguageParser(),
)
docs = loader.load()
len(docs)
6
for document in docs:
    pprint(document.metadata)
{'content_type': 'functions_classes',
 'language': <Language.PYTHON: 'python'>,
 'source': 'example_data/source_code/example.py'}
{'content_type': 'functions_classes',
 'language': <Language.PYTHON: 'python'>,
 'source': 'example_data/source_code/example.py'}
{'content_type': 'simplified_code',
 'language': <Language.PYTHON: 'python'>,
 'source': 'example_data/source_code/example.py'}
{'content_type': 'functions_classes',
 'language': <Language.JS: 'js'>,
 'source': 'example_data/source_code/example.js'}
{'content_type': 'functions_classes',
 'language': <Language.JS: 'js'>,
 'source': 'example_data/source_code/example.js'}
{'content_type': 'simplified_code',
 'language': <Language.JS: 'js'>,
 'source': 'example_data/source_code/example.js'}
print("\n\n--8<--\n\n".join([document.page_content for document in docs]))
class MyClass:
    def __init__(self, name):
        self.name = name

    def greet(self):
        print(f"Hello, {self.name}!")

--8<--

def main():
    name = input("Enter your name: ")
    obj = MyClass(name)
    obj.greet()

--8<--

# Code for: class MyClass:


# Code for: def main():


if __name__ == "__main__":
    main()

--8<--

class MyClass {
  constructor(name) {
    this.name = name;
  }

  greet() {
    console.log(`Hello, ${this.name}!`);
  }
}

--8<--

function main() {
  const name = prompt("Enter your name:");
  const obj = new MyClass(name);
  obj.greet();
}

--8<--

// Code for: class MyClass {

// Code for: function main() {

main();
작은 파일에 대해서는 parser를 비활성화할 수 있습니다. parser_threshold 매개변수는 parser를 사용하여 분할하기 위해 소스 코드 파일이 가져야 하는 최소 줄 수를 나타냅니다.
loader = GenericLoader.from_filesystem(
    "./example_data/source_code",
    glob="*",
    suffixes=[".py"],
    parser=LanguageParser(language=Language.PYTHON, parser_threshold=1000),
)
docs = loader.load()
len(docs)
1
print(docs[0].page_content)
class MyClass:
    def __init__(self, name):
        self.name = name

    def greet(self):
        print(f"Hello, {self.name}!")


def main():
    name = input("Enter your name: ")
    obj = MyClass(name)
    obj.greet()


if __name__ == "__main__":
    main()

Splitting

너무 큰 함수, 클래스 또는 스크립트에 대해서는 추가 분할이 필요할 수 있습니다.
loader = GenericLoader.from_filesystem(
    "./example_data/source_code",
    glob="*",
    suffixes=[".js"],
    parser=LanguageParser(language=Language.JS),
)
docs = loader.load()
from langchain_text_splitters import (
    Language,
    RecursiveCharacterTextSplitter,
)
js_splitter = RecursiveCharacterTextSplitter.from_language(
    language=Language.JS, chunk_size=60, chunk_overlap=0
)
result = js_splitter.split_documents(docs)
len(result)
7
print("\n\n--8<--\n\n".join([document.page_content for document in result]))
class MyClass {
  constructor(name) {
    this.name = name;

--8<--

}

--8<--

greet() {
    console.log(`Hello, ${this.name}!`);
  }
}

--8<--

function main() {
  const name = prompt("Enter your name:");

--8<--

const obj = new MyClass(name);
  obj.greet();
}

--8<--

// Code for: class MyClass {

// Code for: function main() {

--8<--

main();

Tree-sitter Template을 사용하여 언어 추가하기

Tree-Sitter template을 사용하여 언어 지원을 확장하는 것은 몇 가지 필수 단계를 포함합니다:
  1. 새 언어 파일 생성:
    • 지정된 디렉토리(langchain/libs/community/langchain_community/document_loaders/parsers/language)에 새 파일을 생성하는 것으로 시작합니다.
    • **cpp.py**와 같은 기존 언어 파일의 구조와 파싱 로직을 기반으로 이 파일을 모델링합니다.
    • langchain 디렉토리(langchain/libs/langchain/langchain/document_loaders/parsers/language)에도 파일을 생성해야 합니다.
  2. 언어별 파싱:
    • cpp.py 파일에서 사용된 구조를 모방하여 통합하려는 언어에 맞게 조정합니다.
    • 주요 변경 사항은 파싱하려는 언어의 구문과 구조에 맞게 chunk query array를 조정하는 것입니다.
  3. 언어 Parser 테스트:
    • 철저한 검증을 위해 새 언어에 특화된 테스트 파일을 생성합니다. 지정된 디렉토리(langchain/libs/community/tests/unit_tests/document_loaders/parsers/language)에 **test_language.py**를 생성합니다.
    • **test_cpp.py**에서 설정한 예제를 따라 새 언어에서 파싱된 요소에 대한 기본 테스트를 설정합니다.
  4. Parser 및 Text Splitter에 통합:
    • language_parser.py 파일에 새 언어를 통합합니다. 추가된 언어를 인식하고 처리할 수 있도록 LANGUAGE_EXTENSIONS 및 LANGUAGE_SEGMENTERS와 LanguageParser의 docstring을 업데이트해야 합니다.
    • 또한 적절한 파싱을 위해 **text_splitter.py**의 Language class에 언어가 포함되어 있는지 확인합니다.
이러한 단계를 따르고 포괄적인 테스트와 통합을 보장함으로써 Tree-Sitter template을 사용하여 언어 지원을 성공적으로 확장할 수 있습니다. 행운을 빕니다!
Connect these docs programmatically to Claude, VSCode, and more via MCP for real-time answers.
I