Skip to main content
Every step the Engine takes flows through a pipeline: decide, act, reduce, then critic. The critic sits between reduce and check_stop, giving you a structured hook to inspect the agent’s latest decision and its results, assign a quality score, and — when necessary — force a retry or halt execution. This tutorial covers the full critic API: the CriticResult contract, built-in critics, the @critic decorator, instruction and state patches, and composing multiple critics together.

Step 1: The CriticResult Contract

Every critic returns a CriticResult — a dataclass that tells the Engine what to do next. Illustrative fragment (not a standalone program; use the complete example linked on this page).
The fields: The three actions control the loop:
  • "continue" — proceed to the next step as normal.
  • "stop" — halt the Engine immediately. The state’s stop reason is set to StopReason.CRITIC_STOP.
  • "retry" — discard the current step and re-run decide. Any instruction_patch or state_patch is applied before the retry.
A minimal continue result: Illustrative fragment (not a standalone program; use the complete example linked on this page).
A retry with an instruction patch: Illustrative fragment (not a standalone program; use the complete example linked on this page).
A hard stop: Illustrative fragment (not a standalone program; use the complete example linked on this page).

Step 2: Using Built-in Critics

QitOS ships with ready-made critics in qitos.kit.critic.

PassThroughCritic

The simplest critic — always continues with a perfect score. Illustrative fragment (not a standalone program; use the complete example linked on this page).
This is the default when you create an Engine without specifying critics. It is useful as a no-op placeholder or as part of a composed chain where later critics do the real work.

SelfReflectionCritic

Inspects tool results for errors and retries automatically up to a configurable limit. Illustrative fragment (not a standalone program; use the complete example linked on this page).
Behavior:
  • If any tool result contains an {"error": ...} key and retries have not been exhausted, it returns action="retry" with score=0.2.
  • If errors persist beyond max_retries, it returns action="stop" with score=0.0.
  • If no errors are found, it returns action="continue" with score=1.0.
Pass it to the Engine: Illustrative fragment (not a standalone program; use the complete example linked on this page).

ReActSelfReflectionCritic

A richer variant designed for ReAct agents. On error, it builds a structured reflection note describing the failed action and the observed error, then appends it to the state’s metadata so the LLM can learn from the failure on the next retry. Illustrative fragment (not a standalone program; use the complete example linked on this page).

Functional Equivalents

Each built-in critic also ships as a decorated function: Illustrative fragment (not a standalone program; use the complete example linked on this page).

Step 3: Writing a Custom Critic with the @critic Decorator

The @critic decorator converts any plain function into a Critic instance. The function receives (state, decision, results) and can return a quick shorthand or a full CriticResult. Illustrative fragment (not a standalone program; use the complete example linked on this page).
Quick-return shorthands: A bare decorator (no arguments): Illustrative fragment (not a standalone program; use the complete example linked on this page).
With keyword arguments — set a custom name and default score: Illustrative fragment (not a standalone program; use the complete example linked on this page).
When the function does not set a score explicitly, the decorator’s score parameter is used as the default. In the example above, a "continue" return gets score=0.8 instead of the usual 1.0. Wire the critic into the Engine: Illustrative fragment (not a standalone program; use the complete example linked on this page).

Step 4: Critic with Instruction and State Patches

When a critic returns action="retry", it can guide the next iteration in two ways:
  • instruction_patch — a string appended to the agent’s system prompt, giving the LLM additional guidance.
  • state_patch — a dictionary whose keys and values are merged into the agent’s state object via setattr.
These are applied before the Engine calls decide again, so the LLM and the state are already updated when the retry begins. Illustrative fragment (not a standalone program; use the complete example linked on this page).
Using state_patch to inject tracking data: Illustrative fragment (not a standalone program; use the complete example linked on this page).
You can also use the tuple shorthand with an instruction patch: Illustrative fragment (not a standalone program; use the complete example linked on this page).

Step 5: Combining Critics

The Engine accepts a list of critics. They are evaluated in order, and the first non-continue result wins. This lets you stack critics from strictest to most permissive. Illustrative fragment (not a standalone program; use the complete example linked on this page).
Wire them into the Engine — safety first, then format, then the built-in self-reflection as a fallback: Illustrative fragment (not a standalone program; use the complete example linked on this page).
With this ordering:
  1. If safety_gate returns "stop", the Engine halts immediately. Later critics are never called.
  2. If safety_gate returns "continue" but format_check returns "retry", the Engine retries with the instruction patch.
  3. If both return "continue", the SelfReflectionCritic gets a chance to catch tool errors.
This layered approach keeps each critic small and focused, while the ordering gives you full control over priority.

Hooks Lifecycle

Understand the full Engine event lifecycle that critics participate in.

Agent Module

Deep dive into the AgentModule interface that critics inspect.

Critics and Stop Criteria

How critics interact with stop criteria and retry budgets.