Skip to main content
rLLM’s eval framework is built around two protocols: AgentFlow runs an agent on a task and produces an Episode, and Evaluator scores that Episode. Together they form a clean separation between agent logic and evaluation logic.

AgentFlow

An AgentFlow is any callable that takes a task and returns an Episode. The same flow runs at eval time and at training time — at training, config.base_url points at a model gateway that captures token IDs / logprobs transparently, so your flow code doesn’t change between the two modes.
An AgentFlow may orchestrate one or many agents internally. Each agent contributes one or more Trajectories to the returned Episode. Implementations can provide run (sync) or arun (async) — the runner prefers arun when available.

Task and AgentConfig

The two arguments passed to every AgentFlow call:
Task is pure data. The instruction is rendered ahead of time (from a JSONL row, an instruction.md file, or an instruction.md.tpl template). metadata carries everything the verifier needs — for catalog datasets that’s the source row; for sandbox tasks it’s the parsed task.toml. See Tasks (Harbor-compatible) for the loader and execution pipeline.

Example AgentFlow

A minimal math agent that solves a problem in one turn (see cookbooks/math/math_flow.py for the runnable, async version that ships in the repo):

Evaluator

An Evaluator scores an Episode produced by an AgentFlow. It examines the task and the episode’s trajectories, then returns an EvalOutput with a reward and correctness flag.

EvalOutput

The result of evaluating a single episode:
signals lets an evaluator report multiple dimensions of quality. For example, an IFEval evaluator might return separate signals for instruction-following accuracy and format compliance.

Example Evaluator

A simple exact-match evaluator for math:
In practice you rarely write a class — most rLLM evaluators are plain evaluate(task, episode) -> EvalOutput functions exported from a module under rllm.eval.reward_fns. See Reward functions for the convention and how a benchmark wires one up.

Built-in reward functions

A non-exhaustive selection from rllm.eval.reward_fns:

How they work together

The eval runner connects AgentFlow and Evaluator in a simple pipeline:
1

Load dataset

The runner loads tasks from the dataset catalog or a custom source.
2

Run agent

For each task, the runner calls agent_flow.run(task, config) to produce an Episode.
3

Score results

The runner passes each Episode to evaluator.evaluate(task, episode) to get an EvalOutput.
4

Aggregate

Rewards and signals are aggregated into an EvalResult with overall score, per-example breakdowns, and signal averages.
From the CLI, this entire pipeline runs with a single command:
The CLI resolves the appropriate AgentFlow and Evaluator from the benchmark name, pulls the dataset from HuggingFace, and runs the pipeline.

Connection to training

AgentFlow and Evaluator are designed to be reusable across eval and training. The same flow function that powers rllm eval also powers rllm train — the only thing that changes is which base_url config.base_url points at:
  • During eval, config.base_url points at a regular OpenAI-compatible endpoint
  • During training, the trainer routes config.base_url through a model gateway that captures token IDs and logprobs alongside the chat-completion response, so policy-gradient training has the data it needs without any change to your flow code
  • The Evaluator runs unchanged — its EvalOutput.reward is the per-task reward signal the trainer feeds into advantage computation
  • The Episode structure is identical in both paths, so eval results are directly comparable to training metrics
See cookbooks/ for seven worked examples that train end-to-end with this pattern.