Skip to main content
DocsBuild WorkflowVariables And Expressions

Variables And Expressions

Workflow-level parameters, upstream step outputs, classifier routing, and credential references for moving data between steps.

All dynamic values in Braidrun use "variable references". The syntax has only one form: double curly braces.

yaml
{{var:name}}                     # workflow 级变量
{{steps.fetch.data.user.name}}   # 上游步骤的 JSON 输出
{{classifier.category}}          # 分类器路由结果
{{credentials.openai_api_key}}   # 凭据库里的密钥

Four Sources Of Variables

1. var: — Workflow Parameters

The variables / params section at the top of the workflow, or the input of the schedule / webhook preset.

yaml
variables:
  target_date:
    type: string
    default: "yesterday"
  top_n:
    type: number
    default: 10

steps:
  - id: fetch
    type: code
    code: |
      return { date: "{{var:target_date}}", top: {{var:top_n}} };

2. steps.id.* — Output From Upstream Step

There is a structured output after each step is run. The most commonly used subfields:

  • steps.id.output — Plain text or raw return value.
  • steps.id.data — Structured JSON after extract.
  • steps.id.artifacts[0].url — Product file link.
  • steps.id.tokens / cost / duration_ms — Execution statistics.
yaml
steps:
  - id: fetch
    type: code
    extract:
      articles: $.items
      total: $.meta.total

  - id: summarize
    type: single
    agent: writer
    task: |
      我有 {{steps.fetch.data.total}} 条新闻,这是第一条:
      {{steps.fetch.data.articles[0].title}}

3. classifier.* — Routing Results Of Classifier

The result of the previous classifier step is used as a first-level reference for condition / branching.

yaml
steps:
  - id: triage
    type: classifier
    agent: planner
    task: "{{var:user_message}}"
    categories: [billing, technical, other]

  - id: respond
    type: single
    condition: "{{classifier.category}} == 'billing'"
    agent: writer
    task: "客户关于账单的问题是:{{var:user_message}}"

Equivalent to steps.<classifier_step_id>.category - just shorter and more semantic.

4. credentials.* — Credential Reference

Resolution link: User namespace → Team namespace → System namespace; see details Credentials.

Jsonpath Expression

Simple path syntax can be written in curly brackets - basically compatible with a subset of JSONPath:

  • {{steps.fetch.data.articles[0].title}} — array index
  • {{steps.fetch.data.articles[*].title}} — All elements of the array (return array)
  • {{steps.fetch.data.articles[?(@.score>5)]}} — Conditional filtering
  • {{steps.fetch.data | length}} — filter:length / upper / lower / default / trim

Extract: Structure the Step Output

The data returned by LLM or code often needs to "pick out a few fields" for downstream use. Use extract:

yaml
- id: fetch_user
  type: code
  code: |
    const res = await fetch('https://api.example.com/me');
    return await res.json();
  extract:
    name: $.profile.fullName
    company: $.profile.org.name
    is_admin: $.permissions[?(@=='admin')] | length > 0

After that steps.fetch_user.data.name / .company will be available directly.

Condition: Conditional Execution

Any step can have a condition - when the expression is false, this step is skipped (the execution status is marked SKIPPED).

yaml
- id: summarize
  type: single
  agent: writer
  condition: "{{classifier.category}} == 'ai' && length({{steps.fetch.data.articles}}) > 0"
  task: "..."

Supported operators:

  • Compare:== != < > <= >=
  • Logic:&& || !
  • String:contains(x, y) startsWith(x, y) endsWith(x, y) matches(x, /re/)
  • Array:length(x) any(list, cond) all(list, cond)
  • Short judgment:isEmpty(x) isNotEmpty(x)
Condition Skipped Vs Failed

SKIPPED is "normal and not running" and can be used downstream. {{steps.x.status}} == 'SKIPPED' detection. FAILED is a runtime error and will be transmitted to execution.status. Don't mix the two.

Retry: Retry Strategy

yaml
- id: fetch
  type: code
  code: "..."
  retry:
    max_attempts: 3
    backoff: exponential     # fixed | linear | exponential
    initial_delay_ms: 1000
    max_delay_ms: 30000
    retry_on: [network, rate_limit, http_5xx]

Only retry on transient (network/speed limit/5xx) errors. Business exceptions (condition failure, LLM rejection) will not be retried.

Aggregate: Merge Multiple Inputs

Use aggregate when a step collects data from multiple upstream sources. For example, summarize the comments after group_chat:

yaml
- id: combine
  type: code
  aggregate:
    comments: concat_lines({{steps.group_chat_1.messages}})
    total_tokens: sum({{steps.*.tokens}})
  code: "return { comments, total_tokens };"

Available aggregate functions: concat/concat_lines/json_array/join(sep)/first/last/max/min/avg.

DTO Naming Mapping

Use snake_case (group_chat, agent_based) for YAML and camelCase (groupChat, agentBased) for JSON API/DTO. The platform automatically converts in both directions, you don't need to worry.

Common Pitfalls

  • Reference to a step that has not been run — If condition skips the upstream step, downstream references to it will get null/undefined. Use | default(...) to find out.
  • Large fields are directly plugged into taskstask: "{{steps.x.output}}" If the upstream output is 100KB, LLM will burst the token. First extract and compress before transmitting.
  • YAML quotation marks inside — Expressions containing colons or special characters should be enclosed in double quotes: condition: "{{x}} == 1"