Langchain에서 대규모 모델의 출력 형식을 지정하는 방법

소개

우리는 큰 언어 모델에서는 모델이 아무리 강력하더라도 입력 및 출력이 기본적으로 텍스트 형식이라는 것을 알고 있습니다. 텍스트 형식의 입력 및 출력은 사람들에게 매우 친숙하지만 우리가 원하는 경우 여전히 구조화된 처리에 약간의 불편함이 있습니다.

걱정하지 마십시오. langchain은 우리를 위해 이 문제를 생각하고 완벽한 솔루션을 제안했습니다.

랭체인의 출력 파서

langchain의 모든 출력 파서는 BaseOutputParser에서 상속됩니다. 이 기본 클래스는 LLM 대규모 모델 출력에 대한 형식 지정 방법을 제공하며 탁월한 도구 클래스입니다.

먼저 그의 구현을 살펴보겠습니다.

class BaseOutputParser(BaseModel, ABC, Generic[T]):

    @abstractmethod
    def parse(self, text: str) -> T:
        """Parse the output of an LLM call.

        A method which takes in a string (assumed output of a language model )
        and parses it into some structure.

        Args:
            text: output of language model

        Returns:
            structured output
        """

    def parse_with_prompt(self, completion: str, prompt: PromptValue) -> Any:
        """Optional method to parse the output of an LLM call with a prompt.

        The prompt is largely provided in the event the OutputParser wants
        to retry or fix the output in some way, and needs information from
        the prompt to do so.

        Args:
            completion: output of language model
            prompt: prompt value

        Returns:
            structured output
        """
        return self.parse(completion)

    def get_format_instructions(self) -> str:
        """Instructions on how the LLM output should be formatted."""
        raise NotImplementedError

    @property
    def _type(self) -> str:
        """Return the type key."""
        raise NotImplementedError(
            f"_type property is not implemented in class {self.__class__.__name__}."
            " This is required for serialization."
        )

    def dict(self, **kwargs: Any) -> Dict:
        """Return dictionary representation of output parser."""
        output_parser_dict = super().dict()
        output_parser_dict["_type"] = self._type
        return output_parser_dict

BaseOutputParser는 특정 언어 모델의 출력 구문 분석을 구현하기 위해 다른 특정 출력 구문 분석기에 의해 상속될 수 있는 기본 클래스입니다.

이 클래스는 Python의 ABC 모듈을 사용하여 추상 기본 클래스(Abstract Base Class)이며 직접 인스턴스화할 수 없음을 나타냅니다. 대신 하위 클래스는 추상 메서드를 상속하고 구현해야 합니다.

Generic[T]는 이 클래스가 일반 클래스임을 나타냅니다. 여기서 T는 구문 분석된 출력 데이터의 유형을 나타내는 유형 변수입니다.

@abstractmethod 데코레이터는 구문 분석 메서드를 표시하여 이것이 추상 메서드이고 하위 클래스에서 구현되어야 함을 나타냅니다. 구문 분석 메소드는 일반적으로 언어 모델의 출력 텍스트인 문자열 매개변수 텍스트를 받아들인 다음 이를 특정 데이터 구조로 구문 분석하여 반환합니다.

pars_with_prompt 메소드도 추상 메소드이며 두 개의 매개변수를 허용합니다. 완성은 언어 모델의 출력이고 프롬프트는 출력과 관련된 프롬프트 정보입니다. 이 방법은 선택 사항이며 필요한 경우 출력을 구문 분석하는 데 사용할 수 있으며 프롬프트 정보에 따라 출력을 조정할 수도 있습니다.

get_format_instructions 메소드는 언어 모델 출력 형식을 지정하는 방법에 대한 지침을 반환합니다. 이 메서드는 구문 분석된 데이터에 대한 형식 지정 정보를 제공하는 데 사용할 수 있습니다.

_type은 후속 직렬화 또는 기타 작업을 위해 이 파서의 유형을 식별하는 데 사용할 수 있는 속성입니다.

dict 메소드는 직렬화 또는 기타 작업에 사용할 수 있는 출력 구문 분석기 정보가 포함된 사전을 반환합니다.

서브클래스가 구현해야 하는 메서드는 구문 분석입니다. 다른 것들은 보조 기능으로 사용됩니다.

langchain의 출력 파서는 무엇입니까?

그렇다면 langchain의 Output Parser의 구체적인 구현은 무엇입니까? 우리 애플리케이션에서는 어떤 특정 시나리오에 해당합니까?

다음에 하나씩 살펴보겠습니다.

목록 파서

ListOutputParser의 기능은 LLM의 출력을 목록으로 변환하는 것입니다. ListOutputParser는 기본 클래스이기도 하며 특히 해당 하위 클래스인 CommaSeparatedListOutputParser를 사용합니다.

그의 구문 분석 방법을 살펴보십시오.

    def parse(self, text: str) -> List[str]:
        """Parse the output of an LLM call."""
        return text.strip().split(", ")

get_format_instructions도 있습니다:

    def get_format_instructions(self) -> str:
        return (
            "Your response should be a list of comma separated values, "
            "eg: `foo, bar, baz`"
        )

get_format_instructions는 LLM에게 데이터를 반환할 형식을 알려줍니다.

LLM의 출력을 쉼표로 나누면 됩니다.

기본적인 사용 예는 다음과 같습니다.

output_parser = CommaSeparatedListOutputParser()

format_instructions = output_parser.get_format_instructions()
prompt = PromptTemplate(
    template="列出几种{subject}.\n{format_instructions}",
    input_variables=["subject"],
    partial_variables={"format_instructions": format_instructions}
)

_input = prompt.format(subject="水果")
output = model(_input)
print(output)
print(output_parser.parse(output))

다음과 같은 결과를 얻을 수 있습니다.

Apple, Orange, Banana, Grape, Watermelon, Strawberry, Pineapple, Peach, Mango, Cherry
['Apple', 'Orange', 'Banana', 'Grape', 'Watermelon', 'Strawberry', 'Pineapple', 'Peach', 'Mango', 'Cherry']

이것을 보고 궁금한 점이 있을 수 있습니다. 왜 우리는 중국어로 질문하는데 대답은 "왜냐하면"입니까?

왜냐하면output_parser.get_format_instructions가 영어로 설명되어 있기 때문에 LLM에서는 자연스럽게 영어로 대답하기 때문입니다.

걱정하지 마십시오. 다음과 같이 실행 코드를 약간 수정할 수 있습니다.

output_parser = CommaSeparatedListOutputParser()

format_instructions = output_parser.get_format_instructions()
prompt = PromptTemplate(
    template="列出几种{subject}.\n{format_instructions}",
    input_variables=["subject"],
    partial_variables={"format_instructions": format_instructions + "用中文回答"}
)

_input = prompt.format(subject="水果")
output = model(_input)
print(output)
print(output_parser.parse(output))

format_instructions 다음에는 LLM이 중국어로 질문에 답하도록 요청합니다. 이런 방식으로 우리는 다음과 같은 결과를 얻을 수 있습니다:

苹果,橘子,香蕉,梨,葡萄,芒果,柠檬,桃
['苹果,橘子,香蕉,梨,葡萄,芒果,柠檬,桃']

정말 좋지 않나요?

날짜시간 파서

DatetimeOutputParser는 LLM의 출력 형식을 시간에 맞춰 지정하는 데 사용됩니다.

class DatetimeOutputParser(BaseOutputParser[datetime]):
    format: str = "%Y-%m-%dT%H:%M:%S.%fZ"

    def get_format_instructions(self) -> str:
        examples = comma_list(_generate_random_datetime_strings(self.format))
        return f"""Write a datetime string that matches the 
            following pattern: "{self.format}". Examples: {examples}"""

    def parse(self, response: str) -> datetime:
        try:
            return datetime.strptime(response.strip(), self.format)
        except ValueError as e:
            raise OutputParserException(
                f"Could not parse datetime string: {response}"
            ) from e

    @property
    def _type(self) -> str:
        return "datetime"

get_format_instructions에서 그는 반환된 결과가 날짜 문자열임을 LLM에 알립니다.

그런 다음 구문 분석 메서드에서 이 LLM의 출력 형식을 지정하고 마지막으로 날짜/시간을 반환합니다.

특정 애플리케이션을 살펴보겠습니다.

output_parser = DatetimeOutputParser()
template = """回答下面问题:
{question}
{format_instructions}"""
prompt = PromptTemplate.from_template(
    template,
    partial_variables={"format_instructions": output_parser.get_format_instructions()},
)
chain = LLMChain(prompt=prompt, llm=model)
output = chain.run("中华人民共和国是什么时候成立的?")
print(output)
print(output_parser.parse(output))
1949-10-01T00:00:00.000000Z
1949-10-01 00:00:00

대답은 꽤 좋습니다. 그에게 엄지손가락을 치켜세우세요.

열거형 파서

열거형 유형이 있는 경우 EnumOutputParser를 사용해 보세요.

EnumOutputParser의 생성자는 Enum을 전달해야 합니다. 주로 두 가지 메서드를 살펴보겠습니다.

    @property
    def _valid_values(self) -> List[str]:
        return [e.value for e in self.enum]

    def parse(self, response: str) -> Any:
        try:
            return self.enum(response.strip())
        except ValueError:
            raise OutputParserException(
                f"Response '{response}' is not one of the "
                f"expected values: {self._valid_values}"
            )

    def get_format_instructions(self) -> str:
        return f"Select one of the following options: {', '.join(self._valid_values)}"

구문 분석 메소드는 문자열 응답을 수신하고 이를 열거 유형의 멤버로 구문 분석하려고 시도합니다. 구문 분석에 성공하면 열거형 멤버를 반환하고, 구문 분석에 실패하면 모든 유효한 값 목록과 함께 OutputParserException을 발생시킵니다.

get_format_instructions는 LLM에게 Enum의 유효한 값에서 출력을 선택해야 함을 알려줍니다. 이런 방식으로 구문 분석은 올바른 입력 값을 받을 수 있습니다.

구체적인 사용 예는 이전 두 파서의 사용법을 참조하세요. 공간상의 이유로 여기에는 나열하지 않았습니다.

Pydantic(JSON) 파서

JSON은 아마도 일상적인 코드에서 가장 일반적으로 사용되는 데이터 구조일 것입니다. 이 데이터 구조는 매우 중요합니다.

langchain에서 제공된 JSON 파서는 PydanticOutputParser라고 합니다.

JSON을 변환하려면 먼저 JSON 유형 개체를 정의한 다음 LLM에 텍스트 출력을 JSON 형식으로 변환하도록 지시하고 마지막으로 구문 분석 메서드를 호출하여 json 문자열을 JSON 개체로 변환해야 합니다.

예를 살펴보겠습니다:


class Student(BaseModel):
    name: str = Field(description="学生的姓名")
    age: str = Field(description="学生的年龄")

student_query = "告诉我一个学生的信息"

parser = PydanticOutputParser(pydantic_object=Student)

prompt = PromptTemplate(
    template="回答下面问题.\n{format_instructions}\n{query}\n",
    input_variables=["query"],
    partial_variables={"format_instructions": parser.get_format_instructions()+"用中文回答"},
)

_input = prompt.format_prompt(query=student_query)

output = model(_input.to_string())
print(output)
print(parser.parse(output))

여기서는 학생 구조를 정의한 다음 LLM에 학생 정보를 제공하고 이를 json 형식으로 반환하도록 요청합니다.

그런 다음 parser.parse를 사용하여 이 json을 구문 분석하고 최종 학생 정보를 생성합니다.

다음과 같은 결과를 얻을 수 있습니다.

示例输出:{"name": "张三", "age": "18"}
name='张三' age='18'

구조화된 출력 파서

PydanticOutputParser는 매우 강력하지만 때로는 간단한 구조 출력이 필요한 경우 StructuredOutputParser를 고려할 수 있습니다.

구체적인 예를 살펴보겠습니다.

response_schemas = [
    ResponseSchema(name="name", description="学生的姓名"),
    ResponseSchema(name="age", description="学生的年龄")
]
output_parser = StructuredOutputParser.from_response_schemas(response_schemas)

format_instructions = output_parser.get_format_instructions()
prompt = PromptTemplate(
    template="回答下面问题.\n{format_instructions}\n{question}",
    input_variables=["question"],
    partial_variables={"format_instructions": format_instructions}
)

_input = prompt.format_prompt(question="给我一个女孩的名字?")
output = model(_input.to_string())
print(output)
print(output_parser.parse(output))

이 예제는 위의 PydanticOutputParser를 적용한 것이지만 더 간단합니다.

우리는 다음과 같은 결과를 얻을 수 있습니다:

 ` ` `json
{
	"name": "Jane",
	"age": "18"
}
 ` ` `
{'name': 'Jane', 'age': '18'}

출력은 마크다운 형식의 json 문자열을 반환하고, 최종 json은 output_parser.parse를 통해 얻어집니다.

다른 파서

json 외에 xml 형식도 많이 사용되는 형식인데, langchain에서 제공하는 XML 파서는 XMLOutputParser라고 합니다.

또한 파서를 사용할 때 형식 문제가 있는 경우 langchain은 신중하게 OutputFixingParser도 제공합니다. 즉, 첫 번째 파서가 오류를 보고하거나 LLM 출력을 구문 분석할 수 없으면 형식 문제를 해결하기 위해 OutputFixingParser로 대체됩니다.

from langchain.output_parsers import OutputFixingParser

new_parser = OutputFixingParser.from_llm(parser=parser, llm=ChatOpenAI())

new_parser.parse(misformatted)

형식으로 인해 오류가 발생하지 않은 경우 langchain은 다시 시도할 수 있도록 RetryOutputParser도 제공합니다.

from langchain.output_parsers import RetryWithErrorOutputParser

retry_parser = RetryWithErrorOutputParser.from_llm(
    parser=parser, llm=OpenAI(temperature=0)
)

retry_parser.parse_with_prompt(bad_response, prompt_value)

이 파서는 매우 유용하며 직접 사용해 볼 수 있습니다.

요약하다

langchain의 일부 파서는 Python 언어의 다양한 도구를 사용하여 스스로 구현할 수 있습니다. 그러나 실제로 OutputFixingParser 및 RetryOutputParser와 같은 일부 파서는 LLM과 함께 사용됩니다.

그러므로 누구나 langchain에서 제공하는 파서를 최대한 활용하는 것이 좋습니다. 결국, 바퀴는 이미 당신을 위해 만들어졌는데, 자전거를 위해 또 무엇이 필요합니까?

おすすめ

転載: blog.csdn.net/superfjj/article/details/134637839