How to Build Declarative Agents Using YAML Definitions in the Microsoft Agent Framework

You can build declarative agents entirely in YAML by using the AgentFactory class to parse the definition and return a fully configured Agent instance without writing Python agent logic.

The Microsoft Agent Framework enables developers to build declarative agents using YAML definitions that specify models, tools, instructions, and output schemas in a single configuration file. This data-driven approach allows teams to version, share, and deploy agent configurations through CI pipelines or external configuration services while the framework handles the runtime instantiation.

Understanding the Declarative Agent Architecture

The declarative subsystem in python/packages/declarative/agent_framework_declarative/ separates agent definition from implementation. Instead of subclassing Agent in Python, you describe a Prompt Agent through a structured YAML document that the framework validates and executes.

The AgentFactory Entry Point

In python/packages/declarative/agent_framework_declarative/_loader.py, the AgentFactory class serves as the primary entry point for converting YAML definitions into executable agents. The factory parses the YAML, resolves model providers, constructs chat clients, binds tools, and returns an initialized Agent object ready for execution.

The workflow follows six discrete steps:

  1. Parse YAML using yaml.safe_load to produce a plain dictionary
  2. Dispatch to PromptAgent via agent_schema_dispatch for Pydantic-style validation
  3. Resolve provider configuration through _retrieve_provider_configuration
  4. Instantiate the chat client with model IDs, endpoints, and API keys
  5. Parse tools using _parse_tool (line 333) to convert declarative definitions into AFFunctionTool objects
  6. Create the Agent with assembled instructions, tools, and response schemas

The PromptAgent Model Schema

The PromptAgent model in python/packages/declarative/agent_framework_declarative/_models.py defines the Pydantic-style schema that validates your YAML structure. This schema enforces the kind: Prompt contract and accepts fields for name, description, instructions, model configuration, tools, and outputSchema.

Provider Mapping and Chat Client Resolution

The factory uses a dictionary called PROVIDER_TYPE_OBJECT_MAPPING to translate YAML model definitions into concrete chat client classes. When you specify model.provider and model.apiType in your YAML, the factory looks up the corresponding Python class, determines which field receives the model ID, and injects optional endpoint or API-key arguments.

Tool Parsing and Runtime Binding

At line 333 in _loader.py, the _parse_tool method converts each declarative Tool entry—whether function, web_search, or file_search—into the AFFunctionTool objects expected by the underlying agent_framework runtime. This allows your YAML-defined tools to interface seamlessly with the agent's execution engine.

Creating Agents from YAML Definitions

The framework provides multiple pathways for instantiating agents depending on whether your definition lives in a file, a string, or a dictionary.

Loading from a YAML File

Use create_agent_from_yaml_path when your agent definition resides in a version-controlled file or external configuration store.

from agent_framework_declarative import AgentFactory
import asyncio

factory = AgentFactory()  # safe_mode=True by default

agent = factory.create_agent_from_yaml_path(
    "python/samples/02-agents/declarative/openai_agent.yaml"
)

async def chat():
    async for event in agent.run("What is the weather in Paris?", stream=True):
        print(event)

asyncio.run(chat())

This approach is implemented in python/samples/02-agents/declarative/openai_agent.py and supports full async streaming responses.

Using Inline YAML Strings

For dynamic generation or testing scenarios, use create_agent_from_yaml to pass the YAML definition directly as a string.

from agent_framework_declarative import AgentFactory
import asyncio

yaml_def = """
kind: Prompt
name: GreetingAgent
description: Greets the user politely.
instructions: |
  You are a friendly assistant. Reply with a greeting.
model:
  id: gpt-4o
  provider: AzureOpenAI
outputSchema:
  type: object
  properties:
    greeting:
      type: string
"""

factory = AgentFactory()
agent = factory.create_agent_from_yaml(yaml_def)
response = asyncio.run(agent.run("Hi!"))
print(response)

This pattern appears in python/samples/02-agents/declarative/inline_yaml.py and eliminates the need for external files when prototyping.

Async Creation from Dictionaries

When loading configurations from databases or external APIs, use create_agent_from_dict_async (or the synchronous create_agent_from_dict) to bypass YAML parsing entirely and work with Python dictionaries.

from agent_framework_declarative import AgentFactory
import asyncio

agent_def = {
    "kind": "Prompt",
    "name": "AsyncWeatherAgent",
    "instructions": "Answer weather queries concisely.",
    "model": {"id": "gpt-4o", "provider": "AzureOpenAI"},
    "tools": [
        {
            "kind": "function",
            "name": "get_weather",
            "description": "Fetch weather for a city.",
            "bindings": [{"name": "get_weather"}],
        }
    ],
}

async def build_and_chat():
    factory = AgentFactory()
    agent = await factory.create_agent_from_dict_async(agent_def)
    async for event in agent.run("Weather in Tokyo?", stream=True):
        print(event)

asyncio.run(build_and_chat())

The async variants at line 74 in _loader.py mirror every synchronous method, enabling seamless integration with async web frameworks like FastAPI or Quart.

Security and Configuration Options

The AgentFactory constructor at line 81 in _loader.py implements safe mode by default. When safe_mode=True, PowerFx expressions in your YAML cannot read environment variables directly, protecting against untrusted configuration injection.

You can still inject variables safely through:

  • client_kwargs: Pass additional arguments to the chat client constructor
  • env_file_path: Specify a .env file for loading environment variables securely
  • custom_providers: Extend the PROVIDER_TYPE_OBJECT_MAPPING with private model endpoints

Extending with Declarative Workflows

Beyond single agents, the framework supports orchestrating multiple actions through declarative workflows. The workflow engine reads YAML definitions describing sequences of actions like SetValue, SendActivity, or RunAgent.


# python/samples/03-workflows/declarative/simple_workflow/workflow.yaml

name: simple-greeting-workflow
description: A simple workflow that greets the user

actions:
  - kind: SetValue
    id: set_greeting
    path: Local.greeting
    value: Hello
  - kind: SetValue
    id: set_name
    path: Local.name
    value: =If(IsBlank(inputs.name), "World", inputs.name)
  - kind: SendActivity
    id: send_greeting
    activity:
      text: =Concat(Local.greeting, ", ", Local.name, "!")

This example from python/samples/03-workflows/declarative/simple_workflow/workflow.yaml demonstrates how declarative definitions can coordinate complex interactions without Python glue code.

Summary

  • Use AgentFactory in agent_framework_declarative/_loader.py to convert YAML into executable agents without writing Python agent classes.
  • Define agent structure through the PromptAgent schema, specifying models, tools, instructions, and output schemas in a single YAML document.
  • Load configurations from files (create_agent_from_yaml_path), strings (create_agent_from_yaml), or dictionaries (create_agent_from_dict) depending on your deployment needs.
  • Enable safe mode by default to prevent PowerFx expressions from accessing environment variables directly, using client_kwargs or env_file_path for secure configuration injection.
  • Leverage async variants of all factory methods for high-throughput applications using modern Python async patterns.

Frequently Asked Questions

What is the difference between a Prompt Agent and a programmatic Agent?

A Prompt Agent is defined entirely through YAML configuration using the kind: Prompt schema, while a programmatic Agent requires subclassing the base Agent class and implementing logic in Python. Prompt Agents rely on AgentFactory to handle instantiation, tool binding, and chat client configuration automatically based on the YAML definition.

How do I add custom tools to a declarative agent?

Define tools in the YAML tools array using the appropriate kind (such as function, web_search, or file_search), then ensure the function implementations are available in your Python environment. The _parse_tool method at line 333 in _loader.py converts these declarations into AFFunctionTool objects that the runtime executes when the agent invokes them.

Can I use declarative agents with Azure OpenAI or other private endpoints?

Yes. Specify the provider as AzureOpenAI in your YAML model configuration and pass endpoint and API key information through the client_kwargs parameter when initializing AgentFactory. The provider mapping dictionary translates these configurations into the appropriate Azure-specific chat client classes without modifying your YAML definition.

Is it safe to load YAML definitions from untrusted sources?

By default, yes. When safe_mode=True (the default setting), PowerFx expressions in the YAML cannot access environment variables or execute arbitrary code. You can further restrict capabilities by validating YAML content before passing it to the factory or by using the dictionary-based creation methods after sanitizing external input.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →