랭체인
강의 Ch.4 11~13 파일 05-Runnable
유방울
2025. 1. 18. 19:59
Runnable
3가지가 있음
데이터 효과적으로 전달하는 방법
key value 값으로 넣어주느 거 보다는
하나만 넣어주고 싶을 때가 있음
아래는 기존 코드
invoke시 딕셔너리를 사용함
from langchain_core.prompts import PromptTemplate
from langchain_openai import ChatOpenAI
# prompt 와 llm 을 생성합니다.
prompt = PromptTemplate.from_template("{num} 의 10배는?")
llm = ChatOpenAI(temperature=0)
# chain 을 생성합니다.
chain = prompt | llm
# chain 을 실행합니다.
chain.invoke({"num": 5})
AIMessage(content='50입니다.', additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 4, 'prompt_tokens': 16, 'total_tokens': 20, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 'model_name': 'gpt-3.5-turbo-0125', 'system_fingerprint': None, 'finish_reason': 'stop', 'logprobs': None}, id='run-6b9cef1e-ad02-41d6-9c2b-05c1e0948412-0', usage_metadata={'input_tokens': 16, 'output_tokens': 4, 'total_tokens': 20, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}})
Runnablepassthrough (RAG할 때 자주 쓰임)
: 데이터를 받아서 전달한다는 의미
: 받은 것을 전달한 것을 알 수 있음
데이터 입력은 딕셔너리로 들어가야 하니까 파이프를 앞에 추가함
-> 사용자가 값만 넣을 수 있음
from langchain_core.runnables import RunnablePassthrough
# runnable
RunnablePassthrough().invoke({"num": 10})
{'num': 10}
# prompt는 무조건 dictionary type이어야 함
runnable_chain = {"num": RunnablePassthrough()} | prompt | ChatOpenAI()
# dict 값이 RunnablePassthrough() 로 변경되었습니다.
runnable_chain.invoke(10)
AIMessage(content='10의 10배는 100입니다.', additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 12, 'prompt_tokens': 16, 'total_tokens': 28, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 'model_name': 'gpt-3.5-turbo-0125', 'system_fingerprint': None, 'finish_reason': 'stop', 'logprobs': None}, id='run-00126aa7-18d9-4ea5-9ebf-c8b3cf696b9b-0', usage_metadata={'input_tokens': 16, 'output_tokens': 12, 'total_tokens': 28, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}})
그니까 딕셔너리를 파이프라인에 넣었으니까 잘 작동함
why 많이 쓰는지?
나중에 key값을 기억을 못함 근데 넣어야하는 건 기억하니까!
그냥 RunnablePassthrough()를 자주 씀
RunnablePassthrough.assign()
key값은 x로 들어감 그래서 1로 바꾸게 됨
이 1 은 new_num으로 들어감
num : 1 원래값이 들어가고 , 할당되어 계산된 값은 new_num에 들어감
# 입력 키: num, 할당(assign) 키: new_num
# num도 출력되고 new_num 도 출력됨
(RunnablePassthrough.assign(new_num=lambda x: x["num"] * 3)).invoke({"num": 1})
{'num': 1, 'new_num': 3}
RunnableParallel
from langchain_core.runnables import RunnableParallel
# RunnableParallel 인스턴스를 생성합니다. 이 인스턴스는 여러 Runnable 인스턴스를 병렬로 실행할 수 있습니다.
runnable = RunnableParallel(
# RunnablePassthrough 인스턴스를 'passed' 키워드 인자로 전달합니다. 이는 입력된 데이터를 그대로 통과시키는 역할을 합니다.
passed=RunnablePassthrough(),
# 'extra' 키워드 인자로 RunnablePassthrough.assign을 사용하여, 'mult' 람다 함수를 할당합니다. 이 함수는 입력된 딕셔너리의 'num' 키에 해당하는 값을 3배로 증가시킵니다.
extra=RunnablePassthrough.assign(mult=lambda x: x["num"] * 3),
# 'modified' 키워드 인자로 람다 함수를 전달합니다. 이 함수는 입력된 딕셔너리의 'num' 키에 해당하는 값에 1을 더합니다.
modified=lambda x: x["num"] + 1,
)
# runnable 인스턴스에 {'num': 1} 딕셔너리를 입력으로 전달하여 invoke 메소드를 호출합니다.
runnable.invoke({"num": 1})
{'passed': {'num': 1}, 'extra': {'num': 1, 'mult': 3}, 'modified': 2}
chain 도 가능
chain1 = (
{"country": RunnablePassthrough()}
| PromptTemplate.from_template("{country} 의 수도는?")
| ChatOpenAI()
)
chain2 = (
{"country": RunnablePassthrough()}
| PromptTemplate.from_template("{country} 의 면적은?")
| ChatOpenAI()
)
combined_chain = RunnableParallel(capital=chain1, area=chain2)
combined_chain.invoke("대한민국")
{'capital': AIMessage(content='서울특별시입니다.', additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 11, 'prompt_tokens': 19, 'total_tokens': 30, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 'model_name': 'gpt-3.5-turbo-0125', 'system_fingerprint': None, 'finish_reason': 'stop', 'logprobs': None}, id='run-eaa58c44-d545-4115-a2fe-f91a3e0e6737-0', usage_metadata={'input_tokens': 19, 'output_tokens': 11, 'total_tokens': 30, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}}),
'area': AIMessage(content='대한민국의 면적은 총 100,363 km² 입니다.', additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 24, 'prompt_tokens': 20, 'total_tokens': 44, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 'model_name': 'gpt-3.5-turbo-0125', 'system_fingerprint': None, 'finish_reason': 'stop', 'logprobs': None}, id='run-3beb161d-747e-45e4-9b7a-1e4ff47be041-0', usage_metadata={'input_tokens': 20, 'output_tokens': 24, 'total_tokens': 44, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}})}
RunnableLambda
사용자 정의 함수 맵핑 가능
# 오늘 날짜를 출력하고 반환함
from langchain_core.output_parsers import StrOutputParser
from langchain_core.prompts import PromptTemplate
from langchain_openai import ChatOpenAI
from datetime import datetime
# 매개변수를 받는데 a가 어디에도 쓰이지 않아서 a를 지워도 됨
# 없어도 되는 매개변수를 지정한 이유는?
# invoke로 호출해서 값이 들어가면
# runnablelambda를 매개변수가 필요하기 때문임
def get_today(a):
# 오늘 날짜를 가져오기
return datetime.today().strftime("%b-%d")
# 오늘 날짜를 출력
get_today(None)
'Jan-22'
from langchain_core.runnables import RunnableLambda, RunnablePassthrough
# prompt 와 llm 을 생성합니다.
# todya, n 둘 다 변수임
# invoke 3 은 runnablepassthrough로 들어감
# 프롬프트에 입력으로 넣어주는 입력에서 get_today가 today로 들어가는 구조임
prompt = PromptTemplate.from_template(
"{today} 가 생일인 유명인 {n} 명을 나열하세요. 생년월일을 표기해 주세요."
)
llm = ChatOpenAI(temperature=0, model_name="gpt-4o")
# chain 을 생성합니다.
chain = (
{"today": RunnableLambda(get_today), "n": RunnablePassthrough()}
| prompt
| llm
| StrOutputParser()
)
# 출력
print(chain.invoke(3))
1. 다이앤 레인 (Diane Lane) - 1965년 1월 22일
2. 가브리엘 마흐트 (Gabriel Macht) - 1972년 1월 22일
3. 베벌리 미첼 (Beverley Mitchell) - 1981년 1월 22일
itemgetter method 포함 버전
from langchain_core.runnables import RunnableLambda, RunnablePassthrough
from operator import itemgetter
# prompt 와 llm 을 생성합니다.
# todya, n 둘 다 변수임
# invoke 3 은 runnablepassthrough로 들어감
# 프롬프트에 입력으로 넣어주는 입력에서 get_today가 today로 들어가는 구조임
prompt = PromptTemplate.from_template(
"{today} 가 생일인 유명인 {n} 명을 나열하세요. 생년월일을 표기해 주세요."
)
llm = ChatOpenAI(temperature=0, model_name="gpt-4o")
# chain 을 생성합니다.
chain = (
{
"today": RunnableLambda(get_today),
"n": itemgetter("n"),
} # n의 딕셔너리가 invoke되지면 itemgetter로 n의 value값만 가지고 와서 3이 가지고옴
| prompt
| llm
| StrOutputParser()
)
# 출력
print(chain.invoke({"n": 3}))
1월 22일에 태어난 유명인 중 세 명을 소개하겠습니다.
1. **다이앤 레인 (Diane Lane)** - 1965년 1월 22일 출생. 미국의 배우로, 여러 영화와 TV 시리즈에서 활약했습니다.
2. **가브리엘 마흐트 (Gabriel Macht)** - 1972년 1월 22일 출생. 미국의 배우로, TV 시리즈 "Suits"에서 하비 스펙터 역으로 잘 알려져 있습니다.
3. **비비안 리 (Vivien Leigh)** - 1913년 1월 22일 출생. 영국의 배우로, 영화 "바람과 함께 사라지다"에서 스칼렛 오하라 역으로 유명합니다.
이들은 각자의 분야에서 큰 영향을 미친 인물들입니다.
Itemgetter()
dictionary에서 key의 value값을 가지고 오는 것임
아래거 해석하면 word1의 value는 hello임!!
"a": itemgetter("word1")
chain.invoke({"word1": "hello", "word2": "world"})
from operator import itemgetter
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.runnables import RunnableLambda
from langchain_openai import ChatOpenAI
# 문장의 길이를 반환하는 함수입니다.
def length_function(text):
return len(text)
# 두 문장의 길이를 곱한 값을 반환하는 함수입니다.
def _multiple_length_function(text1, text2):
return len(text1) * len(text2)
# _multiple_length_function 함수를 사용하여 두 문장의 길이를 곱한 값을 반환하는 함수입니다.
def multiple_length_function(_dict):
return _multiple_length_function(_dict["text1"], _dict["text2"])
prompt = ChatPromptTemplate.from_template("{a} + {b} 는 무엇인가요?")
model = ChatOpenAI()
chain1 = prompt | model
chain = (
{
"a": itemgetter("word1") | RunnableLambda(length_function),
"b": {"text1": itemgetter("word1"), "text2": itemgetter("word2")}
| RunnableLambda(multiple_length_function),
}
| prompt
| model
)
chain.invoke({"word1": "hello", "word2": "world"})
AIMessage(content='5 + 25는 30입니다.', additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 10, 'prompt_tokens': 22, 'total_tokens': 32, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 'model_name': 'gpt-3.5-turbo-0125', 'system_fingerprint': None, 'finish_reason': 'stop', 'logprobs': None}, id='run-2ddf015c-df0a-4aa8-aa21-9b5c21548149-0', usage_metadata={'input_tokens': 22, 'output_tokens': 10, 'total_tokens': 32, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}})