Skip to main content
A Jobs object combines a survey with agents, scenarios, and models. Calling by() constructs the execution specification; calling run() executes it. Think of a job as the complete set of interviews you want to collect. Each interview runs the survey with one agent, one scenario, and one model and produces one result record containing its answers. EDSL runs the combinations for you, so you can compare conditions without writing a separate loop for each one.

Construct a job

This example crosses four respondent profiles with two message conditions. It constructs eight interviews per model and iteration without making model calls.
Before execution, choose a model, inspect the prompts with job.by(model).prompts(), and estimate costs. A ModelList expands the job across the selected configurations.

Execute and inspect

After configuring credentials and model access:
This step makes model calls and can incur costs. Use disable_remote_inference=True for local orchestration; provider calls may still be remote. Use a test model for an offline smoke test as shown in the Run your first survey tutorial. With one model and iteration, this example returns eight rows. Each row contains a profile’s clarity rating for one message. The selected columns retain the condition and experience level needed to compare those ratings. See Results for filtering, grouping, and exporting them.

Background runs and persistence

Save a job as an .ep package to retain the full run specification. Use Background jobs for long remote work and the CLI Guide to submit, poll, and retrieve results from scripts or coding agents.

Pair inputs or specify assignments

By default, by() crosses agents, scenarios, and models. Use zip_assign() when each agent should receive the scenario at the same position:
The paired collections must have equal lengths by default (strict=True). Each pair is still crossed with the remaining models. job.assign([{"agent": 0, "scenario": 1, "model": 0}, ...]) instead specifies exact combinations using zero-based indices into the job’s component lists. For a condition over combinations, use job.include_when("{{ agent.role == scenario.required_role }}"). The named fields must exist in your inputs. Calling assign() or zip_assign() replaces an inclusion expression; calling include_when() replaces an assignment plan. Inspect job.interviews() after choosing the design and before running it.

Reuse a cache or run asynchronously

job.using(cache) or job.using_cache(cache) attaches a Cache to the job; passing cache=cache to run() is the explicit per-run alternative. survey.using(cache) and question.using(cache) construct Jobs with that cache attached. In an asynchronous function or a notebook that supports top-level await, use await job.run_async(...). It accepts run options such as cache, n, disable_remote_inference, and disable_remote_cache. Background submission is a different workflow: see Background jobs.

Common methods

Execution options

Pass these keywords directly to job.run(). A real model run can incur costs; review model access and costs first. For a small local run using the eight combinations constructed above:
n=2 repeats each of the eight combinations twice, producing 16 interview records with fixed test answers here. It does not create new profiles or conditions. Select iteration alongside the answers when comparing repetitions. Use background jobs for submission and later retrieval, and remote execution for hosted execution settings.

Package a reusable workflow

A Macro combines an input survey, a job that uses those inputs as scenario fields, and named output formats. Use one when other users should supply a few parameters to a repeatable EDSL workflow.
Construction does not execute the job. The test model demonstrates the structure; replace it with your selected provider model for actual feedback. Then supply values keyed by the input survey’s question names:
macro.output() returns a MacroRunOutput; accessing a named format applies the corresponding OutputFormatter to its Results. Provider execution requires credentials and may incur costs. macro.by({"text": "..."}).run() and macro.run(text="...") are parameter-setting shortcuts using the default execution settings; use output() when specifying execution options. macro.with_output_formatter("rows", OutputFormatter(output_type="ScenarioList").select("answer.feedback").to_scenario_list()) returns a macro with another named output. A macro instance can reuse previously generated results for the same prepared job, so repeated calls are not automatically independent model draws. For sharing, macro.push(visibility="private") uploads the workflow; Macro.pull(identifier) retrieves a full macro when you have access. Macro("owner/alias") creates a client for a deployed macro and executes it remotely when called. Access to a deployed macro and its underlying definition can differ.