running-agents
Learn how to run agents with Agent Framework
$ npx -y skills add managedcode/dotnet-skills --agent claude-codeHow it fires
How this agent gets triggered: by you, by Claude, or both.
- Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
- You can call itInvoke it directly when you want it.
Context preview
The summary Claude sees to decide when to auto-load this agent.
Learn how to run agents with Agent Framework
Agent definition
running-agents.mdtitle: Running Agents
description: Learn how to run agents with Agent Framework
zone_pivot_groups: programming-languages
author: markwallace
ms.topic: reference
ms.author: markwallace
ms.date: 09/24/2025
ms.service: agent-framework
Running Agents
The base Agent abstraction exposes various options for running the agent. Callers can choose to supply zero, one, or many input messages. Callers can also choose between streaming and non-streaming. Let's dig into the different usage scenarios.
Streaming and non-streaming
Microsoft Agent Framework supports both streaming and non-streaming methods for running an agent.
::: zone pivot="programming-language-csharp"
For non-streaming, use the `RunAsync` method.
Console.WriteLine(await agent.RunAsync("What is the weather like in Amsterdam?"));For streaming, use the `RunStreamingAsync` method.
await foreach (var update in agent.RunStreamingAsync("What is the weather like in Amsterdam?"))
{
Console.Write(update);
}::: zone-end ::: zone pivot="programming-language-python"
For non-streaming, use the `run` method.
result = await agent.run("What is the weather like in Amsterdam?")
print(result.text)For streaming, use the `run_stream` method.
async for update in agent.run_stream("What is the weather like in Amsterdam?"):
if update.text:
print(update.text, end="", flush=True)::: zone-end
Agent run options
::: zone pivot="programming-language-csharp"
The base agent abstraction does allow passing an options object for each agent run, however the ability to customize a run at the abstraction level is quite limited. Agents can vary significantly and therefore there aren't really common customization options.
For cases where the caller knows the type of the agent they are working with, it is possible to pass type specific options to allow customizing the run.
For example, here the agent is a `ChatClientAgent` and it is possible to pass a `ChatClientAgentRunOptions` object that inherits from `AgentRunOptions`. This allows the caller to provide custom <xref:Microsoft.Extensions.AI.ChatOptions> that are merged with any agent level options before being passed to the `IChatClient` that the `ChatClientAgent` is built on.
var chatOptions = new ChatOptions() { Tools = [AIFunctionFactory.Create(GetWeather)] };
Console.WriteLine(await agent.RunAsync("What is the weather like in Amsterdam?", options: new ChatClientAgentRunOptions(chatOptions)));::: zone-end ::: zone pivot="programming-language-python"
Python agents support customizing each run via the `options` parameter. Options are passed as a TypedDict and can be set at both construction time (via `default_options`) and per-run (via `options`). Each provider has its own TypedDict class that provides full IDE autocomplete and type checking for provider-specific settings.
Common options include:
- `max_tokens`: Maximum number of tokens to generate
- `temperature`: Controls randomness in response generation
- `model_id`: Override the model for this specific run
- `top_p`: Nucleus sampling parameter
- `response_format`: Specify the response format (e.g., structured output)
> [!NOTE] > The `tools` and `instructions` parameters remain as direct keyword arguments and are not passed via the `options` dictionary.
from agent_framework.openai import OpenAIChatClient, OpenAIChatOptions
# Set default options at construction time
agent = OpenAIChatClient().as_agent(
instructions="You are a helpful assistant",
default_options={
"temperature": 0.7,
"max_tokens": 500
}
)
# Run with custom options (overrides defaults)
# OpenAIChatOptions provides IDE autocomplete for all OpenAI-specific settings
options: OpenAIChatOptions = {
"temperature": 0.3,
"max_tokens": 150,
"model_id": "gpt-4o",
"presence_penalty": 0.5,
"frequency_penalty": 0.3
}
result = await agent.run(
"What is the weather like in Amsterdam?",
options=options
)
# Streaming with custom options
async for update in agent.run_stream(
"Tell me a detailed weather forecast",
options={"temperature": 0.7, "top_p": 0.9},
tools=[additional_weather_tool] # tools is still a keyword argument
):
if update.text:
print(update.text, end="", flush=True)Each provider has its own TypedDict class (e.g., `OpenAIChatOptions`, `AnthropicChatOptions`, `OllamaChatOptions`) that exposes the full set of options supported by that provider.
When both `default_options` and per-run `options` are provided, the per-run options take precedence and are merged with the defaults.
::: zone-end
Response types
Both streaming and non-streaming responses from agents contain all content produced by the agent. Content might include data that is not the result (that is, the answer to the user question) from the agent. Examples of other data returned include function tool calls, results from function tool calls, reasoning text, status updates, and many more.
Since not all content returned is the result, it's important to look for specific content types when trying to isolate the result from the other content.
::: zone pivot="programming-language-csharp"
To extract the text result from a response, all `TextContent` items from all `ChatMessages` items need to be aggregated. To simplify this, a `Text` property is available on all response types that aggregates all `TextContent`.
For the non-streaming case, everything is returned in one `AgentResponse` object. `AgentResponse` allows access to the produced messages via the `Messages` property.
var response = await agent.RunAsync("What is the weather like in Amsterdam?");
Console.WriteLine(response.Text);
Console.WriteLine(response.Messages.Count);For the streaming case, `AgentResponseUpdate` objects are streamed as they are produced. Each update might contain a part of the result from the agent, and also various other cont
Read more
title: Running Agents description: Learn how to run agents with Agent Framework zone_pivot_groups: programming-languages author: markwallace ms.topic: reference ms.author: markwallace ms.date: 09/24/2025 ms.service: agent-framework
Running Agents
The base Agent abstraction exposes various options for running the agent. Callers can choose to supply zero, one, or many input messages. Callers can also choose between streaming and non-streaming. Let's dig into the different usage scenarios.
Streaming and non-streaming
Microsoft Agent Framework supports both streaming and non-streaming methods for running an agent.
::: zone pivot="programming-language-csharp"
For non-streaming, use the `RunAsync` method.
Console.WriteLine(await agent.RunAsync("What is the weather like in Amsterdam?"));For streaming, use the `RunStreamingAsync` method.
await foreach (var update in agent.RunStreamingAsync("What is the weather like in Amsterdam?"))
{
Console.Write(update);
}::: zone-end ::: zone pivot="programming-language-python"
For non-streaming, use the `run` method.
result = await agent.run("What is the weather like in Amsterdam?")
print(result.text)For streaming, use the `run_stream` method.
async for update in agent.run_stream("What is the weather like in Amsterdam?"):
if update.text:
print(update.text, end="", flush=True)::: zone-end
Agent run options
::: zone pivot="programming-language-csharp"
The base agent abstraction does allow passing an options object for each agent run, however the ability to customize a run at the abstraction level is quite limited. Agents can vary significantly and therefore there aren't really common customization options.
For cases where the caller knows the type of the agent they are working with, it is possible to pass type specific options to allow customizing the run.
For example, here the agent is a `ChatClientAgent` and it is possible to pass a `ChatClientAgentRunOptions` object that inherits from `AgentRunOptions`. This allows the caller to provide custom <xref:Microsoft.Extensions.AI.ChatOptions> that are merged with any agent level options before being passed to the `IChatClient` that the `ChatClientAgent` is built on.
var chatOptions = new ChatOptions() { Tools = [AIFunctionFactory.Create(GetWeather)] };
Console.WriteLine(await agent.RunAsync("What is the weather like in Amsterdam?", options: new ChatClientAgentRunOptions(chatOptions)));::: zone-end ::: zone pivot="programming-language-python"
Python agents support customizing each run via the `options` parameter. Options are passed as a TypedDict and can be set at both construction time (via `default_options`) and per-run (via `options`). Each provider has its own TypedDict class that provides full IDE autocomplete and type checking for provider-specific settings.
Common options include:
- `max_tokens`: Maximum number of tokens to generate
- `temperature`: Controls randomness in response generation
- `model_id`: Override the model for this specific run
- `top_p`: Nucleus sampling parameter
- `response_format`: Specify the response format (e.g., structured output)
> [!NOTE] > The `tools` and `instructions` parameters remain as direct keyword arguments and are not passed via the `options` dictionary.
from agent_framework.openai import OpenAIChatClient, OpenAIChatOptions
# Set default options at construction time
agent = OpenAIChatClient().as_agent(
instructions="You are a helpful assistant",
default_options={
"temperature": 0.7,
"max_tokens": 500
}
)
# Run with custom options (overrides defaults)
# OpenAIChatOptions provides IDE autocomplete for all OpenAI-specific settings
options: OpenAIChatOptions = {
"temperature": 0.3,
"max_tokens": 150,
"model_id": "gpt-4o",
"presence_penalty": 0.5,
"frequency_penalty": 0.3
}
result = await agent.run(
"What is the weather like in Amsterdam?",
options=options
)
# Streaming with custom options
async for update in agent.run_stream(
"Tell me a detailed weather forecast",
options={"temperature": 0.7, "top_p": 0.9},
tools=[additional_weather_tool] # tools is still a keyword argument
):
if update.text:
print(update.text, end="", flush=True)Each provider has its own TypedDict class (e.g., `OpenAIChatOptions`, `AnthropicChatOptions`, `OllamaChatOptions`) that exposes the full set of options supported by that provider.
When both `default_options` and per-run `options` are provided, the per-run options take precedence and are merged with the defaults.
::: zone-end
Response types
Both streaming and non-streaming responses from agents contain all content produced by the agent. Content might include data that is not the result (that is, the answer to the user question) from the agent. Examples of other data returned include function tool calls, results from function tool calls, reasoning text, status updates, and many more.
Since not all content returned is the result, it's important to look for specific content types when trying to isolate the result from the other content.
::: zone pivot="programming-language-csharp"
To extract the text result from a response, all `TextContent` items from all `ChatMessages` items need to be aggregated. To simplify this, a `Text` property is available on all response types that aggregates all `TextContent`.
For the non-streaming case, everything is returned in one `AgentResponse` object. `AgentResponse` allows access to the produced messages via the `Messages` property.
var response = await agent.RunAsync("What is the weather like in Amsterdam?");
Console.WriteLine(response.Text);
Console.WriteLine(response.Messages.Count);For the streaming case, `AgentResponseUpdate` objects are streamed as they are produced. Each update might contain a part of the result from the agent, and also various other cont
Stop explaining .NET to your AI. Start building. We've all been there: asking Claude to use Entity Framework, only to get EF6 patterns in a .NET 8 project. Explaining to Copilot that Blazor Server and Blazor WebAssembly aren't the same thing.
Repo: managedcode/dotnet-skills
Other agents on dotnet-skills.
- AGENT
Specialist orchestration agent for .NET Aspire work. Use when the problem is clearly about AppHost design, ServiceDefaults, first-party versus CommunityToolkit/Aspire integrations, dashboard and testing, `DistributedApplicationTestingBuilder`, `WebApplicationFactory`
Open agent - agent-as-function-tool
Legacy tutorial alias retained locally; the live Learn URL now resolves into the broader Function Tools surface
Open agent - agent-as-mcp-tool
Learn how to expose an agent as a tool over the MCP protocol
Open agent - create-and-run-durable-agent
Learn how to create and run a durable AI agent with Azure Functions and the durable task extension for Microsoft Agent Framework
Open agent - enable-observability
Enable OpenTelemetry for an agent so agent interactions are automatically logged
Open agent - function-tools-approvals
Learn how to use function tools with human in the loop approvals
Open agent

