파이프 체이닝

Intro

Langchain 을 공부하다가 다음과 같은 흥미로운 표현 방법을 발견했습니다.

1
chain = prompt | llm | parser


이 표현 방법은 LCEL(LangChain Expression Language)이라는 표현법으로, 프롬프트, LLM, 파서를 하나의 파이프라인으로 묶어 연속된 작업을 처리하게끔 하는 표현 방법입니다.

이걸 보고 처음엔 “이게 도대체 무슨 문법이야..” 하다가 이와 비슷한 표현 방법을 이전에도 본 기억이 떠올랐습니다. 바로 airflow에서요.

1
2
3
4
# airflow 의 태스크 파이프라인
start > task1 > group1 > integration_task > end
start > task1 > group2 > integration_task > end
start > task1 > group3 > end

분명 저건 gt(greater than) 연산자인데, 어떻게 파이프라인의 전후를 묶는 역할을 하는 걸까요. 이번 포스팅에서는 그 방법을 알아보도록 하겠습니다.


개념

  • LCEL에서와 같이 파이프(|)로 전후 작업을 묶는 것을 파이프 체이닝(Pipe Chaining) 이라고 지칭합니다.
  • 파이프 체이닝은 생각보다 그 역사가 깊은데, 바로 유닉스 운영체제에서 프로세스 간 통신 매커니즘인 파이프라인(|) 입니다.
  • 유닉스에서 파이프라인은 앞의 프로세스의 출력을 뒤 프로세스의 입력으로 주는 기능을 합니다.
1
2
3
4
$ netstat -nltp | grep 8087

# netstat -nltp : 현재 포트 정보를 프로세스와 함께 표시 -> 출력 : 포트, 프로세스 정보
# grep 8087 : 입력에서 "8087"이 포함된 line을 찾아 출력

파이썬에서의 구현

  • 파이썬에서는 이를 연산자 오버로딩(Operator Overloading)을 통해 구현할 수 있습니다.
  • 쉽게 말해, 원래는 or의 뜻을 가진 연산자 |를 오버로딩해 역할을 바꾸는 것입니다.
  • 연산자 오버로딩에 대한 설명 : 포스팅 - [Python] 연산자 오버로딩 Operator Overloading
  • LCEL의 파이프 체이닝을 흉내내본다면 아래와 같이 표현할 수 있을 것입니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
from abc import ABC, abstractmethod

class LCELObject(ABC):
    def __or__(self, next_object):
        return Chain(self, next_object)
    @abstractmethod
    def process(self, something):
        pass

class Chain:
    def __init__(self, first, second):
        self.first = first
        self.second = second
    def invoke(self, data):
        first_result = self.first.process(data)
        final_result = self.second.process(first_result)
        return final_result

class Prompt(LCELObject):
    def __init__(self, prompt):
        self.prompt = prompt
    def process(self, data:str):
        if data is not None:
            self.prompt = data
        return self.prompt

class LLM(LCELObject):
    def __init__(self, config):
        self.config = config
        self.name = config.get("name")
    def process(self, data):
        return self.name + f" AI 응답 : {data}에 대한 AI 응답입니다."
  • LCELObject : Prompt, LLM, Parser 와 같은 LCEL 표현법의 오브젝트들
  • Chain : LCEL 오브젝트들의 선후관계를 묶은 체인
  • LCELObject 들은 모두 process라는 작업 실행 메서드를 가지고 있음
  • Chain 은 전체 파이프라인을 실행시키는 invoke라는 메서드를 가지고 있음
1
2
3
4
5
6
prompt = Prompt("당신은 사용자를 돕는 상담사입니다.")
llm = LLM({"name":"샘플 LLM"})

chain = prompt | llm
result = chain.invoke("안녕하세요?")
print(result)
  • prompt와 llm 설정을 지정한 뒤 실행
1
샘플 LLM AI 응답 : 안녕하세요?에 대한 AI 응답입니다.

Reference

https://en.wikipedia.org/wiki/Pipeline_(Unix)

Comments