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 |