Run evaluation tests on code changes, choose which results fail CI, and inspect recorded runs in Phoenix.
Your release decision describes the version you reviewed. A later change can break behavior that passed today. Continuous integration (CI) runs tests when code changes so you can catch those regressions during review.Phoenix’s pytest integration supports both code checks and LLM evaluations. You’ll record a Wonder Toys purchase check as a Phoenix experiment, inspect what makes it pass or fail, and see how the same approach applies to your budget judge.
Start with a requirement from your release decision that must keep working after a change:
Application outcome: The saved product IDs and quantities match the confirmed cart. Use a code evaluator to compare them.
Answer quality: A recommendation respects the shopper’s budget. Use the validated LLM judge from Write and validate an LLM judge.
Either can become an evaluation test. The test must state what result is acceptable; a recorded score alone doesn’t fail CI.For the hands-on example, use the saved-order requirement. The supplied test calls the real purchase tool in a fresh process, checks its saved order, and asserts that the check passed.
Replace the endpoint with your Phoenix address. Set PHOENIX_API_KEY if that instance requires authentication.After the command finishes, open Datasets & Experiments → wonder-toys-ci → Experiments in Phoenix. Select the latest experiment and inspect both the evaluator result and the test’s pass annotation.
The purchase evaluation and pytest assertion are recorded on the same run in Phoenix.
What about the application and evaluator unit tests?
The separate unit test suite checks known evaluator mistakes and application behavior. The workflow runs these alongside the recorded evaluation test:
The local run gives you a working test. Open the supplied GitHub Actions workflow to see how it runs on code changes:
Trigger: A pull request changes the evaluation files, Python backend, or workflow itself.
Execution: It runs the unit suite and the marked purchase evaluation test.
Result: A failed test produces a failed workflow check. When recording is configured, Phoenix also receives the marked run.
To use it in your repository, add the workflow under .github/workflows/ and configure PHOENIX_ENDPOINT and, if needed, PHOENIX_API_KEY as repository secrets. The runner must be able to reach that endpoint; its localhost is the runner itself.Without a configured endpoint, this workflow sets PHOENIX_TEST_TRACKING=0. The tests still run and can fail CI, but nothing is sent to Phoenix. Upload problems are reported as warnings; test assertions determine the CI result.After pushing a matching pull request, inspect Phoenix Academy checks and its purchase-checks job in the Checks tab. Confirm the tests run and, when recording is enabled, that the experiment appears in Phoenix.
Step 4: Apply the same pattern to model evaluations
The purchase example uses a code evaluator, but an LLM judge can also be part of CI. For your budget requirement:
Run the application on reviewed shopping cases and record its answers.
Apply the validated budget judge and record its labels and explanations.
Assert the acceptance rule from your release requirements. Decide how failures, needs_review, and execution errors affect that rule.
Use repetitions when needed to understand variation. These tests make model API calls and need the provider credentials used in the earlier judge lesson.Phoenix’s inline evaluation example shows how to record an evaluator result and assert on it. Evaluators attached to the test marker record evidence; they don’t automatically fail the test. An explicit assertion makes the requirement enforceable.CI repeats known requirements. In Evaluate new runs and update tests, you’ll review requests your test set didn’t cover and use them to improve the next set of checks.
{"docsConfig":{"theme":"mint","$schema":"https://mintlify.com/docs.json","name":"Phoenix","colors":{"primary":"#00769E","light":"#39C7FF","dark":"#009DD2"},"logo":{"light":"https://mintcdn.com/arizeai-433a7140/fitXeWll7ezQrIcg/docs/logo/light.png?fit=max\u0026auto=format\u0026n=fitXeWll7ezQrIcg\u0026q=85\u0026s=78e92baf10dd9eced114496ccb88dbf1","dark":"https://mintcdn.com/arizeai-433a7140/fitXeWll7ezQrIcg/docs/logo/dark.png?fit=max\u0026auto=format\u0026n=fitXeWll7ezQrIcg\u0026q=85\u0026s=ef36a0515f1c575affb91f81496ce2f3"},"favicon":"https://mintcdn.com/arizeai-433a7140/fitXeWll7ezQrIcg/docs/favicon.png?fit=max\u0026auto=format\u0026n=fitXeWll7ezQrIcg\u0026q=85\u0026s=7c6d0d7347821750d998c57c334ca376","background":{"color":{"light":"#FFFFFF","dark":"#08090A"}},"navbar":{"links":[]},"footer":{"socials":{"slack":"https://join.slack.com/t/arize-ai/shared_invite/zt-3r07iavnk-ammtATWSlF0pSrd1DsMW7g","x":"https://x.com/ArizePhoenix","bluesky":"https://bsky.app/profile/arize-phoenix.bsky.social","linkedin":"https://www.linkedin.com/showcase/113218220","github":"https://github.com/Arize-ai/phoenix"},"links":[{"items":[{"label":"Blog","href":"https://arize.com/blog?cat=phoenix"}]},{"items":[{"label":"English","href":"/docs/phoenix"},{"label":"日本語 (Japanese)","href":"/docs/phoenix/documentation/jp"},{"label":"中文 (Chinese)","href":"/docs/phoenix/documentation/zh"}]}]},"seo":{"metatags":{"canonical":"https://arize.com"},"indexing":"all"},"fonts":{"heading":{"family":"Geist","weight":600},"body":{"family":"Geist"}},"styling":{"codeblocks":{"theme":{"light":"github-light-default","dark":"github-dark-default"}},"latex":true},"integrations":{"gtm":{"tagId":"GTM-W96XM35"}},"contextual":{"options":["copy","view","chatgpt","claude","perplexity"],"display":"header"},"interaction":{"drilldown":false},"navigation":{"global":{"anchors":[]},"pages":[]}},"deployment":{"deploymentMetadataValue":{"subdomain":"arizeai-433a7140","hasAudioNarration":false,"actualSubdomain":"arizeai-433a7140","customDomains":[],"gitSource":{"type":"github","owner":"arize-ai","repo":"phoenix","deployBranch":"main","contentDirectory":"","isPrivate":false,"mountPath":"","label":""},"chroma":{"liveDeploymentHistoryId":"6aeb70a654566f4ef9909030","collectionId":"cf8dc4e2-57d9-423d-ad83-29aeaea7ea16","schemaVersion":"v3","latestBuiltDeploymentHistoryId":"6aeb70a654566f4ef9909030"},"feedback":{"thumbs":true,"edits":true,"issues":true},"entitlements":{"AI_CHAT":{"status":"ENABLED"},"REMOVE_BRANDING":{"status":"ENABLED"},"CONTEXTUAL_FEEDBACK":{"status":"ENABLED"},"CODE_SNIPPET_FEEDBACK":{"status":"ENABLED"},"WEB_MCP":{"status":"ENABLED"},"NARRATION":{"status":"ENABLED"}},"buildId":"6aeb714e0d822d4dc694a97d","clientVersion":"0.0.4064","searchFilterCounts":[{"language":"default","tag":"Integrations","count":211},{"language":"default","tag":"Documentation","count":175},{"language":"default","tag":"Release Notes","count":175},{"language":"default","tag":"SDK \u0026 API Reference","count":145},{"language":"default","tag":"Rest API","count":106},{"language":"default","tag":"API Reference","count":105},{"language":"default","tag":"LLM Providers","count":71},{"language":"default","tag":"Python","count":69},{"language":"default","tag":"Tracing","count":64},{"language":"default","tag":"Evaluation","count":59}]},"assistantConfig":{"enableAskAiButton":true,"enableAskAiSearchBar":true,"deflection":{"enabled":true,"email":"support@arize.com"}},"basePath":"","hasChatPermissions":true,"hasWebMcpPermissions":true,"toggles":[{"name":"kill-isr","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"agent-mintlify-slack","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"agent-integrations-settings","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"rbac-expansion","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"automations-pr-grouping","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"custom-prefix","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"analytics-v7","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"user-flows-analytics","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"no-publish-flow","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"workos-onboarding","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"llms-txt-v2","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"snapshot-serving-live","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"stripTemperatureMiddleware","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"support-platform-pylon-zendesk","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"snapshot-serving","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"instant-rollback","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"granular-notifications-server","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"granular-notifications","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"automations-fable","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"fixed-ai-action-pricing","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"automation-resolve-merge-conflict","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"scoped-publishing","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"mintql","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"page-level-analytics","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"editor-version-history-v2","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"billing-page-v2","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"full-page-search","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"workspace-projects","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"assistant-v5","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"fork-pr-previews","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"revalidation-jwt","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"vite-origin","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"workos-sso-scim","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"hobby-abuse-scan","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"dashboard-inbox","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"workos-signup","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"turbopuffer-dual-write","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"workos-enabled","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"approve-review-flow","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"sso-setup-deadline-banner","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false}],"googleFontsInlined":true},"customCss":[{"filePath":"docs/style.css","content":"/* cyrillic-ext */\n@font-face {\n font-family: 'Geist Mono';\n font-style: normal;\n font-weight: 300;\n font-display: swap;\n src: url(/_mintlify/assets/fonts/google/s/geistmono/v6/or3nQ6H-1_WfwkMZI_qYFrodmgPn.woff2) format('woff2');\n unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;\n}\n/* cyrillic */\n@font-face {\n font-family: 'Geist Mono';\n font-style: normal;\n font-weight: 300;\n font-display: swap;\n src: url(/_mintlify/assets/fonts/google/s/geistmono/v6/or3nQ6H-1_WfwkMZI_qYFrMdmgPn.woff2) format('woff2');\n unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;\n}\n/* symbols2 */\n@font-face {\n font-family: 'Geist Mono';\n font-style: normal;\n font-weight: 300;\n font-display: swap;\n src: url(/_mintlify/assets/fonts/google/s/geistmono/v6/or3nQ6H-1_WfwkMZI_qYFg08vz7ehw.woff2) format('woff2');\n unicode-range: U+2000-2001, U+2004-2008, U+200A, U+23B8-23BD, U+2500-259F;\n}\n/* vietnamese */\n@font-face {\n font-family: 'Geist Mono';\n font-style: normal;\n font-weight: 300;\n font-display: swap;\n src: url(/_mintlify/assets/fonts/google/s/geistmono/v6/or3nQ6H-1_WfwkMZI_qYFrgdmgPn.woff2) format('woff2');\n unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;\n}\n/* latin-ext */\n@font-face {\n font-family: 'Geist Mono';\n font-style: normal;\n font-weight: 300;\n font-display: swap;\n src: url(/_mintlify/assets/fonts/google/s/geistmono/v6/or3nQ6H-1_WfwkMZI_qYFrkdmgPn.woff2) format('woff2');\n unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C4, U+2113, U+2C60-2C7F, U+A720-A7FF;\n}\n/* latin */\n@font-face {\n font-family: 'Geist Mono';\n font-style: normal;\n font-weight: 300;\n font-display: swap;\n src: url(/_mintlify/assets/fonts/google/s/geistmono/v6/or3nQ6H-1_WfwkMZI_qYFrcdmg.woff2) format('woff2');\n unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;\n}\n/* cyrillic-ext */\n@font-face {\n font-family: 'Geist Mono';\n font-style: normal;\n font-weight: 400;\n font-display: swap;\n src: url(/_mintlify/assets/fonts/google/s/geistmono/v6/or3nQ6H-1_WfwkMZI_qYFrodmgPn.woff2) format('woff2');\n unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;\n}\n/* cyrillic */\n@font-face {\n font-family: 'Geist Mono';\n font-style: normal;\n font-weight: 400;\n font-display: swap;\n src: url(/_mintlify/assets/fonts/google/s/geistmono/v6/or3nQ6H-1_WfwkMZI_qYFrMdmgPn.woff2) format('woff2');\n unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;\n}\n/* symbols2 */\n@font-face {\n font-family: 'Geist Mono';\n font-style: normal;\n font-weight: 400;\n font-display: swap;\n src: url(/_mintlify/assets/fonts/google/s/geistmono/v6/or3nQ6H-1_WfwkMZI_qYFg08vz7ehw.woff2) format('woff2');\n unicode-range: U+2000-2001, U+2004-2008, U+200A, U+23B8-23BD, U+2500-259F;\n}\n/* vietnamese */\n@font-face {\n font-family: 'Geist Mono';\n font-style: normal;\n font-weight: 400;\n font-display: swap;\n src: url(/_mintlify/assets/fonts/google/s/geistmono/v6/or3nQ6H-1_WfwkMZI_qYFrgdmgPn.woff2) format('woff2');\n unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;\n}\n/* latin-ext */\n@font-face {\n font-family: 'Geist Mono';\n font-style: normal;\n font-weight: 400;\n font-display: swap;\n src: url(/_mintlify/assets/fonts/google/s/geistmono/v6/or3nQ6H-1_WfwkMZI_qYFrkdmgPn.woff2) format('woff2');\n unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C4, U+2113, U+2C60-2C7F, U+A720-A7FF;\n}\n/* latin */\n@font-face {\n font-family: 'Geist Mono';\n font-style: normal;\n font-weight: 400;\n font-display: swap;\n src: url(/_mintlify/assets/fonts/google/s/geistmono/v6/or3nQ6H-1_WfwkMZI_qYFrcdmg.woff2) format('woff2');\n unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;\n}\n/* cyrillic-ext */\n@font-face {\n font-family: 'Geist Mono';\n font-style: normal;\n font-weight: 500;\n font-display: swap;\n src: url(/_mintlify/assets/fonts/google/s/geistmono/v6/or3nQ6H-1_WfwkMZI_qYFrodmgPn.woff2) format('woff2');\n unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;\n}\n/* cyrillic */\n@font-face {\n font-family: 'Geist Mono';\n font-style: normal;\n font-weight: 500;\n font-display: swap;\n src: url(/_mintlify/assets/fonts/google/s/geistmono/v6/or3nQ6H-1_WfwkMZI_qYFrMdmgPn.woff2) format('woff2');\n unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;\n}\n/* symbols2 */\n@font-face {\n font-family: 'Geist Mono';\n font-style: normal;\n font-weight: 500;\n font-display: swap;\n src: url(/_mintlify/assets/fonts/google/s/geistmono/v6/or3nQ6H-1_WfwkMZI_qYFg08vz7ehw.woff2) format('woff2');\n unicode-range: U+2000-2001, U+2004-2008, U+200A, U+23B8-23BD, U+2500-259F;\n}\n/* vietnamese */\n@font-face {\n font-family: 'Geist Mono';\n font-style: normal;\n font-weight: 500;\n font-display: swap;\n src: url(/_mintlify/assets/fonts/google/s/geistmono/v6/or3nQ6H-1_WfwkMZI_qYFrgdmgPn.woff2) format('woff2');\n unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;\n}\n/* latin-ext */\n@font-face {\n font-family: 'Geist Mono';\n font-style: normal;\n font-weight: 500;\n font-display: swap;\n src: url(/_mintlify/assets/fonts/google/s/geistmono/v6/or3nQ6H-1_WfwkMZI_qYFrkdmgPn.woff2) format('woff2');\n unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C4, U+2113, U+2C60-2C7F, U+A720-A7FF;\n}\n/* latin */\n@font-face {\n font-family: 'Geist Mono';\n font-style: normal;\n font-weight: 500;\n font-display: swap;\n src: url(/_mintlify/assets/fonts/google/s/geistmono/v6/or3nQ6H-1_WfwkMZI_qYFrcdmg.woff2) format('woff2');\n unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;\n}\n/* cyrillic-ext */\n@font-face {\n font-family: 'Geist Mono';\n font-style: normal;\n font-weight: 600;\n font-display: swap;\n src: url(/_mintlify/assets/fonts/google/s/geistmono/v6/or3nQ6H-1_WfwkMZI_qYFrodmgPn.woff2) format('woff2');\n unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;\n}\n/* cyrillic */\n@font-face {\n font-family: 'Geist Mono';\n font-style: normal;\n font-weight: 600;\n font-display: swap;\n src: url(/_mintlify/assets/fonts/google/s/geistmono/v6/or3nQ6H-1_WfwkMZI_qYFrMdmgPn.woff2) format('woff2');\n unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;\n}\n/* symbols2 */\n@font-face {\n font-family: 'Geist Mono';\n font-style: normal;\n font-weight: 600;\n font-display: swap;\n src: url(/_mintlify/assets/fonts/google/s/geistmono/v6/or3nQ6H-1_WfwkMZI_qYFg08vz7ehw.woff2) format('woff2');\n unicode-range: U+2000-2001, U+2004-2008, U+200A, U+23B8-23BD, U+2500-259F;\n}\n/* vietnamese */\n@font-face {\n font-family: 'Geist Mono';\n font-style: normal;\n font-weight: 600;\n font-display: swap;\n src: url(/_mintlify/assets/fonts/google/s/geistmono/v6/or3nQ6H-1_WfwkMZI_qYFrgdmgPn.woff2) format('woff2');\n unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;\n}\n/* latin-ext */\n@font-face {\n font-family: 'Geist Mono';\n font-style: normal;\n font-weight: 600;\n font-display: swap;\n src: url(/_mintlify/assets/fonts/google/s/geistmono/v6/or3nQ6H-1_WfwkMZI_qYFrkdmgPn.woff2) format('woff2');\n unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C4, U+2113, U+2C60-2C7F, U+A720-A7FF;\n}\n/* latin */\n@font-face {\n font-family: 'Geist Mono';\n font-style: normal;\n font-weight: 600;\n font-display: swap;\n src: url(/_mintlify/assets/fonts/google/s/geistmono/v6/or3nQ6H-1_WfwkMZI_qYFrcdmg.woff2) format('woff2');\n unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;\n}\nhtml:root{--font-family-mono-custom: 'Geist Mono'}html:root{--gray-50: 247 247 248;--gray-100: 241 242 243;--gray-200: 228 229 232;--gray-300: 201 203 208;--gray-400: 161 164 171;--gray-500: 124 128 136;--gray-600: 86 90 98;--gray-700: 61 64 70;--gray-800: 43 45 49;--gray-900: 21 22 22;--gray-950: 8 9 10}html:root{--px-radius-sm: 2px;--px-radius-md: 6px;--px-radius-lg: 8px;--px-accent-bg: #009DD2}html:not(.dark){--px-bg: #FFFFFF;--px-surface: #F7F7F8;--px-code-bg: #F1F2F3;--px-chip: #E9EAEC;--px-line: rgba(8, 9, 10, 0.18);--px-t1: #08090A;--px-t2: #2B2D31;--px-t3: #3D4046;--px-accent: #00769E;--px-accent-line: rgba(0, 157, 210, 0.55);--px-accent-soft: #E7F3FD;--px-on-accent: #08090A;--px-note: #E8F3EC;--px-note-line: rgba(30, 70, 50, 0.24);--px-note-t: #1B4230;--px-warn: #FBEEE0;--px-warn-line: rgba(90, 50, 0, 0.24);--px-warn-t: #54300A;--px-blue: #E7F3FD;--px-blue-line: rgba(0, 82, 125, 0.24);--px-blue-t: #00527D;--px-danger: #FFEBE9;--px-danger-line: rgba(207, 34, 46, 0.28);--px-danger-t: #A40E26}html.dark{--px-bg: #08090A;--px-surface: #151616;--px-code-bg: #050506;--px-chip: #1E2226;--px-line: rgba(194, 196, 234, 0.20);--px-t1: #FFFFFF;--px-t2: #D5D7DB;--px-t3: #A1A4AB;--px-accent: #39C7FF;--px-accent-line: rgba(57, 199, 255, 0.45);--px-accent-soft: #122F3B;--px-on-accent: #08090A;--px-note: #10241A;--px-note-line: rgba(159, 224, 188, 0.30);--px-note-t: #9FE0BC;--px-warn: #2A1B0A;--px-warn-line: rgba(255, 201, 138, 0.30);--px-warn-t: #FFC98A;--px-blue: #0A1A24;--px-blue-line: rgba(57, 195, 255, 0.30);--px-blue-t: #97D8FF;--px-danger: #2A0F12;--px-danger-line: rgba(255, 138, 148, 0.30);--px-danger-t: #FFB3B8}#content{line-height:1.75}#page-title,#content h1{font-size:clamp(2rem, 1.4rem + 2.1vw, 2.875rem);line-height:1.12;letter-spacing:-0.02em;font-weight:600}#content h2,#content h3,#content h4{letter-spacing:-0.01em;font-weight:600}#content code,#content kbd,#content samp{font-feature-settings:'tnum' 1}#content a:not(.card){color:var(--px-accent);text-decoration-thickness:1px;text-underline-offset:3px}:focus-visible{outline:2px solid var(--px-accent-line);outline-offset:2px}html.dark :focus-visible{outline-color:var(--px-accent-line)}::selection{background:var(--px-accent-soft)}#navbar{background:var(--px-bg);border-bottom:1px solid var(--px-line)}#navbar div:has(>.nav-tabs){position:relative}#navbar div:has(>.nav-tabs)::before{content:'';position:absolute;top:0;left:50%;width:100vw;margin-left:-50vw;border-top:1px solid var(--px-line);pointer-events:none}.nav-tabs-item[data-active]{box-shadow:inset 0 -2px 0 var(--px-accent)}#search-bar-entry{background:var(--px-surface);border:1px solid var(--px-line);border-radius:var(--px-radius-md)}#assistant-entry{background:var(--px-surface);border:1px solid var(--px-accent-line);border-radius:var(--px-radius-md)}#assistant-entry svg{color:var(--px-accent)}#page-context-menu{font-size:0.9375rem;align-items:stretch}.page-context-dropdown{font-size:0.9375rem;align-items:stretch}.product-selector{position:relative;display:inline-flex;align-items:stretch;flex:0 0 auto}.product-selector--desktop{margin-left:0.75rem;align-self:center}#page-context-menu-button{padding:10px 14px;border-radius:var(--px-radius-md) 0 0 var(--px-radius-md);background:var(--px-surface);border-color:var(--px-line)}.page-context-dropdown__label{display:inline-flex;align-items:center;padding:10px 14px;border:1px solid var(--px-line);border-radius:var(--px-radius-md) 0 0 var(--px-radius-md);background:var(--px-surface);border-color:var(--px-line);color:var(--px-t1);font-family:inherit;font-weight:400;text-decoration:none}#page-context-menu-button>div{font-size:0.9375rem;gap:9px}#page-context-menu>button[aria-haspopup]{height:auto;align-self:stretch;aspect-ratio:auto;padding:10px 11px;border-radius:0 var(--px-radius-md) var(--px-radius-md) 0;background:var(--px-surface);border-color:var(--px-line)}.page-context-dropdown__chevron{display:inline-flex;align-items:center;height:auto;align-self:stretch;aspect-ratio:auto;padding:10px 11px;border:1px solid var(--px-line);border-left:0;border-radius:0 var(--px-radius-md) var(--px-radius-md) 0;background:var(--px-surface);color:var(--px-t3);cursor:pointer}.page-context-dropdown__chevron svg{display:block;width:1rem;height:1rem;transition:transform 150ms ease}.page-context-dropdown__chevron[aria-expanded=\"true\"] svg{transform:rotate(180deg)}.page-context-dropdown__menu{position:absolute;z-index:60;top:calc(100% + 0.55rem);left:0;min-width:12.5rem;padding:0.4rem;background:var(--px-bg);border:1px solid var(--px-line);border-radius:var(--px-radius-md);box-shadow:0 0.75rem 1.5rem rgb(8 9 10 / 0.18)}.page-context-dropdown__menu[hidden]{display:none}.page-context-dropdown__item{display:flex;align-items:center;justify-content:space-between;min-height:2.5rem;padding:0.55rem 0.65rem;color:var(--px-t1);border-radius:var(--px-radius-sm);text-decoration:none}.page-context-dropdown__item:hover,.page-context-dropdown__item:focus-visible{color:var(--px-accent);background:var(--px-accent-soft)}.page-context-dropdown__item[data-selected=\"true\"]{color:var(--px-accent);font-weight:500}.page-context-dropdown__item[data-selected=\"true\"]::after{content:'✓';margin-left:1.5rem}@media (max-width: 767px){#navbar .product-selector--desktop{display:none}#mobile-nav-content .product-selector--mobile{margin:0.5rem 0 1rem}#mobile-nav-content .page-context-dropdown__label{flex:1 1 auto}#mobile-nav-content .page-context-dropdown__menu{position:static;margin-top:0.4rem}}#sidebar-content li[data-active]>a{background:var(--px-accent-soft);color:var(--px-accent);border-radius:var(--px-radius-md);font-weight:500}.sidebar-group-header{font-weight:600;color:var(--px-t1)}ul.sidebar-group:has(>li[id=\"/docs/phoenix/get-started\"]),ul.sidebar-group:has(>li[id=\"/docs/phoenix/integrations/developer-tools/coding-agents\"]),ul.sidebar-group:has(>li[id=\"/docs/phoenix/pxi\"]){margin-top:1px}.toc-item[data-active],.toc-item[data-active] a{color:var(--px-accent);font-weight:400}.card.card{background:var(--px-surface);border-color:var(--px-line);border-radius:var(--px-radius-lg);box-shadow:none}.frame.frame{background:var(--px-surface);border:1px solid var(--px-line);border-radius:var(--px-radius-lg)}.accordion.accordion,.accordion-group.accordion-group{border-radius:var(--px-radius-md);border-color:var(--px-line)}.accordion.accordion{background:var(--px-surface)}.code-block.code-block,.code-group.code-group{background:var(--px-code-bg);border-color:var(--px-line);border-radius:var(--px-radius-md)}.code-block-background{background:var(--px-code-bg)!important}html.dark .code-block-background{background:var(--px-code-bg)!important}#content :not(pre)>code{background:var(--px-chip);border-radius:var(--px-radius-sm)}.code-block:has([data-floating-buttons]) [data-fade-overlay]{background-image:none}[data-floating-buttons] button{background:var(--px-chip)}.callout .code-block.code-block,.callout .code-group.code-group,.callout .code-block-background,html.dark .callout .code-block-background{background:var(--px-bg)!important}.callout{border-radius:var(--px-radius-lg)}.callout[data-callout-type=\"callout\"],.callout[data-callout-type=\"info\"]{background:var(--px-surface);border-color:var(--px-line)}.callout[data-callout-type=\"callout\"] [data-component-part=\"callout-content\"],.callout[data-callout-type=\"info\"] [data-component-part=\"callout-content\"]{color:var(--px-t2)!important}.callout[data-callout-type=\"tip\"],.callout[data-callout-type=\"check\"]{background:var(--px-note);border-color:var(--px-note-line)}.callout[data-callout-type=\"tip\"] [data-component-part=\"callout-content\"],.callout[data-callout-type=\"check\"] [data-component-part=\"callout-content\"]{color:var(--px-note-t)!important}.callout[data-callout-type=\"warning\"]{background:var(--px-warn);border-color:var(--px-warn-line)}.callout[data-callout-type=\"warning\"] [data-component-part=\"callout-content\"]{color:var(--px-warn-t)!important}.callout[data-callout-type=\"note\"]{background:var(--px-blue);border-color:var(--px-blue-line)}.callout[data-callout-type=\"note\"] [data-component-part=\"callout-content\"]{color:var(--px-blue-t)!important}.callout[data-callout-type=\"danger\"]{background:var(--px-danger);border-color:var(--px-danger-line)}.callout[data-callout-type=\"danger\"] [data-component-part=\"callout-content\"]{color:var(--px-danger-t)!important}[data-component-part=\"step-number\"]>div{background:var(--px-accent-soft);border:1px solid var(--px-accent-line);color:var(--px-accent);font-weight:500}.update.update-container{flex-direction:column;gap:0.5rem;border-top:1px solid var(--px-line)}.update.update-container:first-of-type{border-top:none}.update.update-container>div:first-child{width:100%;position:static}.update.update-container>div:last-child{width:100%;max-width:none}#content th{color:var(--px-t1);font-weight:600;border-bottom:1px solid var(--px-line)}#content td{border-bottom:1px solid var(--px-line)}footer{border-top:1px solid var(--px-line)}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0, 0, 0, 0);white-space:nowrap;border:0}table{display:table!important}.step-title{font-size:1.5rem!important}.dark .invert-on-dark{filter:invert(1)}.release-update-read-more{display:inline-block;margin-top:0.75rem;font-weight:500;color:var(--primary);text-decoration:none}"}],"page":{"pagePath":"docs/phoenix/learn/ci-checks","slug":"/docs/phoenix/learn/ci-checks","pageMetadata":{"title":"Add critical checks to CI","description":"Run evaluation tests on code changes, choose which results fail CI, and inspect recorded runs in Phoenix.","href":"/docs/phoenix/learn/ci-checks"},"description":{"compiledSource":"\"use strict\";\nconst {jsx: _jsx} = arguments[0];\nconst {useMDXComponents: _provideComponents} = arguments[0];\nfunction _createMdxContent(props) {\n const _components = {\n p: \"p\",\n ..._provideComponents(),\n ...props.components\n };\n return _jsx(_components.p, {\n children: \"Run evaluation tests on code changes, choose which results fail CI, and inspect recorded runs in Phoenix.\"\n });\n}\nfunction MDXContent(props = {}) {\n const {wrapper: MDXLayout} = {\n ..._provideComponents(),\n ...props.components\n };\n return MDXLayout ? _jsx(MDXLayout, {\n ...props,\n children: _jsx(_createMdxContent, {\n ...props\n })\n }) : _createMdxContent(props);\n}\nreturn {\n default: MDXContent\n};\n","frontmatter":{},"scope":{}},"pageType":"default","mdx":{"mdxSource":{"compiledSource":"\"use strict\";const{Fragment:_Fragment,jsx:_jsx,jsxs:_jsxs}=arguments[0];const{useMDXComponents:_provideComponents}=arguments[0];function _createMdxContent(props){const _components={a:`a`,code:`code`,li:`li`,p:`p`,pre:`pre`,span:`span`,strong:`strong`,ul:`ul`,..._provideComponents(),...props.components},{Accordion,CodeBlock,Frame,Heading,OptimizedImage}=_components;if(!Accordion)_missingMdxReference(`Accordion`,true);if(!CodeBlock)_missingMdxReference(`CodeBlock`,true);if(!Frame)_missingMdxReference(`Frame`,true);if(!Heading)_missingMdxReference(`Heading`,true);if(!OptimizedImage)_missingMdxReference(`OptimizedImage`,true);return _jsxs(_Fragment,{children:[_jsxs(_components.p,{children:[`Your `,_jsx(_components.a,{href:`/docs/phoenix/learn/ship-decision`,children:`release decision`}),` describes the version you reviewed. A later change can break behavior that passed today. Continuous integration (`,_jsx(_components.strong,{children:`CI`}),`) runs tests when code changes so you can catch those regressions during review.`]}),`\n`,_jsxs(_components.p,{children:[`Phoenix’s `,_jsx(_components.a,{href:`/docs/phoenix/datasets-and-experiments/how-to-experiments/eval-ci-with-pytest`,children:`pytest integration`}),` supports both code checks and LLM evaluations. You’ll record a `,_jsx(_components.a,{href:`https://github.com/Arize-ai/project-rosetta-stone`,children:`Wonder Toys`}),` purchase check as a Phoenix experiment, inspect what makes it pass or fail, and see how the same approach applies to your budget judge.`]}),`\n`,_jsxs(Frame,{children:[_jsx(OptimizedImage,{className:`mint-block dark:mint-hidden`,src:`https://storage.googleapis.com/arize-phoenix-assets/assets/images/phoenix-academy/academy-ci-light-78ea92f148f0.svg`,alt:`A code change runs evaluation tests. Assertions determine the check result, and Phoenix records marked tests for inspection.`}),_jsx(OptimizedImage,{className:`mint-hidden dark:mint-block`,src:`https://storage.googleapis.com/arize-phoenix-assets/assets/images/phoenix-academy/academy-ci-dark-7c45eba9175c.svg`,alt:`A code change runs evaluation tests. Assertions determine the check result, and Phoenix records marked tests for inspection.`})]}),`\n`,_jsx(Heading,{level:`2`,id:`before-you-start`,children:`Before you start`}),`\n`,_jsxs(_components.p,{children:[`Use your Wonder Toys checkout with Python 3.11, `,_jsx(_components.code,{children:`uv`}),`, and a running `,_jsx(_components.a,{href:`/docs/phoenix/learn/capture-first-run#step-1-start-phoenix`,children:`Phoenix instance`}),`. The purchase example uses the `,_jsx(_components.a,{href:`/docs/phoenix/learn/code-checks`,children:`code check from the earlier lesson`}),` and makes no model API calls.`]}),`\n`,_jsx(Heading,{level:`2`,id:`step-1-choose-the-requirement-ci-should-enforce`,children:`Step 1: Choose the requirement CI should enforce`}),`\n`,_jsx(_components.p,{children:`Start with a requirement from your release decision that must keep working after a change:`}),`\n`,_jsxs(_components.ul,{children:[`\n`,_jsxs(_components.li,{children:[_jsx(_components.strong,{children:`Application outcome:`}),` The saved product IDs and quantities match the confirmed cart. Use a code evaluator to compare them.`]}),`\n`,_jsxs(_components.li,{children:[_jsx(_components.strong,{children:`Answer quality:`}),` A recommendation respects the shopper’s budget. Use the validated LLM judge from `,_jsx(_components.a,{href:`/docs/phoenix/learn/llm-judge`,children:`Write and validate an LLM judge`}),`.`]}),`\n`]}),`\n`,_jsx(_components.p,{children:`Either can become an evaluation test. The test must state what result is acceptable; a recorded score alone doesn’t fail CI.`}),`\n`,_jsx(_components.p,{children:`For the hands-on example, use the saved-order requirement. The supplied test calls the real purchase tool in a fresh process, checks its saved order, and asserts that the check passed.`}),`\n`,_jsx(Heading,{level:`2`,id:`step-2-run-and-record-the-evaluation-test`,children:`Step 2: Run and record the evaluation test`}),`\n`,_jsxs(_components.p,{children:[`Open the supplied `,_jsx(_components.a,{href:`https://github.com/Arize-ai/project-rosetta-stone/blob/main/academy/evaluations/ci/test_purchase_outcome.py`,children:`purchase evaluation test`}),`. It uses `,_jsx(_components.code,{children:`@pytest.mark.phoenix(dataset=\"wonder-toys-ci\")`}),` to record the test in Phoenix:`]}),`\n`,_jsxs(_components.ul,{children:[`\n`,_jsxs(_components.li,{children:[_jsx(_components.code,{children:`log_output`}),` records the saved order.`]}),`\n`,_jsxs(_components.li,{children:[_jsx(_components.code,{children:`log_evaluation`}),` records the `,_jsx(_components.code,{children:`ordered_items_match`}),` score and explanation.`]}),`\n`,_jsxs(_components.li,{children:[_jsx(_components.code,{children:`assert`}),` decides whether the test passes. A failed assertion fails pytest and records a `,_jsx(_components.code,{children:`pass=False`}),` annotation.`]}),`\n`]}),`\n`,_jsx(_components.p,{children:`From the Wonder Toys repository root, run:`}),`\n`,_jsx(CodeBlock,{filename:``,numberOfLines:`7`,language:`shellscript`,children:_jsx(_components.pre,{className:`shiki shiki-themes github-light-default github-dark-default`,style:{backgroundColor:`#ffffff`,\"--shiki-dark-bg\":`#0d1117`,color:`#1f2328`,\"--shiki-dark\":`#e6edf3`},language:`shellscript`,children:_jsxs(_components.code,{language:`shellscript`,numberOfLines:`7`,children:[_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#E6EDF3`},children:`PHOENIX_ENDPOINT`}),_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#FF7B72`},children:`=`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#A5D6FF`},children:`http://localhost:6006`}),_jsx(_components.span,{style:{color:`#953800`,\"--shiki-dark\":`#FFA657`},children:` \\\\`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#E6EDF3`},children:`uv `}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#A5D6FF`},children:`run`}),_jsx(_components.span,{style:{color:`#0550AE`,\"--shiki-dark\":`#79C0FF`},children:` --python`}),_jsx(_components.span,{style:{color:`#0550AE`,\"--shiki-dark\":`#79C0FF`},children:` 3.11`}),_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#FF7B72`},children:` \\\\`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#0550AE`,\"--shiki-dark\":`#79C0FF`},children:` --with-requirements`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#A5D6FF`},children:` phoenix/langchain-py/backend/requirements.txt`}),_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#FF7B72`},children:` \\\\`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#0550AE`,\"--shiki-dark\":`#79C0FF`},children:` --with`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#A5D6FF`},children:` 'arize-phoenix-client[pytest]'`}),_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#FF7B72`},children:` \\\\`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#0550AE`,\"--shiki-dark\":`#79C0FF`},children:` --with`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#A5D6FF`},children:` 'arize-phoenix-evals>=3.9.5'`}),_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#FF7B72`},children:` \\\\`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#0550AE`,\"--shiki-dark\":`#79C0FF`},children:` --with`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#A5D6FF`},children:` pytest`}),_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#FF7B72`},children:` \\\\`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#A5D6FF`},children:` python`}),_jsx(_components.span,{style:{color:`#0550AE`,\"--shiki-dark\":`#79C0FF`},children:` -m`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#A5D6FF`},children:` pytest`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#A5D6FF`},children:` academy/evaluations/ci`}),_jsx(_components.span,{style:{color:`#0550AE`,\"--shiki-dark\":`#79C0FF`},children:` -q`})]}),`\n`]})})}),`\n`,_jsxs(_components.p,{children:[`Replace the endpoint with your Phoenix address. Set `,_jsx(_components.code,{children:`PHOENIX_API_KEY`}),` if that instance requires authentication.`]}),`\n`,_jsxs(_components.p,{children:[`After the command finishes, open `,_jsx(_components.strong,{children:`Datasets \u0026 Experiments → wonder-toys-ci → Experiments`}),` in Phoenix. Select the latest experiment and inspect both the evaluator result and the test’s `,_jsx(_components.code,{children:`pass`}),` annotation.`]}),`\n`,_jsx(Frame,{caption:`The purchase evaluation and pytest assertion are recorded on the same run in Phoenix.`,children:_jsx(OptimizedImage,{src:`https://storage.googleapis.com/arize-phoenix-assets/assets/images/phoenix-academy/academy-ci-phoenix-88087c59717b.png`,alt:`Phoenix experiment for the actual local pytest purchase test, showing its saved order, ordered_items_match evaluation, and passing assertion.`})}),`\n`,_jsxs(Accordion,{title:`What about the application and evaluator unit tests?`,id:`what-about-the-application-and-evaluator-unit-tests`,children:[_jsxs(_components.p,{children:[`The separate `,_jsx(_components.a,{href:`https://github.com/Arize-ai/project-rosetta-stone/tree/main/academy/evaluations/tests`,children:`unit test suite`}),` checks known evaluator mistakes and application behavior. The workflow runs these alongside the recorded evaluation test:`]}),_jsx(CodeBlock,{filename:``,numberOfLines:`5`,language:`shellscript`,children:_jsx(_components.pre,{className:`shiki shiki-themes github-light-default github-dark-default`,style:{backgroundColor:`#ffffff`,\"--shiki-dark-bg\":`#0d1117`,color:`#1f2328`,\"--shiki-dark\":`#e6edf3`},language:`shellscript`,children:_jsxs(_components.code,{language:`shellscript`,numberOfLines:`5`,children:[_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#953800`,\"--shiki-dark\":`#FFA657`},children:`uv`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#A5D6FF`},children:` run`}),_jsx(_components.span,{style:{color:`#0550AE`,\"--shiki-dark\":`#79C0FF`},children:` --python`}),_jsx(_components.span,{style:{color:`#0550AE`,\"--shiki-dark\":`#79C0FF`},children:` 3.11`}),_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#FF7B72`},children:` \\\\`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#0550AE`,\"--shiki-dark\":`#79C0FF`},children:` --with-requirements`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#A5D6FF`},children:` phoenix/langchain-py/backend/requirements.txt`}),_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#FF7B72`},children:` \\\\`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#0550AE`,\"--shiki-dark\":`#79C0FF`},children:` --with`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#A5D6FF`},children:` 'arize-phoenix-evals>=3.9.5'`}),_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#FF7B72`},children:` \\\\`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#0550AE`,\"--shiki-dark\":`#79C0FF`},children:` --with`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#A5D6FF`},children:` pytest`}),_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#FF7B72`},children:` \\\\`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#A5D6FF`},children:` python`}),_jsx(_components.span,{style:{color:`#0550AE`,\"--shiki-dark\":`#79C0FF`},children:` -m`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#A5D6FF`},children:` pytest`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#A5D6FF`},children:` academy/evaluations/tests`}),_jsx(_components.span,{style:{color:`#0550AE`,\"--shiki-dark\":`#79C0FF`},children:` -q`})]}),`\n`]})})}),_jsxs(Frame,{caption:`Recorded local unit-test output. This suite is separate from the marked test recorded in Phoenix.`,children:[_jsx(OptimizedImage,{className:`mint-block dark:mint-hidden`,src:`https://storage.googleapis.com/arize-phoenix-assets/assets/images/phoenix-academy/academy-ci-result-light-ed96f35fb1f3.svg`,alt:`Recorded local pytest output showing 23 passing unit tests.`}),_jsx(OptimizedImage,{className:`mint-hidden dark:mint-block`,src:`https://storage.googleapis.com/arize-phoenix-assets/assets/images/phoenix-academy/academy-ci-result-dark-47e688636dc3.svg`,alt:`Recorded local pytest output showing 23 passing unit tests.`})]})]}),`\n`,_jsx(Heading,{level:`2`,id:`step-3-repeat-those-checks-on-pull-requests`,children:`Step 3: Repeat those checks on pull requests`}),`\n`,_jsxs(_components.p,{children:[`The local run gives you a working test. Open the supplied `,_jsx(_components.a,{href:`https://github.com/Arize-ai/project-rosetta-stone/blob/main/.github/workflows/academy-checks.yml`,children:`GitHub Actions workflow`}),` to see how it runs on code changes:`]}),`\n`,_jsxs(_components.ul,{children:[`\n`,_jsxs(_components.li,{children:[_jsx(_components.strong,{children:`Trigger:`}),` A pull request changes the evaluation files, Python backend, or workflow itself.`]}),`\n`,_jsxs(_components.li,{children:[_jsx(_components.strong,{children:`Execution:`}),` It runs the unit suite and the marked purchase evaluation test.`]}),`\n`,_jsxs(_components.li,{children:[_jsx(_components.strong,{children:`Result:`}),` A failed test produces a failed workflow check. When recording is configured, Phoenix also receives the marked run.`]}),`\n`]}),`\n`,_jsxs(_components.p,{children:[`To use it in your repository, add the workflow under `,_jsx(_components.code,{children:`.github/workflows/`}),` and configure `,_jsx(_components.a,{href:`/docs/phoenix/resources/frequently-asked-questions/what-is-my-phoenix-endpoint`,children:_jsx(_components.code,{children:`PHOENIX_ENDPOINT`})}),` and, if needed, `,_jsx(_components.code,{children:`PHOENIX_API_KEY`}),` as `,_jsx(_components.a,{href:`https://docs.github.com/en/actions/security-for-github-actions/security-guides/using-secrets-in-github-actions`,children:`repository secrets`}),`. The runner must be able to reach that endpoint; its `,_jsx(_components.code,{children:`localhost`}),` is the runner itself.`]}),`\n`,_jsxs(_components.p,{children:[`Without a configured endpoint, this workflow sets `,_jsx(_components.a,{href:`/docs/phoenix/datasets-and-experiments/how-to-experiments/eval-ci-with-pytest#configuring-with-environment-variables`,children:_jsx(_components.code,{children:`PHOENIX_TEST_TRACKING=0`})}),`. The tests still run and can fail CI, but nothing is sent to Phoenix. Upload problems are reported as warnings; test assertions determine the CI result.`]}),`\n`,_jsxs(_components.p,{children:[`After pushing a matching pull request, inspect `,_jsx(_components.strong,{children:`Phoenix Academy checks`}),` and its `,_jsx(_components.code,{children:`purchase-checks`}),` job in the `,_jsxs(_components.a,{href:`https://docs.github.com/en/pull-requests/reference/status-checks#checks`,children:[_jsx(_components.strong,{children:`Checks`}),` tab`]}),`. Confirm the tests run and, when recording is enabled, that the experiment appears in Phoenix.`]}),`\n`,_jsx(Heading,{level:`2`,id:`step-4-apply-the-same-pattern-to-model-evaluations`,children:`Step 4: Apply the same pattern to model evaluations`}),`\n`,_jsx(_components.p,{children:`The purchase example uses a code evaluator, but an LLM judge can also be part of CI. For your budget requirement:`}),`\n`,_jsxs(_components.ul,{children:[`\n`,_jsx(_components.li,{children:`Run the application on reviewed shopping cases and record its answers.`}),`\n`,_jsx(_components.li,{children:`Apply the validated budget judge and record its labels and explanations.`}),`\n`,_jsxs(_components.li,{children:[`Assert the acceptance rule from your release requirements. Decide how failures, `,_jsx(_components.code,{children:`needs_review`}),`, and execution errors affect that rule.`]}),`\n`]}),`\n`,_jsx(_components.p,{children:`Use repetitions when needed to understand variation. These tests make model API calls and need the provider credentials used in the earlier judge lesson.`}),`\n`,_jsxs(_components.p,{children:[`Phoenix’s `,_jsx(_components.a,{href:`/docs/phoenix/datasets-and-experiments/how-to-experiments/eval-ci-with-pytest#logging-evaluations`,children:`inline evaluation example`}),` shows how to record an evaluator result and assert on it. Evaluators attached to the test marker record evidence; they don’t automatically fail the test. An explicit assertion makes the requirement enforceable.`]}),`\n`,_jsxs(_components.p,{children:[`CI repeats known requirements. In `,_jsx(_components.a,{href:`/docs/phoenix/learn/production-evals`,children:`Evaluate new runs and update tests`}),`, you’ll review requests your test set didn’t cover and use them to improve the next set of checks.`]})]})}function MDXContent(props={}){const{wrapper:MDXLayout}={..._provideComponents(),...props.components};return MDXLayout?_jsx(MDXLayout,{...props,children:_jsx(_createMdxContent,{...props})}):_createMdxContent(props)}return{default:MDXContent};function _missingMdxReference(id,component){throw new Error(`Expected `+(component?`component`:`object`)+\" `\"+id+\"` to be defined: you likely forgot to import, pass, or provide it.\")}","frontmatter":{},"scope":{"codeStyling":{"theme":{"light":"github-light-default","dark":"github-dark-default"}},"config":{},"pageMetadata":{"title":"Add critical checks to CI","description":"Run evaluation tests on code changes, choose which results fail CI, and inspect recorded runs in Phoenix.","href":"/docs/phoenix/learn/ci-checks"}}},"mdxSourceWithNoJs":null},"mdxExtracts":{"tableOfContents":[{"title":"Before you start","slug":"before-you-start","depth":2,"children":[]},{"title":"Step 1: Choose the requirement CI should enforce","slug":"step-1-choose-the-requirement-ci-should-enforce","depth":2,"children":[]},{"title":"Step 2: Run and record the evaluation test","slug":"step-2-run-and-record-the-evaluation-test","depth":2,"children":[]},{"title":"Step 3: Repeat those checks on pull requests","slug":"step-3-repeat-those-checks-on-pull-requests","depth":2,"children":[]},{"title":"Step 4: Apply the same pattern to model evaluations","slug":"step-4-apply-the-same-pattern-to-model-evaluations","depth":2,"children":[]}],"codeExamples":{}},"jsLinks":["/_mintlify/custom/arizeai-433a7140/ad8f81ded4a76bea/docs/analytics.js","/_mintlify/custom/arizeai-433a7140/f5010f618af65dc2/docs/product-selector.js"],"dynamicCss":".mint-block {\n display: block\n}\n.mint-hidden {\n display: none\n}\n.dark\\:mint-block:is(.dark *) {\n display: block\n}\n.dark\\:mint-hidden:is(.dark *) {\n display: none\n}","subdomain":"arizeai-433a7140","siteName":"Phoenix","title":"Add critical checks to CI - Phoenix","navMaps":{"firstHrefInVersion":{},"firstHrefInLanguage":{}},"lastModified":"2026-11-04T17:52:56.743Z","originalFileLocation":"docs/phoenix/learn/ci-checks.mdx"},"headHtml":"\u003cscript data-mint-head>(function(){var d=document.documentElement,p=null;if(!p){try{p=localStorage.getItem(\"isDarkMode\")}catch(e){}if(p==='true')p='dark';else if(p==='false')p='light';if(p!=='light'\u0026\u0026p!=='dark'\u0026\u0026p!=='system')p=\"system\"}var r=p==='system'?(matchMedia(\"(prefers-color-scheme: dark)\").matches?'dark':'light'):p;d.classList.remove(r==='dark'?'light':'dark');d.classList.add(r);d.setAttribute(\"data-theme-preference\",p)})();\u003c/script>\u003cmeta data-mint-head name=\"description\" content=\"Run evaluation tests on code changes, choose which results fail CI, and inspect recorded runs in Phoenix.\">\u003cmeta data-mint-head name=\"application-name\" content=\"Phoenix\">\u003cmeta data-mint-head name=\"generator\" content=\"Mintlify\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"192x192\" href=\"/mintlify-assets/_mintlify/favicons/arizeai-433a7140/QaORPmVscwA-cjPG/_generated/favicon/android-chrome-192x192.png\" media=\"(prefers-color-scheme: light)\">\u003clink data-mint-head rel=\"apple-touch-icon\" type=\"image/png\" sizes=\"180x180\" href=\"/mintlify-assets/_mintlify/favicons/arizeai-433a7140/QaORPmVscwA-cjPG/_generated/favicon/apple-touch-icon.png\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"16x16\" href=\"/mintlify-assets/_mintlify/favicons/arizeai-433a7140/QaORPmVscwA-cjPG/_generated/favicon/favicon-16x16.png\" media=\"(prefers-color-scheme: light)\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"32x32\" href=\"/mintlify-assets/_mintlify/favicons/arizeai-433a7140/QaORPmVscwA-cjPG/_generated/favicon/favicon-32x32.png\" media=\"(prefers-color-scheme: light)\">\u003clink data-mint-head rel=\"shortcut icon\" type=\"image/x-icon\" href=\"/mintlify-assets/_mintlify/favicons/arizeai-433a7140/QaORPmVscwA-cjPG/_generated/favicon/favicon.ico\" media=\"(prefers-color-scheme: light)\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"192x192\" href=\"/mintlify-assets/_mintlify/favicons/arizeai-433a7140/QaORPmVscwA-cjPG/_generated/favicon-dark/android-chrome-192x192.png\" media=\"(prefers-color-scheme: dark)\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"16x16\" href=\"/mintlify-assets/_mintlify/favicons/arizeai-433a7140/QaORPmVscwA-cjPG/_generated/favicon-dark/favicon-16x16.png\" media=\"(prefers-color-scheme: dark)\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"32x32\" href=\"/mintlify-assets/_mintlify/favicons/arizeai-433a7140/QaORPmVscwA-cjPG/_generated/favicon-dark/favicon-32x32.png\" media=\"(prefers-color-scheme: dark)\">\u003clink data-mint-head rel=\"shortcut icon\" type=\"image/x-icon\" href=\"/mintlify-assets/_mintlify/favicons/arizeai-433a7140/QaORPmVscwA-cjPG/_generated/favicon-dark/favicon.ico\" media=\"(prefers-color-scheme: dark)\">\u003cmeta data-mint-head property=\"og:title\" content=\"Add critical checks to CI - Phoenix\">\u003cmeta data-mint-head property=\"og:description\" content=\"Run evaluation tests on code changes, choose which results fail CI, and inspect recorded runs in Phoenix.\">\u003cmeta data-mint-head property=\"og:site_name\" content=\"Phoenix\">\u003cmeta data-mint-head property=\"og:url\" content=\"https://arize.com/docs/phoenix/learn/ci-checks\">\u003cmeta data-mint-head property=\"og:type\" content=\"website\">\u003cmeta data-mint-head property=\"og:image\" content=\"https://arizeai-433a7140.mintlify.app/_mintlify/api/og/v2?division=Ship+and+repeat\u0026amp;title=Add+critical+checks+to+CI\u0026amp;description=Run+evaluation+tests+on+code+changes%2C+choose+which+results+fail+CI%2C+and+inspect+recorded+runs+in+Phoenix.\u0026amp;theme=157e5839ca66cf301513f501\u0026amp;signature=03fd8ff8d4adef62b1771981677d23a67145c7e9ce589251cfbf242a11e2aed8\">\u003cmeta data-mint-head property=\"og:image:width\" content=\"1200\">\u003cmeta data-mint-head property=\"og:image:height\" content=\"630\">\u003cmeta data-mint-head name=\"twitter:card\" content=\"summary_large_image\">\u003cmeta data-mint-head name=\"twitter:title\" content=\"Add critical checks to CI - Phoenix\">\u003cmeta data-mint-head name=\"twitter:description\" content=\"Run evaluation tests on code changes, choose which results fail CI, and inspect recorded runs in Phoenix.\">\u003cmeta data-mint-head name=\"twitter:image\" content=\"https://arizeai-433a7140.mintlify.app/_mintlify/api/og/v2?division=Ship+and+repeat\u0026amp;title=Add+critical+checks+to+CI\u0026amp;description=Run+evaluation+tests+on+code+changes%2C+choose+which+results+fail+CI%2C+and+inspect+recorded+runs+in+Phoenix.\u0026amp;theme=157e5839ca66cf301513f501\u0026amp;signature=03fd8ff8d4adef62b1771981677d23a67145c7e9ce589251cfbf242a11e2aed8\">\u003cmeta data-mint-head name=\"twitter:image:width\" content=\"1200\">\u003cmeta data-mint-head name=\"twitter:image:height\" content=\"630\">\u003cmeta data-mint-head name=\"msapplication-config\" content=\"/mintlify-assets/_mintlify/favicons/arizeai-433a7140/QaORPmVscwA-cjPG/_generated/favicon/browserconfig.xml\">\u003cmeta data-mint-head name=\"apple-mobile-web-app-title\" content=\"Phoenix\">\u003cmeta data-mint-head name=\"msapplication-TileColor\" content=\"#00769E\">\u003clink data-mint-head rel=\"canonical\" href=\"https://arize.com/docs/phoenix/learn/ci-checks\">\u003clink data-mint-head rel=\"alternate\" type=\"application/xml\" href=\"/sitemap.xml\">\u003clink data-mint-head rel=\"alternate\" type=\"text/markdown\" href=\"/docs/phoenix/learn/ci-checks.md\">\u003cscript data-mint-head type=\"application/ld+json\">{\"@context\":\"https://schema.org\",\"@graph\":[{\"@type\":\"Organization\",\"@id\":\"https://arize.com/#organization\",\"name\":\"Phoenix\",\"url\":\"https://arize.com\",\"logo\":{\"@type\":\"ImageObject\",\"url\":\"https://mintcdn.com/arizeai-433a7140/fitXeWll7ezQrIcg/docs/logo/light.png?fit=max\u0026auto=format\u0026n=fitXeWll7ezQrIcg\u0026q=85\u0026s=78e92baf10dd9eced114496ccb88dbf1\"}},{\"@type\":\"WebSite\",\"@id\":\"https://arize.com#website\",\"name\":\"Phoenix\",\"url\":\"https://arize.com\",\"publisher\":{\"@id\":\"https://arize.com/#organization\"}},{\"@type\":\"WebPage\",\"@id\":\"https://arize.com/docs/phoenix/learn/ci-checks#webpage\",\"url\":\"https://arize.com/docs/phoenix/learn/ci-checks\",\"name\":\"Add critical checks to CI\",\"description\":\"Run evaluation tests on code changes, choose which results fail CI, and inspect recorded runs in Phoenix.\",\"dateModified\":\"2026-11-04T17:52:56.743Z\",\"isPartOf\":{\"@id\":\"https://arize.com#website\"},\"breadcrumb\":{\"@id\":\"https://arize.com/docs/phoenix/learn/ci-checks#breadcrumb\"}},{\"@type\":\"BreadcrumbList\",\"@id\":\"https://arize.com/docs/phoenix/learn/ci-checks#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Ship and repeat\",\"item\":\"https://arize.com/docs/phoenix/learn/ship-decision\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Add critical checks to CI\",\"item\":\"https://arize.com/docs/phoenix/learn/ci-checks\"}]},{\"@type\":[\"Article\",\"TechArticle\"],\"@id\":\"https://arize.com/docs/phoenix/learn/ci-checks#article\",\"headline\":\"Add critical checks to CI\",\"name\":\"Add critical checks to CI\",\"description\":\"Run evaluation tests on code changes, choose which results fail CI, and inspect recorded runs in Phoenix.\",\"url\":\"https://arize.com/docs/phoenix/learn/ci-checks\",\"mainEntityOfPage\":{\"@id\":\"https://arize.com/docs/phoenix/learn/ci-checks#webpage\"},\"dateModified\":\"2026-11-04T17:52:56.743Z\",\"publisher\":{\"@id\":\"https://arize.com/#organization\"},\"isPartOf\":{\"@id\":\"https://arize.com#website\"}}]}\u003c/script>","siteName":"Phoenix","nav":{"global":{"anchors":[]},"tabs":[{"tab":"Documentation","pages":[{"title":"Documentation","href":"/docs/phoenix"}]},{"tab":"Learn","pages":[{"group":"Start here","pages":[{"title":"Welcome to Phoenix Academy","sidebarTitle":"How teams improve an agent","href":"/docs/phoenix/learn"}]},{"group":"Understand behavior","pages":[{"title":"Capture your first agent run","href":"/docs/phoenix/learn/capture-first-run"},{"title":"Add an agent span","href":"/docs/phoenix/learn/add-context-to-traces"},{"title":"Read model, retrieval, and tool steps","href":"/docs/phoenix/learn/read-agent-run"},{"title":"Follow a conversation with sessions","href":"/docs/phoenix/learn/sessions"}]},{"group":"Find failures","pages":[{"title":"Use traces and feedback","href":"/docs/phoenix/learn/find-failures"},{"title":"Investigate and label a failed run","href":"/docs/phoenix/learn/investigate-failure"},{"title":"Turn failures into representative cases","href":"/docs/phoenix/learn/build-test-cases"}]},{"group":"Design evaluations","pages":[{"title":"Choose what to evaluate","href":"/docs/phoenix/learn/choose-evals"},{"title":"Write checks for actions and outcomes","href":"/docs/phoenix/learn/code-checks"},{"title":"Write and validate an LLM judge","href":"/docs/phoenix/learn/llm-judge"}]},{"group":"Test a change","pages":[{"title":"Compare prompt changes in Playground","href":"/docs/phoenix/learn/playground-evals"},{"title":"Run a Phoenix dataset experiment","href":"/docs/phoenix/learn/run-experiment"},{"title":"Compare results and agent steps","href":"/docs/phoenix/learn/compare-experiments"},{"title":"Test sandboxable agents with Harbor","href":"/docs/phoenix/learn/harbor-agent-tests"}]},{"group":"Ship and repeat","pages":[{"title":"Decide whether a change is ready","description":"Use reviewed results and remaining gaps to decide whether to release a change or keep testing.","href":"/docs/phoenix/learn/ship-decision"},{"title":"Add critical checks to CI","href":"/docs/phoenix/learn/ci-checks"},{"title":"Evaluate new runs and update tests","description":"Manually evaluate captured agent runs, review the results in Phoenix, and improve your next test set.","href":"/docs/phoenix/learn/production-evals"}]}]},{"tab":"Integrations","pages":[{"title":"Integrations","href":"/docs/phoenix/integrations"}]},{"tab":"SDK \u0026 API Reference","pages":[{"title":"SDK \u0026 API Reference","href":"/docs/phoenix/sdk-api-reference"}]},{"tab":"Self-Hosting","pages":[{"title":"Self-Hosting","href":"/docs/phoenix/self-hosting"}]},{"tab":"Cookbooks","pages":[{"title":"Cookbooks","href":"/docs/phoenix/cookbook"}]},{"tab":"Release Notes","pages":[{"title":"Release Notes","href":"/docs/phoenix/release-notes"}]}]},"basePath":"","title":"Add critical checks to CI - Phoenix","buildId":"dpl_2gSLkbMoF5kkLsBqYzq9KWtDyJRo","deploymentId":"dpl_2gSLkbMoF5kkLsBqYzq9KWtDyJRo","dynamics":["07c022d89b","a74be98ba7"],"chunks":["Frame-Du0ep7S2.js","DynamicMarkdown-Jk8YAWGN.js","zoom-image-lightbox-DR0NJNUZ.js","lightbox-handle-r17bVUz3.js"]}