변수, 표현식, 트리거
${...} 및 ${{ ... }} 보간 시스템, 크론 및 엔티티 이벤트 트리거, 그리고 다중 실행 연속성 패턴.
워크플로우 YAML은 대부분 정적이지만, 단계 간에 흐르는 값은 그렇지 않습니다. Revka는 런타임 데이터를 문자열 필드에 연결하는 두 가지 보간 시스템과, 워크플로우를 자동으로 실행하는 두 가지 트리거 유형을 제공합니다. 이를 조합하면 상태를 하드코딩하지 않고도 한 실행이 다음 실행으로 이어지도록 만들 수 있습니다.
이 페이지에서는 ${...} 변수 보간, ${{ ... }} 표현식, cron 및 entity-event 트리거 블록, input_map 자동 매핑, 그리고 이 모든 것을 하나로 엮는 다중 실행 연속성 패턴을 설명합니다. 상위 스키마는 워크플로우 YAML 레퍼런스를, 각 단계 본문은 단계 유형 레퍼런스를 참고하십시오.
변수 보간 — ${...}
섹션 제목: “변수 보간 — ${...}”단계의 대부분 사용자 작성 문자열 필드는 ${...} 보간을 지원합니다. 변수는 실행 시점에 현재 워크플로우 상태를 기준으로 확인됩니다. 보간은 에이전트 프롬프트, 셸 명령, Python 인수, 알림 메시지, output 템플릿, 엔티티 게시 필드, resolve의 kind/tag/name_pattern/space, 조건부 조건, 브랜치 값, goto 가드, 이메일 필드에서 동작합니다.
${...} 조회는 항상 텍스트로 렌더링됩니다.
agent: prompt: | Topic: ${inputs.topic} Prior summary: ${resolve_prior.output_data.summary} Run: ${run_id}네임스페이스
섹션 제목: “네임스페이스”| 참조 | 확인되는 값 |
|---|---|
${inputs.name} | 워크플로우 입력 파라미터 |
${trigger.entity_kref} | 트리거한 엔티티의 kref |
${trigger.entity_name} | 트리거한 엔티티의 이름 |
${trigger.entity_kind} | 트리거한 엔티티의 kind |
${trigger.tag} | 트리거한 리비전 태그 |
${trigger.revision_kref} | 트리거한 리비전의 kref |
${trigger.metadata.key} | 트리거한 엔티티의 메타데이터 필드 |
${step_id.output} | 단계의 텍스트 출력 |
${step_id.status} | completed | failed | running | skipped |
${step_id.error} | 실패한 단계의 오류 메시지 |
${step_id.output_data.key} | 단계의 구조화 출력 필드 |
${step_id.files} | 단계가 처리한 파일 목록(쉼표 구분) |
${step_id.agent_id} | 에이전트 ID (에이전트 단계) |
${loop.iteration} | 현재 goto 루프 반복 횟수 |
${env.VAR} | 환경 변수 |
${run_id} | 워크플로우 실행 UUID |
누락된 값
섹션 제목: “누락된 값”첫 번째 실행 패턴이 실패하지 않도록 보간은 의도적으로 너그럽게 동작합니다.
- 확인되지 않은
${step.output_data.key}는""를 반환합니다 — 해당 단계가 아직 실행되지 않았거나, 키가 없거나,resolve단계가found: false를 반환한 경우입니다. - 그 외 확인되지 않은
${...}참조는 리터럴 그대로 남아 실행 진단에 표시되므로, 오타를 쉽게 발견할 수 있습니다.
첫 번째 실행용 resolve 단계에는 fail_if_missing: false를 함께 사용하고, 빈 resolved 필드를 허용하는 프롬프트를 작성하십시오. 아래 연속성 패턴을 참고하십시오.
표현식 보간 — ${{ ... }}
섹션 제목: “표현식 보간 — ${{ ... }}”${{ ... }}는 타입이 있는 표현식 평가기로, conditional 단계의 조건에 사용되는 것과 동일한 안전한 평가기입니다. 단순한 값 조회가 아니라 함수, 산술, 비교, 멤버십 검사, 폴백 로직이 필요할 때 사용하십시오.
${{ ... }} 내부에서는 각 조회를 ${...}로 감싸지 않고 위의 네임스페이스를 직접 참조합니다.
resolve: name_pattern: "daily-${{ lower(inputs.team) }}-*" space: "Revka/${{ lower(inputs.team) }}/Reports"
outputs: next_episode: "${{ int(resolve_cursor.output_data.episode_number) + 1 }}" publish_ready: "${{ review.output_data.score >= 0.8 }}"지원 기능
섹션 제목: “지원 기능”| 기능 | 예시 |
|---|---|
| 비교 | a == b, a != b, score >= 0.8 |
| 불리언 논리 | ok and not blocked, status == 'done' or retry_count > 0 |
| 멤버십 | 'approve' in lower(review.output), review.output contains 'APPROVED' |
| 산술 | int(count) + 1, price * quantity |
| 문자열 헬퍼 | lower(x), upper(x), str(x), format(score, '.2f'), pad(n, 3) |
| 타입 헬퍼 | int(x), float(x), bool(x), len(x) |
| 동등 헬퍼 | eq(a, b) |
| 리스트 / 범위 | range(1, int(inputs.count) + 1) |
동일한 연산자가 conditional 브랜치, goto 가드, parallel/map_reduce 조인 로직에서도 사용됩니다. 이 필드들은 ${{ }} 래퍼 없이 표현식을 직접 받습니다. 예: condition: "review.output_data.score >= 0.8".
트리거
섹션 제목: “트리거”워크플로우는 기본적으로 수동 실행됩니다. 자동 실행을 위해서는 최상위에 triggers: 목록을 추가하십시오. 두 가지 트리거 유형은 같은 목록에 공존할 수 있습니다.
크론 트리거
섹션 제목: “크론 트리거”triggers: - cron: "0 9 * * 1" # every Monday 09:00 timezone: "America/Los_Angeles" # optional IANA timezone (alias: tz)| 필드 | 타입 / 기본값 | 의미 |
|---|---|---|
cron | crontab 문자열 (필수) | 5-필드 crontab, 또는 6/7-필드 cron-crate 형식 |
timezone | IANA 문자열 (선택) | 스케줄 평가에 사용할 타임존; tz를 별칭으로 사용 가능 |
크론 트리거가 있는 워크플로우를 Kumiho에 저장하면(예: 대시보드에서), Revka가 크론 저장소에 스케줄된 작업을 자동으로 등록합니다. 스케줄된 시각에 스케줄러는 POST /api/workflows/run/{name}을 직접 호출합니다.
- 5-필드 표현식은 초(seconds) 필드가 자동으로 앞에 추가되며, 요일 값은 내부 cron crate의 규칙(1 = 일요일)에 맞게 정규화됩니다(0/7 = 일요일).
- 크론 작업은 워크플로우의 생성, 업데이트, 삭제, 사용 중단 시마다 동기화됩니다. 워크플로우를 사용 중단하거나 삭제하면 연관된 크론 작업도 제거됩니다.
- 크론 전용 트리거에는
on_kind/on_tag가 필요하지 않습니다 — 이 필드들은 엔티티 트리거에만 적용됩니다.
엔티티 이벤트 트리거
섹션 제목: “엔티티 이벤트 트리거”엔티티 트리거는 Kumiho에서 revision.tagged 이벤트를 감시합니다. 상위 output 단계가 규칙에 맞는 엔티티를 게시하면 하위 워크플로우가 자동으로 실행됩니다. 이를 통해 워크플로우를 체이닝할 수 있습니다.
triggers: - on_kind: "qs-arc-plan" # entity kind (required) on_tag: "ready" # revision tag (default: "ready") on_name_pattern: "qs-*" # optional glob on entity name on_space: "Revka/WorkflowOutputs/QuantumSoul" # optional space-path prefix input_map: # map trigger data → inputs arc_kref: "${trigger.entity_kref}" arc_name: "${trigger.metadata.arc_name}"필터는 누적(AND) 방식으로 적용됩니다 — 지정된 모든 필터가 일치해야 합니다.
| 필드 | 매칭 동작 |
|---|---|
on_kind | 엔티티 kind의 정확한 일치. 엔티티 트리거에 필수. |
on_tag | 리비전 태그의 정확한 일치. 생략 시 기본값은 ready. |
on_name_pattern | 엔티티 이름에 대한 선택적 글로브 패턴, 예: daily-*. |
on_space | 선택적 space-path 접두사, 예: Revka/Reports. |
input_map 및 자동 매핑
섹션 제목: “input_map 및 자동 매핑”input_map은 트리거 데이터를 하위 워크플로우의 입력으로 변환합니다. 각 키는 입력 이름이고, 각 값은 ${trigger.*} 네임스페이스를 참조하는 ${...} 표현식입니다.
input_map을 생략하거나(또는 필수 입력이 매핑되지 않은 경우), Revka는 자동 매핑을 시도합니다. 트리거한 엔티티의 메타데이터 키가 필수 워크플로우 입력 이름과 일치하면 해당 값이 자동으로 매핑됩니다. 게시하는 output 단계에 metadata_target: item을 설정하면 해당 메타데이터 키를 자동 매핑에 사용할 수 있습니다.
따라서 entity_metadata.arc_name을 포함해 게시하는 상위 단계는, 명시적인 input_map 없이도 하위 arc_name 입력을 충족시킵니다. input_map은 이름이 다르거나 entity_kref처럼 메타데이터가 아닌 필드를 매핑할 때만 명시적으로 사용하십시오.
체이닝 예시
섹션 제목: “체이닝 예시”quantum-soul-arc-room └─ output step publishes: kind=qs-arc-plan, tag=ready └─ event listener matches the trigger on quantum-soul-episode-room └─ quantum-soul-episode-room launches with arc context └─ output step publishes: kind=qs-episode-final, tag=published └─ the next arc-room run resolves this as its cursor다중 실행 연속성 패턴
섹션 제목: “다중 실행 연속성 패턴”이 패턴은 이전 실행 결과를 기반으로 다음 실행을 이어가는 워크플로우를 위한 정규 패턴입니다. 상태를 하드코딩하지 않고도 지난 실행이 무엇을 생성했는지 파악해야 하는 주간 작업 등에 활용할 수 있습니다.
이 패턴은 세 가지 요소로 구성됩니다. 이전 출력을 찾는 resolve 단계(첫 번째 실행에서는 비어 있음), 첫 번째 실행의 기본값을 제공하는 시드 inputs, 그리고 다음 실행이 가져올 새 엔티티를 게시하는 output 단계입니다.
inputs: - name: arc_name default: "awakening-arc-1" # seed for the first run description: Auto-resolved on subsequent runs
steps: # 1. Try to find the previous output (empty on first run) - id: resolve_prior type: resolve resolve: kind: "qs-arc-plan" tag: "ready" fail_if_missing: false # don't fail when nothing exists yet
# 2. Agent uses resolved data OR the seed inputs - id: plan type: agent depends_on: [resolve_prior] agent: prompt: | ## Auto-resolved from last run (empty on first run) Previous arc: ${resolve_prior.output_data.arc_name} Episode range: ${resolve_prior.output_data.episode_range} Continuity: ${resolve_prior.output_data.continuity_context}
## Seed inputs (use when auto-resolved is empty) Arc name: ${inputs.arc_name}
Use auto-resolved values when available; fall back to seeds on first run.
# 3. Publish an entity for the next run to find - id: output type: output depends_on: [plan] output: template: "${plan.output}" entity_name: "qs-arc-${inputs.arc_name}" entity_kind: "qs-arc-plan" entity_tag: "ready" entity_metadata: arc_name: "${inputs.arc_name}" episode_range: "1-8" continuity_context: "${plan.output}"실행 간 동작 방식:
- 첫 번째 실행 —
resolve_prior.output_data.found가false이고 resolved 필드는""로 렌더링됩니다. 에이전트는 시드 입력으로 폴백하고,output단계가 첫 번째 엔티티를 게시합니다. - 두 번째 실행 —
resolve_prior가 첫 번째 실행의 엔티티를 찾습니다. 에이전트는 resolved 연속성 컨텍스트를 사용하고,output은 세 번째 실행을 위한 새 엔티티를 게시합니다. 이후 반복됩니다.
핵심 규칙
섹션 제목: “핵심 규칙”-
비어 있을 수 있는
resolve단계에는 항상fail_if_missing: false를 설정하십시오. -
첫 번째 실행을 위해
inputs에 적절한default값을 지정하십시오. -
프롬프트는 자동 resolved 섹션과 시드 입력 섹션 모두를 포함하도록 구성하고, resolved 값을 우선하도록 에이전트에게 지시하십시오.
-
다음 실행에 필요한 모든 정보를
entity_metadata에 저장하십시오. -
게시 단계의
output.metadata_target과 읽기 단계의resolve.metadata_source를 맞추십시오.metadata_target: item은 트리거 자동 매핑을 활성화하며,metadata_target: revision은metadata_source를 설정하지 않아도resolve단계가 메타데이터를 읽을 수 있게 합니다(metadata_source의 기본값은revision). -
output단계와resolve단계 간에entity_kind+entity_tag가 일치하도록 하여 검색이 실제로 엔티티를 찾을 수 있게 하십시오.