,

LLMOps and CI/CD on Azure for GenAI Apps (Azure Gen AI Series, Part 28)

An Azure LLMOps pipeline looks like MLOps until the gate. Here is how to block a merge on an evaluation score, register the flow, and roll it out blue to green on a managed online endpoint, plus why new pipelines should skip Prompt Flow.

Azure Gen AI Series · Part 28 of 30

A green build tells you the code compiled and the unit tests passed. It tells you nothing about whether your app still answers well. I once merged a one line prompt tweak that sailed through every test and quietly dropped answer quality on about a third of real questions, because no test in the suite ever read a model response. That gap is the whole reason LLMOps exists, and it is where a CI/CD pipeline for a generative app on Azure has to do more than a normal one.

Key takeaways
An Azure LLMOps pipeline runs on the same spine as classic MLOps: commit, pull request, continuous integration, continuous deployment. What changes is the gate. You add an evaluation step that scores model output on a fixed dataset and blocks the merge when quality drops or a safety defect rate climbs. Register the flow or app as a versioned asset, deploy that exact version from the registry to a managed online endpoint, then shift traffic from blue to green with mirroring and small percentages before you commit to 100. One deadline reshapes new builds in 2026: Prompt Flow is on a retirement path, so start greenfield work on the Foundry SDK, not on Prompt Flow.
Prerequisites: you can deploy an Azure OpenAI or Foundry model and call it (Part 3 and Part 11), you have run at least one evaluation in Foundry (Part 23) and know the safety evaluators from Part 27, and you have a GitHub or Azure DevOps repo with a service principal that can reach your Azure Machine Learning workspace. Comfort with a YAML file and a shell is assumed. No prior MLOps background needed; every term is defined on first use.

Why a passing test suite still ships a worse answer

LLM output is not deterministic and it has no single correct label per input, so the checks that guard normal software do not catch a regression in answer quality. A unit test asserts that a function returns 4. There is no equivalent assertion for "this answer is grounded and useful," because two different wordings can both be right, and a quiet drop in grounding passes every string check you write. That is the hole. LLMOps, sometimes called GenAIOps, is the practice of managing the build, test, deploy and monitor loop for LLM-infused apps, with evaluation of the model output as a first-class pipeline step. That last clause is the entire difference from classic machine learning operations.

So you move the judgment into the pipeline. Instead of asserting an exact string, you run the app over a curated dataset and score each response on metrics like groundedness, relevance and a safety defect rate, then gate the merge on those scores. The scoring is done by other models, which feels circular until you accept the trade: a measured, repeatable number you can trend beats a human skimming ten outputs the night before a release. The score can be wrong in a single case. It is consistent across releases, and consistency is what a gate needs.

What LLMOps adds on top of MLOps

Most of an Azure LLMOps pipeline is ordinary MLOps you already know. The endpoint, the registry, the traffic split, all of it is shared plumbing, which is why Microsoft ships one set of tools for both. The pieces that are new sit at the edges: what you version, what the CI check runs, and what the quality gate measures. Here is the delta, stage by stage.

StageClassic MLOpsWhat LLMOps adds
Versioned assetModel binary and weightsFlow or app code, prompts, connections, dataset
CI checkUnit tests and lintExperiment run plus an evaluation run scored on data
Quality gateAccuracy or F1 thresholdGroundedness, relevance, coherence, safety defect rate
Deploy targetManaged online endpointSame endpoint, flow packaged as a container
RolloutBlue-green traffic splitBlue-green, plus prompt or model variant A/B
MonitorData drift, latencyOutput quality drift, token cost, safety flags
flowchart LR
  C[Commit] --> PR{Pull request evaluation gate}
  PR -->|Pass| CI[CI registers a flow version]
  PR -->|Fail| C
  CI --> CD[CD deploys the registered version]
  CD --> E[Managed online endpoint]
CD deploys the registered version, not a fresh build from source. That is what makes a run auditable.

Map the pipeline from commit to endpoint

Microsoft ships a reference implementation, the GenAIOps with Prompt Flow template, and it is worth reading even if you do not adopt it wholesale, because the branch model is the part most teams get wrong. The template uses two long-lived branches, main and development. You cut a feature branch, and when you open a pull request into development, a build validation pipeline runs your experimentation flows. Approve and merge, and the continuous integration pipeline for the dev environment runs the experimentation and evaluation flows in sequence, then registers the flows in the Azure Machine Learning registry. A continuous deployment trigger picks up from there, deploys the standard flow from the registry as a managed online endpoint, and runs integration and smoke tests against it. Promotion to production is a second pass through the same machinery from a release branch.

The template runs on GitHub Actions, Azure DevOps or Jenkins, so the shape holds whatever your org already uses. The orchestrator is not the interesting choice. Where you put the evaluation gate is.

In practice: keep two datasets, not one. A tiny fast set, tens of rows, gates the pull request and finishes in a couple of minutes so it never stalls a review. A larger set, hundreds of rows, runs in the CI pipeline on the development branch where a slower job is fine. Gate a PR on a twenty minute eval and you teach the team to merge without waiting for it. The PR check is a smoke alarm. The CI eval is the full inspection.

Evaluation as the gate that matters

This is the step that earns the name LLMOps. Run the app over the dataset, score each response with evaluators, compare against a baseline, and block the merge if it regresses. The content safety evaluators from Part 27 return a defect rate, the share of responses flagged. The quality evaluators return numeric scores such as groundedness, how well the answer sticks to its source, and relevance, how well it answers the question. Pick a threshold for each and wire the job to exit non-zero when a candidate falls below it. A non-zero exit is what turns a report into a gate.

Here is that gate as a GitHub Actions job. It authenticates to Azure with a federated credential, runs the evaluation, and fails the check when the defect rate is too high.

name: pr-eval-gate
on:
  pull_request:
    branches: [ development ]
permissions:
  id-token: write   # needed for OIDC login, no stored secret
  contents: read
jobs:
  evaluate:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: azure/login@v2
        with:
          client-id: ${{ secrets.AZURE_CLIENT_ID }}
          tenant-id: ${{ secrets.AZURE_TENANT_ID }}
          subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
      - run: pip install -r requirements.txt
      - name: run eval and gate
        run: python eval/run_eval.py --data eval/pr_set.jsonl --gate defect_rate=0.02 groundedness=4.3

Expected result: the job prints a per metric summary and exits 0 when every metric clears its gate, so the PR check goes green. Failure mode: your script must call sys.exit(1) when a gate is missed, or the workflow reports success on a regression and the whole exercise is decorative. Test the red path on purpose by lowering a gate below the current score once.

Candidate vs gate, one PRScores on a 0 to 5 scale over 120 curated questions0123454.64.3groundedness4.44.2relevance0.82.0defect rate %candidategate
Candidate clears every gate, so the merge proceeds. Defect rate is the one metric where lower is better.

Worked example

A retrieval assistant is scored over 120 curated questions before merge. Groundedness comes back at 4.6 out of 5 against a 4.3 gate, relevance at 4.4 against 4.2, and a safety defect rate of 0.8 percent against a 2 percent ceiling. All three clear, the pull request check goes green, and the flow is registered as version 7.

A week later someone rewrites the system prompt to shorten answers. Same 120 questions, one variable changed. Groundedness drops to 4.0, under the 4.3 gate, because the shorter answers cite less of the source. The PR check goes red and the merge is blocked before anything reaches an endpoint. The figures are illustrative, but the method is the point: fixed dataset, one change, an automatic stop. The 4.6 and 4.3 groundedness bars above are exactly this comparison.

Register once, then deploy that version

The rule that keeps a pipeline honest is simple. CI registers a versioned asset, and CD deploys that exact version. Nothing rebuilds from source at deploy time. In Azure that asset is a registered model, environment or flow in the workspace or a shared registry, and a deployment references it by name and version, in the form azureml:my-model:7. For production, register the model and the environment as separate assets so a scoring image change and a model change get their own version numbers rather than hiding inside one deployment.

This looks like bureaucracy until the day a reviewer asks which version served a given week and why an answer changed. With registration, the reply is a version number and an eval report attached to it. Without it, the reply is an afternoon of reading git history and hoping the build was reproducible. I have lived both. The version number is cheaper.

How does safe rollout work on a managed endpoint?

A managed online endpoint is a stable HTTPS address that can hold more than one deployment behind it and route traffic between them by percentage. That routing is what makes blue-green safe. Blue is the version serving today. Green is the candidate. You bring green up with zero traffic, test it in isolation, mirror a slice of real traffic to it to gather metrics without touching client responses, then send a small live percentage, then all of it, then delete blue.

A few limits shape the plan. Mirrored traffic caps at 50 percent, targets only one deployment, and is not available on Kubernetes endpoints. Live traffic percentages across deployments must total either 0 or 100, so you cannot leave an endpoint at 90 by accident. And for real availability, run at least three instances per deployment, because Azure holds back about 20 percent of capacity during upgrades. Here is the full rollout as stages.

StageCommand essenceLive traffic to greenWhat you watch
Bring up greendeployment create, no traffic0 percentcold start, smoke test
Mirrormirror-traffic green=100 percent, 10 shadowedlatency, errors, no client impact
Canarytraffic blue=90 green=1010 percentlive error rate, eval on real traffic
Fulltraffic blue=0 green=100100 percentdashboards, cost, quality drift
Retiredeployment delete bluen/aendpoint stable on green
Disclaimer: the commands below change a live endpoint. Run them against a non-production endpoint first, confirm your instance count and quota can absorb both blue and green running at once during the overlap, and make sure your rollback is a single traffic update back to blue before you shift any live percentage. Traffic changes take effect quickly, so a wrong percentage is a live incident, not a warning.

The rollout itself is a short sequence of Azure CLI calls. This is the operational core of the part.

# blue already serves 100% of ENDPOINT_NAME. Roll out green safely.

# 1. create green with no traffic
az ml online-deployment create --name green 
  --endpoint-name $ENDPOINT_NAME -f green-deployment.yml

# 2. shadow 10% of live traffic to green, clients still get blue
az ml online-endpoint update --name $ENDPOINT_NAME 
  --mirror-traffic "green=10"

# 3. stop mirroring, send 10% of real traffic to green
az ml online-endpoint update --name $ENDPOINT_NAME --mirror-traffic "green=0"
az ml online-endpoint update --name $ENDPOINT_NAME --traffic "blue=90 green=10"

# 4. cut over fully, then retire blue
az ml online-endpoint update --name $ENDPOINT_NAME --traffic "blue=0 green=100"
az ml online-deployment delete --name blue --endpoint $ENDPOINT_NAME --yes --no-wait

Expected result: step 2 shows green in the endpoint with 0 live traffic while its logs fill with mirrored requests; step 3 splits real traffic 90 to 10; step 4 leaves green on 100 percent and removes blue. Failure mode: skip the mirror-traffic green=0 reset in step 3 and the endpoint tries to both mirror and serve green, which is not allowed, so the update is rejected. Always zero the mirror before you assign live traffic.

Live traffic to green by stageMirror shadows traffic without counting as live0255075100percent livebring upmirrorcanary 10full 100retire blue10% shadowed, 0 live
Live traffic stays at zero through the mirror stage. The shadowed 10 percent is copied, not served.

Build new pipelines on the Foundry SDK, not Prompt Flow

Most of the LLMOps material online still centers on Prompt Flow, and it is worth knowing why I would not start a new pipeline there in 2026. Microsoft has put Prompt Flow on a retirement path. Feature development ended in April 2026, the feature enters read-only mode and is fully retired on April 20, 2027, and the guidance is to migrate to the Microsoft Agent Framework before then. Existing flows keep running until that date, so this is not a fire drill. It is a planning fact.

The practical read: if you have a Prompt Flow pipeline in production, it is fine, keep it, and schedule the migration well before the deadline. If you are starting fresh, build on the Foundry SDK path instead, since the CI/CD shape in this part, the eval gate, the registry step, the blue-green rollout, is the same either way. The evaluation gate and the safe rollout do not depend on Prompt Flow at all. Only the authoring layer does, and that is the piece being replaced.

My take: the failure I see most is evaluation bolted on after deployment as a dashboard nobody is obligated to act on. An eval that cannot fail a build is a chart, not a control. Put it in the pull request check on day one, even with five rows of data, and grow the set later. The team that gates a merge on five honest rows ships more safely than the team with a 1,000 row eval that runs after release and pages no one.

The AWS half of this series walks the same discipline on Bedrock and SageMaker Pipelines, and the parallels are close enough to lift if you run both clouds. See LLMOps and CI/CD for Amazon Bedrock and SageMaker for the Amazon equivalent of the registry and safe rollout, and the guide for where each Azure piece fits.

Promote on evals, not on a green build

If you build one thing from this part, make it the evaluation gate. Wire an evaluation run into the pull request check and refuse to promote a flow that regresses on groundedness or trips the safety defect ceiling, exactly the way you refuse to merge failing unit tests. Register every candidate as a versioned asset and deploy that version to a managed online endpoint, never a fresh build at deploy time. Roll it out blue to green: zero traffic, mirror, ten percent, then all. And point new pipelines at the Foundry SDK rather than Prompt Flow, so you are not migrating a production pipeline against a deadline in 2027.

When this is the wrong amount of machinery: a throwaway prototype or a single internal demo does not need any of it. Ship it by hand and add the pipeline when more than one person depends on the output. What to validate first: run your eval dataset through the current production app once to set honest baselines before you turn any threshold into a blocking gate, or your first pipeline will block everything or nothing.

Your move this week: take one Azure GenAI app, write a 50 row eval set, and make a failing groundedness score block a pull request. One gate that stops a bad merge beats a diagram of a pipeline you have not wired. Part 29 assembles these gates and rollouts into full reference architectures on Azure.

Azure Gen AI Series · Part 28 of 30
« Previous: Part 27  |  Guide  |  Next: Part 29 »

References

About The Author


Discover more from Journal of Intelligent Infrastructure – By Dr Pranay Jha

Subscribe to get the latest posts sent to your email.

Leave a Reply

Your email address will not be published. Required fields are marked *

Architect’s Toolkit

About the Author

Dr. Pranay Jha is a Cloud and AI Consultant with 18+ years of experience in hybrid cloud, virtualization, and enterprise infrastructure transformation. He specializes in VMware technologies, multi-cloud strategy, and Generative AI solutions. He holds a PhD in Computer Applications with research focused on Cloud and AI, has published multiple research papers, and has been a VMware vExpert since 2016 and a VMUG Community Leader.

Discover more from Journal of Intelligent Infrastructure - By Dr Pranay Jha

Subscribe now to keep reading and get access to the full archive.

Continue reading