Skip to content

Agentic Calibration ​

Agentic calibration is the closed loop in which an agent decides the next measurement and QDash performs it, judges it, and records it. The loop runs on the agent side as a plan: a graph of nodes a person approved once, each node one task on one qubit with one candidate parameter. A plan with autonomy: auto is an autonomous plan: after every node the agent evaluates the result with the calibration-evaluation model and follows the task knowledge's next_actions table instead of the plan's fixed edges. The platform contract behind the loop (sessions, actions, gates, commits) is in Agent Platform; the operator's procedure is in Agent Calibration.

qdash-agent run-plan (runs outside QDash, under one approved session) $default · node id + one-shot overrides · $complete · $escalate Plan nodes · on_pass · on_rollback · budget Step runner propose · execute · wait · gate Evaluation planner status → next_actions → bounds next node outcome agent side QDash side propose · execute accepted or rejected commit on pass Session grant · autonomy · version Action authorized · rejected system-single-task update_params=false Candidate gate bounds · quality gates Candidate set one save + audit grant run outside grant or stale version: rejected, the plan escalates out of bounds or missing metric: rollback, not written Task result outputs · figure · r2 writes reads Evaluation run qcal_evaluate · trigger single figure Task knowledge task.md · next_actions reference evaluate task_id status · range · fit next_actions Python client API Prefect deployment Data Outside QDash Rejected
The plan, the step runner, and the evaluation planner run outside QDash. QDash authorises each action, measures without writing, gates the candidate, evaluates the figure against the task knowledge, and commits only the accepted set.

Three loops, one boundary ​

The design keeps three loops apart so that each can be bounded and audited on its own.

LoopWhere it runsDecidesBounded by
StepAgentCalibrationRunner.run_step (client)Whether one measurement produced an accepted candidateThe session grant: tasks, qubits, overrides, quality gates, state version
PlanAgentPlanRunner.run_plan (client)Which node runs nextmax_node_executions, max_node_visits per node, the remaining action budget, the node graph
EvaluationEvaluationPlanner.choose_next (client) with POST /evaluations/runs (API)Which plan node the evaluated status leads tonext_actions, max_rounds_per_target, the session's override bounds

Every decision crosses the boundary as a request QDash can refuse. The agent never writes a parameter, never widens its scope, and never runs a task the plan does not name; the planner returns a node id, not a task. A model's judgement reaches the hardware only through the next_actions table a person wrote into task.md.

Before the first measurement ​

Two checks run before any hardware time is spent, so that a plan fails for a reason a person can read rather than in the task's own validation.

The plan runner validates the graph (unique ids, known targets, no reserved $complete, $default, or $escalate id) and the session policy: the qubit, every task, every candidate parameter, reconfiguration if a node asks for it, and max_node_executions against the actions the session still has. A failure is returned as human_escalation with no action created.

The pipeline preflight in POST /calibration-pipelines/validate covers the agent's other entry point, qdash_run_pipeline: run-parameter names and types, the session-scoped readout_duration, and the calibrated inputs each qubit task needs, including the readout duration the stored Rabi parameters were measured with. See Calibration Pipeline and Parameter Resolution.

One node visit ​

ONE NODE VISIT IN AgentPlanRunner.run_plan next node node id + one-shot overrides Node budget executions < limit visits < max_node_visits Run node carried + node + planner overrides Step outcome pass · rollback go on retry · escalation stop Planner decision $default · node id $complete · $escalate Node rule on_pass · on_rollback else the next node yes retry before an operation exists: same idempotency key, max_pre_dispatch_retries no human_escalation max_node_executions or max_node_visits reached stopped, returned as is retry: an operation exists escalation: rejected action human_escalation $escalate · planner error unknown node · $complete after a rollback $complete Plan ends pass: commit the candidate set rollback: no on_rollback Evaluation planner fills the decision (run-plan --planner evaluation, session autonomy auto) After each node 1. POST /evaluations/runs for the node's task result 2. read status · suggested_range · fit_reliability 3. look the status up in the task's next_actions 4. check overrides against the session bounds 5. check node visits against max_rounds_per_target next_actions entry → decision continue → $default (after pass only) retry → same node, overrides or the midpoint of the suggested range run_task → the plan node that runs that task request_human → $escalate $escalate instead status without an entry same status 3 times · fit unreliable 2 times node past max_rounds_per_target override outside the session bounds evaluation skipped, failed, or timed out
A node visit: budget, run, outcome, planner decision, node rule. Red boxes end the plan for a person; the evaluation planner's rules fill the decision box.

The runner checks the node budget (max_node_executions for the plan, max_node_visits for the node about to run, whichever rule led back to it), then runs the node with three override layers: the values earlier nodes accepted (carried), the node's own parameter_overrides, and the one-shot overrides a planner attached to its last decision. Each visit gets a deterministic idempotency key from the plan prefix, the node index, and the visit count, so a revisit is a new action while a transport retry before dispatch reuses the same one.

The step outcome is one of four transitions:

TransitionMeaningWhat the plan does
passThe candidate passed the session's bounds and quality gatesCarries the value, counts a completed node, asks the planner, then follows on_pass or the next node
rollbackThe candidate was rejected; nothing is carriedAsks the planner, then follows on_rollback or stops
retryNo measurement happened, or QDash timed outRe-runs with the same key only while no operation id exists and max_pre_dispatch_retries allows; otherwise returns
human_escalationThe action was rejected by the policy, or a planner or graph errorReturns at once

The planner's answer is checked before it is used: an unknown node, overrides on a sentinel, a non-finite override, or $complete after a rollback ends the plan as human_escalation. A planner that raises ends it the same way. $default is recorded and the node's own rule applies.

With commit_on_success, a plan that ends in pass commits the latest accepted candidate per parameter as one candidate set: QDash re-resolves each referenced task result, re-runs the gates, and writes the set with one Qubit save and one audit record (POST /agent-sessions/{id}/candidate-set-commits). Any other ending leaves every value uncommitted.

The evaluation planner ​

EvaluationPlanner is the only planner shipped with QDash. It turns the calibration evaluation into a plan decision without adding any judgement of its own:

  1. It creates an evaluation run for the node's task result (trigger: single) and waits for it.
  2. It reads the status question's answer (status, suggested_range) and the fit question's (fit_reliability), as Calibration Evaluation defines them.
  3. It looks the status up in the task's next_actions (GET /tasks/{name}/knowledge).
  4. It checks any override against the session's allowed_overrides and the target node's visits against max_rounds_per_target.

The keys of next_actions are exactly the statuses the evaluation can report for that task: SUCCESS plus one code per bullet under ## Common failures in task.md (Knowledge Layout). CheckRabi's table today reads:

StatusActionEffect in the plan
SUCCESScontinue$default: the node's on_pass or the next node
NO_SIGNALrun_task: CheckChevronJump to the plan node that runs CheckChevron; its accepted qubit_frequency is carried on
LOW_CONTRAST, RAPID_DECAY, FREQUENCY_MISMATCH, BEATING_PATTERNrequest_human$escalate with the entry's note as the reason

A retry entry re-runs the same node with the entry's overrides, or with the midpoint of the evaluation's suggested range when suggested_range_parameter names the parameter. No CheckRabi entry uses it, because the evaluator's range describes the swept pulse duration rather than an input.

The planner stops the plan ($escalate) when the table has no entry for the status, when the same status comes back three times for one node, when the fit was unreliable twice in a row, when a node would exceed max_rounds_per_target, when an override leaves the session bounds, and when the evaluation was skipped, failed, or timed out. Each decision is kept as an EvaluationPlannerTrace and printed by run-plan under evaluations, so a reviewer can record a verdict on every evaluation that steered the run.

Statuses ​

Each layer keeps its own status, and the plan reads them from the outside in.

ObjectValuesSet by
Agent sessionactive, waiting_for_human, completed, expired; cancelled is declared but no route sets itactive on creation; an authorised request_human action sets waiting_for_human, complete_session sets completed; a read after expires_at sets expired
Action decisionauthorized, rejectedThe policy check at proposal time; a rejection is recorded and never executed
Action executionnot_started, queued, then the execution's running, completed, failed, cancelled; failed on a dispatch errorDispatch, then each read syncs the linked execution's status
Candidateaccepted true or false with a reasonThe bounds and quality gates on the task result
Candidate commit, candidate set commitcommitting, committed, failedReserved before the Qubit save, settled after it
Evaluation runrunning, completed, interrupted, cancelledThe API worker; a stale heartbeat marks interrupted
Evaluationpending, running, completed, failed, interrupted, cancelledOne per task result in the run
Step transitionpass, retry, rollback, human_escalationThe step runner, from the action, execution, and candidate above
Plan outcomeThe same four transitions, with node_path, completed_nodes, planner_decisions, and the candidate set commitThe plan runner

The session's autonomy (confirm_each or auto) and max_rounds_per_target are policy, not status: they never change after creation, and run-plan --planner evaluation refuses a session without auto. With that planner, max_rounds_per_target is passed to the runner as max_node_visits, and a plan node that commits or applies on its own is refused, because the planner may escalate after the value is already written; only commit_on_success writes.

What the design does not do yet ​

  • The planner runs only from the Python client; the pi runtime has no tool that starts an autonomous plan.
  • A plan is one qubit; parallel plans over topology groups are not implemented.
  • ConfigureEF ordering is not preflighted: each hardware pipeline step gets a fresh Experiment, so it must sit in the same step as the EF tasks it serves.
  • Task results carry no failure class (platform error, missing prerequisite, measurement failure); the preflight and the evaluation statuses are the only taxonomy.
  • Nothing compares the evaluation's verdicts with a person's; the traces exist so that such a comparison can be built.

Implementation Files ​

  • clients/python/src/qdash/client/services/agent_runner.py: step runner, plan runner, planner protocol and sentinels
  • clients/python/src/qdash/client/services/evaluation_planner.py: evaluation planner, range parsing, traces
  • clients/python/src/qdash/client/services/agent_cli.py: start-session --autonomy, run-plan --planner evaluation
  • src/qdash/datamodel/agent_session.py: session policy, AgentAutonomy, statuses
  • src/qdash/datamodel/task_knowledge.py: NextAction, status_codes(), next_action_problems()
  • src/qdash/api/services/agent_session_service.py: action policy, candidate gates, candidate set commit
  • src/qdash/api/services/calibration_pipeline_preflight.py: pipeline preflight
  • clients/python/tests/test_agent_runner.py, clients/python/tests/test_evaluation_planner.py, tests/qdash/api/services/test_agent_session_service.py

Released under the Apache 2.0 License.