작업 노드

adk-action 크레이트는 그래프 기반 워크플로에서 사용되는 14가지 작업 노드 유형을 정의합니다. 각 노드 유형은 개별 작업(HTTP 호출, 데이터 변환, 조건부 분기 등)을 나타내며, adk-graphActionNodeExecutor을 통해 방향성 그래프로 구성할 수 있습니다.

개요

작업 노드는 시각적 및 프로그래밍 방식의 워크플로 그래프를 구성하는 기본 요소입니다. 다음을 제공합니다.

  • 타입이 지정된 작업 — 일반적인 워크플로 패턴을 다루는 14가지 노드 유형
  • StandardProperties — 오류 처리, 추적 및 데이터 매핑을 위한 공유 구성
  • 변수 보간 — 동적 값을 위한 {{variable}} 구문
  • 그래프 통합adk-graphActionNodeExecutor에서 실행

설치

[dependencies]
adk-action = "2.1.0"

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

노드 유형 (14가지)

노드목적범주
Trigger진입점 — 워크플로 실행 시작제어
HTTPHTTP 요청(GET, POST, PUT, DELETE, PATCH) 생성입출력
Set워크플로 변수에 값 할당데이터
Transform표현식 또는 코드를 사용하여 데이터 변환데이터
Switch표현식에 따른 조건부 분기제어
Loop컬렉션을 반복하거나 조건이 충족될 때까지 반복제어
Merge여러 분기를 다시 하나로 결합제어
Wait일정 시간 동안 또는 이벤트가 발생할 때까지 실행 일시 중지제어
Code임의의 코드 실행 (Rust; JS/TS는 구현되지 않음)계산
Database데이터베이스 쿼리 — 구현되지 않음입출력
EmailSMTP을 통해 이메일 전송 — 구현되지 않음입출력
Notification알림 전송(Slack, 웹훅, 푸시)입출력
RSSRSS/Atom 피드 읽기 및 구문 분석입출력
File파일 읽기, 쓰기 및 변환입출력

그래프가 빌드될 때 가용성이 확인됩니다

일부 노드 유형은 구성을 허용하고 검증하지만 해당 백엔드는 존재하지 않습니다.
이러한 노드는 실행 도중이 아니라 StateGraph::compile()에 의해 거부됩니다:

구성상태
Database (모든 유형)거부됨 — 통합된 드라이버가 없음
Email (모니터링 또는 전송)거부됨 — IMAP 및 SMTP가 구현되지 않음
Codelanguage: javascript 또는 typescript거부됨 — 샌드박스 처리된 런타임이 없습니다. rust를 사용하세요
action-http 기능이 없는 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, or RetryThenFail
retryOption<RetryConfig>재시도 횟수 및 재시도 간 지연 시간
notesOption<String>추적을 위한 사람이 읽을 수 있는 설명
on_successOption<String>성공 시 실행할 콜백 노드
on_failureOption<String>실패 시 실행할 콜백 노드
timeout_msOption<u64>최대 실행 시간
continue_on_failbool실패 후 다운스트림 노드가 실행되는지 여부
input_mappingOption<String>입력 데이터를 변환하는 표현식
output_keyOption<String>출력을 저장할 변수 이름

변수 보간

Action 노드는 워크플로 상태를 참조하기 위한 {{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-graphActionNodeExecutor에 의해 실행됩니다:

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" }))
    }
}

이전: ← 재시도 및 성찰 | 다음: 플러그인 →