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:
- Parse YAML using
yaml.safe_loadto produce a plain dictionary - Dispatch to
PromptAgentviaagent_schema_dispatchfor Pydantic-style validation - Resolve provider configuration through
_retrieve_provider_configuration - Instantiate the chat client with model IDs, endpoints, and API keys
- Parse tools using
_parse_tool(line 333) to convert declarative definitions intoAFFunctionToolobjects - 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 constructorenv_file_path: Specify a.envfile for loading environment variables securelycustom_providers: Extend thePROVIDER_TYPE_OBJECT_MAPPINGwith 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
AgentFactoryinagent_framework_declarative/_loader.pyto convert YAML into executable agents without writing Python agent classes. - Define agent structure through the
PromptAgentschema, 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_kwargsorenv_file_pathfor 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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →