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)
위와 같이 streaming
을 True
로 하고 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,
)
실행결과
한국
의
수도
는
서울
입니다
.