Add a new built-in (pre-built) LLM classification evaluator to the Phoenix evals libraries, and optionally promote it to a server-side dataset evaluator. Use when working in the Phoenix repo and asked to add a new built-in eval, pre-built metric, builtin evaluator, or LLM-as-a-judge template that ships with arize-phoenix-evals / @arizeai/phoenix-evals, or to promote an existing one to the dataset evaluators UI. Do not use for writing a user's own custom evaluator for their app (use phoenix-evals).
75
94%
Does it follow best practices?
Run evals on this skill
Adds up to 20 points to the overall score
Passed
No findings from the security scan
A built-in evaluator is a YAML config (source of truth) that gets compiled into Python and TypeScript code, wrapped in evaluator classes, benchmarked, and documented. The whole pipeline is linear — follow these steps in order.
Before writing anything, clarify with the user:
{{input}}, {{output}}, {{reference}}, {{tool_definitions}}). If the user is vague, ask follow-up questions — the placeholders are the contract between the evaluator and the caller.labels, possibly substitutions) and which docs you write in Step 8.Every config in prompts/classification_evaluator_configs/ ships in the client-side libraries (phoenix.evals.metrics in Python, @arizeai/phoenix-evals in TypeScript). Users call those from their own code against traces, dataframes, or experiments.
A promoted dataset evaluator is the subset that also makes sense to run inside the Phoenix server on datasets and experiments. Adding the promoted_dataset_evaluator label is what promotes it. The UI fetches configs filtered by that label (classificationEvaluatorConfigs(labels: ["promoted_dataset_evaluator"]) in AddEvaluatorMenu.tsx and DatasetEvaluatorsPage.tsx), so a labeled config shows up as an LLM Evaluator Template when a user adds an evaluator to a dataset. It then runs automatically on that dataset's experiments.
Promote an evaluator when all of these hold:
substitutions), without user code.Leave it library-only when it needs data the server cannot map, such as retrieved documents passed per call or a whole-session transcript, or when it is mainly a production or trace-level monitor.
Don't hardcode which evaluators are promoted; check the current set with:
grep -l promoted_dataset_evaluator prompts/classification_evaluator_configs/*.yamlCurrent examples to learn from:
CORRECTNESS (simple text, no substitutions), HALLUCINATION (output substituted with output_with_tool_calls), TOOL_SELECTION / TOOL_INVOCATION (structured tool data via substitutions).FAITHFULNESS and DOCUMENT_RELEVANCE (built around retrieved {{context}} / {{document_text}} from RAG spans), USER_FRICTION (judges a {{user_message}} against a {{conversation}}).Create prompts/classification_evaluator_configs/{NAME}_CLASSIFICATION_EVALUATOR_CONFIG.yaml.
Read an existing config to match the current schema. Start with CORRECTNESS_CLASSIFICATION_EVALUATOR_CONFIG.yaml for a simple example, or TOOL_SELECTION_CLASSIFICATION_EVALUATOR_CONFIG.yaml if your evaluator needs structured span data.
choices — Maps label strings to numeric scores. For binary evaluators, use positive/negative labels (e.g., correct: 1.0 / incorrect: 0.0). The labels you pick here flow through to the Python class, TS factory, and benchmarks.
optimization_direction — Use maximize when the positive label is the desired outcome (most evaluators). Use minimize only if the metric measures something undesirable (e.g., hallucination). This affects how Phoenix displays the metric in the UI.
labels — Optional list. Add promoted_dataset_evaluator only if you decided in Step 0 to promote it (see above).
substitutions — Only used by the server, so only relevant for promoted evaluators. Each maps a template placeholder to a formatter in prompts/formatters/server.yaml that renders structured experiment data (tool definitions, tool calls, message arrays) into text. Read that file to see which formatters exist, and copy the pattern from TOOL_SELECTION or HALLUCINATION. Evaluators whose placeholders are plain text fields (input, output, reference) don't need substitutions.
<context>, <output>) for clear data formatting{{placeholder}} (Mustache syntax) for template variablesmake codegen-promptsThis generates code in three places:
packages/phoenix-evals/src/phoenix/evals/__generated__/classification_evaluator_configs/ (Python)src/phoenix/__generated__/classification_evaluator_configs/ (Python, server copy)js/packages/phoenix-evals/src/__generated__/default_templates/ (TypeScript)It also copies prompts/formatters/server.yaml into src/phoenix/server/api/helpers/substitutions/. The server reads that copy for promoted evaluators.
Verify the generated files look correct before moving on.
Create packages/phoenix-evals/src/phoenix/evals/metrics/{name}.py.
Read correctness.py in that directory — it's the canonical example. Your evaluator follows the same pattern: subclass ClassificationEvaluator, pull constants from the generated config, define a Pydantic input schema with fields matching your template placeholders.
After creating the file, add it to the exports in metrics/__init__.py — both the import and the __all__ list. Read the current __init__.py to see the existing pattern.
Add test coverage in packages/phoenix-evals/tests/phoenix/evals/metrics/test_classification_evaluators.py, following the existing cases there.
Create js/packages/phoenix-evals/src/llm/create{Name}Evaluator.ts.
Read createCorrectnessEvaluator.ts — it's the canonical example. The pattern is a factory function that wraps createClassificationEvaluator with defaults from the generated config.
Then:
js/packages/phoenix-evals/src/llm/index.tsjs/packages/phoenix-evals/test/llm/ — read
createFaithfulnessEvaluator.test.ts there for the test patterncd js && pnpm buildFix any TypeScript errors before proceeding.
Create js/benchmarks/evals-benchmarks/src/{name}.eval.ts.
Read existing benchmarks in that directory to match the current patterns:
tool_invocation.eval.ts — multi-category analysis and aggregate metricsaggregateMetrics.ts — shared macro precision/recall/F1 accumulationThe task function must return input and output text in its result so the failed examples printer has access to them.
Consider using a separate agent session for synthetic dataset generation if the examples need realistic domain-specific content — this keeps the dataset creation focused and avoids context-switching.
# Terminal 1: Start Phoenix using the normal configured database. Do not set
# PHOENIX_WORKING_DIR unless the user explicitly requests an isolated instance.
phoenix serve
# Terminal 2: Run the benchmark
cd js
pnpm --filter evals-benchmarks... build
pnpm --filter evals-benchmarks exec vitest run \
src/{name}.eval.ts --config phoenix.vitest.config.tsTarget >80% accuracy. If accuracy is low, look at the failed examples output to decide whether to adjust the prompt (Step 1) or the benchmark examples (Step 6). Iterate until accuracy is acceptable.
There are two Pre-Built Metrics sections in the docs, and docs.json has a nav group named "Pre-Built Metrics" under each:
| Section | Directory | Nav group in docs.json | Who needs a page |
|---|---|---|---|
| Client-Side Evals (SDK) | docs/phoenix/evaluation/pre-built-metrics/ | Evaluation > Client-Side Evals (SDK) > Pre-Built Metrics | Every new evaluator |
| Server-Side Evals (UI) | docs/phoenix/evaluation/server-evals/pre-built-metrics/ | Evaluation > Server-Side Evals (UI) > Pre-Built Metrics > LLM Evaluators | Only promoted dataset evaluators |
Create docs/phoenix/evaluation/pre-built-metrics/{name}.mdx. Read faithfulness.mdx in that directory and follow its section structure: Overview (When to Use), Supported Levels, Input Requirements (Formatting Tips), Output Interpretation, Usage Examples (Python and TypeScript tabs, Using Input Mapping), Configuration (Viewing and Modifying the Prompt), Using with Phoenix, API Reference, Related.
Then update:
docs.json: add the page to the Client-Side Evals (SDK) > Pre-Built Metrics group.docs/phoenix/evaluation/pre-built-metrics.mdx: add a card to the landing page grid.Create docs/phoenix/evaluation/server-evals/pre-built-metrics/{name}.mdx. Read server-evals/pre-built-metrics/correctness.mdx and follow it. This page is about the UI, not code. It has an Overview (When to Use, and Input Mapping showing which dataset column fills each template field and what the server formats automatically), Output Labels, Using in Phoenix (the Add Evaluator → LLM Evaluator Template steps), and See Also.
Cross-link the two pages: the server page links to the client page and the client page links to the server page.
Then update:
docs.json: add the page to the Server-Side Evals (UI) > Pre-Built Metrics > LLM Evaluators group.docs/phoenix/evaluation/server-evals/pre-built-metrics.mdx: add a card under "LLM Evaluators".Read each file to see the existing pattern before editing.
Don't edit sitemap.xml. It is regenerated from docs.json after merge to main.
Before calling it done, verify:
make codegen-prompts ran successfullymetrics/__init__.pytest_classification_evaluators.pyllm/index.tscd js && pnpm build)promoted_dataset_evaluator label (and any substitutions) set only if promoteddocs.json (Client-Side Evals group), and landing carddocs.json (Server-Side Evals > LLM Evaluators), server landing card and cross-links between the two pagesAfter completing the workflow, verify these instructions matched reality:
make codegen-prompts generate to different locations?If anything drifted, update this SKILL.md before finishing so the next person (or agent) doesn't hit the same surprises.
Arize-ai/phoenix
8ac3caf
If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.