Declarative Agents: Defining Your Agent Fleet in YAML
Define agents and multi-agent workflows in version-controlled YAML instead of code. Learn declarative agents and declarative workflows 1.0 in Microsoft Agent Framework.
Published on • August 20, 2026
AI Assistant

Your agent fleet is defined in code, which means every change to an instruction, a tool, or a routing branch requires a deploy. Product owners can’t review a call graph. Solution architects read framework code to understand behavior. And when a support agent adds a new ticket category, it’s an engineering ticket.
The alternative is declarative agents: define your agents and workflows as data. Microsoft Agent Framework lets you specify instructions, model settings, tools, memory configuration, and orchestration topology in version-controlled YAML files—then load and run them with a single API call.
In this tutorial, you will learn how to define agents in YAML, and how declarative workflows 1.0 move entire orchestrations out of code.
Prerequisites
- Python 3.10–3.13 with
pip install agent-framework-declarative(1.0) - Or .NET 8+ with
Microsoft.Agents.AI.Declarative - A chat client provider (Foundry, Azure OpenAI, or OpenAI)
Define an Agent with YAML
A declarative agent is a self-contained document describing everything the agent needs:
kind: Prompt
name: Assistant
description: Helpful assistant
instructions: >-
You are a helpful assistant. You answer questions in the language specified
by the user. You return your answers in a JSON format.
model:
id: =Env.AZURE_OPENAI_MODEL
connection:
kind: remote
endpoint: =Env.FOUNDRY_PROJECT_ENDPOINT
options:
temperature: 0.9
topP: 0.95
outputSchema:
properties:
language:
type: string
required: true
description: The language of the answer.
answer:
type: string
required: true
description: The answer text.
Note the =Env.* values: YAML supports PowerFx expressions, so configuration can bind to environment variables at load time. One YAML file works across Python and .NET—same definition, both runtimes.
Load and Run It
Python, using the declarative package’s AgentFactory:
import asyncio
from pathlib import Path
from agent_framework.declarative import AgentFactory
from azure.identity.aio import AzureCliCredential
async def main():
yaml_path = Path(__file__).parent / "agent-config.yaml"
async with (
AzureCliCredential() as credential,
AgentFactory(client_kwargs={"credential": credential})
.create_agent_from_yaml_path(yaml_path) as agent,
):
response = await agent.run("Why is the sky blue?")
print("Agent response:", response.text)
asyncio.run(main())
C#, using ChatClientPromptAgentFactory:
using Azure.AI.OpenAI;
using Azure.Identity;
using Microsoft.Agents.AI;
using Microsoft.Extensions.AI;
var chatClient = new AzureOpenAIClient(
new Uri(Environment.GetEnvironmentVariable("AZURE_OPENAI_ENDPOINT")),
new DefaultAzureCredential());
var agentFactory = new ChatClientPromptAgentFactory(chatClient);
var agent = await agentFactory.CreateFromYamlAsync(
await File.ReadAllTextAsync("agent.yaml"));
The same YAML defines the agent on both platforms—cross-platform compatibility, versioned as configuration, shared across teams.
What YAML Can Express
The declarative schema covers the full surface of an agent:
- Instructions and system prompts
- Model selection, parameters, provider, API type
- Connections — remote, API-key, reference, anonymous
- Input/output schemas — typed properties with required flags
- Tools — FunctionTool, WebSearchTool, FileSearchTool, CodeInterpreterTool, McpTool (with approval modes), OpenApiTool, CustomTool
That last one matters: MCP server integration is configuration, not code. Point an agent at an MCP server in YAML, and it dynamically discovers and invokes those tools.
Declarative Workflows 1.0: Orchestration as Data
Declarative workflows reached 1.0 in July 2026 across both SDKs. Now the orchestration itself—how agents coordinate, where execution branches, when people step in—lives in YAML:
name: support-router
description: Routes support requests to the right specialist
inputs:
message:
type: string
description: The customer's request
actions:
- kind: InvokeAzureAgent
id: triage
agentName: TriageAgent
outputKey: category
- kind: If
condition: =Local.category == "billing"
actions:
- kind: InvokeAzureAgent
id: billing
agentName: BillingAgent
else:
- kind: InvokeAzureAgent
id: support
agentName: SupportAgent
- kind: SendActivity
id: respond
text: =Workflow.Outputs.reply
Load it like any other workflow:
from agent_framework.declarative import WorkflowFactory
factory = WorkflowFactory()
workflow = factory.create_workflow_from_yaml_path("support_router.yaml")
# workflow is a standard Workflow - run, stream, or compose it like any other.
# The agents it names (TriageAgent, BillingAgent, ...) live in your Foundry project.
The routing lives in the definition, not in application control flow. To add a category or reorder checks, you edit the list—no executors to rewire.
The Action Catalog
Declarative workflows support a fixed catalog of action kinds:
- Variable actions —
SetValue,AppendValue,ResetVariable - Control flow —
If,Switch,Foreach,RepeatUntil,GotoAction - Agent invocation —
InvokeAzureAgent,InvokePromptAgent - Tool invocation —
InvokeFunctionTool,InvokeMcpTool,HttpRequestAction - Human-in-the-loop —
Question,WaitForInput
Because a declarative workflow loads into the same Workflow type as a code-first one, it runs, streams, and composes identically. You give up nothing at runtime.
Declarative vs. Code-First
YAML isn’t always the answer. Here’s the decision guide:
| Scenario | Recommended |
|---|---|
| Standard orchestration patterns | Declarative |
| Workflows that change frequently | Declarative |
| Non-developers need to modify workflows | Declarative |
| Sequential routing over registered agents | Declarative |
| Parallelism (superstep fan-out) | Code-first |
| Stateful custom nodes | Code-first |
| Agents not registered in a hosted platform | Code-first |
| Maximum flexibility and control | Code-first |
The dividing line isn’t “simple vs. complex”—it’s whether your custom code needs to be a node in the graph, or can live as a leaf the graph calls. A declarative workflow’s state is a flat bag of variables; a code-first executor can hold arbitrary state.
Mixing is fine and often right: load the routing layer from YAML, keep the parts that actually compute in code, and compose them—a declarative workflow is just a Workflow instance.
Putting It All Together
For complete, runnable declarative agent and workflow samples, see:
- https://github.com/microsoft/agent-framework/tree/main/python/samples/03-workflows/declarative
- https://github.com/microsoft/agent-framework/tree/main/python/samples/02-agents/declarative
Conclusion & Next Steps
You now know how to define an agent fleet as data: version-controlled YAML for agents and workflows, PowerFx for dynamic configuration, and the same runtime powering code-first and declarative alike.
Next steps:
- Extract an existing routing workflow into YAML and version it with your app.
- Point an agent at an MCP server via the
McpTooldeclarative configuration. - Set up a visual designer workflow if your team prefers editing graphs over YAML.
Orchestration is a document rather than a call graph. That separation pays off beyond cleaner code: reviews, diffs, and changes that ship on their own.