- Critics (step-level validators) validate each step after
reduce()runs. They can allow the run to continue, force a stop, or trigger a retry of the current step. - Stop criteria (loop-termination rules) are checked after critics pass. They evaluate state and runtime metrics to decide whether to end the loop.
Critics
A critic receives the current state, the decision (the model’s structured output for a step) taken, and the action (the tool call dispatched based on the decision) results, then returns a structured verdict dict.The Critic contract
Supported critic actions
Any
action value other than "stop" or "retry" is treated as "continue".
Adding critics to a run
Pass a list ofCritic instances to agent.run():
Writing a custom critic
step.critic_outputs and visible in the qita board Critic section for each step.
Stop criteria
Stop criteria are evaluated after every step’s critic pass. Each criterion receives the current state, the step count, and a runtime info dict (withelapsed_seconds).
The StopCriteria contract
Built-in criteria
QitOS ships four built-in criteria inqitos.engine.stop_criteria:
FinalResultCriteria (default)
Stops when state.final_result is set to a non-empty string. It is the only criterion used when you do not pass stop_criteria to the Engine.
MaxStepsCriteria
Stops when the step count reaches max_steps. The Engine applies this automatically from the RuntimeBudget — you rarely need to instantiate it manually.
MaxRuntimeCriteria
Stops when wall-clock time exceeds a threshold.
StagnationCriteria
Stops when state has not changed for max_stagnant_steps consecutive steps. Uses a signature function to detect change (defaults to checking final_result and phase).
MaxTokensCriteria
Stops when cumulative token usage exceeds a budget. The Engine passes total_tokens in runtime_info on each step, so this criterion tracks real token consumption across the entire run.
Passing criteria to a run
Writing a custom criterion
Budget-based stopping with TaskBudget
For structured task definitions, useTaskBudget to express all three budget dimensions together:
TaskBudget values to its internal RuntimeBudget before the run starts, overriding any constructor defaults.
StopReason values
StopReason is a string enum in qitos.core.errors. The value is written to state.stop_reason at the end of every run.
Check
result.state.stop_reason after a run to distinguish a successful completion from a budget exhaustion:
