Skip to main content
Some agent tasks have a clear definition of “done” that the model alone cannot reliably hit on the first try: a haiku in the right syllable pattern, a refactor with all tests passing, a report that hits every required section. RubricMiddleware lets you declare what done looks like as a rubric and have the agent self-evaluate and iterate until the rubric is satisfied (or a configured maximum iteration cap is hit). When the deep agent finishes reasoning and has an output, a separate grader sub-agent reviews the transcript against the rubric. If the grader returns needs_revision, its feedback is injected back into the conversation and the agent runs again. The loop terminates on satisfied, max_iterations_reached, failed, or grader_error.
RubricMiddleware is in beta. The API may change in the future.

Rubric verdicts

Every grader pass produces one of five verdicts:

Configuration

Example: grading a lipogram with a custom tool and prompt

The following example builds a deep agent that writes a short paragraph about the ocean without using the letter “e”. The rubric asks for zero instances of “e”, at least two vivid sensory details, and a 3-4 sentence length.
expand
The rubric field on input state is the trigger. Each grader iteration is delivered to on_evaluation as a RubricEvaluation dictionary containing the verdict, explanation, and per-criterion gaps. When no rubric is supplied on input state, the middleware does not run.

Observing iteration progress

Run the agent with agent.stream(..., stream_mode="custom") instead of agent.invoke(...) to receive each grader event as it fires. The same RubricEvaluation payload is also delivered synchronously to the on_evaluation callback after each grader pass, and the full history is reachable through agent.get_state(config).values on a checkpointed thread.

Rubric persistence

A single agent.invoke() call runs the rubric loop to completion and returns with a terminal verdict: satisfied, failed, or max_iterations_reached. Rubrics carry over to follow-up invocations only if a checkpointer is attached and the same thread_id is passed alongside the invocation. In these cases, the same rubric persists across future invoke calls until the user passes a new one in. Interrupts (KeyboardInterrupt, asyncio.CancelledError) propagate out of agent.invoke() uncaught. On a checkpointed thread, the next invocation with the same rubric resumes the in-flight grading run.