A composite evaluator runs several sub-checks against the same example and combines their scores into one number. Reach for it when “quality” depends on multiple aspects — correctness, format, conciseness, citations — and you want a single value to compare runs by, plus a breakdown to debug them.
The example below mixes:
- An LLM judgment for correctness, built with
arize-phoenix-evalsClassificationEvaluator. - A deterministic code check for format — a regex for a citation tag at the end of the answer.
The score is the weighted average; every sub-score and the LLM’s reasoning land in the explanation so you can audit how the final number was built.
Each axis runs independently — some can be LLM-judged, others pure code — and their scores blend into one number you can rank runs by.
Code#
Sandbox dependencies — paste into the sandbox configuration’s Dependencies field, one package per line:
The Python prompt template uses f-string-style {variable} placeholders; the TypeScript variant uses Mustache-style {{ variable }} — that difference is in arize-phoenix-evals / @arizeai/phoenix-evals themselves, not in your evaluator.
Sandbox dependencies — paste into the sandbox configuration’s Dependencies field, one package per line:
Input mapping#
| Parameter | Bind to |
|---|---|
output | The model output to grade, usually output. |
reference | The ground-truth answer, usually reference. |
If you add more sub-scores (e.g. a conciseness check that needs the original input), expose them as new parameters here.
Output configuration#
Continuous score in the range 0.0 to 1.0 (matches the choice scores you configured on each sub-evaluator). Optimization direction: maximize.
Runtime requirements#
| Setting | Value |
|---|---|
| Sandbox | A hosted backend that matches your language. Python: E2B, Daytona — Python, Vercel Sandbox — Python, or Modal. TypeScript: Daytona — TypeScript or Vercel Sandbox — TypeScript (the local Deno sandbox is --no-npm and cannot install the npm packages). |
| Dependencies | Python: arize-phoenix-evals, openai. TypeScript: @arizeai/phoenix-evals, @ai-sdk/openai, ai. |
| Internet access | Required — the LLM sub-score calls api.openai.com. |
| Environment variables | OPENAI_API_KEY — set as a secret reference to a key in Settings → Secrets. |
arize-phoenix-evals pulls in pydantic and the LLM provider SDKs you use. Cold-installing it can take 20–60s on a hosted sandbox — bump the configuration’s Timeout accordingly, and re-use the same configuration across runs so the provider can warm-cache the environment.
Variants#
Tune the weights or add more axes#
The WEIGHTS dict is the only knob — push correctness toward 1.0 for a near-pure correctness signal, or add a third axis (e.g. tone, length, safety) by appending a new ClassificationEvaluator and another entry in the dict. Each new LLM-judged sub-score adds one more API call per example, so weigh latency and cost when stacking too many axes.
All-code composite (no LLM)#
If every sub-check is deterministic, drop phoenix.evals entirely — the evaluator runs in the in-process WebAssembly or Deno sandbox with no dependencies, no network, and no API key. Useful for cheap multi-rule checks: “has citation tag”, “ends with period”, “under 500 tokens”.
All-LLM composite (no code rules)#
Replace the format regex with a second ClassificationEvaluator for conciseness, tone, or whatever other axis you care about. Every sub-score becomes a judge call, so latency and cost scale linearly with the number of axes.
Related#
- LLM Jury — instead of combining different axes of one judgment, combine the same judgment from multiple LLMs.

