Skip to main content

parallel & parallel map

Two concurrency primitives, both executed deterministically by the runtime: what runs concurrently is agent calls, never control flow.

parallel: branch concurrency

let parts = parallel {
condensed = agent(ops.main) { task "..." expect Condensed timeout 5m }
actions = agent(ops.main) { task "..." expect ActionItem[] timeout 5m }
}
// parts.condensed : Condensed
// parts.actions : ActionItem[]
  • Branches evaluate concurrently with barrier semantics: if any branch fails, the whole parallel fails
  • The result type is an object { branchName: branchType } — fields are accessed directly, and the whole value can be assigned to a structurally identical named type
  • Branch names are unique (duplicate-branch); branches cannot see each other

Use it for independent outputs of one phase (summary + action items; style + facts + compliance) that are logically parallel and mutually independent.

parallel map: batch concurrency

let verdicts = parallel map input.items as item limit 6 {
agent(moderator) {
task "Review one piece of content against policy"
input { item: item }
expect ModerationVerdict
timeout 90s
}
}
// verdicts : ModerationVerdict[]
  • The source must be an array (parallel-map-source-not-array)
  • The loop variable (item above) is visible only inside the map body
  • limit N caps concurrency for this map (positive integer); unbounded by default, still constrained by workflow/host limits
  • Result order matches input order regardless of completion order
  • Typing: T[] → U[] (U being the body type, usually the expect type)

Use it for per-item processing over batches (emails, resumes, invoices, translation sections, competitor lists).

The map body is "one expression"

The body is a single expression (usually agent(...)) — not a statement block: no if/require inside. That is design, not omission:

  • Branching belongs in the agent's task and expect (let the model act on the input), or upstream by splitting into two maps with an if
  • Structured post-processing (uniqueness, counts, ranges) happens in the stage after the map
stage classify -> ClassifiedEmail[] {
let items = parallel map input.emails as item limit 4 {
agent(assistant) { task "..." expect ClassifiedEmail }
}
require unique(items[*].id) else fail "ids must stay unique" // after the map
return items
}

The effective concurrency ceiling

The minimum of three layers:

effective = min(host.maxConcurrency, workflow.limits.concurrency, map limit)

Enforced by a host-side semaphore. A map limit lets you tighten the valve for heavy work (e.g. web research) without loosening the whole workflow's budget.

Budget interaction

Concurrent structures remain subject to limits:

  • agent_runs: all branches/map items count against the same budget — exceeding raises LimitExceededError
  • duration: wall clock; exceeding raises WorkflowTimeoutError

Estimate agent_runs by the upper bound of items: an input of [1..50] × one call per item needs at least 50, plus the non-map calls.

Pattern: staged maps

For a judge-then-process pipeline, chain two stages:

stage audit -> ArticleVerdict[] { // judge: concurrent reads
parallel map input.articles as item limit 4 {
agent(curator) { tools [read_file, grep] expect ArticleVerdict }
}
}

stage rewrite after audit -> RewriteOutcome[] { // act: concurrent writes, tighter
parallel map audit as item limit 2 {
agent(curator) {
tools [read_file, write_file]
write input.write_scope
expect RewriteOutcome
}
}
}

Between the two maps you can assert (e.g. unique(outcomes[*].id)) — deterministic checks that could not live inside a map.

Next steps

  • verify — how concurrent results get judged
  • Examples: content-moderation · translation-flow · competitive-analysis (see the examples page)