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 (
itemabove) is visible only inside the map body limit Ncaps 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 raisesLimitExceededErrorduration: wall clock; exceeding raisesWorkflowTimeoutError
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)