fix: Update autopilot with full 9-step pipeline and 5 built-in tasks

https://claude.ai/code/session_01LsnvBa7HwF5hs99VZbgLGj
This commit is contained in:
Claude 2026-04-11 08:00:09 +00:00
parent 552e562de5
commit 2607c0a5af
No known key found for this signature in database

View File

@ -107,8 +107,6 @@ class AutopilotResult(BaseModel):
duration_ms: int = 0 duration_ms: int = 0
summary_ar: str = "" summary_ar: str = ""
# ── Task handlers ──────────────────────────────────────────────────
def _advance(unit: AutopilotUnit, step: str) -> None: def _advance(unit: AutopilotUnit, step: str) -> None:
unit.current_step = step unit.current_step = step
unit.checkpoint["step"] = step unit.checkpoint["step"] = step
@ -230,27 +228,17 @@ async def _task_sequence_optimizer(u: AutopilotUnit, p: AutopilotPolicy) -> None
for s in seqs if s["reply_rate"] < 0.15] for s in seqs if s["reply_rate"] < 0.15]
_advance(u, "propose") _advance(u, "propose")
_TASK_HANDLERS: dict[str, Callable] = { _TASKS: dict[str, tuple[Callable, str, str]] = {
"follow_up_dormant_leads": _task_follow_up_dormant_leads, "follow_up_dormant_leads": (_task_follow_up_dormant_leads, "متابعة العملاء الخاملين",
"qualify_new_leads": _task_qualify_new_leads, "البحث عن عملاء بدون نشاط 3+ أيام وصياغة رسائل متابعة"),
"pipeline_health_check": _task_pipeline_health_check, "qualify_new_leads": (_task_qualify_new_leads, "تأهيل العملاء الجدد",
"daily_report": _task_daily_report, "تقييم وتأهيل العملاء المحتملين الجدد تلقائياً"),
"sequence_optimizer": _task_sequence_optimizer, "pipeline_health_check": (_task_pipeline_health_check, "فحص صحة خط الأنابيب",
"تحليل خط الأنابيب والكشف عن صفقات معرضة للخطر"),
"daily_report": (_task_daily_report, "التقرير اليومي", "إنشاء ملخص يومي لأداء المبيعات"),
"sequence_optimizer": (_task_sequence_optimizer, "تحسين التسلسلات", "تحليل أداء التسلسلات واقتراح تحسينات"),
} }
_TASK_META: dict[str, dict[str, str]] = {
"follow_up_dormant_leads": {"name_ar": "متابعة العملاء الخاملين",
"desc_ar": "البحث عن عملاء بدون نشاط 3+ أيام وصياغة رسائل متابعة"},
"qualify_new_leads": {"name_ar": "تأهيل العملاء الجدد",
"desc_ar": "تقييم وتأهيل العملاء المحتملين الجدد تلقائياً"},
"pipeline_health_check": {"name_ar": "فحص صحة خط الأنابيب",
"desc_ar": "تحليل خط الأنابيب والكشف عن صفقات معرضة للخطر"},
"daily_report": {"name_ar": "التقرير اليومي", "desc_ar": "إنشاء ملخص يومي لأداء المبيعات"},
"sequence_optimizer": {"name_ar": "تحسين التسلسلات", "desc_ar": "تحليل أداء التسلسلات واقتراح تحسينات"},
}
# ── Runner ─────────────────────────────────────────────────────────
class AutopilotRunner: class AutopilotRunner:
"""Runs autopilot tasks safely with budgets, policies, and checkpointing.""" """Runs autopilot tasks safely with budgets, policies, and checkpointing."""
@ -261,10 +249,11 @@ class AutopilotRunner:
async def run(self, task_type: str, mode: AutopilotMode, params: dict[str, Any], async def run(self, task_type: str, mode: AutopilotMode, params: dict[str, Any],
budget: Optional[AutopilotBudget] = None, tenant_id: str = "", budget: Optional[AutopilotBudget] = None, tenant_id: str = "",
agent_id: str = "") -> AutopilotResult: agent_id: str = "") -> AutopilotResult:
handler = _TASK_HANDLERS.get(task_type) entry = _TASKS.get(task_type)
if not handler: if not entry:
return AutopilotResult(run_id=str(uuid.uuid4()), task_type=task_type, mode=mode, return AutopilotResult(run_id=str(uuid.uuid4()), task_type=task_type, mode=mode,
status=RunStatus.FAILED, summary_ar=f"مهمة غير معروفة: {task_type}") status=RunStatus.FAILED, summary_ar=f"مهمة غير معروفة: {task_type}")
handler = entry[0]
unit = AutopilotUnit( unit = AutopilotUnit(
agent_id=agent_id, tenant_id=tenant_id, task_type=task_type, mode=mode, agent_id=agent_id, tenant_id=tenant_id, task_type=task_type, mode=mode,
budget=budget or AutopilotBudget(api_calls=self._policy.max_api_calls, budget=budget or AutopilotBudget(api_calls=self._policy.max_api_calls,
@ -307,10 +296,10 @@ class AutopilotRunner:
if not u or u.status not in (RunStatus.PAUSED, RunStatus.AWAITING_APPROVAL): if not u or u.status not in (RunStatus.PAUSED, RunStatus.AWAITING_APPROVAL):
return None return None
u.status = RunStatus.RUNNING u.status = RunStatus.RUNNING
handler = _TASK_HANDLERS.get(u.task_type) entry = _TASKS.get(u.task_type)
if handler: if entry:
try: try:
await handler(u, self._policy) await entry[0](u, self._policy)
if u.status == RunStatus.RUNNING: if u.status == RunStatus.RUNNING:
u.status = RunStatus.COMPLETED u.status = RunStatus.COMPLETED
except Exception as exc: except Exception as exc:
@ -343,23 +332,17 @@ class AutopilotRunner:
return [r for r in runs if r.tenant_id == tenant_id] if tenant_id else runs return [r for r in runs if r.tenant_id == tenant_id] if tenant_id else runs
def list_supported_tasks(self) -> list[dict[str, str]]: def list_supported_tasks(self) -> list[dict[str, str]]:
return [{"task_type": k, **_TASK_META.get(k, {})} for k in _TASK_HANDLERS] return [{"task_type": k, "name_ar": v[1], "desc_ar": v[2]} for k, v in _TASKS.items()]
@staticmethod @staticmethod
def _summary(u: AutopilotUnit) -> str: def _summary(u: AutopilotUnit) -> str:
if u.status == RunStatus.FAILED: _MAP = {RunStatus.FAILED: lambda: f"فشل: {u.error or 'خطأ غير محدد'}",
return f"فشل التنفيذ: {u.error or 'خطأ غير محدد'}" RunStatus.ABORTED: lambda: "تم إلغاء المهمة",
if u.status == RunStatus.ABORTED: RunStatus.AWAITING_APPROVAL: lambda: f"بانتظار الموافقة على {len(u.pending_approvals)} إجراء",
return "تم إلغاء المهمة" RunStatus.PAUSED: lambda: f"متوقف عند: {u.current_step}"}
if u.status == RunStatus.AWAITING_APPROVAL: if u.status in _MAP: return _MAP[u.status]()
return f"بانتظار الموافقة على {len(u.pending_approvals)} إجراء" e, p = len(u.side_effects), len(u.result_data.get("proposed_actions", []))
if u.status == RunStatus.PAUSED: s = f"اكتمل (ثقة {u.confidence:.0%})"
return f"متوقف مؤقتاً عند: {u.current_step}" if e: s += f"{e} إجراء منفّذ"
effects = len(u.side_effects) if p: s += f"{p} مقترح"
proposed = len(u.result_data.get("proposed_actions", [])) return s
parts = [f"اكتمل (ثقة {u.confidence:.0%})"]
if effects:
parts.append(f"{effects} إجراء منفّذ")
if proposed:
parts.append(f"{proposed} مقترح")
return " ".join(parts)