지난번엔 langchain의 공식 문서를 어느정도 훑어보았고, 이제는 본격적으로 코드에 대해서 뜯어보려한다. 우선 langchain의 폴더 구성은 이렇게 되어있다.

langchain과 같은 큰 규모의 생태계를 가진 코드를 분석하는게 생각보다 막막함을 코드를 fork 뜨자마자 느꼈다. 수많은 파일들이 각각 어떤 역할을 하는지 파악하는게 쉬운 일이 아니었다. 이전 이동욱 개발자님의 오픈소스 분석 방법에 관한 글을 보면, 전체적인 아키텍처를 먼저 파악하고 디테일을 보는 것이 우선이라고 하셨으니, 전체적인 아키텍처가 어떻게 구성되어있는지 파악하기 위해 노력했다.
오픈 소스 분석 방법 | 개발자 이동욱
오픈 소스 분석 방법 머리속 한켠에는 오픈 소스 활동을 다시 하고 싶다는 생각을 가지고 있지만, 바쁘다는 핑계로 미뤄왔다. 우연히 어떤 블로그에서 오픈 소스 분석 방법 이라는 글을 읽고나
dongwooklee96.github.io
그래서 우선 공식문서와 readme, md 파일들을 꼼꼼히 읽어보면서 전체적인 아키텍처 구조를 파악하고 핵심 파일들이 모여있는 libs/core/langchain_core/ 의 코드들을 보면서 다시 bottom to top 방식으로 읽어보려했다. 너무 고민이 길어지는 것 같아 일단 무작정 아무거나 들어가서 읽어본 결과 내가 분석하려는 langchian의 핵심 기능들 소스 코드는 lib의 langchain_core에 모여있음을 알게되었고, 이곳의 폴더를 하나하나 열어보며 구성을 파악해보았다.
langchain_core의 __init__.py 파일을 보면
"""``langchain-core`` defines the base abstractions for the LangChain ecosystem.
The interfaces for core components like chat models, LLMs, vector stores, retrievers,
and more are defined here. The universal invocation protocol (Runnables) along with
a syntax for combining components (LangChain Expression Language) are also defined here.
**No third-party integrations are defined here.** The dependencies are kept purposefully
very lightweight.
"""
이렇게 나와있다. 즉 langchain-core에서 langchain의 핵심 요소인 chat model, llm, vector store, retriever 등 핵심 기능들이 구현되어있는데, 각각의 요소들이 어떻게 정의되는지에 대해서 나와있는듯하다. langchain의 기능들이 어떻게 구성되어있는지 분석해보고싶었으니, 주소를 잘 잡은 것 같다.

내부 폴더를 열어보니 역시나 이렇게 langchain_core 에 각 기능들의 구현체들이 있는데 가장 먼저 보이는 _api 폴더를 읽어봤다.

_api 폴더는 총 5개의 코드 파일로 구성되어있는데, 이들은 langchain의 API 관리 시스템으로 모든 API 도구들을 관리하는 모듈인 것으로 파악했다. __init__.py는 폴더 내 모듈을 패키지로 인식되기 위한 파일일테니 건너뛰도록하고, 이제 본격적으로 각각 코드들을 뜯어보도록하자.
1. beta_decorator.py, deprecation.py (경고 출력 데코레이터)
beta_decorator.py는 langchain에서 일부 기능들을 beta decorator를 붙여서 경고하는 기능을 갖고있다. 아래의 class 이름만봐도 langchain 사용자에게 warning을 제공하는 기능임을 알 수 있다.
class LangChainBetaWarning(DeprecationWarning):
"""A class for issuing beta warnings for LangChain users."""
전체 코드는 200줄 가까이 되어서 전부 옮길 수는 없지만, 읽어보면 '베타 기능'으로 클래스를 지정하면 경고가 붙도록 설계되어있다. 즉, 베타 기능을 외부에서 호출하려할때는 경고 문구가 만들어지도록 하는 기능이다. 또한 주석을 보면 matplotlib의 deprecation 시스템을 참고해서 설계했다고한다.
2. internal.py (경고 출력시 내부코드, 외부코드 판별)
import inspect
def is_caller_internal(depth: int = 2) -> bool:
"""Return whether the caller at `depth` of this function is internal."""
try:
frame = inspect.currentframe()
except AttributeError:
return False
if frame is None:
return False
try:
for _ in range(depth):
frame = frame.f_back
if frame is None:
return False
# Directly access the module name from the frame's global variables
module_globals = frame.f_globals
caller_module_name = module_globals.get("__name__", "")
return caller_module_name.startswith("langchain")
finally:
del frame
internal.py는 함수를 호출한 코드가 langchain 내부의 코드인지, 사용자의 코드인지 판별하는 코드이다. 판별하는 방식은 간단한데, frame 객체를 가져와서 이름 앞에 "langchain"이 붙는지 확인하는 방식이다. 위에서 decorator로 경고를 표시할 때 langchain 함수 내부 호출일 경우에는 경고할 필요가 없기 때문에 이를 판별할 수 있는 코드를 삽입한 것이다.
3. path.py (경로 지정)
import os
from pathlib import Path
from typing import Optional, Union
HERE = Path(__file__).parent
# Get directory of langchain package
PACKAGE_DIR = HERE.parent
SEPARATOR = os.sep
def get_relative_path(
file: Union[Path, str], *, relative_to: Path = PACKAGE_DIR
) -> str:
"""Get the path of the file as a relative path to the package directory.
Args:
file: The file path to convert.
relative_to: The base path to make the file path relative to.
Returns:
The relative path as a string.
"""
if isinstance(file, str):
file = Path(file)
return str(file.relative_to(relative_to))
def as_import_path(
file: Union[Path, str],
*,
suffix: Optional[str] = None,
relative_to: Path = PACKAGE_DIR,
) -> str:
"""Path of the file as a LangChain import exclude langchain top namespace.
Args:
file: The file path to convert.
suffix: An optional suffix to append to the import path.
relative_to: The base path to make the file path relative to.
Returns:
The import path as a string.
"""
if isinstance(file, str):
file = Path(file)
path = get_relative_path(file, relative_to=relative_to)
if file.is_file():
path = path[: -len(file.suffix)]
import_path = path.replace(SEPARATOR, ".")
if suffix:
import_path += "." + suffix
return import_path
마지막으로 path.py는 api의 경로를 지정해주는 유틸리티를 모아놓은 코드이다. 파일 경로를 상대 경로로 지정해주는 get_relative_path 함수가 있고, python import 경로로 지정해주는 as_import_path 함수가 있다. 모듈 경로를 자동으로 추적할 수 있게끔 만들어주는 용도인듯하다.
langchain에서 api를 관리하는 코드들을 읽어보았는데, 잘 정리된 코드의 의미를 해석해보는 재미가 있다. 시간을 좀 더 넉넉하게 두고 천천히 뜯어보면 더 좋겠지만, 시간상 이정도로 마무리하려한다.
'오픈소스' 카테고리의 다른 글
| [오픈소스 분석 5일차] Langchain에서 문서를 로드하고 처리하는 방식 (0) | 2025.11.07 |
|---|---|
| [오픈소스 분석 3일차] LangChain Agent Core Components (0) | 2025.10.26 |
| [오픈소스 분석 2일차] Langchain 공식문서 톺아보기 (0) | 2025.10.26 |
| [오픈소스 분석 1일차] 어떤 오픈소스를 분석해볼까? (0) | 2025.10.13 |