Skip to main content

نظرة عامة

تعامل التطبيقات المحادثية مع كل سطر من المستخدم كـ تشغيل flow جديد بنفس معرّف الجلسة. توفر CrewAI مساعدات لسجل الرسائل، وتوجيه النية الاختياري، وتأجيل التتبع، والبث المنظّم للجولات، إضافة إلى REPL محلي عبر flow.chat().

واجهات الجولات

استخدم flow.handle_turn(message, session_id=...) لكل رسالة مستخدم من REST أو WebSocket أو الاختبارات أو الواجهات المخصصة. استخدم flow.chat() عندما تريد حلقة دردشة محلية في الطرفية لـ Flow محادثي. لا يقبل Flow.kickoff() الوسيطين user_message= أو session_id=. في التدفقات المحادثية، يخزن handle_turn() الرسالة المعلقة ويستدعي داخلياً kickoff(inputs={"id": session_id}) بعد إعادة ضبط حالة التنفيذ الخاصة بالجولة. ترفع handle_turn() وstream_turn() وchat() الخطأ ValueError ما لم يكن الوضع المحادثاتي مفعّلاً. يؤدي تطبيق @ConversationConfig(...) إلى تفعيله تلقائياً؛ وإلا فعيّن conversational = True.

بداية سريعة

بث جولة

استخدم stream_turn() عندما تحتاج واجهة مستخدم أو بيئة تشغيل إلى أحداث منظّمة لجولة دردشة واحدة. يعيد جلسة بث تحتوي على إطارات مرتبة لتوجيه Flow، وأجزاء LLM، ونشاط الأدوات، ورسائل المحادثة.
راجع عقد بيئة البث للاطلاع على عقد الإطارات الكامل وقائمة القنوات.

دورة حياة الجولة

يشغّل كل handle_turn المسار التالي:
  1. إعداد الجولة — يخزن رسالة المستخدم المعلقة، ويحل معرّف الجلسة، ويعيد ضبط تعقّب التنفيذ الخاص بالجولة، ثم يستدعي kickoff(inputs={"id": session_id}).
  2. استعادة الحالة — إذا وُجد inputs["id"] وكان @persist مهيّأً، تُحمّل أحدث لقطة.
  3. FlowStarted — في أول جولة للجلسة المؤجلة فقط.
  4. ترطيب الجولة المعلقة — تُضاف رسالة المستخدم إلى state.messages، وتُضبط current_user_message / last_user_message، ويُجرى التصنيف اختيارياً عند ضبط intents / default_intents مع intent_llm.
  5. تنفيذ الرسم — طرق @start التي يعرّفها المستخدم (إن وجدت) → route_conversation (نقطة البدء/الموجّه المدمجة) → معالج @listen المختار. تستدعي route_conversation أيضاً المساعد القابل للتجاوز conversation_start().
  6. نهاية التشغيل — يُتخطى flow_finished لكل جولة وإنهاء التتبع عند تفعيل التأجيل؛ كما لا تغلق استدعاءات Agent.kickoff() المتداخلة أو crews دفعة الأب.
استدعِ append_assistant_message(reply) عندما لا تطابق الرد الظاهر قيمة الإرجاع، أو عند قصّ التاريخ. تُسجَّل أيضاً سلسلة الإرجاع العامة كمساعد وتُضمَّن في لقطة @persist، فتستعيدها نسخة Flow جديدة. سطر المستخدم محفوظ عبر handle_turn — لا تُضفه مرة أخرى.

نظرة عامة على الإعداد

يؤدي تزيين صنف فرعي من Flow بـ ConversationConfig إلى إرفاق افتراضيات الدردشة وتفعيل الوضع المحادثاتي معاً. راجع مرجع الحقول الكامل أدناه. ويمكنك تجاوز التصنيف المسبق لكل جولة عبر handle_turn(..., intents=..., intent_llm=...).

مساعدات ChatState منخفضة المستوى

تظل ChatState وConversationalConfig القديمة ومساعدات crewai.flow.conversation قابلة للاستيراد للتنسيق المتقدم أو الاختبارات أو الأغلفة المخصصة. وهي منفصلة عن واجهتي ConversationState / ConversationConfig، ولا تضيف وسيطي user_message= أو session_id= إلى Flow.kickoff().
ConversationalInputs هو TypedDict لمفاتيح kickoff(inputs={...}) الاصطلاحية: id وuser_message وlast_intent. تخزن ConversationState رسائل messages ككائنات ConversationMessage، وتوفر أيضاً current_user_message وended وevents وagent_threads. استخدم conversation_messages عند تمرير سجلها القانوني إلى LLM.

API المحادثة على Flow

معاملات handle_turn

معاملات kickoff

يقبل Flow.kickoff() كلاً من inputs وinput_files وfrom_checkpoint وrestore_from_state_id. مرر inputs={"id": session_id} عندما تحتاج إلى تنفيذ flow خام، لكن استخدم handle_turn() عندما يمثل الاستدعاء رسالة دردشة.

سمات المثيل

طرق وخصائص

مساعدات الوحدة (crewai.flow.conversation)

يمكن استيرادها من crewai.flow.conversation للاختبارات أو التنسيق المخصص. تستخدم هذه المساعدات بنية ConversationalConfig القديمة؛ كما تمسح prepare_conversational_turn() قيمة last_intent، بخلاف handle_turn() التي تحتفظ بها كسياق للموجّه.

أنماط توجيه النية

أ. تصنيف مسبق عبر ConversationConfig (الأبسط)

عيّن default_intents وintent_llm. يصنّف كل handle_turn() الرسالة الحالية مسبقاً. تكون الأولوية لنتيجة غير فارغة يعيدها route_turn() مخصص؛ وإلا تستخدم route_conversation النية المصنّفة للجولة الحالية.

ب. تصنيف داخل route_turn (مطالبات أغنى)

عيّن default_intents=None كي يضيف handle_turn() رسالة المستخدم فقط. داخل route_turn()، استدعِ classify_intent بمطالبة أو أوصاف مخصصة:
للبحث على الويب أو أدوات متعددة الخطوات استخدم @listen("RESEARCH") مع Agent.kickoff() وأدوات — وليس LLM.call() فقط.

عندما ينتهي الـ flow ويستمر المستخدم

يُكمل كل handle_turn() تشغيل رسم واحد، وتستمر المحادثة عبر handle_turn() آخر يستخدم session_id نفسه. مع دورة حياة التتبع المؤجلة افتراضياً، يصدر ذلك التشغيل conversation_turn_completed، بينما يصدر FlowFinished مرة واحدة عندما تغلق finalize_session_traces() الجلسة. ويستعيد @persist الرسائل والأعلام والسياق. نمط الحفظ: يُفضّل @persist على خطوة نهائية واحدة (مثل finalize) وليس على صنف Flow بالكامل. يحفظ الاستمرار على مستوى الصنف بعد كل طريقة؛ وتستخدم load_state أحدث صف، وقد يكون لقطة في منتصف التشغيل (مثلاً بعد bootstrap مباشرة) لا تتضمن تحديثات المعالج من الجولة نفسها. لا تستخدم @human_feedback لأسطر المتابعة في الدردشة إلا عند الحاجة لموافقة بشرية على مخرجات خطوة محددة.

Flow المحادثاتي

اشترك في رسم الدردشة المحادثاتي بتعيين conversational = True على صنف فرعي من Flow أو بتطبيق @ConversationConfig(...). يوفر Flow الأساسي عندئذٍ route_conversation كنقطة البدء/الموجّه المدمجة، إضافة إلى مستمعي converse_turn وend_conversation. يظل المستمع المهمل answer_from_history_turn متاحاً للتوافق. يدير الإطار state.messages، ويمكنه تشغيل LLM للموجّه، ويبقي دفعة trace مفتوحة عبر الجولات. أنت تكتب المسارات المخصصة؛ والإطار يتولى الباقي. استخدمه عندما تريد دردشة متعددة الجولات مع موجّه قائم على LLM ومعالجات لكل مسار دون توصيل دورة الحياة يدوياً. استخدم Flow[ChatState] (النمط الأدنى مستوى في الأعلى) عندما تحتاج تحكماً كاملاً.

مثال سريع

للدردشة المحلية في الطرفية، استخدم chat():
يلف chat() استدعاءات handle_turn() داخل REPL، ويخرج عند exit / quit، ويتجاهل الأسطر الفارغة افتراضياً، ويستدعي finalize_session_traces() عند انتهاء الجلسة.

ConversationConfig

مزخرف صنف يُلحق افتراضيات الدردشة على مستوى الصنف.
تم إهمال answer_from_history_prompt وanswer_from_history_llm ومسار answer_from_history، وستُزال في إصدار مستقبلي. فهي تكرر converse، الذي يتولى بالفعل السجل القانوني، وتضيف استدعاء LLM للتحقق من أهلية الإجابة، ويجري تجاوزها عندما يعيد الموجّه التلقائي المعتاد مساراً. تظل الإعدادات الحالية تعمل وتُصدر DeprecationWarning.
عند عدم وجود مسارات مخصصة، تسقط الجولات إلى converse. ومع وجود مسارات مخصصة وLLM للمحادثة/الموجّه، ينشئ الإطار RouterConfig افتراضية؛ لا توفر واحدة صراحةً إلا لتخصيص المطالبة أو قائمة المسارات أو الأوصاف أو سلوك fallback. أما ضبط default_intents فيستخدم مسار التصنيف المسبق القديم. إذا لم يُهيأ LLM للمحادثة، يعيد converse_turn المدمج عنصراً نائباً للإعداد بدلاً من توليد إجابة.

RouterConfig وفهرس المسارات المُولَّد تلقائياً

تُبنى رسالة الموجّه إلى LLM تلقائياً. لكل مسار يختار الإطار وصفاً بهذا الترتيب من الأولوية:
  1. RouterConfig.route_descriptions[label] — تجاوز صريح.
  2. Flow.builtin_route_descriptions[label] — نص جاهز من الإطار لـ converse وend ولمسار التوافق المهمل answer_from_history (مصاغ لـ LLM التوجيه).
  3. قيمة description المعلنة للطريقة (تستخدمها التدفقات التعريفية وإسقاطات DSL).
  4. أول سطر غير فارغ من docstring معالج @listen(label).
  5. فارغ (المسار يظهر في الفهرس بلا وصف).
عملياً، إضافة مسار جديد = @listen("X") + docstring من سطر واحد:
…وسيرى LLM التوجيه:
RouterConfig.prompt مخصص لـ تأطير النطاق (شخصية المساعد، قواعد العمل، النبرة). فهرس المسارات يُبنى تلقائياً — لا تُدرج المسارات في prompt؛ سيختل التزامن لحظة إضافة معالج جديد.

تسمية المعالجات

السلسلة النصية في @listen("…") هي تسمية مسار للموجّه (اسم حدث)، وليست اسم طريقة Python. تتشارك تسميات المسارات وأحداث اكتمال الطرق مساحة مشغلات واحدة، ولذلك تؤدي تسمية المعالج باسم مساره نفسه إلى إعادة تشغيل المعالج في حلقة. استخدم اسماً مختلفاً للطريقة — تستخدم أمثلة التوثيق بادئة handle_*:
لا تكرر تسمية المسار في اسم الطريقة:

المسارات المدمجة

يمكنك تجاوز أي من هذه بتعريف معالج بنفس الاسم في الصنف الفرعي.

دلالات handle_turn()

flow.handle_turn(message) يُشغّل جولة واحدة:
  1. يعيد ضبط تعقّب التنفيذ لكل جولة (_completed_methods, _method_outputs) ليُعاد تشغيل الرسم — بدون ذلك، استدعاءات kickoff المتكررة على نفس النسخة ستُحدث دائرة قصر من الجولة الثانية لأن Flow.kickoff_async يعتبر inputs={"id": ...} استعادة من نقطة تفتيش.
  2. يُلحق رسالة المستخدم بـ state.messages ويضبط current_user_message / last_user_message. يُحافَظ على last_intent من الجولة السابقة كي يستخدمها LLM التوجيه كإشارة.
  3. يُشغّل طرق @start التي يعرّفها المستخدم (إن وجدت)، ثم route_conversation كنقطة البدء/الموجّه المدمجة، ثم معالج @listen المختار. وتستدعي route_conversation المساعد القابل للتجاوز conversation_start().
  4. يخزّن الموجّه قراره في state.last_intent (يكون مرئياً لسياق التوجيه في الجولة التالية).
  5. إذا أعاد معالجك سلسلة نصية ولم يستدعِ append_assistant_message، فإن handle_turn يُلحقها نيابةً عنك ويحفظ state.messages المحدَّث حتى تشمل استعادة @persist جولة المساعد.
استدعِ handle_turn() لرسائل الدردشة. استدعاء kickoff(inputs={"id": ...}) مباشرةً يشغل الرسم بدون غلاف الجولة المحادثية.

chat() للـ REPL المحلي

flow.chat() هو غلاف الطرفية الجاهز فوق handle_turn():
يتولى الحلقة المحلية الشائعة:
  1. يطلب رسالة من المستخدم.
  2. يتوقف عند exit / quit أو EOFError أو KeyboardInterrupt.
  3. يستدعي handle_turn(message, session_id=...).
  4. يطبع نتيجة المساعد.
  5. ينهي traces الجلسة المؤجلة داخل كتلة finally.
يُفعّل chat(defer_trace_finalization=True) مؤقتاً علم التأجيل على مستوى المثيل للـ REPL، ثم يعيد قيمته السابقة عند الخروج. خصص سلوك الطرفية عبر I/O قابل للحقن:
لتطبيقات الويب والـ workers الخلفية والاختبارات ووسائط النقل المخصصة، استمر في استخدام handle_turn() مباشرةً.

سلوك موجّه مخصص

لتشغيل آثار جانبية (إعداد ناقل أحداث، قياس عن بُعد) في كل قرار توجيه، تجاوز route_turn:
لتجاوز موجّه LLM بالكامل واختيار مسار برمجياً، أعد سلسلة نصية غير فارغة من route_turn. لا يؤدي إرجاع قيمة falsy من التجاوز إلى استدعاء _route_with_config()؛ بل يسقط التوجيه إلى النية المصنّفة مسبقاً لهذه الجولة، ثم إلى مسار التوافق المهمل answer_from_history عند إعداده، وأخيراً إلى converse. تكون last_intent من الجولة السابقة متاحة في سياق الموجّه، لكنها لا تُعاد أبداً كـ fallback.

append_assistant_message وappend_agent_result

داخل معالج @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" أو قائمة بالأسماء).

تعريف تدفق محادثاتي بصيغة JSON/YAML

يمكن لـ التدفق التعريفي أن يكون محادثاتيًا أيضًا. أضف كتلة conversational في المستوى الأعلى وعرّف مساراتك الخاصة كطرق تستمع (listen) إلى تسمية مسار:
تعريف الكتلة هو الاشتراك نفسه — القيمة الافتراضية لـ enabled هي true. اضبطها على enabled: false للاحتفاظ بالإعدادات مع إيقاف المحادثة. يؤدي ذلك أيضاً إلى تعطيل إنشاء الطرق المدمجة، ولذلك يجب أن توفر التعريفة رسماً عادياً غير محادثاتي. تُوفَّر لك ثلاثة أشياء: تقبل حقول llm وrouter.llm وintent_llm التعريفية إما معرّف نموذج أو خريطة إعدادات مثل {model: openai/gpt-4o-mini, max_tokens: 512}. وتدعم كتلة conversational أيضاً default_intents وvisible_agent_outputs وdefer_trace_finalization وحقول RouterConfig الموضحة أعلاه. تظل تعريفات answer_from_history_prompt / answer_from_history_llm المهملة مقبولة للتوافق. شغّله من Python بنفس واجهات الجولة المستخدمة مع تدفق محادثاتي معرّف بصنف:

تسمية المسارات

تتشارك تسميات المسارات وأسماء الطرق مساحة اسم واحدة للمشغّلات، لذا يجب ألا يحمل المعالج اسم المسار الذي يستمع إليه — يُرفض create_video الذي يستمع إلى create_video عند بناء التدفق. استخدم بادئة handle_*.

ما لا يمكن للتعريفة التعبير عنه

يفتح crewai run واجهة المحادثة النصية للتدفق المحادثاتي التعريفي — نفس الواجهة التي يحصل عليها Flow محادثاتي مكتوب بلغة Python. تحتاج حلقة المحادثة إلى طرفية، ولذلك يخرج التشغيل بدون طرفية برمز غير صفري مع إرشادات بدلاً من تنفيذ جولة واحدة؛ شغّله من Python هناك عبر handle_turn() أو stream_turn(). وتعمل الطريقة التعريفية ذات كتلة human_feedback: (وفي Python: ‏@human_feedback) على REPL طرفي، لأن runtime يجمع الملاحظات بمطالبة حاجزة لا تستطيع TUI خدمتها. لا يُقبل --inputs مع Flow محادثاتي — فمدخل كل جولة هو الرسالة التي تكتبها — واستئناف جلسة حسب المعرّف غير موصول بواجهة CLI بعد؛ استخدم flow.handle_turn(message, session_id=...) من Python لذلك.

التتبع عبر الجولات

مع defer_trace_finalization=True (افتراضي في ConversationConfig):
  • دفعة trace واحدة لجلسة الدردشة.
  • flow_started في الجولة الأولى فقط؛ flow_finished مرة في finalize_session_traces().
  • kickoff لكل جولة لا يطبع “Trace batch finalized”.
  • العمل المتداخل (Agent.kickoff(), crews, Exa) يُلحق بدفعة الأب؛ flow داخلي من AgentExecutor لا يغلق دفعة الجلسة مبكراً.
flow.chat() يستدعي finalize_session_traces() نيابةً عنك. عندما تملك الحلقة عبر handle_turn()، استدعِ finalize_session_traces() عند انتهاء الجلسة. يخفي suppress_flow_events=True لوحات Rich ويمنع أحداث تنفيذ الطرق. وتظل أحداث بدء/انتهاء Flow تصدر، فيبقى بالإمكان تتبع دورة حياة Flow الخارجية، بينما تُحذف spans الطرق الفردية.

دورة حياة trace لـ Flow المحادثاتي

يستخدم Flow المحادثاتي دورة حياة التتبع نفسها: القيمة الافتراضية لـ defer_trace_finalization هي True، ولذلك يبقي كل handle_turn() trace الجلسة مفتوحاً. تمنع الجولات المؤجلة أيضاً إصدار flow_failed لكل جولة؛ وعند حدوث خطأ في جولة أو إلغاء الجلسة، أنهِ الجلسة صراحةً. يغلق ذلك الدفعة بحدث FlowFinished على مستوى الجلسة بدلاً من حدث FlowFailed لكل جولة. لُف REPL/الحلقة دائماً بـ try/finally واستدعِ flow.finalize_session_traces() عند الخروج. بدون ذلك، تبقى دفعة trace مفتوحة وقد لا تُصدَّر المحادثة النهائية أبداً.

البث

استخدم stream_turn() للواجهات المحادثية، وكرّر عبر كائنات StreamFrame المرتبة التي يعيدها:
بالنسبة إلى Flow غير محادثاتي، يؤدي ضبط stream = True إلى جعل kickoff() يعيد StreamSession. لا تضبط flow.stream = True عند استخدام handle_turn()؛ إذ تملك stream_turn() دورة حياة البث المحادثاتي.

الاستيراد

مراجع