콘텐츠로 이동

스트리밍 (Streaming)

개념

스트리밍은 작업이 아직 진행 중인 동안에도 애플리케이션이 실행 업데이트를 받아볼 수 있게 해주는 방식이에요. 최종 결과를 기다리는 대신, LLM 토큰·도구 활동·Flow 생명주기 이벤트·대화 메시지가 발생하는 그대로 렌더링할 수 있어요.

CrewAI는 스트리밍 표면(surface)이 두 가지예요.

표면 사용처 출력
프레임 스트리밍 (Frame streaming) Flow, 직접 LLM 호출, 대화 턴 순서가 보장된 StreamFrame 객체
크루 청크 스트리밍 (Crew chunk streaming) stream=True를 켠 Crew CrewStreamingOutput 청크

새 런타임 통합, UI, 터미널 앱, 서비스 브리지, 대화형 인터페이스에는 프레임 스트리밍을 쓰세요. 런타임 전반에 걸쳐 이벤트 구조가 하나로 안정적으로 유지되기 때문이에요.

StreamFrame

StreamFrame은 스트리밍이 가능한 런타임이 내보내는 공통 객체예요.

frame.id           # 고유한 프레임 id
frame.seq          # 실행 로컬 순서 (가능할 때)
frame.type         # 소스 이벤트 타입. 예: "llm_stream_chunk"
frame.channel      # "llm", "flow", "tools", "messages", "lifecycle", "custom"
frame.namespace    # 소스/런타임 네임스페이스
frame.timestamp    # 이벤트 타임스탬프
frame.parent_id    # 부모 이벤트 id (가능할 때)
frame.previous_id  # 이전 이벤트 id (가능할 때)
frame.data         # 구조화된 이벤트 페이로드
frame.event        # frame.data의 별명
frame.content      # 토큰류 프레임의 출력 가능 텍스트, 아니면 ""

대부분의 소비자가 실제로 쓰는 필드는 이 정도예요.

필드 어디에 쓰나요
channel 프레임을 올바른 UI 영역으로 라우팅
type 채널 안에서 특정 이벤트 처리
content 토큰류 텍스트 출력
event 도구 이름·메시지 역할 같은 구조화 메타데이터 읽기
seq 실행 순서 보존

채널 (Channels)

프레임은 상위 수준의 채널로 묶여요.

채널 담는 내용
llm LLM 호출 생명주기, 텍스트 청크, thinking 청크
flow Flow 생명주기, 메서드 실행, 라우팅, pause·resume 이벤트
tools 도구 사용 시작·종료·오류 이벤트
messages 대화 기록(transcript) 이벤트
lifecycle 다른 채널에 속하지 않는 런타임 생명주기 이벤트
custom 내장 채널에 매핑되지 않는 이벤트

스트림 자체는 하나의 순서 있는 시간축으로 유지돼요. 채널 투영(projection)은 그 시간축의 일부만 보고 싶은 소비자를 위한 것이라고 보면 돼요.

스트림 세션 (Stream Sessions)

프레임 스트리밍은 스트림 세션을 반환해요.

stream = flow.stream_events(inputs={"topic": "AI agents"})

세션은 이터레이터이자 최종 결과를 담는 보관소 역할을 동시에 해요.

with stream:
    for frame in stream:
        print(frame.content, end="", flush=True)

result = stream.result

stream.result를 읽기 전에 반드시 스트림을 소비해야 해요. 너무 일찍 결과를 읽으면 런타임이 아직 프레임을 만들고 있을 수 있기 때문에 오류가 나요.

코드 스니펫

특정 채널만 필요할 때 (채널 투영)

with flow.stream_events(inputs={"topic": "AI agents"}) as stream:
    for frame in stream.llm:
        print(frame.content, end="", flush=True)

result = stream.result

사용 가능한 투영은 이렇게 정리돼요.

투영 프레임
stream.events 모든 프레임
stream.llm LLM 프레임
stream.flow Flow 프레임
stream.tools 도구 프레임
stream.messages 대화 메시지 프레임
stream.interleave([...]) 선택한 채널을 상대 순서로

스트리밍할 런타임에 맞는 진입점 (Entrypoints)

런타임 스트리밍 진입점
Flow flow.stream_events(...)
stream=True를 켠 Flow flow.kickoff(...) 이 스트림 세션 반환
비동기 Flow flow.astream(...) 또는 stream=True일 때 await flow.kickoff_async(...)
직접 LLM 호출 llm.stream_events(...)
대화형 Flow 턴 flow.stream_turn(...)
Crew Crew(..., stream=True).kickoff(...)CrewStreamingOutput 반환

실무 관점

직접 llm.call(...)을 쓰면 여전히 최종 조합된 LLM 결과를 반환해요. LLM 청크가 도착하는 대로 순회하고 싶다면 llm.stream_events(...)를 써야 해요.

두 가지를 헷갈리기 쉬워요. stream.result는 스트림 소비가 끝난 뒤에만 읽을 수 있고, 채널 투영은 화면에서 그려주는 영역을 나눌 때만 쓰면 돼요. 스트림 자체는 항상 하나의 순서 있는 시간축이니까요.

그리고 런타임 통합이나 대화형 인터페이스를 새로 만든다면 크루 청크 스트리밍보다 프레임 스트리밍을 기본으로 잡는 게 좋아요. 이벤트 구조가 하나로 일관되게 유지되거든요.

더 알아보기