| title | تدفقات المحادثة |
|---|---|
| description | أنشئ تطبيقات دردشة متعددة الجولات مع kickoff لكل جولة وسجل الرسائل وتوجيه النية والتتبع وجسور WebSocket. |
| icon | comments |
| mode | wide |
تعامل التطبيقات المحادثية مع كل سطر من المستخدم كـ تشغيل flow جديد بنفس معرّف الجلسة. توفر CrewAI مساعدات لسجل الرسائل وتصنيف النية الاختياري وتأجيل التتبع وجسور الواجهة — دون API منفصل chat() على Flow.
| المفهوم | التنفيذ |
|---|---|
| معرّف الجلسة | kickoff(session_id=...) → inputs["id"] → state.id |
| سطر المستخدم | kickoff(user_message=...) يُضاف إلى state.messages قبل تشغيل الرسم |
| اكتمال الجولة | FlowFinished لهذا التشغيل فقط؛ تستمر المحادثة في kickoff التالي |
| تتبع الجلسة | ConversationalConfig(defer_trace_finalization=True) + finalize_session_traces() |
استخدم flow.kickoff(user_message=..., session_id=...) لكل رسالة مستخدم (REST أو WebSocket أو CLI). لا تنشئ غلاف chat() مخصصاً على Flow.
| API | الاستخدام |
|---|---|
kickoff(user_message=..., session_id=...) |
كل رسالة مستخدم |
kickoff_async(...) |
نفس المعاملات؛ دخول async أصلي |
ask() |
مطالبة حاجزة داخل خطوة واحدة |
@human_feedback |
الموافقة/الرفض على مخرجات خطوة — وليس السطر التالي |
ChatSession.handle_turn(...) |
طبقة نقل فوق kickoff |
from uuid import uuid4
from crewai.flow import (
ChatState,
ConversationalConfig,
Flow,
listen,
or_,
persist,
router,
start,
)
from crewai.flow.persistence import SQLiteFlowPersistence
class SupportFlow(Flow[ChatState]):
conversational_config = ConversationalConfig(
default_intents=["order", "help", "goodbye"],
intent_llm="gpt-4o-mini",
defer_trace_finalization=True,
)
@start()
def bootstrap(self):
if not self.state.session_ready:
self.state.session_ready = True
return "ready"
@router(bootstrap)
def route(self):
return self.state.last_intent or "help"
@listen("order")
def handle_order(self):
reply = "طلبك في الطريق."
self.append_message("assistant", reply)
return reply
@listen("help")
def handle_help(self):
reply = "كيف يمكنني المساعدة؟"
self.append_message("assistant", reply)
return reply
@listen("goodbye")
def handle_goodbye(self):
reply = "وداعاً!"
self.append_message("assistant", reply)
return reply
@persist(SQLiteFlowPersistence("support.db"))
@listen(or_(handle_order, handle_help, handle_goodbye))
def finalize(self):
return self.state.model_dump()
session_id = str(uuid4())
flow = SupportFlow()
flow.kickoff(user_message="أين طلبي؟", session_id=session_id)
flow.kickoff(user_message="وماذا عن الإرجاع؟", session_id=session_id)
flow.finalize_session_traces()كل kickoff مع user_message يشغّل:
_configure_conversational_kickoff— دمجsession_id/user_messageفيinputsوتطبيقConversationalConfig.- استعادة الحالة — عند وجود
inputs["id"]و@persist. FlowStarted— في أول جولة للجلسة المؤجلة فقط.prepare_conversational_turn— إضافة رسالة المستخدم وlast_user_messageوتصنيف اختياري.- تنفيذ الرسم —
@start→@router→ معالجات@listen. - نهاية التشغيل — يُتخطى
flow_finishedوالتتبع لكل جولة عند التأجيل؛Agent.kickoff()/ crews لا تغلق دفعة الأب.
استدعِ append_message("assistant", reply) في المعالجات. سطر المستخدم محفوظ عند kickoff — لا تُضفه مرة أخرى.
عيّن على صنف Flow كـ conversational_config: ClassVar[ConversationalConfig | None].
| الحقل | الافتراضي | الغرض |
|---|---|---|
default_intents |
None |
تسميات outcome للتصنيف التلقائي قبل kickoff |
intent_llm |
None |
نموذج التصنيف (مطلوب عند وجود intents) |
interactive_prompt |
"You: " |
مطالبة kickoff(interactive=True) |
interactive_timeout |
None |
مهلة لكل سطر في الوضع التفاعلي |
exit_commands |
exit, quit |
كلمات إنهاء الوضع التفاعلي |
defer_trace_finalization |
True |
إبقاء دفعة trace واحدة مفتوحة بين الجولات |
يمكن التجاوز لكل kickoff عبر intents= وintent_llm=.
from crewai.flow import ChatState
class MyChatState(ChatState):
# موروث: id, messages, last_user_message, last_intent, session_ready
research_turn_count: int = 0
custom_flag: bool = False| الحقل | الدور |
|---|---|
id |
UUID الجلسة (مثل session_id / inputs["id"]) |
messages |
قائمة {role, content} لسجل LLM |
last_user_message |
آخر سطر مستخدم في هذه الجولة |
last_intent |
تسمية المسار بعد التصنيف (إن وُجد) |
session_ready |
علم bootstrap لمرة واحدة |
ConversationalInputs هو TypedDict لـ kickoff(inputs={...}): id, user_message, last_intent.
| المعامل | الغرض |
|---|---|
user_message |
نص هذه الجولة (أو {"role": "user", "content": "..."}) |
session_id |
UUID المحادثة → inputs["id"] / state.id |
intents |
تسميات outcome لـ classify_intent قبل kickoff |
intent_llm |
LLM للتصنيف (مطلوب مع intents) |
interactive |
حلقة CLI عبر ask() (للعروض المحلية فقط) |
interactive_prompt |
مطالبة الوضع التفاعلي |
interactive_timeout |
مهلة ask() لكل سطر |
exit_commands |
كلمات إنهاء الوضع التفاعلي |
inputs |
حقول حالة إضافية |
restore_from_state_id |
استنساخ من flow محفوظ آخر |
| السمة | الغرض |
|---|---|
conversational_config |
افتراضيات ConversationalConfig على مستوى الصنف |
defer_trace_finalization |
علم المثيل؛ يُضبط تلقائياً من config عند kickoff |
suppress_flow_events |
يخفي لوحات console؛ التتبع يُسجّل |
stream |
بث؛ مع ChatSession.handle_turn(..., stream=True) |
| الاسم | الوصف |
|---|---|
append_message(role, content, **extra) |
إضافة إلى state.messages |
conversation_messages |
سجل للقراءة فقط لاستدعاءات LLM |
classify_intent(text, outcomes, *, llm, context=None) |
تعيين outcome |
receive_user_message(text, *, outcomes=None, llm=None) |
إضافة رسالة مستخدم؛ last_intent اختياري |
finalize_session_traces() |
إصدار flow_finished المؤجل وإنهاء دفعة trace |
_should_defer_trace_finalization() |
هل يُؤجل إنهاء trace لكل جولة |
input_history |
سجل تدقيق مطالبات وردود ask() |
| الدالة | الوصف |
|---|---|
normalize_kickoff_inputs(...) |
دمج kwargs المحادثة في inputs |
get_conversation_messages(flow) |
قراءة الرسائل من الحالة أو المخزن |
append_message(flow, ...) |
مثل طريقة المثيل |
prepare_conversational_turn(flow, ...) |
تهيئة الجولة (عادةً kickoff يستدعيها) |
receive_user_message(flow, ...) |
مثل طريقة المثيل |
set_state_field(flow, name, value) |
تعيين حقل dict أو Pydantic |
get_conversational_config(flow) |
قراءة conversational_config |
input_history_to_messages(entries) |
تحويل input_history لصيغة رسائل LLM |
عيّن default_intents وintent_llm. كل kickoff يصنّف قبل @router؛ اقرأ self.state.last_intent في route().
عيّن default_intents=None ليضيف kickoff الرسالة فقط. في route() استدعِ classify_intent:
@router(bootstrap)
def route(self):
intent = self.classify_intent(
self._routing_prompt(self.state.last_user_message),
("GREETING", "ORDER", "RESEARCH", "GOODBYE"),
llm=self.conversational_config.intent_llm or "gpt-4o-mini",
)
self.state.last_intent = intent
return intentللبحث على الويب أو أدوات متعددة الخطوات استخدم @listen("RESEARCH") مع Agent.kickoff() وأدوات — وليس LLM.call() فقط.
FlowFinished يعني أن تنفيذ الرسم هذا اكتمل. تستمر المحادثة بـ kickoff آخر ونفس session_id. @persist يستعيد messages والأعلام والسياق.
نمط الحفظ: يُفضّل @persist على خطوة نهائية واحدة (مثل finalize) وليس على صنف Flow بالكامل. الحفظ على مستوى الصنف بعد كل method قد يفقد تحديثات المعالجات في نفس الجولة.
لا تستخدم @human_feedback لأسطر المتابعة في الدردشة إلا عند الحاجة لموافقة بشرية على مخرجات خطوة محددة.
crewai.experimental.ConversationalFlow هو صنف فرعي ذو رأي يتولى السباكة لكل جولة نيابةً عنك: يأتي مع رسم @start / @router / converse_turn / end_conversation مدمج، ويدير state.messages، ويُشغّل LLM التوجيه، ويبقي دفعة trace مفتوحة عبر الجولات. أنت تكتب المسارات المخصصة فقط؛ والإطار يتولى الباقي.
استخدمه عندما تريد دردشة متعددة الجولات مع موجّه قائم على LLM ومعالجات لكل مسار دون توصيل دورة الحياة يدوياً. انزل إلى Flow[ChatState] (في الأعلى) عندما تحتاج تحكماً كاملاً.
from crewai import LLM
from crewai.experimental import ConversationConfig, ConversationalFlow, RouterConfig
from crewai.flow import listen
ROUTER_LLM = LLM(model="gpt-4o-mini")
@ConversationConfig(
system_prompt="A multi-agent assistant for ordinary chat and tool-backed tasks.",
llm=ROUTER_LLM,
router=RouterConfig(), # المسارات + الأوصاف تُكتشف تلقائياً من معالجات @listen
)
class SupportFlow(ConversationalFlow):
@listen("INTERNET_SEARCH")
def handle_internet_search(self) -> str:
"""Fresh web research, current news, real-time lookups."""
...
self.append_assistant_message(reply)
return reply
@listen("CREWAI_DOCS")
def handle_crewai_docs(self) -> str:
"""Look up the CrewAI documentation for framework/API questions."""
...
self.append_assistant_message(reply)
return reply
flow = SupportFlow()
try:
flow.handle_turn("ماذا يمكنك أن تفعل؟") # يوجَّه إلى converse (مدمج)
flow.handle_turn("ابحث في الويب عن أخبار الذكاء الاصطناعي.") # يوجَّه إلى INTERNET_SEARCH
flow.handle_turn("لخص النتيجة الأولى.") # يعود إلى converse
finally:
flow.finalize_session_traces()مزخرف صنف يُلحق افتراضيات الدردشة على مستوى الصنف.
| الحقل | الافتراضي | الغرض |
|---|---|---|
system_prompt |
slices.conversational_system_prompt من i18n |
رسالة system يستخدمها converse_turn المدمج. مرر "" للتعطيل التام. |
llm |
None |
LLM المحادثة (يستخدمه converse_turn وكاحتياطي للموجّه). |
router |
None |
RouterConfig للتوجيه عبر LLM. بدونه، يسقط الـ flow دائماً إلى converse. |
answer_from_history_prompt |
افتراضي الإطار | رسالة system للمسار الاختياري answer_from_history. |
answer_from_history_llm |
None |
يُفعّل الاختصار answer_from_history عند تعيينه. |
intent_llm |
None |
LLM لمسار التصنيف المسبق القديم intents=/default_intents. |
default_intents |
None |
تسميات النتائج للتصنيف المسبق القديم. |
visible_agent_outputs |
None |
"all" أو قائمة بأسماء الـ agents الذين تُرفع مخرجاتهم من append_agent_result() إلى رسائل عامة. |
defer_trace_finalization |
True |
يبقي دفعة trace واحدة مفتوحة عبر استدعاءات handle_turn(). |
RouterConfig(
prompt="تأطير اختياري للنطاق (سياسة، صوت، شخصية).",
response_format=MyRoute, # اختياري؛ يُولَّد تلقائياً عند الإغفال
llm=ROUTER_LLM, # يسقط إلى ConversationConfig.llm
routes=["INTERNET_SEARCH", "CREWAI_DOCS"], # اختياري؛ يُستنتج من المستمعين
route_descriptions={
"INTERNET_SEARCH": "تجاوز الـ docstring لهذا المسار فقط.",
},
default_intent="converse", # يُستخدم عند فشل LLM أو غيابه
fallback_intent="converse", # يُستخدم عندما يعيد LLM مساراً غير صالح
intent_field="intent",
)تُبنى رسالة الموجّه إلى LLM تلقائياً. لكل مسار يختار الإطار وصفاً بهذا الترتيب من الأولوية:
RouterConfig.route_descriptions[label]— تجاوز صريح.ConversationalFlow.builtin_route_descriptions[label]— نص جاهز من الإطار لـconverseوendوanswer_from_history(مصاغ لـ LLM التوجيه).- أول سطر غير فارغ من docstring معالج
@listen(label). - فارغ (المسار يظهر في الفهرس بلا وصف).
عملياً، إضافة مسار جديد = @listen("X") + docstring من سطر واحد:
@listen("INTERNET_SEARCH")
def handle_internet_search(self) -> str:
"""Fresh web research, current news, real-time lookups."""
...…وسيرى LLM التوجيه:
Routes:
- CREWAI_DOCS: Look up the CrewAI documentation for framework/API questions.
- INTERNET_SEARCH: Fresh web research, current news, real-time lookups.
- converse: Ordinary chat, follow-ups, summaries, clarifications…
- end: User signals the conversation is finished (goodbye, exit, done).
RouterConfig.prompt مخصص لـ تأطير النطاق (شخصية المساعد، قواعد العمل، النبرة). فهرس المسارات يُبنى تلقائياً — لا تُدرج المسارات في prompt؛ سيختل التزامن لحظة إضافة معالج جديد.
| المسار | المعالج | الغرض |
|---|---|---|
converse |
converse_turn |
معالج الدردشة الافتراضي. يستدعي ConversationConfig.llm بـ system prompt + التاريخ القانوني للرسائل. |
end |
end_conversation |
يضبط state.ended = True ويُصدر رد إنهاء. |
answer_from_history |
answer_from_history_turn |
اختياري. يُوجَّه إليه عندما يكون ConversationConfig.answer_from_history_llm مُعيَّناً ويمكن الإجابة على الرسالة من التاريخ فقط. |
يمكنك تجاوز أي من هذه بتعريف معالج بنفس الاسم في الصنف الفرعي.
flow.handle_turn(message) يُشغّل جولة واحدة:
- يعيد ضبط تعقّب التنفيذ لكل جولة (
_completed_methods,_method_outputs) ليُعاد تشغيل الرسم — بدون ذلك، استدعاءاتkickoffالمتكررة على نفس النسخة ستُحدث دائرة قصر من الجولة الثانية لأنFlow.kickoff_asyncيعتبرinputs={"id": ...}استعادة من نقطة تفتيش. - يُلحق رسالة المستخدم بـ
state.messagesويضبطcurrent_user_message/last_user_message. يُحافَظ علىlast_intentمن الجولة السابقة كي يستخدمها LLM التوجيه كإشارة. - يُشغّل
conversation_start→route_conversation→ معالج@listenالمختار. - يخزّن الموجّه قراره في
state.last_intent(يكون مرئياً لسياق التوجيه في الجولة التالية). - إذا أعاد معالجك سلسلة نصية ولم يستدعِ
append_assistant_message، فإنhandle_turnيُلحقها نيابةً عنك.
يمكنك أيضاً استدعاء flow.kickoff(user_message=..., session_id=...) مباشرةً — نفس منطق الإعادة والتشغيل يعمل. handle_turn هو الغلاف المريح.
لتشغيل آثار جانبية (إعداد ناقل أحداث، قياس عن بُعد) في كل قرار توجيه، تجاوز route_turn:
class SupportFlow(ConversationalFlow):
def route_turn(self, context: dict[str, Any]) -> str | None:
self.event_bus = MyBus(self)
return super().route_turn(context)لتجاوز موجّه LLM واختيار مسار برمجياً، أعد سلسلة نصية من route_turn؛ إعادة None تسقط إلى _route_with_config(...).
داخل معالج @listen(label)، اختر:
self.append_assistant_message(text)— يضيف جولة مساعد مرئية للمستخدم إلىstate.messages. سيراهاconverse_turnفي الجولة التالية.self.append_agent_result(agent_name, result, visibility="private")— يسجّل حدثاً منظماً فيstate.eventsوموضوعاً فيstate.agent_threads[agent_name]. الرؤية العامة تستدعيappend_assistant_messageأيضاً. استخدم النتائج الخاصة للعمل الجانبي الذي يجب ألا يلوث التاريخ القانوني.
يمكن لـ ConversationConfig.visible_agent_outputs رفع النتائج الخاصة لـ agents محددين إلى عامة عالمياً ("all" أو قائمة بالأسماء).
مع defer_trace_finalization=True (افتراضي في ConversationalConfig):
- دفعة trace واحدة لجلسة الدردشة.
flow_startedفي الجولة الأولى فقط؛flow_finishedمرة فيfinalize_session_traces().kickoffلكل جولة لا يطبع "Trace batch finalized".- العمل المتداخل (
Agent.kickoff(), crews, Exa) يُلحق بدفعة الأب؛ flow داخلي منAgentExecutorلا يغلق دفعة الجلسة مبكراً.
try:
while True:
line = input("You: ").strip()
if not line:
break
flow.kickoff(user_message=line, session_id=session_id)
finally:
flow.finalize_session_traces()ChatSession.close() يستدعي finalize_session_traces() عند التأجيل.
suppress_flow_events=True يخفي لوحات Rich فقط؛ أحداث trace والـ methods تُصدر.
تستخدم ConversationalFlow التجريبية نفس دورة حياة tracing: defer_trace_finalization افتراضياً True، فيبقي كل handle_turn() أثر الجلسة مفتوحاً. أنهِ دوماً عند نهاية الجلسة — لُف حلقتك بـ try/finally واستدعِ flow.finalize_session_traces() عند الخروج. بدون ذلك، تبقى الدفعة مفتوحة وقد لا تُصدَّر آخر محادثة أبداً.
اضبط stream = True على صنف Flow. عندئذٍ يُصدر kickoff(...) أحداث assistant_delta (وما يرتبط بها) عبر ناقل الأحداث القياسي.
from crewai.flow import (
ChatState,
ConversationalConfig,
ConversationalInputs,
Flow,
listen,
persist,
router,
start,
)- إتقان إدارة حالة Flow
- أنشئ أول Flow
- Demo:
lib/crewai/runner_conversational_flow_simple.py— REPL بسيط معRESEARCHووكيل Exa