Prompt flow still works and still solves the problem it was built for. It also has a published end date, and starting a two-year project on it this week would be a mistake nobody warned you about.
Read the dates first
Microsoft has announced that prompt flow feature development ended on 20 April 2026 and that the feature will be fully retired on 20 April 2027. After the retirement date the web authoring experience in Foundry and Azure Machine Learning, the VS Code extensions and the related container images are documented as no longer supported or available.
The container images matter sooner than the retirement date does. Microsoft states the prompt flow runtime images — including promptflow-runtime, promptflow-runtime-stable and promptflow-python — are no longer receiving updates of any kind, including security and package updates. A deployed prompt flow endpoint is therefore running an image that will not be patched, which is a compliance question in most organisations well before April 2027.
Those dates are Microsoft’s published schedule at the time of writing and retirement dates have been extended before. Check the current prompt flow documentation before making a decision that depends on them — but plan for the direction, which is not ambiguous.
So this page is written for two readers: somebody maintaining an existing flow who needs to understand its shape, and somebody evaluating it who should know the horizon before spending a week. If you are starting fresh, read the last section first.
A flow is a DAG in a YAML file
Strip away the visual editor and a flow is a folder containing a flow.dag.yaml file, the source files its nodes reference, and some system folders. The YAML declares inputs, outputs and a list of nodes; edges are implied by one node referencing another’s output in a ${...} expression.
Reading that file is how you understand a flow somebody else built. The canvas shows the same graph, but the YAML is the artefact under version control, the thing a diff is taken against, and the thing you export when migrating.
inputs:
question:
type: string
outputs:
answer:
type: string
reference: ${format_answer.output}
nodes:
- name: classify
type: llm
source:
type: code
path: classify.jinja2
inputs:
deployment_name: chat-default
temperature: 0
question: ${inputs.question}
connection: aoai-connection
api: chat
- name: format_answer
type: python
source:
type: code
path: format_answer.py
inputs:
raw: ${classify.output}
The LLM node and the Python node
An llm node points at a Jinja2 template rather than a plain string, which is what makes a flow more than a wrapper around one call: the prompt is a template with typed inputs, and the connection and deployment name are node inputs rather than hard-coded client configuration.
system:
You classify support questions into one of: billing, technical, other.
Answer with a single lowercase word and nothing else.
user:
{{question}}
The connection field is doing real work. It names a project connection holding the endpoint and credential, so the flow contains no secret and the same flow runs against a development and a production resource by changing one name. That is the same connection object described on the data connection page.
A python node is an ordinary function decorated as a tool. It is where anything deterministic belongs — parsing, validation, a lookup, the fallback when the model returns something unexpected:
from promptflow import tool
VALID = {"billing", "technical", "other"}
@tool
def format_answer(raw: str) -> str:
label = raw.strip().lower()
return label if label in VALID else "other"
Putting that validation in a node rather than in the prompt is the single practice that most improves a flow’s reliability, because it turns a probabilistic output into a closed set before anything downstream sees it.
Deploying it as an endpoint
- Test the flow interactively first, with a batch run over a small evaluation dataset rather than a single input. A flow that works on one question tells you nothing about the classifier.
- Deploy it to a managed online endpoint from the flow’s Deploy action. This provisions compute — the flow becomes a container serving an HTTP endpoint, and it bills for uptime rather than per call.
- Grant the endpoint’s managed identity access to the Azure OpenAI resource the connection points at. The connection resolves at runtime under the endpoint’s identity, not yours, and this is the most common cause of a flow that works in the authoring session and fails once deployed.
- Call it as a normal REST endpoint with a key or token, sending the flow’s declared inputs as JSON and receiving its declared outputs.
Note the billing shape: a deployed flow is compute you rent, on top of the tokens its LLM nodes consume. Two meters, and the first one runs whether or not anybody calls it.
The part worth keeping
If you take one thing out of prompt flow before it goes, take the evaluation apparatus rather than the flow. A batch run executes the flow across a dataset of inputs and collects every output as a run artefact; an evaluation flow is a second flow that consumes those outputs alongside the expected answers and emits metrics. That pairing is the reason the tool existed, and it is entirely portable.
The asset is the dataset. A JSONL file of realistic inputs with known correct outputs — including the awkward ones, the ambiguous ones and the ones that previously went wrong in production — is what tells you whether a prompt change, a model version bump or a temperature adjustment helped. It costs an afternoon to assemble, it does not depend on any framework, and it survives every migration in this cluster. The flow around it does not.
Two practices make the metrics mean something. Evaluate the whole flow rather than individual nodes, because a classifier that improves while the downstream formatter degrades is a net loss the node-level numbers will hide. And keep at least one deterministic metric — exact match on a closed set, schema validity, a regex — alongside any model-graded one, because a model-graded score that moves when you change the model being graded is not measuring what you think.
A run is also a debugging tool rather than only a scoreboard. Each one records the inputs and outputs of every node, so an unexpected final answer can be traced to the node that produced the unexpected intermediate. Reproducing that after migration means logging node boundaries deliberately, which is worth designing for on the way out rather than rediscovering later.
What replaces it
Microsoft’s documented migration target is the Microsoft Agent Framework, with published guidance on mapping each flow node to its equivalent. The migration is a rewrite rather than a converter run: you export flow.dag.yaml and map node by node.
The useful thing about having built a flow properly is that this migration is mostly mechanical. LLM nodes become model calls with the same templates, Python tool nodes become ordinary functions, and the DAG becomes explicit control flow in code — which is often shorter than the YAML it replaces. Flows that leaned on the visual canvas for branching logic are the painful ones.
If you are choosing today rather than migrating, the honest answer is to write the orchestration in code from the start. Where the workflow is long-running or needs to survive a process restart, a durable orchestrator gives you checkpointing that a flow never had.
Top comments (0)