langchain / / 2024. 8. 26. 08:40

[langchain] Streaming 파라미터가 미치는 영향

ChatOpenAI를 사용할 때 streaming 파라미터가 있다. True로 사용할 때 응답값이 streaming으로 리턴한다고 되어 있다.

이 값을 False로 사용하고 stream() 함수를 호출하면 streaming으로 결과가 넘어올까 궁금해서 테스트를 해보았다.

llm = ChatOpenAI(
    temperature=0, 
    model_name="gpt-4o",  
    streaming=False,
)

template = "{country}의 수도는?"

prompt = PromptTemplate.from_template(template=template)
chain = prompt | llm | StrOutputParser()

def stream():
    for chunk in chain.stream({"country": "서울"}):
        print(chunk)


stream()

결론부터 말하면 streaming=False로 사용을 하더라도 응답값은 streaming으로 넘어오고 있었다.

[응답 결과]

서울
은
 대한민국
의
 수도
입니다
.
 대한민국
의
 정치
,
 경제
,
 문화
 중심
지
로
서
 중요한
 역할
을
 하고
 있습니다
.

streaming은 openai에 요청할 때 streaming을 사용할지 여부를 파라미터로 넘기는 것 같은데 왜 streaming으로 넘어오는지 궁금해서 내부 소스를 확인해 보았다.

langchain_openai에 chat_models/base.py 소스 내부를 확인해보면 아래 소스가 있다.

def _stream(
        self,
        messages: List[BaseMessage],
        stop: Optional[List[str]] = None,
        run_manager: Optional[CallbackManagerForLLMRun] = None,
        **kwargs: Any,
    ) -> Iterator[ChatGenerationChunk]:
        message_dicts, params = self._create_message_dicts(messages, stop)
        params = {**params, **kwargs, "stream": True} # stream이 강제적으로 True로 설정되어 있음

        default_chunk_class = AIMessageChunk
        with self.client.create(messages=message_dicts, **params) as response:
            for chunk in response:
                if not isinstance(chunk, dict):
                    chunk = chunk.model_dump()
                ...
                yield chunk

이 코드는 chain.stream를 호출할 때 실행되는 코드인데 stream이나 astream을 호출하면 파라미터에 stream: True로 강제적으로 설정되어 넘어가는 코드를 확인할 수 있다. 즉, stream이나 astream을 호출하는 경우에는 ChatOpenAI의 streaming과 무관하게 무조건 stream: True로 openai에 요청을 한다는 뜻이다.

그러면 invoke를 호출할 때 streaming값의 영향도는 어떨까?

llm = ChatOpenAI(
    ...
    streaming=True,
)

response = chain.invoke({"country": "서울"})
print(response)

위와 같이 streamingTrue로 하고 invoke를 호출해 보았다. langchain 내부코드 (chat_models/base.py)에서 _stream을 호출한다. 즉, stream()을 호출한 것과 동일하게 호출이 되는 것을 확인할 수 있다.

def _generate(
        self,
        messages: List[BaseMessage],
        stop: Optional[List[str]] = None,
        run_manager: Optional[CallbackManagerForLLMRun] = None,
        **kwargs: Any,
    ) -> ChatResult:
        if self.streaming: # streaming이 True이면 _stream을 호출
            stream_iter = self._stream(
                messages, stop=stop, run_manager=run_manager, **kwargs
            )
            return generate_from_stream(stream_iter)
        message_dicts, params = self._create_message_dicts(messages, stop)
        params = {**params, **kwargs}
        response = self.client.create(messages=message_dicts, **params)
        return self._create_chat_result(response)

즉, 다음과 같다고 볼 수 있다. streaming=True + chain.invoke() === chain.stream()

테스트해 본 결과 차이점은 응답결과를 출력하는 형식에 있다.

streaming=True + chain.invoke()

한글자씩 chunk로 출력

서
울
은

대
한
민
국
의
...

chain.stream()

청크를 단어 형식으로 반환하는 경우가 더 많다.

서울
은
 대한민국
의
...

요약

결론적으로 invoke와 stream 코드를 호출할 때를 정리하면 아래와 같다.

llm = ChatOpenAI(
    temperature=0, 
    model_name="gpt-4o",  
    streaming=False, # streaming
)

chain = prompt | llm | StrOutputParser()
chain.invoke() # 호출 메소드
  • 호출 메소드
    • chain.invoke() 혹은 chain.stream()을 호출하는 방식을 비교
  • streaming
    • ChatOpenAI의 매개변수에 streaming을 True/False로 넘김
  • chat_models/base.py
    • langchain 내부에서 호출하는 함수를 비교
  • openai stream 값
    • openai 호출 시 stream: True/False의 값을 비교

[실행 결과]

호출 메소드 (chain) streaming (ChatOpenAI) chat_models/base.py (langchain 내부 호출) openai stream 값 (openai 호출 시)
invoke false로 호출하면 def _generate() 호출 false
invoke true로 호출하면 def _stream() 호출 true
stream false로 호출하면 def _stream() 호출 true
stream true로 호출하면 def _stream() 호출 true

[참고] openai 호출 시 stream 값 비교

1. stream=False로 호출

client = OpenAI(api_key=environ.get("OPENAI_API_KEY"))

completion = client.chat.completions.create(
    model="gpt-4o-mini",
    messages=[{"role": "user", "content": "한국의 수도는?"}],
    stream=False,
)

print(completion.choices[0].message.content)

실행결과

한국의 수도는 서울입니다.

2. stream=True로 호출

client = OpenAI(api_key=environ.get("OPENAI_API_KEY"))


stream = client.chat.completions.create(
    model="gpt-4o-mini",
    messages=[{"role": "user", "content": "한국의 수도는?"}],
    stream=True,
)

for chunk in stream:
    if chunk.choices[0].delta.content is not None:
        print(
            chunk.choices[0].delta.content,
        )

실행결과

한국
의
 수도
는
 서울
입니다
.
반응형
  • 네이버 블로그 공유
  • 네이버 밴드 공유
  • 페이스북 공유
  • 카카오스토리 공유