عقد الإجراءات

تعرّف الحزمة adk-action 14 نوعًا من عقد الإجراءات المستخدمة في سير العمل المعتمد على الرسوم البيانية. يمثّل كل نوع من العقدة عملية منفصلة (استدعاء HTTP، تحويل بيانات، فرع شرطي، إلخ) يمكن تركيبها في رسوم بيانية موجهة عبر adk-graph وActionNodeExecutor الخاصة به.

نظرة عامة

تُعد عقد الإجراءات اللبنات الأساسية لرسوم سير العمل المرئية والبرمجية. وهي توفر:

  • عمليات ذات أنواع محددة — 14 نوعًا من العقد تغطي أنماط سير العمل الشائعة
  • StandardProperties — إعدادات مشتركة لمعالجة الأخطاء، والتتبع، وتعيين البيانات
  • استيفاء المتغيرات — صيغة {{variable}} للقيم الديناميكية
  • تكامل الرسم البياني — يُنفَّذ بواسطة adk-graph وActionNodeExecutor الخاصة به

التثبيت

[dependencies]
adk-action = "2.0.0"

# Or specific action features via umbrella crate
adk-rust = { version = "2.0.0", features = ["action"] }

أنواع العقد (14)

العقدةالغرضالفئة
Triggerنقطة الدخول — تبدأ تنفيذ سير العملتحكم
HTTPإجراء طلبات HTTP (GET, POST, PUT, DELETE, PATCH)إدخال/إخراج
Setتعيين القيم لمتغيرات سير العملالبيانات
Transformتحويل البيانات باستخدام التعابير أو الشيفرةالبيانات
Switchالتفرع الشرطي بناءً على التعابيرالتحكم
Loopالتكرار عبر المجموعات أو حتى يتحقق شرطالتحكم
Mergeدمج عدة فروع مرة أخرىControl
Waitإيقاف التنفيذ مؤقتًا لمدة أو حتى حدثControl
Codeتنفيذ تعليمات برمجية عشوائية (Rust; JS/TS غير منفذ)Compute
Databaseالاستعلام عن قواعد البيانات — غير منفذI/O
Emailإرسال رسائل البريد الإلكتروني عبر SMTP — غير مطبقI/O
Notificationإرسال الإشعارات (Slack، webhook، push)I/O
RSSقراءة وتحليل خلاصات RSS/AtomI/O
Fileقراءة الملفات وكتابتها وتحويلهاI/O

يتم التحقق من التوافر عند بناء الرسم البياني

تقبل بعض أنواع العقد إعدادًا وتتحقق منه بينما لا توجد واجهتها الخلفية. يتم رفض هذه الحالات بواسطة StateGraph::compile()، وليس في منتصف التنفيذ:

التهيئةالحالة
Database (any type)مرفوض — لا يوجد برنامج تشغيل مدمج
Email (monitor or send)مرفوض — لم يتم تنفيذ IMAP و SMTP
Code مع language: javascript أو typescriptمرفوض — لا توجد بيئة تشغيل معزولة؛ استخدم rust
Http بدون ميزة action-httpمرفوض — فعّل الميزة

يُسَمّي الرفض العقدة والسبب، بحيث يفشل سير العمل الذي لا يمكن تشغيله أثناء تجميعه بدلاً من أن يفشل بعد أن تكون العقد السابقة قد أحدثت آثارًا جانبية بالفعل.

نفّذ Node::validate على عقدة مخصّصة للمشاركة في الفحص نفسه.

StandardProperties

تحمل كل عقدة إجراء StandardProperties — إعدادًا مشتركًا يتحكم في سلوك التنفيذ:

use adk_action::{StandardProperties, ErrorHandling, RetryConfig};

let props = StandardProperties::builder()
    // Error handling
    .on_error(ErrorHandling::ContinueOnFail)
    .retry(RetryConfig {
        max_attempts: 3,
        wait_between_ms: 1000,
    })

    // Tracing
    .notes("Fetch user profile from API")

    // Callbacks
    .on_success("notify_complete")
    .on_failure("alert_team")

    // Execution
    .timeout_ms(30_000)
    .continue_on_fail(true)

    // Input/output mapping
    .input_mapping("{{trigger.body.user_id}}")
    .output_key("user_profile")

    .build();

StandardProperties الحقول

الحقلالنوعالوصف
on_errorErrorHandlingStop، ContinueOnFail، أو RetryThenFail
retryOption<RetryConfig>محاولات إعادة المحاولة والمهلة بين كل محاولة وأخرى
notesOption<String>وصف قابل للقراءة البشرية للتتبّع
on_successOption<String>عقدة الاستدعاء التي تُنفَّذ عند النجاح
on_failureOption<String>عقدة الاستدعاء التي تُنفَّذ عند الفشل
timeout_msOption<u64>الحد الأقصى لوقت التنفيذ
continue_on_failboolما إذا كانت العقد اللاحقة تنفذ بعد الفشل
input_mappingOption<String>تعبير لتحويل بيانات الإدخال
output_keyOption<String>اسم المتغير لتخزين المخرجات

استيفاء المتغيرات

تدعم عقد الإجراءات بناء الجملة {{variable}} للإشارة إلى حالة سير العمل:

use adk_action::HttpNode;

let node = HttpNode::builder()
    .url("https://api.example.com/users/{{trigger.body.user_id}}")
    .method("GET")
    .headers(vec![
        ("Authorization".into(), "Bearer {{env.API_TOKEN}}".into()),
    ])
    .build();

مصادر المتغيرات

البادئةالمصدرمثال
triggerحمولة عقدة المشغّل{{trigger.body.email}}
envمتغيرات البيئة{{env.DATABASE_URL}}
nodesالمخرجات من العقد السابقة{{nodes.fetch_user.json.name}}
workflowمتغيرات على مستوى سير العمل{{workflow.run_id}}

يستخدم الوصول المتداخل ترميز النقطة: {{nodes.http_1.json.data[0].id}}

أمثلة على العقد

HTTP عقدة

use adk_action::{HttpNode, HttpMethod};

let node = HttpNode::builder()
    .method(HttpMethod::Post)
    .url("https://api.example.com/orders")
    .headers(vec![
        ("Content-Type".into(), "application/json".into()),
    ])
    .body(r#"{"item": "{{trigger.body.item}}", "qty": {{trigger.body.quantity}}}"#)
    .properties(StandardProperties::builder()
        .timeout_ms(10_000)
        .on_error(ErrorHandling::RetryThenFail)
        .retry(RetryConfig { max_attempts: 3, wait_between_ms: 2000 })
        .output_key("order_response")
        .build())
    .build();

عقدة التبديل

use adk_action::{SwitchNode, SwitchCase};

let node = SwitchNode::builder()
    .cases(vec![
        SwitchCase {
            condition: "{{nodes.classify.json.category}} == 'urgent'".into(),
            output: "urgent_path".into(),
        },
        SwitchCase {
            condition: "{{nodes.classify.json.category}} == 'normal'".into(),
            output: "normal_path".into(),
        },
    ])
    .fallback("default_path")
    .build();

عقدة الحلقة

use adk_action::{LoopNode, LoopMode};

let node = LoopNode::builder()
    .mode(LoopMode::ForEach {
        items: "{{nodes.fetch_users.json.users}}".into(),
        item_var: "current_user".into(),
    })
    .body_nodes(vec!["process_user", "save_result"])
    .properties(StandardProperties::builder()
        .notes("Process each user in the list")
        .build())
    .build();

عقدة التعيين

use adk_action::SetNode;

let node = SetNode::builder()
    .assignments(vec![
        ("status".into(), "processing".into()),
        ("started_at".into(), "{{workflow.timestamp}}".into()),
        ("user_email".into(), "{{trigger.body.email}}".into()),
    ])
    .build();

التكامل مع adk-graph

تُنفَّذ عقد الإجراءات بواسطة adk-graph ActionNodeExecutor:

use adk_graph::{Graph, ActionNodeExecutor};
use adk_action::{TriggerNode, HttpNode, SetNode};

// Define nodes
let trigger = TriggerNode::webhook("order_received");
let fetch = HttpNode::get("https://api.example.com/inventory/{{trigger.body.sku}}");
let update = SetNode::new(vec![("available", "{{nodes.fetch.json.quantity}}")]);

// Build graph
let graph = Graph::builder()
    .node("trigger", trigger)
    .node("check_inventory", fetch)
    .node("update_status", update)
    .edge("trigger", "check_inventory")
    .edge("check_inventory", "update_status")
    .build()?;

// Execute
let executor = ActionNodeExecutor::new();
let result = executor.run(graph, initial_context).await?;

تعريف أنواع عقد مخصصة

نفّذ الواجهة ActionNode:

use adk_action::{ActionNode, ActionContext, ActionResult, StandardProperties};
use async_trait::async_trait;

struct CustomNode {
    config: MyConfig,
    properties: StandardProperties,
}

#[async_trait]
impl ActionNode for CustomNode {
    fn node_type(&self) -> &str { "custom" }
    fn properties(&self) -> &StandardProperties { &self.properties }

    async fn execute(&self, ctx: &ActionContext) -> ActionResult {
        let input = ctx.resolve("{{trigger.body.data}}")?;
        // Custom logic...
        Ok(serde_json::json!({ "result": "processed" }))
    }
}

السابق: ← إعادة المحاولة والتأمل | التالي: الإضافات →