Programming/Langchain

LCEL 에 관하여

Seung-o 2024. 12. 5. 13:15

1. LCEL (LangChain Expression Language) 란?

 

랭체인 서버를 만들다보면, 다양한 예제에서 아래와 같이 | 연산자를 활용하는 모습을 볼 수 있다. 이는 LCEL이라는 코드 작성 방식이다.

 

return prompt | llm | output_parser

 

LCEL (LangChain Expression Language)은 랭체인에서 코드를 작성하는 새로운 방법이다. LCEL은 프롬프트와 LLM을 | 연산자로 연결하여 작성하여 체인을 구현한다. 

 

2. LCEL의 기본 사용법

 

가장 간단한 형태로, prompt와 model을 연결해보자. 

 

from dotenv import load_dotenv
from langchain_openai import ChatOpenAI
from langchain.prompts import PromptTemplate

load_dotenv()


prompt = PromptTemplate.from_template(
    """요리 레시피를 생각해줘.

요리 이름: {dish}"""
)

model = ChatOpenAI(model_name="gpt-3.5-turbo", temperature=0)

chain = prompt | model

result = chain.invoke({"dish": "카레"})
print(result.content)

 

위 예시처럼, prompt와 model 을 엮어서 하나의 체인으로 구성할 수 있고, 연쇄적 실행을 확인 가능하다. 입력값의 { "dish": "카레 } 는 첫 프롬프트의 입력 값으로 들어가고, 프롬프트는 모델의 호출 값으로서 작동한다. 

 

과거 chain = LLMChain(prompt=prompt, llm=llm) 과 같이 사용했던 것과 비교하면, 더 직관적이라고 느껴진다. 

 

나아가, output parser도 아래와 같이 표현 가능하다. 

output_parser = PydanticOutputParser(pydantic_object=Recipe)
chain = prompt | model | output_parser

result = chain.invoke({"dish": "카레"})
print(result)

 

3. LCEL의 작동 방식

 

LCEL은 LangChain의 각종 모듈이 상속하고 있는 'Runnable 인터페이스' 등에 의해 실현되고 있다. LangChain의 langchain-core라는 소스코드를 살펴보면, Runnable은 추상 기반 클래스로 정의되어 있다. ( 참고로 파이썬의 추상 기반 클래스는 ABC 라고 표현된다 )

 

class Runnable(Generic[Input, Output], ABC):
	...
    def __or__(
        self,
        other: Union[
            Runnable[Any, Other],
            Callable[[Any], Other],
            Callable[[Iterator[Any]], Iterator[Other]],
            Mapping[str, Union[Runnable[Any, Other], Callable[[Any], Other], Any]],
        ],
    ) -> RunnableSerializable[Input, Other]:
        """Compose this Runnable with another object to create a RunnableSequence."""
        return RunnableSequence(self, coerce_to_runnable(other))

    def __ror__(
        self,
        other: Union[
            Runnable[Other, Any],
            Callable[[Other], Any],
            Callable[[Iterator[Other]], Iterator[Any]],
            Mapping[str, Union[Runnable[Other, Any], Callable[[Other], Any], Any]],
        ],
    ) -> RunnableSerializable[Other, Output]:
        """Compose this Runnable with another object to create a RunnableSequence."""
        return RunnableSequence(coerce_to_runnable(other), self)

 

파이썬에서는 __or__와 __ror__에 의해 연산자 |를 오버로딩할 수 있으므로, LCEL이 훌륭히 동작 가능하다. 

 

자바스크립트/타입스크립트에서 LCEL을 사용하는 방식

자바스크립트/타입스크립트에서는 연산자 오버로딩을 구현할 수 없기에, 아래와 같은 인터페이스로 구현하는 것이 최선이다.

const chain = prompt.pipe(model);​

 

4. LCEL 복잡한 예제

 

4-1. 일반 함수와 LCEL

 

LCEL에서는 Chain의 연쇄 기능에 임의의 일반 함수도 넣을 수 있다. 

 

from langchain.schema.runnable import RunnableLambda

def upper(inp: str) -> str:
    return inp.upper()

chain = prompt | model | output_parser | RunnableLambda(upper)

result = chain.invoke({"dish": "카레"})
print(result)

 

 

4-2. RAG와 LCEL

 

RAG를 활용하면, Vector Store의 검색 결과를 프롬프트에 포함시킬 수 있다. RAG를 LCEL로 다음과 같이 표현 가능하다. 

 

from dotenv import load_dotenv
from langchain_openai import ChatOpenAI
from langchain_openai import OpenAIEmbeddings
from langchain.prompts import ChatPromptTemplate
from langchain.schema.output_parser import StrOutputParser
from langchain.schema.runnable import RunnablePassthrough
from langchain.vectorstores.faiss import FAISS

load_dotenv()

texts = [
    "저의 취미는 독서입니다.",
    "제가 좋아하는 음식은 카레입니다.",
    "제가 싫어하는 음식은 만두입니다.",
]
vectorstore = FAISS.from_texts(texts, embedding=OpenAIEmbeddings())

retriever = vectorstore.as_retriever(search_kwargs={"k": 1})

prompt = ChatPromptTemplate.from_template(
    """다음 context에만 기반해 답변해 주세요.

{context}

질문: {question}
"""
)

model = ChatOpenAI(model="gpt-3.5-turbo", temperature=0)

output_parser = StrOutputParser()

chain = (
    {"context": retriever, "question": RunnablePassthrough()}
    | prompt
    | model
    | output_parser
)

result = chain.invoke("제가 좋아하는 음식은 무엇일까요?")
print(result)

 

 

'Programming > Langchain' 카테고리의 다른 글

랭체인 에이전트  (1) 2024.10.31
랭체인 활용 - Data Connection  (3) 2024.10.09
랭체인 기초  (1) 2024.09.19