Unify flow streaming frame items

This commit is contained in:
lorenzejay
2026-06-29 14:50:17 -07:00
parent a48f45c917
commit 90b06a4523
15 changed files with 739 additions and 220 deletions

View File

@@ -52,7 +52,7 @@ class ResearchFlow(Flow):
## البث المتزامن
عند استدعاء `kickoff()` على تدفق مع تفعيل البث، يُرجع كائن `FlowStreamingOutput` يمكنك التكرار عليه:
عند استدعاء `kickoff()` على تدفق مع تفعيل البث، يُرجع جلسة stream تنتج عناصر `StreamFrame` مرتبة:
```python Code
flow = ResearchFlow()
@@ -60,44 +60,43 @@ flow = ResearchFlow()
# Start streaming execution
streaming = flow.kickoff()
# Iterate over chunks as they arrive
for chunk in streaming:
print(chunk.content, end="", flush=True)
# Iterate over stream items as they arrive
for item in streaming:
print(item.content, end="", flush=True)
# Access the final result after streaming completes
result = streaming.result
print(f"\n\nFinal output: {result}")
```
### معلومات جزء البث
### معلومات عنصر البث
يوفر كل جزء سياقاً حول مصدره في التدفق:
يوفر كل عنصر محتوى قابلاً للطباعة وبيانات حدث مهيكلة:
```python Code
streaming = flow.kickoff()
for chunk in streaming:
print(f"Agent: {chunk.agent_role}")
print(f"Task: {chunk.task_name}")
print(f"Content: {chunk.content}")
print(f"Type: {chunk.chunk_type}") # TEXT or TOOL_CALL
for item in streaming:
print(f"Channel: {item.channel}")
print(f"Type: {item.type}")
print(f"Content: {item.content}")
print(f"Event payload: {item.event}")
```
### الوصول إلى خصائص البث
يوفر كائن `FlowStreamingOutput` خصائص وطرق مفيدة:
توفر جلسة stream خصائص وطرق مفيدة:
```python Code
streaming = flow.kickoff()
# Iterate and collect chunks
for chunk in streaming:
print(chunk.content, end="", flush=True)
# Iterate and collect items
for item in streaming:
print(item.content, end="", flush=True)
# After iteration completes
print(f"\nCompleted: {streaming.is_completed}")
print(f"Full text: {streaming.get_full_text()}")
print(f"Total chunks: {len(streaming.chunks)}")
print(f"Total frames: {len(streaming.frames)}")
print(f"Final result: {streaming.result}")
```
@@ -114,9 +113,9 @@ async def stream_flow():
# Start async streaming
streaming = await flow.kickoff_async()
# Async iteration over chunks
async for chunk in streaming:
print(chunk.content, end="", flush=True)
# Async iteration over stream items
async for item in streaming:
print(item.content, end="", flush=True)
# Access final result
result = streaming.result
@@ -422,7 +421,7 @@ except Exception as e:
## الإلغاء وتنظيف الموارد
يدعم `FlowStreamingOutput` الإلغاء السلس بحيث يتوقف العمل الجاري فوراً عند انقطاع اتصال المستهلك.
تدعم جلسة stream الإلغاء السلس بحيث يتوقف العمل الجاري فوراً عند انقطاع اتصال المستهلك.
### مدير السياق غير المتزامن
@@ -430,8 +429,8 @@ except Exception as e:
streaming = await flow.kickoff_async()
async with streaming:
async for chunk in streaming:
print(chunk.content, end="", flush=True)
async for item in streaming:
print(item.content, end="", flush=True)
```
### الإلغاء الصريح
@@ -439,8 +438,8 @@ async with streaming:
```python Code
streaming = await flow.kickoff_async()
try:
async for chunk in streaming:
print(chunk.content, end="", flush=True)
async for item in streaming:
print(item.content, end="", flush=True)
finally:
await streaming.aclose() # غير متزامن
# streaming.close() # المكافئ المتزامن
@@ -451,7 +450,7 @@ finally:
## ملاحظات مهمة
- يفعّل البث تلقائياً بث LLM لأي أطقم مستخدمة داخل التدفق
- يجب التكرار عبر جميع الأجزاء قبل الوصول إلى خاصية `.result`
- يجب التكرار عبر جميع عناصر stream قبل الوصول إلى خاصية `.result`
- يعمل البث مع كل من حالة التدفق المنظمة وغير المنظمة
- يلتقط بث التدفق المخرجات من جميع الأطقم واستدعاءات LLM في التدفق
- يتضمن كل جزء سياقاً حول الوكيل والمهمة التي ولدته

View File

@@ -0,0 +1,195 @@
---
title: عقد بث وقت التشغيل
description: بث إطارات وقت تشغيل مرتبة من التدفقات واستدعاءات LLM المباشرة ودورات المحادثة.
icon: tower-broadcast
mode: "wide"
---
## نظرة عامة
يوفر CrewAI عقد بث قائمًا على الإطارات للأنظمة التي تحتاج إلى أكثر من أجزاء نصية بسيطة. يصدر العقد كائنات `StreamFrame` مرتبة لأحداث دورة حياة Flow، وتوكنات LLM المباشرة، ونشاط الأدوات، ورسائل المحادثة، والأحداث المخصصة.
استخدم هذه الواجهة عندما تبني واجهة مستخدم، أو جسر خدمة، أو تطبيق طرفية، أو وقت تشغيل نشر يحتاج إلى تدفق ثابت من الأحداث المهيكلة أثناء تشغيل Flow أو دورة محادثة أو استدعاء LLM مباشر.
## StreamFrame
لكل إطار نفس الغلاف:
```python
from crewai.types.streaming import StreamFrame
frame.version # "v1"
frame.id # معرف إطار فريد
frame.seq # ترتيب محلي للتنفيذ، عند توفره
frame.type # نوع الحدث المصدر، مثل "flow_started"
frame.channel # "llm", "flow", "tools", "messages", "lifecycle", or "custom"
frame.namespace # نطاق المصدر/وقت التشغيل
frame.timestamp # طابع وقت الحدث
frame.parent_id # معرف الحدث الأب، عند توفره
frame.previous_id # معرف الحدث السابق، عند توفره
frame.data # حمولة الحدث
frame.event # اسم بديل لـ frame.data
frame.content # نص قابل للطباعة لإطارات التوكن، وإلا ""
```
حقل `channel` هو أسرع طريقة لتوجيه الإطارات في المستهلكين:
| القناة | تحتوي على |
|--------|-----------|
| `llm` | توكنات وأجزاء التفكير من أحداث بث LLM |
| `flow` | دورة حياة Flow، وتنفيذ الدوال، والتوجيه، وأحداث الإيقاف/الاستئناف |
| `tools` | أحداث استخدام الأدوات |
| `messages` | أحداث سجل المحادثة |
| `lifecycle` | أحداث دورة حياة وقت التشغيل التي لا تخص قناة أخرى |
| `custom` | أحداث لا تُطابق قناة مدمجة |
يحافظ `frame.type` على نوع الحدث المصدر، حتى يتمكن المستهلكون من التعامل مع أحداث محددة داخل القناة.
## بث Flow
عيّن `stream=True` على Flow لجعل `kickoff()` يعيد جلسة stream:
```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 قبل قراءة `stream.result`. يؤدي الوصول إلى النتيجة مبكرًا إلى رفع `RuntimeError` حتى لا يتعامل المستهلكون بالخطأ مع تشغيل جزئي على أنه مكتمل.
يمكنك أيضًا استدعاء `flow.stream_events(...)` مباشرة عندما تريد البث لاستدعاء واحد بدون تعيين `stream=True` على مثيل Flow.
## التصفية حسب القناة
يوفر `StreamSession` إسقاطات حسب القناة تحافظ على ترتيب الإطارات العالمي داخل القناة المحددة:
```python
stream = flow.stream_events()
with stream:
for frame in stream.llm:
print(frame.content, end="", flush=True)
result = stream.result
```
الإسقاطات المتاحة هي:
| الإسقاط | الإطارات |
|---------|----------|
| `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
```
تملك الجلسة غير المتزامنة نفس إسقاطات الجلسة المتزامنة.
## بث استدعاء LLM مباشر
ما زال `llm.call(...)` يعيد النتيجة النهائية المجمعة. استخدم `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(...)` البث مؤقتًا للاستدعاء المغلف ثم يستعيد إعداد `stream` السابق في LLM بعد ذلك. تستمر تكاملات المزودين في إصدار أحداث بث LLM الأساسية؛ يوفر هذا المساعد واجهة مكرر مشتركة فوق تلك الأحداث لكل مزودي LLM.
## دورات المحادثة
يمكن للتدفقات المحادثية بث دورة مستخدم واحدة باستخدام `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 لذلك الدور ثم يستعيد إعداد `stream` السابق في LLM بعد ذلك. يجب على معالجات المسارات المخصصة التي تنشئ Agents أو مثيلات LLM خاصة بها تهيئة تلك النماذج للبث إذا احتاجت إلى إخراج على مستوى التوكن.
## التنظيف
استخدم الجلسة كمدير سياق عندما يكون ذلك ممكنًا. إذا انقطع اتصال العميل قبل استهلاك stream بالكامل، فأغلق الجلسة صراحة:
```python
stream = flow.stream_events()
try:
for frame in stream.events:
print(frame.type)
finally:
if not stream.is_exhausted:
stream.close()
```
للتدفقات غير المتزامنة، استخدم `await stream.aclose()`.
## بث الأجزاء القديم
ما زال بث Crew مع `stream=True` يعيد واجهة `CrewStreamingOutput` المعتمدة على الأجزاء والموضحة في [بث تنفيذ Crew](/ar/learn/streaming-crew-execution). وما زالت استدعاءات `llm.call(...)` المباشرة تعيد نتيجة LLM النهائية. عقد الإطارات مخصص لأوقات التشغيل التي تحتاج إلى غلاف حدث ثابت عبر Flows، واستدعاءات LLM المباشرة، ودورات المحادثة، والأدوات، والرسائل.