langchain / / 2024. 8. 25. 11:32

[langchain] invoke, ainvoke, stream, astream의 차이점

LangChain에서 자주 사용되는 네 가지 메서드인 invoke, ainvoke, stream, astream의 차이점과 각각의 사용 사례에 대해 알아보자.


기본 코드

openai를 호출하는 간단한 예제로 테스트를 해보자. 아래는 기본 코드이다.

from dotenv import load_dotenv
from langchain.prompts import PromptTemplate
from langchain_core.output_parsers import StrOutputParser
from langchain_openai import ChatOpenAI

load_dotenv()

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

template = "{country}의 수도는?"
prompt = PromptTemplate.from_template(template=template)
chain = prompt | llm | StrOutputParser()

1. invoke: 동기적 요청 처리

invoke는 LangChain에서 가장 기본적인 메서드로, 동기적으로 언어 모델에 요청을 보내고 결과를 반환한다. 이 메서드는 요청을 보낸 후, 결과를 받을 때까지 코드가 멈추는 동기 방식으로 동작한다.

사용 예시

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

invoke()

응답결과

서울은 대한민국의 수도입니다. 대한민국의 정치, 경제, 문화의 중심지로서 중요한 역할을 하고 있습니다. 추가로 궁금한 사항이 있으시면 말씀해 주세요!

장점

  • 단순성: 코드가 순차적으로 실행되므로 이해하기 쉽고 디버깅이 간편하다.
  • 명확한 흐름: 한 번에 하나의 요청을 처리하므로, 코드의 실행 흐름을 쉽게 추적할 수 있다.

단점

  • 성능 제한: 여러 요청을 동시에 처리할 수 없으므로, 대량의 요청을 처리하는 데 시간이 오래 걸릴 수 있다.
  • 블로킹: 응답이 올 때까지 코드 실행이 멈추므로, 긴 응답 시간이 걸릴 경우 프로그램의 다른 부분이 지연될 수 있다.

2. ainvoke: 비동기적 요청 처리

ainvoke는 비동기 방식으로 언어 모델에 요청을 보내고 결과를 반환한다. 이 메서드는 Python의 asyncio 라이브러리를 활용하여 동작하며, 요청을 비동기적으로 처리할 수 있다. 이는 여러 요청을 동시에 처리하거나, 다른 작업과 병렬로 실행할 수 있다는 장점이 있다.

사용 예시

async def ainvoke():
    response = await chain.ainvoke({"country": "서울"})
    print(response)

asyncio.run(ainvoke())

응답결과

서울은 대한민국의 수도입니다. 대한민국의 정치, 경제, 문화의 중심지로서 중요한 역할을 하고 있습니다. 추가로 궁금한 사항이 있으시면 말씀해 주세요!

장점

  • 비동기 처리: 여러 요청을 동시에 보낼 수 있어, 대규모 요청을 처리하는 데 적합하다.
  • 비블로킹: 요청 처리 중에도 다른 작업을 병렬로 수행할 수 있어, 응답 시간이 긴 경우에도 효율적으로 프로그램을 실행할 수 있다.

단점

  • 복잡성 증가: 비동기 코드 작성 시, await 키워드와 함께 async 함수를 사용해야 하므로 코드가 복잡해질 수 있다.
  • 추적 어려움: 비동기 함수가 여러 개일 경우, 코드 흐름을 추적하는 데 어려움이 있을 수 있다.

3. stream: 동기적 스트리밍

stream은 동기적으로 언어 모델의 응답을 스트리밍 방식으로 처리할 수 있게 된다. 스트리밍은 모델이 전체 응답을 완료하기 전에 부분적으로 응답을 받을 수 있는 방법을 제공한다. 이는 특히 큰 응답을 처리할 때 유용하다.

사용 예시

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

stream()

응답결과

서울
은
 대한민국
의
 수도
입니다
.

.....

하는
 도시
입니다
.

만일 한줄로 표시하고 싶으면 아래처럼 사용하면 된다.

async def astream():
    async for chunk in chain.stream({"country": "서울"}):
        print(chunk, end="", flush=True)

응답결과

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

장점

  • 효율적인 데이터 처리: 대용량 응답을 부분적으로 처리할 수 있어 메모리 사용량을 줄이고, 초기 응답을 빠르게 확인할 수 있다.
  • 순차적 스트리밍: 전체 응답을 기다리지 않고, 실시간으로 데이터를 처리할 수 있다.

단점

  • 순차적 데이터 처리: 응답을 부분적으로 처리하는 동안 코드 실행이 멈추므로, 비동기적으로 다른 작업을 수행하기 어렵다.
  • 응답 관리 복잡성: 부분적으로 데이터를 처리하기 때문에, 전체 응답을 조합하거나 관리하는 데 추가 작업이 필요할 수 있다.

4. astream: 비동기적 스트리밍

astream은 비동기 방식으로 스트리밍 데이터를 처리할 수 있게 한다. 이는 stream과 유사하지만, 비동기적으로 처리할 수 있어 여러 요청을 병렬로 스트리밍하거나, 다른 작업과 함께 실행할 수 있다.

사용 예시

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

응답결과

서울
은
 대한민국
의
 수도
입니다
.

.....

하는
 도시
입니다
.

만일 응답을 json형식으로 리턴하고 싶다면 아래와 같이 StrOutputParser를 json으로 리턴하면 된다.

chain = prompt | llm | {"result": StrOutputParser()}


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

응답결과

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

장점

  • 비동기 스트리밍: 여러 스트리밍 요청을 동시에 처리할 수 있어, 대규모 데이터 처리에 적합하다.
  • 병렬 처리: 스트리밍 데이터 처리 중에도 다른 작업을 병렬로 수행할 수 있어, 응답을 기다리는 동안 다른 작업을 동시에 실행할 수 있다.

단점

  • 복잡한 코드: 비동기 스트리밍 코드는 ainvokestream의 복잡성을 모두 가지고 있어, 코드 작성과 유지보수가 어렵다.
  • 추적 어려움: 여러 비동기 스트림이 동시에 실행될 때, 코드 흐름을 관리하기 어려울 수 있다.

5. 언제 무엇을 사용할까?

invoke를 사용할 때:

  • 코드가 간단하고, 동기적으로 한 번에 하나의 요청만 처리해도 충분한 경우.
  • 디버깅과 코드 이해가 쉬운 구조를 원할 때.

ainvoke를 사용할 때:

  • 여러 요청을 동시에 처리해야 하거나, 비동기적으로 실행할 필요가 있을 때.
  • 다른 작업과 병렬로 요청을 처리하고자 할 때.

stream을 사용할 때:

  • 대용량 응답을 실시간으로 처리해야 하며, 전체 응답을 기다리지 않고 데이터를 받아보고 싶을 때.
  • 동기적으로 순차적인 스트리밍이 필요한 경우.

astream을 사용할 때:

  • 비동기적으로 대용량 응답을 스트리밍하면서, 다른 작업을 동시에 수행해야 할 때.
  • 여러 스트림을 병렬로 처리하고자 할 때.

요약

함수명 동기/비동기 설명
invoke 동기 입력을 받아 결과를 반환, 실행 중 다른 작업 불가능
ainvoke 비동기 비동기적으로 입력을 처리, 다른 작업과 병행 가능
stream 동기 데이터 스트리밍, 중간 결과를 실시간으로 반환
astream 비동기 비동기적으로 데이터 스트리밍, 중간 결과를 실시간으로 반환
반응형
  • 네이버 블로그 공유
  • 네이버 밴드 공유
  • 페이스북 공유
  • 카카오스토리 공유