Skip to content
Blog

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 actionsSetValue, AppendValue, ResetVariable
  • Control flowIf, Switch, Foreach, RepeatUntil, GotoAction
  • Agent invocationInvokeAzureAgent, InvokePromptAgent
  • Tool invocationInvokeFunctionTool, InvokeMcpTool, HttpRequestAction
  • Human-in-the-loopQuestion, 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:

ScenarioRecommended
Standard orchestration patternsDeclarative
Workflows that change frequentlyDeclarative
Non-developers need to modify workflowsDeclarative
Sequential routing over registered agentsDeclarative
Parallelism (superstep fan-out)Code-first
Stateful custom nodesCode-first
Agents not registered in a hosted platformCode-first
Maximum flexibility and controlCode-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:

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 McpTool declarative 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.

References