mirror of
https://github.com/crewAIInc/crewAI.git
synced 2026-07-01 13:18:10 +00:00
474 lines
24 KiB
Plaintext
474 lines
24 KiB
Plaintext
---
|
|
title: تدفقات المحادثة
|
|
description: أنشئ تطبيقات دردشة متعددة الجولات مع kickoff لكل جولة وسجل الرسائل وتوجيه النية والتتبع وجسور WebSocket.
|
|
icon: comments
|
|
mode: "wide"
|
|
---
|
|
|
|
## نظرة عامة
|
|
|
|
تعامل التطبيقات المحادثية مع كل سطر من المستخدم كـ **تشغيل flow جديد** بنفس **معرّف الجلسة**. توفر CrewAI مساعدات لسجل الرسائل وتصنيف النية الاختياري وتأجيل التتبع وجسور الواجهة، إضافة إلى REPL محلي `flow.chat()` للتدفقات المحادثية.
|
|
|
|
| المفهوم | التنفيذ |
|
|
|---------|---------|
|
|
| معرّف الجلسة | `handle_turn(..., session_id=...)` → `kickoff(inputs={"id": ...})` → `state.id` |
|
|
| سطر المستخدم | `handle_turn(message)` يضيف الرسالة إلى `state.messages` قبل تشغيل الرسم |
|
|
| اكتمال الجولة | `FlowFinished` لهذا **التشغيل** فقط؛ تستمر المحادثة في `handle_turn` التالي |
|
|
| تتبع الجلسة | `ConversationConfig(defer_trace_finalization=True)` + `finalize_session_traces()` |
|
|
|
|
## واجهات الجولات
|
|
|
|
استخدم **`flow.handle_turn(message, session_id=...)`** لكل رسالة مستخدم من REST أو WebSocket أو الاختبارات أو الواجهات المخصصة. استخدم **`flow.chat()`** عندما تريد حلقة دردشة محلية في الطرفية لـ `Flow` محادثي.
|
|
|
|
لا يقبل `Flow.kickoff()` الوسيطين `user_message=` أو `session_id=`. في التدفقات المحادثية، يخزن `handle_turn()` الرسالة المعلقة ويستدعي داخلياً `kickoff(inputs={"id": session_id})`.
|
|
|
|
| API | الاستخدام |
|
|
|-----|-----------|
|
|
| `handle_turn(message, session_id=...)` | غلاف مريح لجولة واحدة في `Flow` محادثي |
|
|
| `chat()` | REPL محلي في الطرفية لـ `Flow` محادثي |
|
|
| `kickoff(inputs={...})` | تشغيل متقدم للـ flow بدون معالجة جولة محادثية |
|
|
| `ask()` | مطالبة حاجزة **داخل** خطوة واحدة |
|
|
| `@human_feedback` | الموافقة/الرفض على **مخرجات خطوة** — وليس السطر التالي |
|
|
| `ChatSession.handle_turn(...)` | طبقة نقل فوق `handle_turn` |
|
|
|
|
## بداية سريعة
|
|
|
|
```python
|
|
from uuid import uuid4
|
|
|
|
from crewai import Flow
|
|
from crewai.flow import listen
|
|
from crewai.experimental.conversational import (
|
|
ConversationConfig,
|
|
ConversationState,
|
|
)
|
|
|
|
|
|
@ConversationConfig(defer_trace_finalization=True)
|
|
class SupportFlow(Flow[ConversationState]):
|
|
conversational = True
|
|
|
|
def route_turn(self, context):
|
|
message = (self.state.current_user_message or "").lower()
|
|
if "طلب" in message or "order" in message:
|
|
return "order"
|
|
if "وداع" in message or "goodbye" in message:
|
|
return "goodbye"
|
|
return "help"
|
|
|
|
@listen("order")
|
|
def handle_order(self):
|
|
reply = "طلبك في الطريق."
|
|
self.append_assistant_message(reply)
|
|
return reply
|
|
|
|
@listen("help")
|
|
def handle_help(self):
|
|
reply = "كيف يمكنني المساعدة؟"
|
|
self.append_assistant_message(reply)
|
|
return reply
|
|
|
|
@listen("goodbye")
|
|
def handle_goodbye(self):
|
|
reply = "وداعاً!"
|
|
self.append_assistant_message(reply)
|
|
return reply
|
|
|
|
|
|
session_id = str(uuid4())
|
|
flow = SupportFlow()
|
|
|
|
try:
|
|
flow.handle_turn("أين طلبي؟", session_id=session_id)
|
|
flow.handle_turn("وماذا عن الإرجاع؟", session_id=session_id)
|
|
finally:
|
|
flow.finalize_session_traces()
|
|
```
|
|
|
|
## دورة حياة الجولة
|
|
|
|
كل `handle_turn` يشغّل:
|
|
|
|
1. **`_configure_conversational_kickoff`** — دمج `session_id` / `user_message` في `inputs` وتطبيق `ConversationalConfig`.
|
|
2. **استعادة الحالة** — عند وجود `inputs["id"]` و`@persist`.
|
|
3. **`FlowStarted`** — في أول جولة للجلسة المؤجلة فقط.
|
|
4. **`prepare_conversational_turn`** — إضافة رسالة المستخدم و`last_user_message` وتصنيف اختياري.
|
|
5. **تنفيذ الرسم** — `@start` → `@router` → معالجات `@listen`.
|
|
6. **نهاية التشغيل** — يُتخطى `flow_finished` والتتبع لكل جولة عند التأجيل؛ `Agent.kickoff()` / crews لا تغلق دفعة الأب.
|
|
|
|
استدعِ **`append_assistant_message(reply)`** في المعالجات. سطر المستخدم محفوظ عبر `handle_turn` — لا تُضفه مرة أخرى.
|
|
|
|
## `ConversationalConfig` (افتراضيات على مستوى الصنف)
|
|
|
|
عيّن على صنف `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=`.
|
|
|
|
## `ChatState` (شكل الحالة الموصى به للحفظ)
|
|
|
|
```python
|
|
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`.
|
|
|
|
## API المحادثة على `Flow`
|
|
|
|
### معاملات `kickoff` / `kickoff_async`
|
|
|
|
| المعامل | الغرض |
|
|
|---------|--------|
|
|
| `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()` |
|
|
|
|
### مساعدات الوحدة (`crewai.flow.conversation`)
|
|
|
|
| الدالة | الوصف |
|
|
|--------|--------|
|
|
| `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 |
|
|
|
|
## أنماط توجيه النية
|
|
|
|
### أ. تصنيف مسبق عبر `ConversationalConfig` (الأبسط)
|
|
|
|
عيّن `default_intents` و`intent_llm`. كل kickoff يصنّف قبل `@router`؛ اقرأ `self.state.last_intent` في `route()`.
|
|
|
|
### ب. تصنيف داخل `@router` (مطالبات أغنى)
|
|
|
|
عيّن `default_intents=None` ليضيف kickoff الرسالة فقط. في `route()` استدعِ `classify_intent`:
|
|
|
|
```python
|
|
@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()` فقط.
|
|
|
|
## عندما ينتهي الـ flow ويستمر المستخدم
|
|
|
|
`FlowFinished` يعني أن **تنفيذ الرسم هذا** اكتمل. تستمر المحادثة بـ `kickoff` آخر ونفس `session_id`. `@persist` يستعيد `messages` والأعلام والسياق.
|
|
|
|
**نمط الحفظ:** يُفضّل `@persist` على **خطوة نهائية واحدة** (مثل `finalize`) وليس على صنف `Flow` بالكامل. الحفظ على مستوى الصنف بعد كل method قد يفقد تحديثات المعالجات في نفس الجولة.
|
|
|
|
لا تستخدم `@human_feedback` لأسطر المتابعة في الدردشة إلا عند الحاجة لموافقة بشرية على مخرجات خطوة محددة.
|
|
|
|
## `Flow` المحادثاتي (تجريبي)
|
|
|
|
<Warning>
|
|
**ميزة تجريبية.** سطح `Flow` المحادثاتي (`conversational = True`،
|
|
`handle_turn`، `ConversationConfig`، `RouterConfig`،
|
|
`ConversationState`، الرسم البياني المدمج والمساعدات) يقع تحت
|
|
`crewai.experimental` وقد يتغير شكله قبل التخرج. ثبّت إصدار CrewAI إذا
|
|
كنت تعتمد على سلوك محدد، وراقب changelog للتحديثات الكاسرة. الملاحظات
|
|
والمشاكل مرحب بها.
|
|
</Warning>
|
|
|
|
فعّل الرسم المحادثاتي بتعيين `conversational = True` على صنف فرعي من `Flow`. عندئذٍ يُظهر `Flow` الأساسي رسم `@start` / `@router` / `converse_turn` / `end_conversation` مدمجاً، ويدير `state.messages`، ويُشغّل LLM التوجيه، ويبقي دفعة trace مفتوحة عبر الجولات. أنت تكتب **المسارات المخصصة** فقط؛ والإطار يتولى الباقي.
|
|
|
|
استخدمه عندما تريد دردشة متعددة الجولات مع موجّه قائم على LLM ومعالجات لكل مسار دون توصيل دورة الحياة يدوياً. استخدم `Flow[ChatState]` (النمط الأدنى مستوى في الأعلى) عندما تحتاج تحكماً كاملاً.
|
|
|
|
### مثال سريع
|
|
|
|
```python
|
|
from crewai import LLM, Flow
|
|
from crewai.flow import listen
|
|
from crewai.experimental.conversational import (
|
|
ConversationConfig,
|
|
ConversationState,
|
|
RouterConfig,
|
|
)
|
|
|
|
|
|
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(Flow[ConversationState]):
|
|
conversational = True
|
|
|
|
@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()
|
|
```
|
|
|
|
للدردشة المحلية في الطرفية، استخدم `chat()`:
|
|
|
|
```python
|
|
def kickoff() -> None:
|
|
SupportFlow().chat()
|
|
```
|
|
|
|
يلف `chat()` استدعاءات `handle_turn()` داخل REPL، ويخرج عند `exit` / `quit`، ويتجاهل الأسطر الفارغة افتراضياً، ويستدعي `finalize_session_traces()` عند انتهاء الجلسة.
|
|
|
|
### `ConversationConfig`
|
|
|
|
مزخرف صنف يُلحق افتراضيات الدردشة على مستوى الصنف.
|
|
|
|
| الحقل | الافتراضي | الغرض |
|
|
|-------|-----------|-------|
|
|
| `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` وفهرس المسارات المُولَّد تلقائياً
|
|
|
|
```python
|
|
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 تلقائياً. لكل مسار يختار الإطار وصفاً بهذا الترتيب من الأولوية:
|
|
|
|
1. `RouterConfig.route_descriptions[label]` — تجاوز صريح.
|
|
2. `Flow.builtin_route_descriptions[label]` — نص جاهز من الإطار لـ `converse` و`end` و`answer_from_history` (مصاغ لـ LLM التوجيه).
|
|
3. أول سطر غير فارغ من docstring معالج `@listen(label)`.
|
|
4. فارغ (المسار يظهر في الفهرس بلا وصف).
|
|
|
|
عملياً، **إضافة مسار جديد = `@listen("X")` + docstring من سطر واحد**:
|
|
|
|
```python
|
|
@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` مُعيَّناً ويمكن الإجابة على الرسالة من التاريخ فقط. |
|
|
|
|
يمكنك تجاوز أي من هذه بتعريف معالج بنفس الاسم في الصنف الفرعي.
|
|
|
|
### دلالات `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. يُشغّل `conversation_start` → `route_conversation` → معالج `@listen` المختار.
|
|
4. يخزّن الموجّه قراره في `state.last_intent` (يكون مرئياً لسياق التوجيه في الجولة التالية).
|
|
5. إذا أعاد معالجك سلسلة نصية ولم يستدعِ `append_assistant_message`، فإن `handle_turn` يُلحقها نيابةً عنك.
|
|
|
|
استدعِ `handle_turn()` لرسائل الدردشة. استدعاء `kickoff(inputs={"id": ...})` مباشرةً يشغل الرسم بدون غلاف الجولة المحادثية.
|
|
|
|
### `chat()` للـ REPL المحلي
|
|
|
|
`flow.chat()` هو غلاف الطرفية الجاهز فوق `handle_turn()`:
|
|
|
|
```python
|
|
flow = SupportFlow()
|
|
flow.chat()
|
|
```
|
|
|
|
يتولى الحلقة المحلية الشائعة:
|
|
|
|
1. يطلب رسالة من المستخدم.
|
|
2. يتوقف عند `exit` / `quit` أو `EOFError` أو `KeyboardInterrupt`.
|
|
3. يستدعي `handle_turn(message, session_id=...)`.
|
|
4. يطبع نتيجة المساعد.
|
|
5. ينهي traces الجلسة المؤجلة داخل كتلة `finally`.
|
|
|
|
خصص سلوك الطرفية عبر I/O قابل للحقن:
|
|
|
|
```python
|
|
flow.chat(
|
|
session_id="demo-session",
|
|
prompt="You: ",
|
|
assistant_prefix="Assistant: ",
|
|
exit_commands=("exit", "quit", "bye"),
|
|
)
|
|
```
|
|
|
|
لتطبيقات الويب والـ workers الخلفية والاختبارات ووسائط النقل المخصصة، استمر في استخدام `handle_turn()` مباشرةً.
|
|
|
|
### سلوك موجّه مخصص
|
|
|
|
لتشغيل آثار جانبية (إعداد ناقل أحداث، قياس عن بُعد) في كل قرار توجيه، تجاوز `route_turn`:
|
|
|
|
```python
|
|
class SupportFlow(Flow[ConversationState]):
|
|
conversational = True
|
|
|
|
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(...)`.
|
|
|
|
### `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"` أو قائمة بالأسماء).
|
|
|
|
## التتبع عبر الجولات
|
|
|
|
مع `defer_trace_finalization=True` (افتراضي في `ConversationalConfig`):
|
|
|
|
- **دفعة trace واحدة** لجلسة الدردشة.
|
|
- **`flow_started`** في الجولة الأولى فقط؛ **`flow_finished`** مرة في `finalize_session_traces()`.
|
|
- **`kickoff` لكل جولة** لا يطبع "Trace batch finalized".
|
|
- **العمل المتداخل** (`Agent.kickoff()`, crews, Exa) يُلحق بدفعة **الأب**؛ flow داخلي من `AgentExecutor` لا يغلق دفعة الجلسة مبكراً.
|
|
|
|
```python
|
|
flow.chat(session_id=session_id)
|
|
```
|
|
|
|
`flow.chat()` يستدعي `finalize_session_traces()` نيابةً عنك. عندما تملك الحلقة عبر `handle_turn()` أو `kickoff(...)`، استدعِ `finalize_session_traces()` عند انتهاء الجلسة.
|
|
|
|
`suppress_flow_events=True` يخفي لوحات Rich فقط؛ أحداث trace والـ methods تُصدر.
|
|
|
|
### دورة حياة trace لـ `Flow` المحادثاتي
|
|
|
|
يستخدم [`Flow` المحادثاتي](#flow-المحادثاتي-تجريبي) التجريبي نفس دورة حياة tracing: `defer_trace_finalization` افتراضياً `True`، فيبقي كل `handle_turn()` أثر الجلسة مفتوحاً. أنهِ دوماً عند نهاية الجلسة — لُف حلقتك بـ `try/finally` واستدعِ `flow.finalize_session_traces()` عند الخروج. بدون ذلك، تبقى الدفعة مفتوحة وقد لا تُصدَّر آخر محادثة أبداً.
|
|
|
|
## البث
|
|
|
|
اضبط `stream = True` على صنف `Flow`. عندئذٍ يُصدر `kickoff(...)` أحداث `assistant_delta` (وما يرتبط بها) عبر ناقل الأحداث القياسي.
|
|
|
|
## الاستيراد
|
|
|
|
```python
|
|
from crewai.flow import (
|
|
ChatState,
|
|
ConversationalConfig,
|
|
ConversationalInputs,
|
|
Flow,
|
|
listen,
|
|
persist,
|
|
router,
|
|
start,
|
|
)
|
|
```
|
|
|
|
## مراجع
|
|
|
|
- [إتقان إدارة حالة Flow](/ar/guides/flows/mastering-flow-state)
|
|
- [أنشئ أول Flow](/ar/guides/flows/first-flow)
|
|
- Demo: `lib/crewai/runner_conversational_flow_simple.py` — REPL بسيط مع `RESEARCH` ووكيل Exa
|