Phoenix Evals
GitHubOfficialFirst committed by jimbobbennettDistributed by GitHub
- Skill
- Tool integration
- Software development
- Apache-2.0
- Low risk
AutoGineer summary
Written by AutoGineer from the skill’s own files, not by the author
Phoenix Evals provides a structured approach to building, running, and validating evaluators for AI and LLM applications. It supports both Python and TypeScript, offering tools for code-based and LLM-based evaluation, experiment management, and error analysis. The framework emphasizes a code-first approach, encouraging developers to define deterministic evaluations before moving to LLM-based judges, and provides mechanisms to validate evaluator accuracy against human-labeled data.
What it does
- Builds custom code-based and LLM-based evaluators for AI applications.
- Supports batch evaluation of datasets using structured dataframes.
- Facilitates experiment management, including dataset creation and synthetic data generation.
- Provides tools for error analysis, including axial coding and failure taxonomy creation.
- Enables validation of evaluator accuracy against human-provided labels.
- Integrates with tracing and observability to sample and review application traces.
When to use it
- When you need to systematically evaluate the performance of LLM application outputs.
- When building RAG systems that require specific retrieval and faithfulness metrics.
- When you need to categorize and analyze failure patterns in AI agent workflows.
- When transitioning from manual testing to automated, repeatable evaluation experiments.
- When you need to validate that your automated judges align with human expectations.
Advantages
- Supports a code-first approach to evaluation for deterministic results.
- Provides a clear path for validating judge model accuracy.
- Offers structured methods for building failure taxonomies.
- Enables consistent evaluation across both Python and TypeScript environments.
- Integrates directly with tracing data for context-aware evaluation.
Usage examples
- “How do I build a custom LLM evaluator in Python?”
- The skill provides guidance on using the LLM class and custom templates to define evaluators that assess specific model outputs.
- “How can I categorize failures in my AI agent?”
- The skill outlines an axial coding process to group failure notes into structured taxonomies like planning, tool selection, or execution errors.
- “How do I run a batch evaluation on my dataset?”
- The skill demonstrates using the evaluate_dataframe function to process a dataframe and return scores for defined metrics.
Install
Download the skill as a ZIP:
https://mp-staging.autogineer.com/agent-skills/github/phoenix-evals/downloadUnzip it into the cross-client skills folder of your project:
mkdir -p .agents/skills && unzip phoenix-evals.zip -d .agents/skills
Documentation by jimbobbennett
Phoenix Evals
Build evaluators for AI/LLM applications. Code first, LLM for nuance, validate against humans.
Quick Reference
Workflows
Starting Fresh: observe-tracing-setup → error-analysis → axial-coding → evaluators-overview
Building Evaluator: fundamentals → common-mistakes-python → evaluators-{code|llm}-{python|typescript} → validation-evaluators-{python|typescript}
RAG Systems: evaluators-rag → evaluators-code-* (retrieval) → evaluators-llm-* (faithfulness)
Production: production-overview → production-guardrails → production-continuous
Reference Categories
| Prefix | Description |
|---|---|
fundamentals-* | Types, scores, anti-patterns |
observe-* | Tracing, sampling |
error-analysis-* | Finding failures |
axial-coding-* | Categorizing failures |
evaluators-* | Code, LLM, RAG evaluators |
experiments-* | Datasets, running experiments |
validation-* | Validating evaluator accuracy against human labels |
production-* | CI/CD, monitoring |
Key Principles
| Principle | Action |
|---|---|
| Error analysis first | Can't automate what you haven't observed |
| Custom > generic | Build from your failures |
| Code first | Deterministic before LLM |
| Validate judges | >80% TPR/TNR |
| Binary > Likert | Pass/fail, not 1-5 |
Files
- ATTRIBUTION.mdAdded by AutoGineer931 B
- LICENSEAdded by AutoGineer10.0 KiB
- SKILL.md4.5 KiB
- references/axial-coding.md2.3 KiB
- references/common-mistakes-python.md7.0 KiB
- references/error-analysis-multi-turn.md1.3 KiB
- references/error-analysis.md4.2 KiB
- references/evaluate-dataframe-python.md4.4 KiB
- references/evaluators-code-python.md3.0 KiB
- references/evaluators-code-typescript.md1.2 KiB
- references/evaluators-custom-templates.md1.2 KiB
- references/evaluators-llm-python.md2.6 KiB
- references/evaluators-llm-typescript.md1.4 KiB
- references/evaluators-overview.md1.1 KiB
- references/evaluators-pre-built.md2.4 KiB
- references/evaluators-rag.md3.1 KiB
- references/experiments-datasets-python.md4.8 KiB
- references/experiments-datasets-typescript.md2.7 KiB
- references/experiments-overview.md1.7 KiB
- references/experiments-running-python.md2.9 KiB
- references/experiments-running-typescript.md3.1 KiB
- references/experiments-synthetic-python.md1.8 KiB
- references/experiments-synthetic-typescript.md2.2 KiB
- references/fundamentals-anti-patterns.md1.6 KiB
- references/fundamentals-model-selection.md1.4 KiB
- references/fundamentals.md1.8 KiB
- references/observe-sampling-python.md2.4 KiB
- references/observe-sampling-typescript.md3.9 KiB
- references/observe-tracing-setup.md3.3 KiB
- references/production-continuous.md3.6 KiB
- references/production-guardrails.md1.3 KiB
- references/production-overview.md2.3 KiB
- references/setup-python.md1.9 KiB
- references/setup-typescript.md981 B
- references/validation-evaluators-python.md1.0 KiB
- references/validation-evaluators-typescript.md4.4 KiB
- references/validation.md1.7 KiB
references/common-mistakes-python.md
Common Mistakes (Python)
Patterns that LLMs frequently generate incorrectly from training data.
Legacy Model Classes
# WRONG
from phoenix.evals import OpenAIModel, AnthropicModel
model = OpenAIModel(model="gpt-4")
# RIGHT
from phoenix.evals import LLM
llm = LLM(provider="openai", model="gpt-4o")Why: OpenAIModel, AnthropicModel, etc. are legacy 1.0 wrappers in phoenix.evals.legacy.
The LLM class is provider-agnostic and is the current 2.0 API.
Using run_evals Instead of evaluate_dataframe
# WRONG — legacy 1.0 API
from phoenix.evals import run_evals
results = run_evals(dataframe=df, evaluators=[eval1], provide_explanation=True)
# Returns list of DataFrames
# RIGHT — current 2.0 API
from phoenix.evals import evaluate_dataframe
results_df = evaluate_dataframe(dataframe=df, evaluators=[eval1])
# Returns single DataFrame with {name}_score dict columnsWhy: run_evals is the legacy 1.0 batch function. evaluate_dataframe is the current
2.0 function with a different return format.
Wrong Result Column Names
# WRONG — column doesn't exist
score = results_df["relevance"].mean()
# WRONG — column exists but contains dicts, not numbers
score = results_df["relevance_score"].mean()
# RIGHT — extract numeric score from dict
scores = results_df["relevance_score"].apply(
lambda x: x.get("score", 0.0) if isinstance(x, dict) else 0.0
)
score = scores.mean()Why: evaluate_dataframe returns columns named {name}_score containing Score dicts
like {"name": "...", "score": 1.0, "label": "...", "explanation": "..."}.
Deprecated project_name Parameter
# WRONG
df = client.spans.get_spans_dataframe(project_name="my-project")
# RIGHT
df = client.spans.get_spans_dataframe(project_identifier="my-project")Why: project_name is deprecated in favor of project_identifier, which also
accepts project IDs.
Wrong Client Constructor
# WRONG
client = Client(endpoint="https://app.phoenix.arize.com")
client = Client(url="https://app.phoenix.arize.com")
# RIGHT — for remote/cloud Phoenix
client = Client(base_url="https://app.phoenix.arize.com", api_key="...")
# ALSO RIGHT — for local Phoenix (falls back to env vars or localhost:6006)
client = Client()Why: The parameter is base_url, not endpoint or url. For local instances,
Client() with no args works fine. For remote instances, base_url and api_key are required.
Too-Aggressive Time Filters
# WRONG — often returns zero spans
from datetime import datetime, timedelta
df = client.spans.get_spans_dataframe(
project_identifier="my-project",
start_time=datetime.now() - timedelta(hours=1),
)
# RIGHT — use limit to control result size instead
df = client.spans.get_spans_dataframe(
project_identifier="my-project",
limit=50,
)Why: Traces may be from any time period. A 1-hour window frequently returns
nothing. Use limit= to control result size instead.
Not Filtering Spans Appropriately
# WRONG — fetches all spans including internal LLM calls, retrievers, etc.
df = client.spans.get_spans_dataframe(project_identifier="my-project")
# RIGHT for end-to-end evaluation — filter to top-level spans
df = client.spans.get_spans_dataframe(
project_identifier="my-project",
root_spans_only=True,
)
# RIGHT for RAG evaluation — fetch child spans for retriever/LLM metrics
all_spans = client.spans.get_spans_dataframe(
project_identifier="my-project",
)
retriever_spans = all_spans[all_spans["span_kind"] == "RETRIEVER"]
llm_spans = all_spans[all_spans["span_kind"] == "LLM"]Why: For end-to-end evaluation (e.g., overall answer quality), use root_spans_only=True.
For RAG systems, you often need child spans separately — retriever spans for
DocumentRelevance and LLM spans for Faithfulness. Choose the right span level
for your evaluation target.
Assuming Span Output is Plain Text
# WRONG — output may be JSON, not plain text
df["output"] = df["attributes.output.value"]
# RIGHT — parse JSON and extract the answer field
import json
def extract_answer(output_value):
if not isinstance(output_value, str):
return str(output_value) if output_value is not None else ""
try:
parsed = json.loads(output_value)
if isinstance(parsed, dict):
for key in ("answer", "result", "output", "response"):
if key in parsed:
return str(parsed[key])
except (json.JSONDecodeError, TypeError):
pass
return output_value
df["output"] = df["attributes.output.value"].apply(extract_answer)Why: LangChain and other frameworks often output structured JSON from root spans,
like {"context": "...", "question": "...", "answer": "..."}. Evaluators need
the actual answer text, not the raw JSON.
Using @create_evaluator for LLM-Based Evaluation
# WRONG — @create_evaluator doesn't call an LLM
@create_evaluator(name="relevance", kind="llm")
def relevance(input: str, output: str) -> str:
pass # No LLM is involved
# RIGHT — use ClassificationEvaluator for LLM-based evaluation
from phoenix.evals import ClassificationEvaluator, LLM
relevance = ClassificationEvaluator(
name="relevance",
prompt_template="Is this relevant?\n{{input}}\n{{output}}\nAnswer:",
llm=LLM(provider="openai", model="gpt-4o"),
choices={"relevant": 1.0, "irrelevant": 0.0},
)Why: @create_evaluator wraps a plain Python function. Setting kind="llm"
marks it as LLM-based but you must implement the LLM call yourself.
For LLM-based evaluation, prefer ClassificationEvaluator which handles
the LLM call, structured output parsing, and explanations automatically.
Using llm_classify Instead of ClassificationEvaluator
# WRONG — legacy 1.0 API
from phoenix.evals import llm_classify
results = llm_classify(
dataframe=df,
template=template_str,
model=model,
rails=["relevant", "irrelevant"],
)
# RIGHT — current 2.0 API
from phoenix.evals import ClassificationEvaluator, async_evaluate_dataframe, LLM
classifier = ClassificationEvaluator(
name="relevance",
prompt_template=template_str,
llm=LLM(provider="openai", model="gpt-4o"),
choices={"relevant": 1.0, "irrelevant": 0.0},
)
results_df = await async_evaluate_dataframe(dataframe=df, evaluators=[classifier])Why: llm_classify is the legacy 1.0 function. The current pattern is to create
an evaluator with ClassificationEvaluator and run it with async_evaluate_dataframe().
Using HallucinationEvaluator
# WRONG — deprecated
from phoenix.evals import HallucinationEvaluator
eval = HallucinationEvaluator(model)
# RIGHT — use FaithfulnessEvaluator
from phoenix.evals.metrics import FaithfulnessEvaluator
from phoenix.evals import LLM
eval = FaithfulnessEvaluator(llm=LLM(provider="openai", model="gpt-4o"))Why: HallucinationEvaluator is deprecated. FaithfulnessEvaluator is its replacement,
using "faithful"/"unfaithful" labels with maximized score (1.0 = faithful).
Requirements and compatibility
Written for: GitHub Copilot
Works with: Claude Code, Claude apps, OpenAI Codex, GitHub Copilot, Cursor, Gemini CLI, Kiro, OpenCode, Goose, Amp, Roo Code, JetBrains Junie, Factory Droid, Devin (ex-Windsurf), Mistral Vibe, OpenHands, Letta
“Works with” means the tool reads the open Agent Skills format; a skill written for one tool may use features others lack.
Needs: Needs Python
The author says: Requires Phoenix server. Python skills need phoenix and openai packages; TypeScript skills need @arizeai/phoenix-client.
Security
What our scanner noticed· Low risk
- Instructions that use the network
A static scan is not a guarantee. Skills are third-party content: review a skill before you let an agent use it.
Attribution and licence
- First committed by
jimbobbennett
- Publisher
- GitHub (Official)
- Distributed by
- GitHub
- Source
- github/awesome-copilot · skills/phoenix-evals · commit 797337f
- Licence
- Apache License 2.0 (redistribution allowed)
- LICENSE (written by AutoGineer)
- ATTRIBUTION.md (written by AutoGineer)
- Synced
- Contributors
- jimbobbennett, stbenjam
AutoGineer is not affiliated with or endorsed by jimbobbennett or GitHub.
Download ZIP
The ZIP holds the skill’s files, its licence files and an ATTRIBUTION.md. Download phoenix-evals.zip