mirror of
https://github.com/crewAIInc/crewAI.git
synced 2026-07-02 05:38:12 +00:00
196 lines
7.0 KiB
Plaintext
196 lines
7.0 KiB
Plaintext
---
|
|
title: 스트리밍 런타임 계약
|
|
description: Flow, 직접 LLM 호출, 대화 턴에서 정렬된 런타임 프레임을 스트리밍합니다.
|
|
icon: tower-broadcast
|
|
mode: "wide"
|
|
---
|
|
|
|
## 개요
|
|
|
|
CrewAI는 단순한 텍스트 청크보다 더 많은 정보가 필요한 런타임을 위해 프레임 기반 스트리밍 계약을 제공합니다. 이 계약은 Flow 생명주기 이벤트, 직접 LLM 토큰, 도구 활동, 대화 메시지, 사용자 지정 이벤트에 대해 정렬된 `StreamFrame` 객체를 방출합니다.
|
|
|
|
UI, 서비스 브리지, 터미널 앱, 배포 런타임을 만들 때 Flow, 채팅 턴, 직접 LLM 호출이 실행되는 동안 안정적인 구조화 이벤트 스트림이 필요하다면 이 API를 사용하세요.
|
|
|
|
## StreamFrame
|
|
|
|
모든 프레임은 같은 envelope를 가집니다:
|
|
|
|
```python
|
|
from crewai.types.streaming import StreamFrame
|
|
|
|
frame.version # "v1"
|
|
frame.id # 고유 프레임 id
|
|
frame.seq # 사용 가능한 경우 실행 로컬 순서
|
|
frame.type # "flow_started" 같은 원본 이벤트 타입
|
|
frame.channel # "llm", "flow", "tools", "messages", "lifecycle", "custom"
|
|
frame.namespace # 소스/런타임 namespace
|
|
frame.timestamp # 이벤트 timestamp
|
|
frame.parent_id # 사용 가능한 경우 부모 이벤트 id
|
|
frame.previous_id # 사용 가능한 경우 이전 이벤트 id
|
|
frame.data # 이벤트 payload
|
|
frame.event # frame.data의 alias
|
|
frame.content # 토큰류 프레임의 출력 가능한 텍스트, 그 외에는 ""
|
|
```
|
|
|
|
`channel` 필드는 소비자에서 프레임을 라우팅하는 가장 빠른 방법입니다:
|
|
|
|
| 채널 | 포함 내용 |
|
|
|------|-----------|
|
|
| `llm` | LLM 스트리밍 이벤트의 토큰 및 thinking 청크 |
|
|
| `flow` | Flow 생명주기, 메서드 실행, 라우팅, pause/resume 이벤트 |
|
|
| `tools` | 도구 사용 이벤트 |
|
|
| `messages` | 대화 transcript 이벤트 |
|
|
| `lifecycle` | 다른 채널에 속하지 않는 런타임 생명주기 이벤트 |
|
|
| `custom` | 내장 채널에 매핑되지 않는 이벤트 |
|
|
|
|
`frame.type`은 원본 이벤트 타입을 보존하므로, 소비자는 채널 안에서 특정 이벤트를 처리할 수 있습니다.
|
|
|
|
## Flow 스트리밍
|
|
|
|
Flow에 `stream=True`를 설정하면 `kickoff()`가 stream session을 반환합니다:
|
|
|
|
```python
|
|
from crewai.flow import Flow, start
|
|
|
|
|
|
class ReportFlow(Flow):
|
|
@start()
|
|
def generate(self):
|
|
return "done"
|
|
|
|
|
|
flow = ReportFlow(stream=True)
|
|
stream = flow.kickoff()
|
|
|
|
with stream:
|
|
for chunk in stream:
|
|
print(chunk.content, end="", flush=True)
|
|
if chunk.type == "tool_usage_started":
|
|
print(chunk.event["tool_name"])
|
|
|
|
result = stream.result
|
|
```
|
|
|
|
`stream.result`를 읽기 전에 stream을 소비해야 합니다. 결과를 너무 일찍 접근하면 `RuntimeError`가 발생하여, 소비자가 부분 실행을 완료된 실행으로 잘못 처리하지 않도록 합니다.
|
|
|
|
Flow 인스턴스에 `stream=True`를 설정하지 않고 단일 호출만 스트리밍하려면 `flow.stream_events(...)`를 직접 호출할 수도 있습니다.
|
|
|
|
## 채널별 필터링
|
|
|
|
`StreamSession`은 선택한 채널 안에서 전역 프레임 순서를 보존하는 채널 projection을 제공합니다:
|
|
|
|
```python
|
|
stream = flow.stream_events()
|
|
|
|
with stream:
|
|
for frame in stream.llm:
|
|
print(frame.content, end="", flush=True)
|
|
|
|
result = stream.result
|
|
```
|
|
|
|
사용 가능한 projection은 다음과 같습니다:
|
|
|
|
| Projection | 프레임 |
|
|
|------------|--------|
|
|
| `stream.events` | 모든 프레임 |
|
|
| `stream.llm` | LLM 프레임 |
|
|
| `stream.messages` | 대화 메시지 프레임 |
|
|
| `stream.flow` | Flow 프레임 |
|
|
| `stream.tools` | 도구 프레임 |
|
|
| `stream.interleave([...])` | 선택한 채널 집합 |
|
|
|
|
소비자가 일부 채널만 원하지만 상대 순서도 필요하다면 `stream.interleave(["flow", "llm", "messages"])`를 사용하세요.
|
|
|
|
## 비동기 스트리밍
|
|
|
|
비동기 소비자는 `astream()`을 사용하세요:
|
|
|
|
```python
|
|
flow = ReportFlow()
|
|
stream = flow.astream()
|
|
|
|
async with stream:
|
|
async for chunk in stream.events:
|
|
print(chunk.channel, chunk.type, chunk.content)
|
|
|
|
result = stream.result
|
|
```
|
|
|
|
비동기 세션은 동기 세션과 같은 projection을 제공합니다.
|
|
|
|
## 직접 LLM 호출 스트리밍
|
|
|
|
`llm.call(...)`은 계속 최종 조립 결과를 반환합니다. 구조화된 이벤트 payload를 유지하면서 청크가 도착하는 대로 반복 처리하려면 `llm.stream_events(...)`를 사용하세요:
|
|
|
|
```python
|
|
from crewai import LLM
|
|
|
|
|
|
llm = LLM(model="gpt-4o-mini")
|
|
stream = llm.stream_events(
|
|
messages=[
|
|
{
|
|
"role": "user",
|
|
"content": "Explain CrewAI streaming in two short sentences.",
|
|
}
|
|
]
|
|
)
|
|
|
|
with stream:
|
|
for chunk in stream:
|
|
print(chunk.content, end="", flush=True)
|
|
|
|
result = stream.result
|
|
```
|
|
|
|
`llm.stream_events(...)`는 감싼 호출 동안 일시적으로 streaming을 활성화하고, 이후 LLM의 이전 `stream` 설정을 복원합니다. provider 통합은 계속 기본 LLM stream 이벤트를 방출하며, 이 helper는 모든 LLM provider에서 그 이벤트 위에 공통 iterator API를 제공합니다.
|
|
|
|
## 대화 턴
|
|
|
|
대화형 Flow는 `stream_turn()`으로 사용자 턴 하나를 스트리밍할 수 있습니다:
|
|
|
|
```python
|
|
from crewai import Flow
|
|
from crewai.experimental.conversational import ConversationConfig, ConversationState
|
|
|
|
|
|
@ConversationConfig(llm="gpt-4o-mini", defer_trace_finalization=True)
|
|
class ChatFlow(Flow[ConversationState]):
|
|
conversational = True
|
|
|
|
|
|
flow = ChatFlow()
|
|
stream = flow.stream_turn("What can you help me with?", session_id="session-1")
|
|
|
|
with stream:
|
|
for frame in stream.events:
|
|
if frame.channel == "llm" and frame.type == "llm_stream_chunk":
|
|
print(frame.content, end="", flush=True)
|
|
|
|
reply = stream.result
|
|
```
|
|
|
|
`stream_turn()` 중에는 내장 대화 응답 경로가 해당 턴에 대해 LLM 토큰 스트리밍을 활성화하고 이후 LLM의 이전 `stream` 설정을 복원합니다. 자체 agent 또는 LLM 인스턴스를 만드는 사용자 지정 route handler는 토큰 단위 출력이 필요하다면 해당 LLM을 streaming으로 구성해야 합니다.
|
|
|
|
## 정리
|
|
|
|
가능하면 세션을 context manager로 사용하세요. stream이 끝나기 전에 클라이언트 연결이 끊기면 세션을 명시적으로 닫으세요:
|
|
|
|
```python
|
|
stream = flow.stream_events()
|
|
|
|
try:
|
|
for frame in stream.events:
|
|
print(frame.type)
|
|
finally:
|
|
if not stream.is_exhausted:
|
|
stream.close()
|
|
```
|
|
|
|
비동기 stream에서는 `await stream.aclose()`를 사용하세요.
|
|
|
|
## 레거시 청크 스트리밍
|
|
|
|
`stream=True`를 사용하는 Crew 스트리밍은 계속 [스트리밍 Crew 실행](/ko/learn/streaming-crew-execution)에 설명된 청크 중심 `CrewStreamingOutput` API를 반환합니다. 직접 `llm.call(...)` 호출도 계속 최종 LLM 결과를 반환합니다. 프레임 계약은 Flow, 직접 LLM 호출, 대화 턴, 도구, 메시지 전반에서 안정적인 이벤트 envelope가 필요한 런타임을 위한 것입니다.
|