블로그 이미지
Peter Note
Web & LLM FullStacker, Application Architecter, KnowHow Dispenser and Bike Rider

Publication

Category

Recent Post

2024. 8. 11. 14:37 [LLM FullStacker]/Python

LangChain 소스 코드를 분석하면서 Python도 함께 배우고 있습니다. 이해되지 않는 것으 GPT를 통해 열심히 개념을 알아가고 있습니다.

1. Union에 대한 궁금증

Union은 Python의 타입 힌팅(type hinting)에서 사용되며, 함수의 매개변수나 반환 값이 둘 이상의 서로 다른 타입을 가질 수 있음을 나타낼 때 사용됩니다. Python 3.10 이전에는 typing 모듈의 Union을 사용했으며, Python 3.10부터는 더 간단한 | 연산자를 사용하여 동일한 기능을 제공할 수 있습니다.

 

예시

 

1. Python 3.9 이하에서의 Union 사용:

from typing import Union

def process_value(value: Union[int, float]) -> Union[int, float]:
    return value * 2

이 함수 process_valueintfloat 타입의 값을 받아서 두 배로 증가시킨 후 반환합니다. 반환 값도 intfloat이 될 수 있음을 Union을 통해 명시하고 있습니다.

 

2. Python 3.10 이후에서의 | 연산자 사용:

def process_value(value: int | float) -> int | float:
    return value * 2

 

위와 동일한 기능을 | 연산자를 사용하여 간단히 표현할 수 있습니다.

 

Union 사용이 적합한 경우

 

다양한 타입을 처리할 때: 함수나 메소드가 여러 타입을 받을 수 있고, 그 타입에 따라 다르게 동작할 때 Union을 사용합니다.

점진적 타입 검사를 통해 오류를 줄일 때: 코드에 타입 힌트를 명확히 하여, 코드의 가독성과 안정성을 높이고, 코드 편집기나 정적 분석 도구가 더 나은 오류 검출을 할 수 있도록 도울 수 있습니다.

 

주의사항

 

Union을 사용할 때, 가능한 한 사용 범위를 좁히는 것이 좋습니다. 너무 많은 타입을 허용하면 코드가 복잡해지고 유지보수가 어려워질 수 있습니다.

 

Union은 주로 함수의 입력과 출력에 다양한 타입을 허용할 때 매우 유용한 도구입니다. 필요에 따라 적절히 사용하는 것이 중요합니다.

 


 

2. ininstance 에 대한 궁금증

isinstance는 Python에서 특정 객체가 특정 클래스나 타입의 인스턴스인지 확인할 때 사용되는 내장 함수입니다. 이 함수는 객체와 클래스(또는 클래스의 튜플)를 인수로 받아, 객체가 해당 클래스의 인스턴스이거나 그 클래스의 서브클래스의 인스턴스인 경우 True를 반환하고, 그렇지 않으면 False를 반환합니다.

 

isinstance의 기본 사용법

result = isinstance(object, classinfo)

object: 타입을 확인하려는 객체.

classinfo: 클래스, 클래스의 튜플, 또는 타입들.

 

예시

 

1. 단일 클래스에 대한 검사:

# 객체가 정수형인지 확인
x = 10
print(isinstance(x, int))  # True

 

2. 여러 클래스에 대한 검사:

# 객체가 정수형 또는 실수형인지 확인
x = 10.5
print(isinstance(x, (int, float)))  # True

 

4. 상속 관계에서의 검사:

class Animal:
    pass

class Dog(Animal):
    pass

dog = Dog()
print(isinstance(dog, Animal))  # True
print(isinstance(dog, Dog))     # True

이 예제에서 Dog 클래스는 Animal 클래스를 상속받기 때문에, dog 객체는 Animal 클래스의 인스턴스이기도 합니다.

 

isinstance의 주요 용도

 

타입 확인: 함수나 메소드 내부에서 인수의 타입을 확인하여, 타입에 따라 다른 로직을 수행할 때 유용합니다.

상속 관계 검사: 객체가 특정 클래스에서 파생된 클래스의 인스턴스인지 확인할 수 있습니다.

입력 검증: 함수에 전달된 인수의 타입이 예상한 타입과 일치하는지 검증하여, 타입 오류를 미리 방지할 수 있습니다.

 

isinstance와 type의 차이점

 

isinstance는 상속 관계를 고려하는 반면, type 함수는 객체의 정확한 타입을 확인할 때 사용됩니다. 예를 들어, 상속받은 클래스의 인스턴스는 type을 사용하면 부모 클래스와 매칭되지 않습니다.

class Animal:
    pass

class Dog(Animal):
    pass

dog = Dog()

# isinstance를 사용하면 True
print(isinstance(dog, Animal))  # True

# type을 사용하면 False
print(type(dog) == Animal)  # False, Dog != Animal

이와 같이, isinstance는 더 유연하게 타입을 확인할 수 있는 반면, type은 정확한 타입 비교에 사용됩니다.

posted by Peter Note
2024. 8. 11. 14:31 [LLM FullStacker]/Python

 

1. Dictionary 병합

LangChain 소스를 분석하다 이런 코드를 보았다.

class ChatPromptTemplate(BaseChatPromptTemplate):
    def __init__(
        self,
        messages: Sequence[MessageLikeRepresentation],
        *,
        template_format: Literal["f-string", "mustache", "jinja2"] = "f-string",
        **kwargs: Any,
    ) -> None:
        ...
        // 파이썬 초보자가 봤을 때 이게 뭐지?  
        kwargs = {
            **dict(
                input_variables=sorted(input_vars),
                optional_variables=sorted(optional_variables),
                partial_variables=partial_vars,
            ),
            **kwargs,
        }

이 코드에서 kwargs 변수를 정의하는 방식은 Python에서 딕셔너리를 병합하는 기법 중 하나입니다. 이 코드의 주요 목적은 두 개의 딕셔너리를 결합하여 하나의 딕셔너리를 만드는 것입니다. 여기서 kwargs는 일반적으로 함수나 메소드에서 사용되는 키워드 인수들을 담고 있는 딕셔너리입니다.

코드의 의미 분석

kwargs = {
    **dict(
        input_variables=sorted(input_vars),
        optional_variables=sorted(optional_variables),
        partial_variables=partial_vars,
    ),
    **kwargs,
}
  1. dict(...):
    • dict(...) 표현식은 새로운 딕셔너리를 생성합니다.
    • 이 딕셔너리는 세 개의 키-값 쌍을 가지고 있습니다:
      • input_variables: input_vars를 정렬한 리스트.
      • optional_variables: optional_variables를 정렬한 리스트.
      • partial_variables: partial_vars라는 변수에 담긴 값.
  2. sorted(input_vars)sorted(optional_variables):
    • sorted(input_vars)sorted(optional_variables)는 각각 input_varsoptional_variables 리스트를 정렬한 결과를 반환합니다.
    • 이 결과는 새로운 딕셔너리의 값으로 사용됩니다.
  3. **dict(...):
    • ** 연산자는 딕셔너리 언패킹 연산자입니다. 이 연산자를 사용하면 딕셔너리의 키-값 쌍을 다른 딕셔너리로 풀어서 넣을 수 있습니다.
    • **dict(...)input_variables, optional_variables, partial_variables라는 키와 그에 대응하는 값들을 풀어서 {}로 감싸인 새로운 딕셔너리로 만듭니다.
  4. **kwargs:
    • 기존의 kwargs 딕셔너리도 **kwargs로 언패킹되어 추가됩니다.
    • 이때, 만약 kwargs 딕셔너리에 input_variables, optional_variables, partial_variables와 동일한 키가 존재하면, kwargs 딕셔너리의 값이 우선하여 기존 값을 덮어씁니다.
  5. 최종 kwargs 딕셔너리:
    • 최종적으로, 첫 번째 dict(...)로 생성된 딕셔너리와 기존의 kwargs 딕셔너리가 병합된 새로운 kwargs 딕셔너리가 만들어집니다.
    • 이 딕셔너리는 함수나 메소드에 전달될 키워드 인수들로 사용될 수 있습니다.

예시

input_vars = ['var3', 'var1', 'var2']
optional_variables = ['opt3', 'opt1', 'opt2']
partial_vars = {'part1': 'value1'}

kwargs = {'input_variables': ['override_var'], 'new_key': 'new_value'}

kwargs = {
    **dict(
        input_variables=sorted(input_vars),
        optional_variables=sorted(optional_variables),
        partial_variables=partial_vars,
    ),
    **kwargs,
}

print(kwargs)

위 코드를 실행하면, 최종 kwargs 딕셔너리는 다음과 같이 출력됩니다:

{
    'input_variables': ['override_var'],  # kwargs에서 덮어씌워짐
    'optional_variables': ['opt1', 'opt2', 'opt3'],
    'partial_variables': {'part1': 'value1'},
    'new_key': 'new_value'
}

요약

  • 이 코드는 dict(...)로 만든 딕셔너리와 기존 kwargs 딕셔너리를 병합하여 새로운 kwargs 딕셔너리를 생성합니다.
  • **dict(...)**kwargs를 사용하여 두 딕셔너리를 결합합니다. 중복되는 키가 있을 경우, kwargs에 있는 값이 우선합니다.
  • 이 기법은 키워드 인수를 동적으로 처리할 때 유용합니다.

2. sorted 용법에 대한 궁금증

sorted는 Python의 내장 함수 중 하나로, 주어진 iterable(리스트, 튜플, 문자열 등)의 요소들을 정렬하여 새로운 리스트로 반환하는 함수입니다. sorted는 원본 데이터를 변경하지 않고, 정렬된 새로운 리스트를 반환합니다.

 

sorted 함수의 기본 사용법

sorted(iterable, key=None, reverse=False)

iterable: 정렬할 대상이 되는 iterable 객체(예: 리스트, 튜플, 문자열 등).

key: 정렬 기준을 지정하는 함수. 각 요소에 대해 이 함수가 호출된 결과를 기준으로 정렬합니다.

reverse: True로 설정하면 내림차순으로 정렬하고, 기본값인 False는 오름차순으로 정렬합니다.

 

예시

 

1. 리스트 정렬:

numbers = [3, 1, 4, 1, 5, 9, 2, 6, 5]
sorted_numbers = sorted(numbers)
print(sorted_numbers)  # [1, 1, 2, 3, 4, 5, 5, 6, 9]

 

2. 문자열 정렬:

word = "python"
sorted_letters = sorted(word)
print(sorted_letters)  # ['h', 'n', 'o', 'p', 't', 'y']

 

3. 튜플 정렬:

tuples = [(1, 'b'), (3, 'a'), (2, 'c')]
sorted_tuples = sorted(tuples)
print(sorted_tuples)  # [(1, 'b'), (2, 'c'), (3, 'a')]

 

4. key 매개변수 사용:

key 매개변수는 정렬 기준을 정의하는 함수입니다. 각 요소에 대해 이 함수가 호출된 결과를 기준으로 정렬됩니다.

words = ["apple", "banana", "cherry", "date"]
sorted_words = sorted(words, key=len)
print(sorted_words)  # ['date', 'apple', 'banana', 'cherry']

 

5. reverse=True 사용:

정렬 결과를 내림차순으로 반환합니다.

numbers = [3, 1, 4, 1, 5, 9, 2, 6, 5]
sorted_numbers_desc = sorted(numbers, reverse=True)
print(sorted_numbers_desc)  # [9, 6, 5, 5, 4, 3, 2, 1, 1]

 

요약

 

sorted 함수는 iterable의 요소를 정렬하여 새로운 리스트로 반환합니다.

key 매개변수를 사용하여 정렬 기준을 설정할 수 있으며, reverse 매개변수를 사용하여 정렬 방향(오름차순/내림차순)을 설정할 수 있습니다.

원본 데이터는 변경되지 않으며, 정렬된 새로운 리스트가 반환됩니다.


3. cast 에 대한 궁금증

cast는 Python의 타입 힌팅(type hinting)과 관련된 기능 중 하나로, typing 모듈에서 제공하는 함수입니다. cast는 특정 값을 지정된 타입으로 “캐스팅”한다고 표시하는 역할을 합니다. 그러나 실제로 값을 변환하거나 변경하지는 않으며, 주로 타입 힌팅을 통해 코드의 가독성을 높이고, 정적 분석 도구들이 올바른 타입을 추론하도록 돕기 위한 목적으로 사용됩니다.

 

cast의 기본 사용법

from typing import cast

cast(typ, val)

typ: 캐스팅할 타입. Python의 타입(예: int, str, List[int] 등)이나 사용자 정의 클래스 등을 지정할 수 있습니다.

val: 실제 값. typ로 캐스팅할 값입니다.

 

cast의 역할

 

정적 분석 지원: cast는 Python 코드에서 변수나 표현식의 타입을 명시적으로 지정하는 데 사용됩니다. 정적 분석 도구나 IDE에서 코드의 타입을 더 잘 이해하고, 타입 관련 경고나 오류를 감지하는 데 도움이 됩니다.

런타임에는 아무 영향 없음: cast는 런타임에 아무런 영향을 미치지 않습니다. 즉, cast를 사용해도 실제로 값의 타입이 변경되거나 변환되지 않습니다.

 

예시

 

1. 기본 사용법:

from typing import cast, List

def get_items() -> List[str]:
    return ["apple", "banana", "cherry"]

items = cast(List[str], get_items())
print(items)  # ['apple', 'banana', 'cherry']

여기서 cast(List[str], get_items())get_items()의 반환값이 List[str] 타입임을 명시적으로 지정합니다.

 

2. 변수 타입 힌트:

from typing import cast

value: int = cast(int, "42")  # 정적 분석 도구에게 value가 int임을 알려줌
print(value)  # "42", 실제로는 str 타입임

이 예시에서 cast(int, "42")"42"int 타입이라고 명시하지만, 실제로는 str 타입입니다. 이 코드에서 cast는 단지 정적 타입 힌팅을 위한 것이며, value의 타입이 실제로 int로 변환되지 않습니다.

 

3. 사용자 정의 클래스:

from typing import cast

class Animal:
    def speak(self):
        pass

class Dog(Animal):
    def speak(self):
        return "Woof!"

class Cat(Animal):
    def speak(self):
        return "Meow!"

def get_animal() -> Animal:
    return Dog()

animal = cast(Dog, get_animal())
print(animal.speak())  # "Woof!", 정적 분석 도구는 animal을 Dog로 간주함

여기서 cast(Dog, get_animal())get_animal()이 반환하는 객체가 Dog 타입임을 명시적으로 지정하여, animal 변수가 Dog로 간주되도록 합니다. 실제로는 get_animal()이 반환하는 객체가 Dog이기 때문에 문제가 없지만, 정적 분석 도구에 명시적으로 알려주기 위해 사용됩니다.

 

요약

 

cast는 Python에서 타입 힌팅을 명시적으로 지정하기 위한 도구로, 런타임에는 영향을 미치지 않습니다.

주로 정적 분석 도구나 IDE에서 타입 추론을 돕기 위해 사용됩니다.

cast는 값의 실제 타입을 변경하지 않으며, 코드의 가독성과 안전성을 높이는 역할을 합니다.

posted by Peter Note
2024. 8. 11. 14:26 [LLM FullStacker]/Python

return cls(messages, template_format=template_format)는 Python에서 클래스 메소드나 다른 클래스 내부 메소드에서 새로운 클래스 인스턴스를 생성하여 반환할 때 사용되는 패턴입니다. 이 문장에서 cls는 현재 클래스 자체를 가리키고, messagestemplate_format은 그 클래스의 생성자 __init__ 메소드에 전달되는 인수입니다.

이 코드의 의미

  1. cls:
    • cls는 클래스 메소드 또는 클래스 내부의 다른 메소드에서 해당 클래스 자체를 참조하는 키워드입니다. cls를 사용하면 현재 클래스의 인스턴스를 생성할 수 있습니다.
    • 일반적으로 @classmethod 데코레이터가 붙은 메소드 내에서 사용되며, 이 메소드가 호출될 때 해당 클래스 자체가 첫 번째 인수로 cls에 전달됩니다.
  2. cls(messages, template_format=template_format):
    • 이 구문은 현재 클래스의 인스턴스를 생성하는 표현입니다.
    • messagestemplate_format=template_format은 생성자에 전달되는 인수입니다. 여기서 messages는 위치 인수로, template_format은 키워드 인수로 전달됩니다.
    • 이는 클래스의 __init__ 메소드가 messagestemplate_format이라는 두 개의 인수를 받을 것으로 예상된다는 것을 의미합니다.
  3. return:
    • 새로 생성된 클래스 인스턴스를 반환합니다. 이 반환된 객체는 이 메소드를 호출한 코드에서 사용할 수 있게 됩니다.

예시 코드

아래는 이 패턴이 어떻게 사용되는지에 대한 예시입니다:

class PromptTemplate:
    def __init__(self, messages, template_format=None):
        self.messages = messages
        self.template_format = template_format

    @classmethod
    def from_messages(cls, messages, template_format="default"):
        # 새로운 PromptTemplate 인스턴스를 생성하여 반환
        return cls(messages, template_format=template_format)

# 사용 예시
messages = ["Hello, World!", "How are you?"]
template = PromptTemplate.from_messages(messages, template_format="custom")
print(template.messages)  # ['Hello, World!', 'How are you?']
print(template.template_format)  # 'custom'

요약

  • return cls(messages, template_format=template_format)는 현재 클래스의 인스턴스를 생성하여 반환하는 코드입니다.
  • cls는 해당 클래스 자체를 참조하며, 이 코드 구문은 클래스 메소드 또는 클래스 내부의 다른 메소드에서 새로운 인스턴스를 생성하기 위해 사용됩니다.
  • 이 패턴은 클래스의 __init__ 메소드가 해당 인수들을 받아들일 것으로 예상합니다. 따라서 이 코드가 사용되는 클래스는 __init__ 메소드가 messagestemplate_format을 인수로 받아야 합니다.
posted by Peter Note

LLM에 프롬프트시에 다양한 포멧을 정의하는 할 수 있게 도와주는 것이 LangChain의 PromptTemplate 이다. 

 

개념


PromptTemplate은 단순 질의시 사용한다. 

  - from_template -> PromptTemplate 의 인스턴스를 반환한다. (파이썬의 cls 용법 , 메서드 중간 * 인자 용법 참조)

class PromptTemplate(StringPromptTemplate):
    @classmethod
    def from_template(
        cls,
        template: str,
        *,
        template_format: str = "f-string",
        partial_variables: Optional[Dict[str, Any]] = None,
        **kwargs: Any,
    ) -> PromptTemplate:
        """Load a prompt template from a template.

        *Security warning*:
            Prefer using `template_format="f-string"` instead of
            `template_format="jinja2"`, or make sure to NEVER accept jinja2 templates
            from untrusted sources as they may lead to arbitrary Python code execution.

            As of LangChain 0.0.329, Jinja2 templates will be rendered using
            Jinja2's SandboxedEnvironment by default. This sand-boxing should
            be treated as a best-effort approach rather than a guarantee of security,
            as it is an opt-out rather than opt-in approach.

            Despite the sand-boxing, we recommend never using jinja2 templates
            from untrusted sources.

        Args:
            template: The template to load.
            template_format: The format of the template. Use `jinja2` for jinja2,
                             and `f-string` or None for f-strings.
                             Defaults to `f-string`.
            partial_variables: A dictionary of variables that can be used to partially
                               fill in the template. For example, if the template is
                              `"{variable1} {variable2}"`, and `partial_variables` is
                              `{"variable1": "foo"}`, then the final prompt will be
                              `"foo {variable2}"`. Defaults to None.
            kwargs: Any other arguments to pass to the prompt template.

        Returns:
            The prompt template loaded from the template.
        """

        input_variables = get_template_variables(template, template_format)
        _partial_variables = partial_variables or {}

        if _partial_variables:
            input_variables = [
                var for var in input_variables if var not in _partial_variables
            ]

        return cls(
            input_variables=input_variables,
            template=template,
            template_format=template_format,  # type: ignore[arg-type]
            partial_variables=_partial_variables,
            **kwargs,
        )

 

PromptTemplate 인스턴스의 invoke 호출하기 

from langchain_openai import ChatOpenAI
from langchain_core.prompts import PromptTemplate
from langchain_core.output_parsers import StrOutputParser

# ChatOpenAI 모델을 인스턴스화합니다.
model = ChatOpenAI()
# 주어진 토픽에 대한 농담을 요청하는 프롬프트 템플릿을 생성합니다.
prompt = PromptTemplate.from_template("{topic} 에 대하여 3문장으로 설명해줘.")

# invoke 호출시에 topic 입력값을 준다. 
chain = prompt | model | StrOutputParser()
chain.invoke({"topic": "ChatGPT"})


출처: LangChain KR

 

LCEL에 적용할 수 있는 것은 전부 Runnable을 상속받아야 하고, Runnable의 abstractmethod인 invoke를 구현해야 한다. "chain.invoke" 호출은 최초 Runnable의 invoke를 구현하고 있는   BasePromptTemplate -->  StringPromptTemplate --> PromptTemplate 상속 관계에서 Runnable을 상속받아 invoke 메서드를 구현하고 있는 BasePromptTemplate의 invoke 보자. 

 

  - 입력값: Dictionary 예)  {"topic": "ChatGPT"}

  - 반환값: PromptValue 인스턴스이다. 

class BasePromptTemplate(
    RunnableSerializable[Dict, PromptValue], Generic[FormatOutputType], ABC
):
    ... 중략 ...
    
    def invoke(
        self, input: Dict, config: Optional[RunnableConfig] = None
    ) -> PromptValue:
        """Invoke the prompt.

        Args:
            input: Dict, input to the prompt.
            config: RunnableConfig, configuration for the prompt.

        Returns:
            PromptValue: The output of the prompt.
        """
        config = ensure_config(config)
        if self.metadata:
            config["metadata"] = {**config["metadata"], **self.metadata}
        if self.tags:
            config["tags"] = config["tags"] + self.tags
        return self._call_with_config(
            self._format_prompt_with_error_handling,
            input,
            config,
            run_type="prompt",
        )

LangChain KR - 프롬프트 템플릿: https://wikidocs.net/231233

 

1-3-2. 프롬프트 템플릿 (PromptTemplate)

PromptTemplate은 단일 문장 또는 간단한 명령을 입력하여 단일 문장 또는 간단한 응답을 생성하는 데 사용되는 프롬프트를 구성할 수 있는 문자열 템플릿입니다. Pytho…

wikidocs.net

 

 

ChatPromptTemplate 은 채팅 형식에서 사용한다. PromptTemplate 을 감싸서 System, Human, AI 롤(role)로 감싸면서 각각에 대한 Message 인스턴스와 MessagePromptTemplate 인스턴스를 Wrapping한다. 

 

  - 채팅형식: ChatPromptTemplate 사용 (system, humna, ai) 

  - 미리 프롬프트 영역 잡아 놓기: MessagePlaceholder 사용

  - from_messages 로 system, human mesage 를 구성 -> format_messages (또는 format) 으로 입력값 key=value 설정

     - LLM 호출전에 format_messages 형태로 

from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder

chat_prompt = ChatPromptTemplate.from_messages(
    [
        (
            "system",
            "당신은 요약 전문 AI 어시스턴트입니다. 당신의 임무는 주요 키워드로 대화를 요약하는 것입니다.",
        ),
        MessagesPlaceholder(variable_name="conversation"),
        ("human", "지금까지의 대화를 {word_count} 단어로 요약합니다."),
    ]
)

...

// 호출 case-1)
messages = chat_prompt.format_messages(
    word_count=5,
    conversation=[
        ("human", "안녕하세요! 저는 오늘 새로 입사한 테디 입니다. 만나서 반갑습니다."),
        ("ai", "반가워요! 앞으로 잘 부탁 드립니다."),
    ],
)
llm.invoke(messages)

// 호출 case-2) 또는 LCEL을 사용하여 chain 을 구성 후 입력값 설정하기 
chain = chat_template | llm
chain.invoke({word_count=5,
    conversation=[
        ("human", "안녕하세요! 저는 오늘 새로 입사한 테디 입니다. 만나서 반갑습니다."),
        ("ai", "반가워요! 앞으로 잘 부탁 드립니다."),
    ]})
    
// 출처: LangChain KR

 

예의 from_messages 호출 반환 chat_prompt 객체 내역

ChatPromptTemplate(input_variables=['conversation', 'word_count'], input_types={'conversation': typing.List[typing.Union[langchain_core.messages.ai.AIMessage, langchain_core.messages.human.HumanMessage, langchain_core.messages.chat.ChatMessage, langchain_core.messages.system.SystemMessage, langchain_core.messages.function.FunctionMessage, langchain_core.messages.tool.ToolMessage]]}, messages=[SystemMessagePromptTemplate(prompt=PromptTemplate(input_variables=[], template='당신은 요약 전문 AI 어시스턴트입니다. 당신의 임무는 주요 키워드로 대화를 요약하는 것입니다.')), MessagesPlaceholder(variable_name='conversation'), HumanMessagePromptTemplate(prompt=PromptTemplate(input_variables=['word_count'], template='지금까지의 대화를 {word_count} 단어로 요약합니다.'))])

 

예의 format_message 호출 반환 messages 객체 내역

[SystemMessage(content='당신은 요약 전문 AI 어시스턴트입니다. 당신의 임무는 주요 키워드로 대화를 요약하는 것입니다.'),
 HumanMessage(content='안녕하세요! 저는 오늘 새로 입사한 테디 입니다. 만나서 반갑습니다.'),
 AIMessage(content='반가워요! 앞으로 잘 부탁 드립니다.'),
 HumanMessage(content='지금까지의 대화를 5 단어로 요약합니다.')]

 

반환 타입에서 messages 서브 패키지의 다양한 메세지타입이 보인다. 이는 다음의 상속관계를 갖는다. 

BaseMessage --> SystemMessage, AIMessage, HumanMessage, ChatMessage, FunctionMessage, ToolMessage
            --> BaseMessageChunk --> SystemMessageChunk, AIMessageChunk, HumanMessageChunk, ChatMessageChunk, FunctionMessageChunk, ToolMessageChunk

 

여기서 BaseMessage 는 chat_models의 호출 형태로 invoke, stream 호출시 List[BaseMessage] 행태로 전달할 수 있다. 

chat models들은 모두 langchain_core.language_models 서브패키지의 BaseChatModel을 상속하고 있고, 여러 호출 메서드를 제공한다.

        +---------------------------+----------------------------------------------------------------+---------------------------------------------------------------------+--------------------------------------------------------------------------------------------------+
        | Method                    | Input                                                          | Output                                                              | Description                                                                                      |
        +===========================+================================================================+=====================================================================+==================================================================================================+
        | `invoke`                  | str | List[dict | tuple | BaseMessage] | PromptValue           | BaseMessage                                                         | A single chat model call.                                                                        |
        +---------------------------+----------------------------------------------------------------+---------------------------------------------------------------------+--------------------------------------------------------------------------------------------------+
        | `ainvoke`                 | '''                                                            | BaseMessage                                                         | Defaults to running invoke in an async executor.                                                 |
        +---------------------------+----------------------------------------------------------------+---------------------------------------------------------------------+--------------------------------------------------------------------------------------------------+
        | `stream`                  | '''                                                            | Iterator[BaseMessageChunk]                                          | Defaults to yielding output of invoke.                                                           |
        +---------------------------+----------------------------------------------------------------+---------------------------------------------------------------------+--------------------------------------------------------------------------------------------------+
        | `astream`                 | '''                                                            | AsyncIterator[BaseMessageChunk]                                     | Defaults to yielding output of ainvoke.                                                          |
        +---------------------------+----------------------------------------------------------------+---------------------------------------------------------------------+--------------------------------------------------------------------------------------------------+
        | `astream_events`          | '''                                                            | AsyncIterator[StreamEvent]                                          | Event types: 'on_chat_model_start', 'on_chat_model_stream', 'on_chat_model_end'.                 |
        +---------------------------+----------------------------------------------------------------+---------------------------------------------------------------------+--------------------------------------------------------------------------------------------------+
        | `batch`                   | List[''']                                                      | List[BaseMessage]                                                   | Defaults to running invoke in concurrent threads.                                                |
        +---------------------------+----------------------------------------------------------------+---------------------------------------------------------------------+--------------------------------------------------------------------------------------------------+
        | `abatch`                  | List[''']                                                      | List[BaseMessage]                                                   | Defaults to running ainvoke in concurrent threads.                                               |
        +---------------------------+----------------------------------------------------------------+---------------------------------------------------------------------+--------------------------------------------------------------------------------------------------+
        | `batch_as_completed`      | List[''']                                                      | Iterator[Tuple[int, Union[BaseMessage, Exception]]]                 | Defaults to running invoke in concurrent threads.                                                |
        +---------------------------+----------------------------------------------------------------+---------------------------------------------------------------------+--------------------------------------------------------------------------------------------------+
        | `abatch_as_completed`     | List[''']                                                      | AsyncIterator[Tuple[int, Union[BaseMessage, Exception]]]            | Defaults to running ainvoke in concurrent threads.                                               |
        +---------------------------+----------------------------------------------------------------+---------------------------------------------------------------------+--------------------------------------------------------------------------------------------------+

LangChain KR - 프롬프트 템플릿: https://wikidocs.net/231328

 

1-3-3. 챗 프롬프트 템플릿 (ChatPromptTemplate)

ChatPromptTemplate은 대화형 상황에서 여러 메시지 입력을 기반으로 단일 메시지 응답을 생성하는 데 사용됩니다. 이는 대화형 모델이나 챗봇 개발에 주로 사용됩니다. …

wikidocs.net

 

 

패키지

langchain_core 패키지의  prompts 와 messages 서브패키지를 사용합니다. 아래 상속관계를 보면 BaseMessagePromptTemplate 는 개별 메시지 단위로 프롬프트를 구성하는 것이고, BasePromptTemplate를  상속받은 ChatPromptTemplate은 BaseMessagePromptTemplate 상속받은 클래스들과 str, tuple등의 메세지를 담을 수 있다.

    BasePromptTemplate --> PipelinePromptTemplate
                           StringPromptTemplate --> PromptTemplate
                                                    FewShotPromptTemplate
                                                    FewShotPromptWithTemplates
                           BaseChatPromptTemplate --> AutoGPTPrompt
                                                      ChatPromptTemplate --> AgentScratchPadChatPromptTemplate



    BaseMessagePromptTemplate --> MessagesPlaceholder
                                  BaseStringMessagePromptTemplate --> ChatMessagePromptTemplate
                                                                      HumanMessagePromptTemplate
                                                                      AIMessagePromptTemplate
                                                                      SystemMessagePromptTemplate

 

ChatPromptTemplate의 __init__ 코드 내에서 _convert_to_message를 호출하면서 다양한 타입을 프롬프트 템플릿으로 만들 수 있다. 

class ChatPromptTemplate(BaseChatPromptTemplate):
    """Prompt template for chat models.

    Use to create flexible templated prompts for chat models.

    Examples:

        .. versionchanged:: 0.2.24

            You can pass any Message-like formats supported by
            ``ChatPromptTemplate.from_messages()`` directly to ``ChatPromptTemplate()``
            init.

        .. code-block:: python

            from langchain_core.prompts import ChatPromptTemplate

            template = ChatPromptTemplate([
                ("system", "You are a helpful AI bot. Your name is {name}."),
                ("human", "Hello, how are you doing?"),
                ("ai", "I'm doing well, thanks!"),
                ("human", "{user_input}"),
            ])

            prompt_value = template.invoke(
                {
                    "name": "Bob",
                    "user_input": "What is your name?"
                }
            )
            # Output:
            # ChatPromptValue(
            #    messages=[
            #        SystemMessage(content='You are a helpful AI bot. Your name is Bob.'),
            #        HumanMessage(content='Hello, how are you doing?'),
            #        AIMessage(content="I'm doing well, thanks!"),
            #        HumanMessage(content='What is your name?')
            #    ]
            #)

    Messages Placeholder:

        .. code-block:: python

            # In addition to Human/AI/Tool/Function messages,
            # you can initialize the template with a MessagesPlaceholder
            # either using the class directly or with the shorthand tuple syntax:

            template = ChatPromptTemplate([
                ("system", "You are a helpful AI bot."),
                # Means the template will receive an optional list of messages under
                # the "conversation" key
                ("placeholder", "{conversation}")
                # Equivalently:
                # MessagesPlaceholder(variable_name="conversation", optional=True)
            ])

            prompt_value = template.invoke(
                {
                    "conversation": [
                        ("human", "Hi!"),
                        ("ai", "How can I assist you today?"),
                        ("human", "Can you make me an ice cream sundae?"),
                        ("ai", "No.")
                    ]
                }
            )

            # Output:
            # ChatPromptValue(
            #    messages=[
            #        SystemMessage(content='You are a helpful AI bot.'),
            #        HumanMessage(content='Hi!'),
            #        AIMessage(content='How can I assist you today?'),
            #        HumanMessage(content='Can you make me an ice cream sundae?'),
            #        AIMessage(content='No.'),
            #    ]
            #)

    Single-variable template:

        If your prompt has only a single input variable (i.e., 1 instance of "{variable_nams}"),
        and you invoke the template with a non-dict object, the prompt template will
        inject the provided argument into that variable location.


        .. code-block:: python

            from langchain_core.prompts import ChatPromptTemplate

            template = ChatPromptTemplate([
                ("system", "You are a helpful AI bot. Your name is Carl."),
                ("human", "{user_input}"),
            ])

            prompt_value = template.invoke("Hello, there!")
            # Equivalent to
            # prompt_value = template.invoke({"user_input": "Hello, there!"})

            # Output:
            #  ChatPromptValue(
            #     messages=[
            #         SystemMessage(content='You are a helpful AI bot. Your name is Carl.'),
            #         HumanMessage(content='Hello, there!'),
            #     ]
            # )

    """  # noqa: E501

    messages: List[MessageLike]
    """List of messages consisting of either message prompt templates or messages."""
    validate_template: bool = False
    """Whether or not to try validating the template."""

    def __init__(
        self,
        messages: Sequence[MessageLikeRepresentation],
        *,
        template_format: Literal["f-string", "mustache", "jinja2"] = "f-string",
        **kwargs: Any,
    ) -> None:
        """Create a chat prompt template from a variety of message formats.

        Args:
            messages: sequence of message representations.
                  A message can be represented using the following formats:
                  (1) BaseMessagePromptTemplate, (2) BaseMessage, (3) 2-tuple of
                  (message type, template); e.g., ("human", "{user_input}"),
                  (4) 2-tuple of (message class, template), (4) a string which is
                  shorthand for ("human", template); e.g., "{user_input}".
            template_format: format of the template. Defaults to "f-string".
            input_variables: A list of the names of the variables whose values are
                required as inputs to the prompt.
            optional_variables: A list of the names of the variables that are optional
                in the prompt.
            partial_variables: A dictionary of the partial variables the prompt
                template carries. Partial variables populate the template so that you
                don't need to pass them in every time you call the prompt.
            validate_template: Whether to validate the template.
            input_types: A dictionary of the types of the variables the prompt template
                expects. If not provided, all variables are assumed to be strings.

        Returns:
            A chat prompt template.

        Examples:

            Instantiation from a list of message templates:

            .. code-block:: python

                template = ChatPromptTemplate([
                    ("human", "Hello, how are you?"),
                    ("ai", "I'm doing well, thanks!"),
                    ("human", "That's good to hear."),
                ])

            Instantiation from mixed message formats:

            .. code-block:: python

                template = ChatPromptTemplate([
                    SystemMessage(content="hello"),
                    ("human", "Hello, how are you?"),
                ])

        """
        _messages = [
            _convert_to_message(message, template_format) for message in messages
        ]

        # Automatically infer input variables from messages
        input_vars: Set[str] = set()
        optional_variables: Set[str] = set()
        partial_vars: Dict[str, Any] = {}
        for _message in _messages:
            if isinstance(_message, MessagesPlaceholder) and _message.optional:
                partial_vars[_message.variable_name] = []
                optional_variables.add(_message.variable_name)
            elif isinstance(
                _message, (BaseChatPromptTemplate, BaseMessagePromptTemplate)
            ):
                input_vars.update(_message.input_variables)

        kwargs = {
            **dict(
                input_variables=sorted(input_vars),
                optional_variables=sorted(optional_variables),
                partial_variables=partial_vars,
            ),
            **kwargs,
        }
        cast(Type[ChatPromptTemplate], super()).__init__(messages=_messages, **kwargs)

 

_convert_to_message 펑션 코드 

def _convert_to_message(
    message: MessageLikeRepresentation,
    template_format: Literal["f-string", "mustache", "jinja2"] = "f-string",
) -> Union[BaseMessage, BaseMessagePromptTemplate, BaseChatPromptTemplate]:
    """Instantiate a message from a variety of message formats.

    The message format can be one of the following:

    - BaseMessagePromptTemplate
    - BaseMessage
    - 2-tuple of (role string, template); e.g., ("human", "{user_input}")
    - 2-tuple of (message class, template)
    - string: shorthand for ("human", template); e.g., "{user_input}"

    Args:
        message: a representation of a message in one of the supported formats.
        template_format: format of the template. Defaults to "f-string".

    Returns:
        an instance of a message or a message template.

    Raises:
        ValueError: If unexpected message type.
        ValueError: If 2-tuple does not have 2 elements.
    """
    if isinstance(message, (BaseMessagePromptTemplate, BaseChatPromptTemplate)):
        _message: Union[
            BaseMessage, BaseMessagePromptTemplate, BaseChatPromptTemplate
        ] = message
    elif isinstance(message, BaseMessage):
        _message = message
    elif isinstance(message, str):
        _message = _create_template_from_message_type(
            "human", message, template_format=template_format
        )
    elif isinstance(message, tuple):
        if len(message) != 2:
            raise ValueError(f"Expected 2-tuple of (role, template), got {message}")
        message_type_str, template = message
        if isinstance(message_type_str, str):
            _message = _create_template_from_message_type(
                message_type_str, template, template_format=template_format
            )
        else:
            _message = message_type_str(
                prompt=PromptTemplate.from_template(
                    cast(str, template), template_format=template_format
                )
            )
    else:
        raise NotImplementedError(f"Unsupported message type: {type(message)}")

    return _message

 

 

API

클래스 메서드인 from_messages() 를 호출하면 cls를 반환하므로 ChatPromptTemplate 인스턴스를 반환한다.  

  - from_messages() -> ChatPromptTemplate

    @classmethod
    def from_messages(
        cls,
        messages: Sequence[MessageLikeRepresentation],
        template_format: Literal["f-string", "mustache", "jinja2"] = "f-string",
    ) -> ChatPromptTemplate:
        """Create a chat prompt template from a variety of message formats.

        Examples:

            Instantiation from a list of message templates:

            .. code-block:: python

                template = ChatPromptTemplate.from_messages([
                    ("human", "Hello, how are you?"),
                    ("ai", "I'm doing well, thanks!"),
                    ("human", "That's good to hear."),
                ])

            Instantiation from mixed message formats:

            .. code-block:: python

                template = ChatPromptTemplate.from_messages([
                    SystemMessage(content="hello"),
                    ("human", "Hello, how are you?"),
                ])

        Args:
            messages: sequence of message representations.
                  A message can be represented using the following formats:
                  (1) BaseMessagePromptTemplate, (2) BaseMessage, (3) 2-tuple of
                  (message type, template); e.g., ("human", "{user_input}"),
                  (4) 2-tuple of (message class, template), (4) a string which is
                  shorthand for ("human", template); e.g., "{user_input}".
            template_format: format of the template. Defaults to "f-string".

        Returns:
            a chat prompt template.
        """
        return cls(messages, template_format=template_format)

 

입력값 맵핑은 format_messages() 또는 format() 을 사용한다. 

  - format_messages() -> List[BaseMessage]

    def format_messages(self, **kwargs: Any) -> List[BaseMessage]:
        """Format the chat template into a list of finalized messages.

        Args:
            **kwargs: keyword arguments to use for filling in template variables
                      in all the template messages in this chat template.

        Returns:
            list of formatted messages.
        """
        kwargs = self._merge_partial_and_user_variables(**kwargs)
        result = []
        for message_template in self.messages:
            if isinstance(message_template, BaseMessage):
                result.extend([message_template])
            elif isinstance(
                message_template, (BaseMessagePromptTemplate, BaseChatPromptTemplate)
            ):
                message = message_template.format_messages(**kwargs)
                result.extend(message)
            else:
                raise ValueError(f"Unexpected input: {message_template}")
        return result

 

PromptTemplate의 from_template 인스턴스

from langchain_core.prompts import PromptTemplate

prompt = PromptTemplate.from_template("tell me a joke about {topic}")
prompt

// 결과
PromptTemplate(input_variables=['topic'], template='tell me a joke about {topic}')

 

ChatPromptTemplate의 from_template 인스턴스

  - HumanMessagePromptTemplate이 PromptTemplate을 한번 더 감싼 객체

from langchain_core.prompts import ChatPromptTemplate

prompt = ChatPromptTemplate.from_template("tell me a joke about {topic}")
prompt

// 결과
ChatPromptTemplate(input_variables=['topic'], messages=[HumanMessagePromptTemplate(prompt=PromptTemplate(input_variables=['topic'], template='tell me a joke about {topic}'))])

 

<참조>

- api: https://api.python.langchain.com/en/latest/core_api_reference.html#module-langchain_core.prompts

- LangCain Core 소스: https://github.com/langchain-ai/langchain/tree/master/libs/core/langchain_core/prompts

 

langchain/libs/core/langchain_core/prompts at master · langchain-ai/langchain

🦜🔗 Build context-aware reasoning applications. Contribute to langchain-ai/langchain development by creating an account on GitHub.

github.com

- LangChain KR - 프롬프트 템플릿: https://wikidocs.net/231233

 

1-3-2. 프롬프트 템플릿 (PromptTemplate)

PromptTemplate은 단일 문장 또는 간단한 명령을 입력하여 단일 문장 또는 간단한 응답을 생성하는 데 사용되는 프롬프트를 구성할 수 있는 문자열 템플릿입니다. Pytho…

wikidocs.net

- LangChain KR - 챗 프롬프트 템플릿: https://wikidocs.net/231328

 

1-3-3. 챗 프롬프트 템플릿 (ChatPromptTemplate)

ChatPromptTemplate은 대화형 상황에서 여러 메시지 입력을 기반으로 단일 메시지 응답을 생성하는 데 사용됩니다. 이는 대화형 모델이나 챗봇 개발에 주로 사용됩니다. …

wikidocs.net

 

posted by Peter Note

출처: DeepLearning.ai

 

Retriever은 VectorStore에서 데이터를 검색하는 인터페이스이다. Retriever는 VectorStore에서 as_retriever() 메서드 호출로 생성한다. 리트리버의 두가지 쿼리 방식에 대해 알아보자. 

  - Sematic Similarity

     - k: relevance 관련성 있는 것을 결과에 반영할 갯수

  - Maxium Marginal relavance

     - k: relevance 관련성 있는 것을 결과에 반영할 갯수

     - fetch_k: diverse 다양성 있는 것을 결과에 반영할 갯수

     

 

패키지

Semantic Similarity는 의미적으로 유사한 것을 찾아준다. Reriever를 VectorStore를 통해 얻게 되면 VectoreStore 클래스의 similarity_search() 를 사용한다. 그리고, VectoreStore 클래스를 상속받은 구현체는 similarity_search가 abstractmethod 이기에 구현을 해야 한다. 예로)  Chroma는 VectorStore를 상속받아 구현하고 있다.

  - 관련성있는 것을 찾기 위하여 k 갯수를 설정한다. 

class Chroma(VectorStore):
    def similarity_search(
        self,
        query: str,
        k: int = DEFAULT_K,
        filter: Optional[Dict[str, str]] = None,
        **kwargs: Any,
    ) -> List[Document]:
        """Run similarity search with Chroma.

        Args:
            query (str): Query text to search for.
            k (int): Number of results to return. Defaults to 4.
            filter (Optional[Dict[str, str]]): Filter by metadata. Defaults to None.

        Returns:
            List[Document]: List of documents most similar to the query text.
        """
        docs_and_scores = self.similarity_search_with_score(
            query, k, filter=filter, **kwargs
        )
        return [doc for doc, _ in docs_and_scores]

 

MMR(Maximum Marginal Relevance) 는 관련성과 다양성을 동시에 검색하여 제공한다. VectorStore의 max_marginal_relevance_search() 를 사용한다.  Chroma 구현내용을 보자.

   - k: 관련성 결과반영 갯수

   - fetch_k: 다양성 결과반영 갯수 

class Chroma(VectorStore):
    def max_marginal_relevance_search(
        self,
        query: str,
        k: int = DEFAULT_K,
        fetch_k: int = 20,
        lambda_mult: float = 0.5,
        filter: Optional[Dict[str, str]] = None,
        where_document: Optional[Dict[str, str]] = None,
        **kwargs: Any,
    ) -> List[Document]:
        """Return docs selected using the maximal marginal relevance.
        Maximal marginal relevance optimizes for similarity to query AND diversity
        among selected documents.

        Args:
            query: Text to look up documents similar to.
            k: Number of Documents to return. Defaults to 4.
            fetch_k: Number of Documents to fetch to pass to MMR algorithm.
            lambda_mult: Number between 0 and 1 that determines the degree
                        of diversity among the results with 0 corresponding
                        to maximum diversity and 1 to minimum diversity.
                        Defaults to 0.5.
            filter (Optional[Dict[str, str]]): Filter by metadata. Defaults to None.

        Returns:
            List of Documents selected by maximal marginal relevance.
        """
        if self._embedding_function is None:
            raise ValueError(
                "For MMR search, you must specify an embedding function on" "creation."
            )

        embedding = self._embedding_function.embed_query(query)
        docs = self.max_marginal_relevance_search_by_vector(
            embedding,
            k,
            fetch_k,
            lambda_mult=lambda_mult,
            filter=filter,
            where_document=where_document,
        )
        return docs

 

VectorStore에서 from_documents 또는 from_texts 메서도에 content와 embedding을 파라미터 설정하고 호출하면 VST 타입의 VectorStore 객체를 반환 받을 수 있다. 이를 통해 similarity_search와 max_marginal_relevance_search 를 사용할 수 있다. 

class VectorStore(ABC):
    @classmethod
    @abstractmethod
    def from_texts(
        cls: Type[VST],
        texts: List[str],
        embedding: Embeddings,
        metadatas: Optional[List[dict]] = None,
        **kwargs: Any,
    ) -> VST:
        """Return VectorStore initialized from texts and embeddings.

        Args:
            texts: Texts to add to the vectorstore.
            embedding: Embedding function to use.
            metadatas: Optional list of metadatas associated with the texts.
                Default is None.
            **kwargs: Additional keyword arguments.

        Returns:
            VectorStore: VectorStore initialized from texts and embeddings.
        """

 

similarity_search와 max_marginal_relevance_search 를 사용예를 보자.

from langchain.vectorstores import Chroma
from langchain.openai import OpenAIEmbeddings

persistence_path = 'db/chroma'
embeddings = OpenAIEmbeddings()
vectorestore = Chroma(persist_directory=persistence_path, embedding_function=embeddings)

texts = [
    """홍길동을 홍길동이라 부르지 못하는 이유는 무엇인가""",
    """심청이가 물에 빠진것을 효라고 말할 수 있는가, 그것이 진심이었나""",
    """춘향이가 이몽룡을 기다른 것은 진심이었나""",
]

smalldb = Chroma.from_texts(texts, embedding=embedding)
question = "진심이라는 의미"

smalldb.similarity_search(question, k=1)
// 결과 
[Document(page_content='춘향이가 이몽룡을 기다른 것은 진심이었나', metadata={})]


smalldb.max_marginal_relevance_search(question,k=2, fetch_k=3)
// 결과
[Document(page_content='춘향이가 이몽룡을 기다른 것은 진심이었나', metadata={}),
 Document(page_content='심청이가 물에 빠진것을 효라고 말할 수 있는가, 그것이 진심이었나', metadata={})]

 

위의 두가지외에 다른 검색 타입까지 합쳐 놓은 것이 langchain_core VectorStore의 as_retriever() 이다. from_documents 또는 from_texts를 호출하면 VST (Vector Store Type) 인스턴스를 반환 받아 as_retriever()를 호출한다. as_retriever()는 langchain_community VectorStoreRetriever를 반환하고, 이는 langchain_core의 retrievers.py 파일의 BaseRetriever 클래스를 상속받아 구현하고 있다. 

  - 0.1.46 이후 get_relevant_documents 사용하지 않고 invoke로 대체한다. 

  - _get_relevant_documents 를 구현해야 한다.

  - search_type 

     - similarity

     - similarity_score_threshold : score_threshold 값 설정 이상만 결과에 반영

     - mmr 

  - 결과값: List[Document] 

class VectorStoreRetriever(BaseRetriever):
    """Base Retriever class for VectorStore."""

    vectorstore: VectorStore
    """VectorStore to use for retrieval."""
    search_type: str = "similarity"
    """Type of search to perform. Defaults to "similarity"."""
    search_kwargs: dict = Field(default_factory=dict)
    """Keyword arguments to pass to the search function."""
    allowed_search_types: ClassVar[Collection[str]] = (
        "similarity",
        "similarity_score_threshold",
        "mmr",
    )
    ...
    
 class BaseRetriever(RunnableSerializable[RetrieverInput, RetrieverOutput], ABC):
    def invoke(
        self, input: str, config: Optional[RunnableConfig] = None, **kwargs: Any
    ) -> List[Document]:
        """Invoke the retriever to get relevant documents.
        ...
        """
        ...
        
    
    // 하위 클래스 생성시 호출이 된다. cls는 하위 클래스이다. get_relevant_documents를 할당함.
    def __init_subclass__(cls, **kwargs: Any) -> None:
        super().__init_subclass__(**kwargs)
        # Version upgrade for old retrievers that implemented the public
        # methods directly.
        if cls.get_relevant_documents != BaseRetriever.get_relevant_documents:
            warnings.warn(
                "Retrievers must implement abstract `_get_relevant_documents` method"
                " instead of `get_relevant_documents`",
                DeprecationWarning,
            )
            swap = cls.get_relevant_documents
            cls.get_relevant_documents = (  # type: ignore[assignment]
                BaseRetriever.get_relevant_documents
            )
            cls._get_relevant_documents = swap  # type: ignore[assignment]
        if (
            hasattr(cls, "aget_relevant_documents")
            and cls.aget_relevant_documents != BaseRetriever.aget_relevant_documents
        ):
            warnings.warn(
                "Retrievers must implement abstract `_aget_relevant_documents` method"
                " instead of `aget_relevant_documents`",
                DeprecationWarning,
            )
            aswap = cls.aget_relevant_documents
            cls.aget_relevant_documents = (  # type: ignore[assignment]
                BaseRetriever.aget_relevant_documents
            )
            cls._aget_relevant_documents = aswap  # type: ignore[assignment]
        parameters = signature(cls._get_relevant_documents).parameters
        cls._new_arg_supported = parameters.get("run_manager") is not None
        # If a V1 retriever broke the interface and expects additional arguments
        cls._expects_other_args = (
            len(set(parameters.keys()) - {"self", "query", "run_manager"}) > 0
        )
    
    @abstractmethod
    def _get_relevant_documents(
        self, query: str, *, run_manager: CallbackManagerForRetrieverRun
    ) -> List[Document]:
        """Get documents relevant to a query.

        Args:
            query: String to find relevant documents for.
            run_manager: The callback handler to use.
        Returns:
            List of relevant documents.
        """
        ...
        
    @deprecated(since="0.1.46", alternative="invoke", removal="0.3.0")
    def get_relevant_documents(
       ....

 

 

API

langchain_core의 BaseRetriever --> langchain_core의 VectorStoreRetriever 또는  langchain_community에서 다양한 <name>Retriever 를 구현한다. 

  - invoke, ainvoke 호출

  - get_relevant_documents 호출은 invoke 로 대체되고, v0.3.0 에서 삭제될 예정이다. 

 

<참조>

- 공식문서: https://python.langchain.com/v0.2/docs/integrations/retrievers/

 

Retrievers | 🦜️🔗 LangChain

A retriever is an interface that returns documents given an unstructured query.

python.langchain.com

- API: https://api.python.langchain.com/en/latest/community_api_reference.html#module-langchain_community.retrievers

- LangChain KR: https://wikidocs.net/234016

 

01. 벡터저장소 지원 검색기(VectorStore-backed Retriever)

.custom { background-color: #008d8d; color: white; padding: 0.25em 0.5…

wikidocs.net

 

posted by Peter Note