diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index 2bcb24db..79e15983 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -21,13 +21,18 @@ title: Page title description: Brief description zone_pivot_groups: programming-languages # If showing code in multiple languages author: github-username -ms.topic: conceptual +ms.topic: article ms.author: microsoft-alias ms.date: MM/DD/YYYY ms.service: semantic-kernel --- ``` +Use `article` by default. The docsets also use `how-to`, `overview`, +`quickstart`, `reference`, `sample`, `tutorial`, and +`upgrade-and-migration-article` when those types match the page's purpose. +Do not use `conceptual`. + ### Zone Pivots for Multi-Language Content Zone pivots show language-specific content. **Note the different language support per docset:** diff --git a/agent-framework/AGENTS.md b/agent-framework/AGENTS.md index 3b1c1001..2df753a7 100644 --- a/agent-framework/AGENTS.md +++ b/agent-framework/AGENTS.md @@ -74,6 +74,14 @@ agent-framework/ │ ├── openai-endpoints.md │ ├── m365.md │ └── purview.md +├── hosting/ # Hosting model selection and guides +│ ├── index.md # Managed vs self-hosted overview +│ ├── foundry-hosted-agent.md +│ └── self-hosting/ +│ ├── index.md # Shared self-hosting state and protocol choices +│ ├── responses.md +│ ├── telegram.md +│ └── a2a.md ├── devui/ # DevUI reference (top-level) │ ├── index.md │ └── ... @@ -97,12 +105,14 @@ agent-framework/ builds complexity step by step. Agents/workflows/integrations are reference docs organized by topic — users land here from "Go deeper" links. -2. **Zone pivots for languages**: Every page that shows code uses - `zone_pivot_groups: programming-languages` in its YAML frontmatter. - Code blocks are wrapped in `:::zone pivot="programming-language-csharp"`, - `:::zone pivot="programming-language-python"`, and - `:::zone pivot="programming-language-go"` sections when the feature is - supported in those SDKs. +2. **Zone pivots for languages**: Use + `zone_pivot_groups: programming-languages` and matching + `:::zone pivot="..."` sections only when a page presents code in multiple + supported SDKs. You can also use a non-empty "coming soon" zone for an SDK + that is planned but not yet supported. When you use a pivot group, include a + non-empty zone for every pivot ID it defines; otherwise the validator reports + blank language tabs. Do not declare a multi-language pivot group on a + language-specific page. 3. **Code snippets as source of truth**: Prefer `:::code` directives that point to sample files in the code repo, so docs stay synced with runnable samples. @@ -173,19 +183,24 @@ title: "Page Title" description: "One-line description for SEO" zone_pivot_groups: programming-languages author: eavanvalkenburg -ms.topic: article # or "tutorial" for get-started +ms.topic: article # Use overview, tutorial, how-to, reference, or upgrade-and-migration-article when appropriate ms.date: MM/DD/YYYY ms.service: agent-framework --- ``` +Use `article` by default. Do not use `conceptual`; it is not a supported +topic value. Use `overview` for section landing pages and choose `tutorial`, +`how-to`, `reference`, or `upgrade-and-migration-article` only when they +match the page's purpose. + ## TOC.yml conventions - **Single flat TOC**: All entries are in the root `TOC.yml` — no nested sub-TOC files (`href: .../TOC.yml`). This avoids breadcrumb compatibility issues and keeps navigation in a single source of truth. - Top-level items: Agent Framework, Get Started, Agents, Workflows, Integrations, - DevUI, Migration Guide, API Reference, Support + Hosting, DevUI, Migration Guide, API Reference, Support - Each section uses `items:` for child pages - `expanded: true` only on Get Started (first section users see) @@ -226,6 +241,11 @@ Every docs page maps to sample files in both repos: | `integrations/hyperlight.md` | `02-agents/context_providers/code_act/code_act.py` | `02-agents/AgentWithCodeAct/` | | `integrations/a2a.md` | `04-hosting/a2a/` | `04-hosting/a2a/` | | `integrations/durable-extension.md` | `04-hosting/azure_functions/`, `04-hosting/durabletask/` | `04-hosting/DurableAgents/`, `04-hosting/DurableWorkflows/` | +| `hosting/self-hosting/index.md` | `04-hosting/af-hosting/` | N/A | +| `hosting/self-hosting/responses.md` | `04-hosting/af-hosting/local_responses/`, `04-hosting/af-hosting/local_responses_workflow/` | N/A | +| `hosting/self-hosting/telegram.md` | `04-hosting/af-hosting/local_telegram/` | N/A | +| `hosting/self-hosting/a2a.md` | `04-hosting/a2a/` | N/A | +| `hosting/self-hosting/mcp.md` | `04-hosting/mcp/` | N/A | ## When adding a new docs page diff --git a/agent-framework/TOC.yml b/agent-framework/TOC.yml index 2c789a6b..75dfc4ca 100644 --- a/agent-framework/TOC.yml +++ b/agent-framework/TOC.yml @@ -232,10 +232,22 @@ items: href: integrations/ag-ui/testing-with-dojo.md - name: Hosting items: + - name: Overview + href: hosting/index.md - name: Foundry Hosted Agents href: hosting/foundry-hosted-agent.md - - name: A2A - href: hosting/agent-to-agent.md + - name: Self-hosting + items: + - name: Overview + href: hosting/self-hosting/index.md + - name: OpenAI Responses + href: hosting/self-hosting/responses.md + - name: Telegram + href: hosting/self-hosting/telegram.md + - name: A2A + href: hosting/self-hosting/a2a.md + - name: MCP + href: hosting/self-hosting/mcp.md - name: The Agent Development Journey items: - name: Overview diff --git a/agent-framework/get-started/hosting.md b/agent-framework/get-started/hosting.md index 86c633eb..4849e253 100644 --- a/agent-framework/get-started/hosting.md +++ b/agent-framework/get-started/hosting.md @@ -132,6 +132,8 @@ app.Run(); :::zone pivot="programming-language-python" +Azure Functions is one self-managed hosting option. For a comparison of Microsoft-managed Foundry Hosted Agents, self-hosting, and durable Azure Functions workloads, see [Hosting Agent Framework applications](../hosting/index.md). + Install the Azure Functions hosting package, Foundry client, and Azure authentication package: ```bash @@ -239,6 +241,7 @@ http.ListenAndServe(":5000", mux) - [A2A Protocol](../integrations/a2a.md) — expose and consume agents via A2A - [Durable Extension](../integrations/durable-extension.md) — durable C# and Python agent and workflow hosting - [AG-UI Protocol](../integrations/ag-ui/index.md) — web-based agent UIs +- [Hosting overview](../hosting/index.md) — choose Foundry Hosted Agents, self-hosting, or durable hosting - [Foundry Hosted Agents docs](/azure/ai-foundry/agents/concepts/hosted-agents) — understand hosted agents in Microsoft Foundry - [Foundry Hosted Agents sample (Python)](https://github.com/microsoft-foundry/foundry-samples/tree/main/samples/python/hosted-agents/agent-framework) — run an end-to-end Agent Framework hosted-agent sample diff --git a/agent-framework/hosting/foundry-hosted-agent.md b/agent-framework/hosting/foundry-hosted-agent.md index bb46fd32..4d7c7add 100644 --- a/agent-framework/hosting/foundry-hosted-agent.md +++ b/agent-framework/hosting/foundry-hosted-agent.md @@ -5,7 +5,7 @@ zone_pivot_groups: programming-languages author: taochen ms.topic: article ms.author: taochen -ms.date: 05/27/2026 +ms.date: 07/17/2026 ms.service: agent-framework --- @@ -24,9 +24,9 @@ ms.service: agent-framework # Foundry Hosted Agents -[Hosted agents](/azure/foundry/agents/concepts/hosted-agents) in Microsoft Foundry Agent Service let you deploy Agent Framework agents as containerized applications to Microsoft-managed infrastructure. The platform handles scaling, session state persistence, security, and lifecycle management so you can focus on your agent's logic. +[Hosted agents](/azure/foundry/agents/concepts/hosted-agents) in Microsoft Foundry Agent Service let you deploy Agent Framework agents as containerized applications to Microsoft-managed infrastructure. The platform handles scaling, session state persistence, security, and lifecycle management so you can focus on your agent's logic. Microsoft Foundry Hosted Agents is generally available. -With the Agent Framework hosting integration, you can take any `Agent` or workflow and expose it through the Foundry Responses or Invocations protocol with minimal code. +With the Agent Framework hosting integration, you can expose an `Agent`, including a workflow wrapped with `Workflow.as_agent()`, through the Foundry Responses or Invocations protocol with minimal code. ## When to use hosted agents @@ -38,7 +38,7 @@ Choose Foundry hosted agents when you want: - **OpenAI-compatible endpoints** — clients can interact with your agent using any OpenAI-compatible SDK through the Responses protocol. > [!NOTE] -> Foundry hosted agents are currently in preview. See the [Foundry hosted agents documentation](/azure/foundry/agents/concepts/hosted-agents#limits-pricing-and-availability-preview) for the latest availability, limits, and pricing. +> The Python `agent-framework-foundry-hosting` integration is prerelease. Microsoft Foundry Hosted Agents, the managed hosting service, is generally available. ## Prerequisites @@ -67,14 +67,16 @@ dotnet add package Azure.AI.Projects --prerelease - Python 3.10 or later -Install the hosting Python package: +Install the prerelease hosting package, Foundry client, and Azure authentication package: ```bash -pip install agent-framework agent-framework-foundry-hosting +pip install --pre agent-framework-foundry agent-framework-foundry-hosting azure-identity ``` :::zone-end +In Foundry, the platform supplies the caller's user context and call context; the hosting infrastructure uses them to isolate state per user and forward request context to Foundry services. Local runs don't receive that platform context, so applications must supply their own identity and state controls when needed. + ## Responses protocol The **Responses** protocol is the recommended starting point for most agents. It exposes an OpenAI-compatible `/responses` endpoint, and the platform manages conversation history, streaming, and session lifecycle automatically. @@ -363,7 +365,7 @@ Once deployed, your agent is accessible through its dedicated Foundry endpoint a - [Deploy a hosted agent with the Foundry SDK](/azure/foundry/agents/how-to/deploy-hosted-agent) - [Manage hosted agents](/azure/foundry/agents/how-to/manage-hosted-agent) -- [Azure Functions (Durable) hosting](../integrations/azure-functions.md) -- [A2A Hosting](agent-to-agent.md) +- [Azure Functions and durable hosting](../integrations/durable-extension.md) +- [Self-host A2A agents](self-hosting/a2a.md) - [Python samples](https://github.com/microsoft-foundry/foundry-samples/tree/main/samples/python/hosted-agents/agent-framework) - [C# samples](https://github.com/microsoft-foundry/foundry-samples/tree/main/samples/csharp/hosted-agents/agent-framework) diff --git a/agent-framework/hosting/index.md b/agent-framework/hosting/index.md new file mode 100644 index 00000000..bf6b37a3 --- /dev/null +++ b/agent-framework/hosting/index.md @@ -0,0 +1,51 @@ +--- +title: Hosting Agent Framework applications +description: Choose between Microsoft-managed Foundry Hosted Agents and self-hosting Agent Framework applications. +author: eavanvalkenburg +ms.topic: overview +ms.author: edvan +ms.date: 07/21/2026 +ms.service: agent-framework +--- + +# Hosting Agent Framework applications + +After you build an agent or workflow, first choose who operates its infrastructure. This is an operational choice between Microsoft-managed Foundry Hosted Agents and self-hosting; it is separate from the protocol that clients use to reach your agent. + +## Choose a hosting model + +| | [Foundry Hosted Agents](foundry-hosted-agent.md) | [Self-hosting](self-hosting/index.md) | +|---|---|---| +| **Who operates the infrastructure?** | Microsoft Foundry Agent Service runs the container, scaling, session lifecycle, and platform integration. | Your application runs in your web service, container, runtime, or existing infrastructure. | +| **What do you operate?** | Your agent code and Foundry configuration. | Routes, identity, authorization, request policy, storage, deployment, scaling, and native client libraries. | +| **Choose this when** | You want Microsoft-managed agent hosting. | You need application-level control or must integrate with your existing infrastructure. | +| **Start here** | [Host an agent in Foundry](foundry-hosted-agent.md) | [Self-host an Agent Framework application](self-hosting/index.md) | + +Microsoft Foundry Hosted Agents is generally available. The current Python self-hosting packages are prerelease; see the self-hosting guide for package-specific lifecycle information. + +For Azure Functions triggers, durable execution, or long-running orchestration, use the [Durable Extension](../integrations/durable-extension.md). It is a self-managed hosting path with Durable Task infrastructure. + +## Choose a protocol separately + +The hosting model does not determine the protocol. For example, the OpenAI Responses protocol works with both models: + +- **Foundry Hosted Agents** expose managed Responses and Invocations endpoints and support the Activity protocol for Microsoft 365 channels. +- **Self-hosting** lets your application use the Responses helpers to expose a `/responses` endpoint with its own framework, routing, and policy. + +After choosing a host, select the client integration that fits your scenario: + +- [OpenAI-compatible endpoints](../integrations/openai-endpoints.md) for Responses and Chat Completions-compatible APIs. +- [Agent-to-Agent (A2A)](../integrations/a2a.md) for agent interoperability. +- [AG-UI](../integrations/ag-ui/index.md) for web-based agent applications. +- [Telegram bots](self-hosting/telegram.md) for a self-hosted native Telegram Bot API integration. +- [MCP tools](self-hosting/mcp.md) for exposing an agent or workflow as a native MCP tool. + +## Next steps + +> [!div class="nextstepaction"] +> [Choose Foundry Hosted Agents](foundry-hosted-agent.md) + +**Go deeper:** + +- [Self-hosting](self-hosting/index.md) +- [Durable Extension](../integrations/durable-extension.md) diff --git a/agent-framework/hosting/self-hosting/a2a.md b/agent-framework/hosting/self-hosting/a2a.md new file mode 100644 index 00000000..4d8be516 --- /dev/null +++ b/agent-framework/hosting/self-hosting/a2a.md @@ -0,0 +1,126 @@ +--- +title: Self-host A2A agents +description: Choose an opinionated A2A executor, an app-owned adapter, or conversion helpers for Agent Framework agents and workflows. +zone_pivot_groups: programming-languages +author: eavanvalkenburg +ms.topic: article +ms.author: edvan +ms.date: 07/23/2026 +ms.service: agent-framework +--- + +# Self-host A2A agents + +:::zone pivot="programming-language-csharp" + +Use the .NET A2A hosting packages to expose an Agent Framework agent through ASP.NET Core. See [A2A integration](../../integrations/a2a.md) for package setup and a complete server example. + +:::zone-end + +:::zone pivot="programming-language-go" + +Use the Go `provider/a2aprovider` package with the official A2A Go server handlers. See [A2A integration](../../integrations/a2a.md) for a complete server example. + +:::zone-end + +:::zone pivot="programming-language-python" + +Agent Framework provides two Python packages for hosting agents and workflows through the official [A2A SDK](https://pypi.org/project/a2a-sdk/): + +| Package | Integration model | Use it when | +|---|---|---| +| `agent-framework-a2a` | An opinionated `A2AExecutor` that converts requests, runs an agent, and publishes A2A task events and artifacts. | You want the standard Agent Framework-to-A2A behavior and only need to assemble the A2A SDK server. | +| `agent-framework-hosting-a2a` | Incremental building blocks for an app-owned executor. Start with the foundational agent or workflow converters, and optionally use `AgentA2AAdapter` or `WorkflowA2AAdapter`, which build on those converters to add native card generation and mode validation. | Your application needs to own session mapping, task transitions, event delivery, artifact boundaries, output conversion, or a multi-protocol host. | + +Both packages use native A2A SDK types and server components. Your application supplies the request handler, task store, routes or SDK application builder, authentication, and deployment. With `agent-framework-hosting-a2a`, the application can construct the agent card directly or let an adapter generate it. + +## Use the opinionated A2A executor + +Install `agent-framework-a2a` when the built-in server adapter matches your lifecycle: + +```bash +pip install --pre agent-framework-a2a starlette uvicorn +``` + +`A2AExecutor` implements the A2A SDK's `AgentExecutor`. It reads the user input from the A2A request context, creates an Agent Framework session from the A2A context ID, runs the agent in streaming or non-streaming mode, converts supported output content, and publishes task status and artifact events through the SDK's `TaskUpdater`. + +Compose it with the A2A SDK's `DefaultRequestHandler`, task store, agent card, and Starlette application or another supported server integration. Configure streaming with `A2AExecutor(agent, stream=True)`, pass stable agent run options through `run_kwargs`, or subclass `A2AExecutor` and override `handle_events` when you need a different output mapping. + +`A2AExecutor` is scoped to an A2A endpoint and manages its A2A execution and session mapping directly. Use the hosting packages when the same agent must be available through several protocols in one application. + +For the complete server setup, see [Expose an Agent Framework agent over A2A](../../integrations/a2a.md#exposing-an-agent-framework-agent-over-a2a). + +## Use an adapter in an app-owned executor + +Install the hosting package when your application owns the native A2A executor but wants Agent Framework to generate the public card and validate conversions: + +```bash +pip install --pre agent-framework-hosting-a2a starlette uvicorn +``` + +`AgentA2AAdapter` accepts an agent or `AgentState`. Its asynchronous `get_card` method derives the public name and description, uses conservative text modes by default, and can infer native A2A skills from Agent Framework `SkillsProvider` instances. Server capabilities and supported interfaces remain explicit because they describe the application endpoint rather than the agent's `run` method. + +The adapter exposes `a2a_to_run` and `a2a_from_run` methods that validate values against the configured card modes by default. The application still owns the A2A executor, task lifecycle, event queue, artifact boundaries, session policy, authentication, routes, and deployment. + +This executor uses one adapter for inbound conversion, agent state, and outbound conversion: + +:::code language="python" source="~/../agent-framework-code/python/samples/04-hosting/a2a/agent_framework_to_a2a.py" range="33-100"::: + +The server setup creates the adapter, generates its native `AgentCard`, and composes the app-owned executor with the A2A SDK request handler: + +:::code language="python" source="~/../agent-framework-code/python/samples/04-hosting/a2a/agent_framework_to_a2a.py" range="103-138"::: + +## Build an app-owned A2A executor + +Use the standalone hosting helpers when your application also needs direct control over card creation: + +```bash +pip install --pre agent-framework-hosting-a2a starlette uvicorn +``` + +The helpers are framework-neutral: + +- `a2a_to_run` converts an A2A `Message` to Agent Framework run arguments. +- `a2a_from_run` converts Agent Framework responses and streaming updates to A2A `Part` values. + +Your executor selects session keys and owns task transitions, event queues, artifact IDs, message boundaries, and outbound delivery. `a2a_from_run` returns a flat part list so the application can group those parts into A2A messages or artifacts and apply message-level metadata. + +The hosting setup also supports multi-protocol applications. Share the same agent target and `AgentState` infrastructure across A2A, OpenAI Responses, Telegram, and MCP routes, while each protocol endpoint keeps its own conversion, authorization, and session-key policy. This lets clients reach one agent through different protocols at the same time without creating a separate agent deployment for each endpoint. + +Compose the helpers in a native A2A SDK executor. This sample creates and updates A2A tasks, converts the inbound message into an Agent Framework run, persists the updated `AgentState` session after the stream finishes, and publishes returned parts as artifacts. + +:::code language="python" source="~/../agent-framework-code/python/samples/04-hosting/a2a/a2a_server.py" range="57-124"::: + +The sample uses Starlette and Uvicorn, but the helpers are not tied to either. Use your application framework or an A2A SDK application builder to serve the A2A agent card and JSON-RPC routes: + +:::code language="python" source="~/../agent-framework-code/python/samples/04-hosting/a2a/a2a_server.py" range="171-192"::: + +## Host a workflow with an adapter + +`WorkflowA2AAdapter` provides the same card-generation and conversion boundary for a workflow or `WorkflowState`. It infers conservative input and output modes from the workflow's declared types, or you can supply explicit modes for an application-specific representation. + +The standalone `a2a_to_workflow_run` and `a2a_from_workflow_run` helpers provide typed workflow input and output conversion. The adapter exposes them as asynchronous `a2a_to_run` and synchronous `a2a_from_run` methods that validate against its effective card modes. Input conversion accepts one A2A text, raw, or data part for the workflow's single start-executor input type, and output conversion maps completed public workflow outputs to native A2A parts. Call `get_card` before validated output conversion when the adapter must infer output modes. + +The application remains responsible for the native A2A executor and for streaming progress, task status, artifacts, checkpoints, and human-in-the-loop continuation. Pending human-input requests aren't converted automatically, so the host must implement its own continuation policy. + +## Secure sessions and task state + +`A2AExecutor` uses the A2A context ID as the Agent Framework session ID. The adapter-based and helper-based samples combine the A2A tenant and context ID to demonstrate an application-selected mapping. In every approach, a production host must authenticate the caller before it reaches the A2A request handler, derive the tenant and subject from that trusted identity, and authorize all task, context, continuation, and cancellation IDs. + +> [!IMPORTANT] +> The A2A SDK's default task and push-configuration stores are in-memory and scope ownership by user name. For a multi-tenant service, use an `owner_resolver` that derives ownership from the same trusted tenant and subject, and use durable task and session stores when replicas can restart or scale out. + +For a complete helper-based server and multi-agent examples, see the [A2A hosting samples](https://github.com/microsoft/agent-framework/tree/main/python/samples/04-hosting/a2a). For A2A clients and protocol capabilities, see [A2A integration](../../integrations/a2a.md). + +## Next steps + +> [!div class="nextstepaction"] +> [Learn about A2A integration](../../integrations/a2a.md) + +**Go deeper:** + +- [Self-hosting overview](index.md) +- [OpenAI Responses](responses.md) +- [Telegram](telegram.md) + +:::zone-end diff --git a/agent-framework/hosting/self-hosting/index.md b/agent-framework/hosting/self-hosting/index.md new file mode 100644 index 00000000..779dcf50 --- /dev/null +++ b/agent-framework/hosting/self-hosting/index.md @@ -0,0 +1,107 @@ +--- +title: Self-host Agent Framework applications +description: Build an application-owned server and add one or more Agent Framework protocols. +zone_pivot_groups: programming-languages +author: eavanvalkenburg +ms.topic: article +ms.author: edvan +ms.date: 07/22/2026 +ms.service: agent-framework +--- + +# Self-host Agent Framework applications + +:::zone pivot="programming-language-csharp" + +> [!NOTE] +> Self-hosting protocol helpers for .NET are coming soon. The hosting model will let your application own its server, state, and protocol integrations. + +:::zone-end + +:::zone pivot="programming-language-go" + +> [!NOTE] +> Self-hosting protocol helpers are not currently available for Go. + +:::zone-end + +:::zone pivot="programming-language-python" + +Self-hosting lets you run an Agent Framework agent or workflow in your own web application, container, service, or runtime. Your application controls routing, identity, authorization, request policy, storage, deployment, and scaling. Add one or more protocol integrations to that server based on the clients you need to support. + +Use this option when you need to integrate an agent endpoint with your existing application infrastructure. If you want Microsoft Foundry to run the agent for you, see [Foundry Hosted Agents](../foundry-hosted-agent.md). If you need Azure Functions triggers or durable execution, see [Durable Extension](../../integrations/durable-extension.md). + +The design of these packages is such that is allows for maximum flexibility for the developer. This means that if you want to build a host that exposes a agent with the Responses API, and abuse the parameters for other purposes (i.e. map `temperature` to `top_p`), you can do that. If you don't want to store sessions, you can do that, if you want to allow the caller to control the full agent run, you can do that too. We will not get in the way, we provide helpers for the common cases, and make you responsible for the rest, to allow you to build the exact host that you need. + +> [!IMPORTANT] +> `agent-framework-hosting`, `agent-framework-hosting-responses`, `agent-framework-hosting-telegram`, `agent-framework-a2a`, `agent-framework-hosting-a2a`, and `agent-framework-hosting-mcp` are prerelease Python packages. Install prerelease versions explicitly and review release notes before updating a production deployment. + +```bash +pip install --pre agent-framework-hosting +``` + +## What the hosting helpers provide + +The generic hosting package provides shared execution state for an application-owned server: + +- `AgentState` pairs an agent target with a `SessionStore` and creates sessions when the application selects a new key. +- `SessionStore` stores, retrieves, and deletes sessions by an application-selected ID. Its default store is process-local and has no eviction policy. +- `WorkflowState` resolves a workflow target. Your application owns checkpoint storage and any mapping from a client continuation ID to a checkpoint. + +`AgentState` is not a server or protocol registry. Your application selects an authorized session key, resolves the target, and saves the post-run state. It can use the same target and shared application infrastructure for one or several protocol endpoints. + +## Customize session storage + +`SessionStore` is a small async storage class with `get`, `set`, and `delete` methods. The default implementation keeps sessions in process memory. Subclass it and override those methods to store `AgentSession` objects in Redis, a database, blob storage, or another application-owned store, then pass the instance to `AgentState(session_store=...)`. + +`SessionStore` and [history providers](../../agents/conversations/storage.md) persist separate parts of an agent conversation. A session store saves one session object per session ID, including session metadata and provider state. A dedicated `HistoryProvider` stores the conversation separately, typically as one record per message. This separation is recommended for durable hosts because appending individual messages is generally more efficient than rewriting a growing session object after every turn. A history provider is defined per agent, by passing the desired history provider class to the `context_providers` parameter. + +> [!NOTE] +> The default history provider: `InMemoryHistoryProvider` is the exception: it stores the full conversation in `AgentSession.state`. When that provider is used, `SessionStore` persists the conversation inside the session object. For longer conversations or production storage, use a dedicated history provider so the session store can remain focused on lightweight session state. + +## Bring your own framework or client library + +The hosting packages aren't tied to a web framework or client library. The samples use FastAPI and `aiogram` because they provide concise runnable examples, not because the helpers require them. + +- For HTTP endpoints, use the routing and request/response APIs of your application framework, such as FastAPI, Starlette, Django, Flask, Azure Functions, or another framework. +- For protocol clients such as Telegram, use any client library that can supply a protocol update and execute the operations produced by the helper. + +The application selects its framework and client library; the Agent Framework packages only convert protocol data and manage optional execution state. They don't register routes, authenticate callers, authorize access to state, choose allowed model options, or provide durable storage. + +## Add protocols to your server + +Choose one or more protocol integrations: + +| Protocol | Package and integration | +|---|---|---| +| [OpenAI Responses](responses.md) | `agent-framework-hosting-responses` | +| [Telegram](telegram.md) | `agent-framework-hosting-telegram` | +| [A2A](a2a.md) | `agent-framework-a2a` or `agent-framework-hosting-a2a` | +| [MCP](mcp.md) | `agent-framework-hosting-mcp` | + +Each protocol page describes its setup. However they are designed to allow you to build a single host with one or more protocols enabled and a callable target; either an agent or a workflow. Since we do not limit you to one web framework, you can choose the one you want, and setup the host with those protocols with ease. + +## Secure session continuation + +Treat every protocol-provided identifier as untrusted input. Before using an ID to load a session, checkpoint, task, or other state: + +1. Authenticate the caller. +2. Authorize the caller to access the referenced state. +3. Partition durable state by the authenticated tenant, user, or workspace. +4. Persist session and checkpoint state only after the run or stream has completed. + +This self-hosting pattern lets your application implement only the protocol endpoints and policies it needs; it doesn't attempt to implement the complete API surface of every supported protocol. + +## Next steps + +> [!div class="nextstepaction"] +> [Add the OpenAI Responses protocol](responses.md) + +**Go deeper:** + +- [Telegram](telegram.md) +- [A2A](a2a.md) +- [MCP](mcp.md) +- [Foundry Hosted Agents](../foundry-hosted-agent.md) + +:::zone-end diff --git a/agent-framework/hosting/self-hosting/mcp.md b/agent-framework/hosting/self-hosting/mcp.md new file mode 100644 index 00000000..6ae35321 --- /dev/null +++ b/agent-framework/hosting/self-hosting/mcp.md @@ -0,0 +1,84 @@ +--- +title: Self-host agents as MCP tools +description: Expose an Agent Framework agent or workflow as a native MCP tool from an application-owned server. +zone_pivot_groups: programming-languages +author: eavanvalkenburg +ms.topic: article +ms.author: edvan +ms.date: 07/21/2026 +ms.service: agent-framework +--- + +# Self-host agents as MCP tools + +:::zone pivot="programming-language-csharp" + +> [!NOTE] +> Self-hosting MCP tool support in .NET is coming soon. + +:::zone-end + +:::zone pivot="programming-language-go" + +> [!NOTE] +> Self-hosting MCP tool support is not currently available for Go. + +:::zone-end + +:::zone pivot="programming-language-python" + +Use `agent-framework-hosting-mcp` to expose an Agent Framework agent or workflow as a tool on the native [Model Context Protocol](https://modelcontextprotocol.io/) SDK. The package does not choose a web framework or wrap the MCP SDK server lifecycle; your application still owns the `Server`, handler registration, transport, session-key policy, authentication, authorization, and deployment. + +```bash +pip install --pre agent-framework-hosting-mcp +``` + +## Convert at the protocol boundary + +`mcp_to_run(...)` converts validated MCP tool arguments into Agent Framework messages and selected chat options, and `mcp_from_run(...)` converts a completed response into native MCP `ContentBlock` values. Use these two functions directly when an application's tool contract needs a fully custom native schema and handler: + +:::code language="python" source="~/../agent-framework-code/python/samples/04-hosting/mcp/manual_app.py" range="63-97"::: + +Only argument names listed in `chat_option_arguments` are copied into `run["options"]`; other MCP arguments stay available on the message's raw representation but aren't forwarded to the model client. + +## Host an agent as one generated tool + +`AgentMCPTool` derives the native tool name, description, and schema from an agent, and keeps listing, parsing, execution, and result conversion aligned so the two can't drift: + +:::code language="python" source="~/../agent-framework-code/python/samples/04-hosting/mcp/agent_app.py" range="59-82"::: + +`AgentMCPTool` uses the agent's name and description unless overridden. `parameters` adds app-owned JSON Schema properties that stay available in the raw MCP arguments, and `chat_option_parameters` adds properties whose values are explicitly copied into Agent Framework chat options. + +## Persist a session per call + +Pass an existing `AgentState` and a `session_id_parameter` to let repeated calls with the same opaque, app-defined `session_id` continue one conversation: + +:::code language="python" source="~/../agent-framework-code/python/samples/04-hosting/mcp/session_app.py" range="90-107"::: + +`AgentMCPTool` only performs the `AgentState` session get/run/set sequence; your application must authenticate or authorize the session identifier and serialize concurrent calls for the same session, as the sample does with a per-session `asyncio.Lock`. This isn't `previous_response_id`-style branching — an application that needs to fork a conversation should accept separate source and destination IDs, copy the source session, and store the result under the destination key. + +## Host a workflow as a tool + +`WorkflowMCPTool` derives one native MCP tool from a workflow's start-executor input type and converts completed workflow outputs. Dataclass, Pydantic, and other object-shaped inputs become top-level MCP arguments; primitive inputs are wrapped in a configurable argument name: + +:::code language="python" source="~/../agent-framework-code/python/samples/04-hosting/mcp/workflow_app.py" range="66-70"::: + +Workflow instances preserve execution state, so applications that need independent calls should supply a `WorkflowState` factory with `cache_target=False`, as shown above. Checkpoint restoration, human-in-the-loop responses, and continuation identifiers remain application-owned; if a workflow requests external input, the adapter raises instead of returning an empty successful tool result. + +For the complete set of runnable servers — including the FastMCP variant that derives its schema from a decorated function — see the [MCP hosting samples](https://github.com/microsoft/agent-framework/tree/main/python/samples/04-hosting/mcp). + +> [!IMPORTANT] +> Treat the MCP session identifier and any app-defined `session_id` argument as untrusted input. Authenticate and authorize the caller before using either to load or save session state, and derive durable partitioning from the authenticated tenant, user, or workspace rather than the raw value. + +## Next steps + +> [!div class="nextstepaction"] +> [Learn about A2A hosting](a2a.md) + +**Go deeper:** + +- [Self-hosting overview](index.md) +- [OpenAI Responses](responses.md) +- [Telegram](telegram.md) + +:::zone-end diff --git a/agent-framework/hosting/self-hosting/responses.md b/agent-framework/hosting/self-hosting/responses.md new file mode 100644 index 00000000..0b97b4f7 --- /dev/null +++ b/agent-framework/hosting/self-hosting/responses.md @@ -0,0 +1,73 @@ +--- +title: Self-host OpenAI Responses endpoints +description: Use the Agent Framework Responses helpers in your application-owned server. +zone_pivot_groups: programming-languages +author: eavanvalkenburg +ms.topic: article +ms.author: edvan +ms.date: 07/21/2026 +ms.service: agent-framework +--- + +# Self-host OpenAI Responses endpoints + +:::zone pivot="programming-language-csharp" + +> [!NOTE] +> Self-hosting helpers for OpenAI Responses endpoints in .NET are coming soon. + +:::zone-end + +:::zone pivot="programming-language-go" + +> [!NOTE] +> Self-hosting helpers for OpenAI Responses endpoints are not currently available for Go. + +:::zone-end + +:::zone pivot="programming-language-python" + +Use `agent-framework-hosting-responses` to convert OpenAI Responses-shaped requests and responses at an endpoint your application owns. Your server chooses the web framework, route, authentication, authorization, request options, and session storage. + +```bash +pip install --pre agent-framework agent-framework-foundry agent-framework-hosting agent-framework-hosting-responses azure-identity +``` + +The FastAPI sample is one implementation. The same helpers work with Django, Flask, Starlette, Azure Functions, or another framework. + +## Host an agent endpoint + +This sample converts the request to Agent Framework run values, applies an application-defined option allowlist, and persists the updated session under the newly created response ID. + +:::code language="python" source="~/../agent-framework-code/python/samples/04-hosting/af-hosting/local_responses/app.py" range="107-179"::: + +`AgentState` resolves the target and loads or creates a session. Save the session after the run, or after a streaming run finishes, because the run updates it. + +For the complete application, including the agent definition and request-option allowlist, see the [local Responses sample](https://github.com/microsoft/agent-framework/tree/main/python/samples/04-hosting/af-hosting/local_responses). + +## Host a workflow endpoint + +`WorkflowState` resolves the workflow, but your application owns checkpoint storage and the mapping from a response ID to a checkpoint. This sample restores the checkpoint selected by an authorized `previous_response_id`, then saves a cursor for the next response. + +:::code language="python" source="~/../agent-framework-code/python/samples/04-hosting/af-hosting/local_responses_workflow/app.py" range="220-272"::: + +The sample's file-backed storage is for local development. Use durable storage when replicas can restart or scale out. + +> [!IMPORTANT] +> Treat `previous_response_id` and `conversation_id` as untrusted input. Authenticate and authorize the caller before using either ID to load or save a session or checkpoint. + +For the broader wire format, see [OpenAI-compatible endpoints](../../integrations/openai-endpoints.md). + +## Next steps + +> [!div class="nextstepaction"] +> [Add Telegram](telegram.md) + +**Go deeper:** + +- [Self-hosting overview](index.md) +- [A2A](a2a.md) +- [MCP](mcp.md) +- [Foundry Hosted Agents](../foundry-hosted-agent.md) + +:::zone-end diff --git a/agent-framework/hosting/self-hosting/telegram.md b/agent-framework/hosting/self-hosting/telegram.md new file mode 100644 index 00000000..a9489783 --- /dev/null +++ b/agent-framework/hosting/self-hosting/telegram.md @@ -0,0 +1,61 @@ +--- +title: Self-host Telegram bots +description: Use the Agent Framework Telegram helpers in an application-owned bot server. +zone_pivot_groups: programming-languages +author: eavanvalkenburg +ms.topic: article +ms.author: edvan +ms.date: 07/22/2026 +ms.service: agent-framework +--- + +# Self-host Telegram bots + +:::zone pivot="programming-language-csharp" + +> [!NOTE] +> Self-hosting helpers for Telegram bots in .NET are coming soon. + +:::zone-end + +:::zone pivot="programming-language-go" + +> [!NOTE] +> Self-hosting helpers for Telegram bots are not currently available for Go. + +:::zone-end + +:::zone pivot="programming-language-python" + +`agent-framework-hosting-telegram` converts Telegram Bot API updates into Agent Framework run values and renders final or streaming runs as Bot API operations. It does not provide a bot client, polling runtime, webhook router, command registry, or delivery framework. + +```bash +pip install --pre agent-framework agent-framework-foundry agent-framework-hosting agent-framework-hosting-telegram azure-identity +``` + +Use any Telegram client library that can supply an update payload and execute the operations returned by the helpers. The sample uses `aiogram`, but the helpers are not tied to it. + +## Process an update + +The `aiogram` webhook sample verifies Telegram's secret header, dispatches the update, and uses a bot-scoped session ID to preserve an agent session for each private chat or shared group chat. + +:::code language="python" source="~/../agent-framework-code/python/samples/04-hosting/af-hosting/local_telegram/app.py" range="176-243"::: + +For polling and webhook setup, command handling, inbound media policy, streaming edits, and production deployment guidance, see the [local Telegram sample](https://github.com/microsoft/agent-framework/tree/main/python/samples/04-hosting/af-hosting/local_telegram). + +> [!IMPORTANT] +> Verify Telegram webhook deliveries before processing updates. A webhook secret authenticates Telegram's delivery, but it does not authorize the Telegram user or chat to access application data. Treat chat and user IDs as untrusted until your application applies its authorization policy. + +## Next steps + +> [!div class="nextstepaction"] +> [Add A2A](a2a.md) + +**Go deeper:** + +- [Self-hosting overview](index.md) +- [OpenAI Responses](responses.md) +- [MCP](mcp.md) +- [Foundry Hosted Agents](../foundry-hosted-agent.md) + +:::zone-end diff --git a/agent-framework/index.yml b/agent-framework/index.yml index 9c07f007..0699651e 100644 --- a/agent-framework/index.yml +++ b/agent-framework/index.yml @@ -6,7 +6,7 @@ metadata: title: Agent Framework documentation description: Agent Framework documentation. ms.topic: hub-page - ms.date: 09/05/2025 + ms.date: 07/14/2026 ms.service: agent-framework searchScope: [] titleSuffix: "" @@ -32,17 +32,17 @@ productDirectory: imageSrc: /agent-framework/media/getstarted.svg links: - url: /agent-framework/get-started/your-first-agent - text: "Step 1: Your First Agent" - - url: /agent-framework/get-started/add-tools - text: "Step 2: Add Tools" + text: "Steps 1-2: Your First Agent and Tools" - url: /agent-framework/get-started/multi-turn text: "Step 3: Multi-Turn Conversations" - url: /agent-framework/get-started/memory text: "Step 4: Memory & Persistence" - url: /agent-framework/get-started/workflows text: "Step 5: Workflows" + - url: /agent-framework/get-started/harness + text: "Step 6: Agent Harness" - url: /agent-framework/get-started/hosting - text: "Step 6: Host Your Agent" + text: "Step 7: Host Your Agent" - title: Agents imageSrc: /agent-framework/media/concept.svg links: @@ -50,6 +50,8 @@ productDirectory: text: Agents overview - url: /agent-framework/agents/tools/index text: Tools + - url: /agent-framework/agents/harness + text: Agent Harness - url: /agent-framework/agents/skills text: Agent Skills - url: /agent-framework/agents/conversations/index @@ -67,17 +69,21 @@ productDirectory: text: Edges - url: /agent-framework/workflows/workflows text: Workflow Builder & Execution - - title: Integrations + - title: Integrations & Hosting imageSrc: /agent-framework/media/minihub.svg links: - url: /agent-framework/integrations/index text: Integrations overview + - url: /agent-framework/hosting/ + text: Hosting overview + - url: /agent-framework/hosting/foundry-hosted-agent + text: Foundry Hosted Agents + - url: /agent-framework/hosting/self-hosting + text: Self-hosting - url: /agent-framework/integrations/durable-extension - text: Durable Extension + text: Azure Functions and durable hosting - url: /agent-framework/integrations/a2a text: A2A Protocol - - url: /agent-framework/devui/index - text: DevUI - title: Support imageSrc: /agent-framework/media/howtoguide.svg links: diff --git a/agent-framework/integrations/a2a.md b/agent-framework/integrations/a2a.md index 38469a33..94d9f824 100644 --- a/agent-framework/integrations/a2a.md +++ b/agent-framework/integrations/a2a.md @@ -5,7 +5,7 @@ zone_pivot_groups: programming-languages author: dmkorolev ms.service: agent-framework ms.topic: tutorial -ms.date: 07/01/2026 +ms.date: 07/23/2026 ms.author: dmkorolev --- @@ -350,9 +350,11 @@ async with A2AAgent( response = await agent.run("Hello!") ``` -## Exposing an Agent Framework Agent over A2A +## Exposing an Agent Framework agent over A2A -The `A2AExecutor` adapts any Agent Framework `Agent` to the A2A server-side protocol. You can host it with the official [`a2a-sdk`](https://pypi.org/project/a2a-sdk/) Starlette/ASGI server so that other A2A clients can discover and call your agent. +The `agent-framework-a2a` package provides an opinionated `A2AExecutor` that adapts any Agent Framework agent to the A2A server-side protocol. It runs the agent, maps supported output content to A2A events and artifacts, and manages task status updates through the official [`a2a-sdk`](https://pypi.org/project/a2a-sdk/). + +Your application assembles the surrounding A2A SDK server: the agent card, `DefaultRequestHandler`, task store, routes or application builder, authentication, and deployment. For a comparison with the app-owned adapters and standalone conversion helpers in `agent-framework-hosting-a2a`, see [Self-host A2A agents](../hosting/self-hosting/a2a.md). ```python import uvicorn @@ -393,7 +395,7 @@ agent = Agent( ) request_handler = DefaultRequestHandler( - agent_executor=A2AExecutor(agent), + agent_executor=A2AExecutor(agent, stream=True), task_store=InMemoryTaskStore(), agent_card=public_agent_card, ) @@ -410,9 +412,6 @@ uvicorn.run(server, host="0.0.0.0", port=9999) `A2AExecutor` streams agent updates as A2A artifacts when the underlying agent supports streaming and propagates the A2A `context_id` as the agent session's `session_id`. You can subclass `A2AExecutor` and override the `handle_events` method to implement custom transformations from your agent's output format to A2A protocol events. -> [!TIP] -> See the [`agent_framework_to_a2a.py` sample](https://github.com/microsoft/agent-framework/blob/main/python/samples/04-hosting/a2a/agent_framework_to_a2a.py) for a complete runnable example. - ::: zone-end ::: zone pivot="programming-language-go" diff --git a/agent-framework/workflows/human-in-the-loop.md b/agent-framework/workflows/human-in-the-loop.md index a2ba8fce..6ba3a9c3 100644 --- a/agent-framework/workflows/human-in-the-loop.md +++ b/agent-framework/workflows/human-in-the-loop.md @@ -5,7 +5,7 @@ zone_pivot_groups: programming-languages author: TaoChenOSU ms.topic: tutorial ms.author: taochen -ms.date: 05/27/2026 +ms.date: 07/16/2026 ms.service: agent-framework --- @@ -304,6 +304,8 @@ The `RequestPort` pattern described above works with custom executors and `Workf Agents can use tools that require human approval before execution. When the agent attempts to call an approval-required tool, the workflow pauses and emits a `RequestInfoEvent` just like the `RequestPort` pattern, but the event payload contains a `ToolApprovalRequestContent` (C# and Go) or a `Content` with `type == "function_approval_request"` (Python) instead of a custom request type. +For interactive scenarios where an agent needs to gather more information from the user and iterate before proceeding; rather than only approving or rejecting a tool call; use the **[handoff orchestration](./orchestrations/handoff.md)**. Handoff is interactive by default: when an agent responds without handing off to another agent, control returns to the user for the next input, which enables multi-turn back-and-forth within the orchestration. Sequential, concurrent, and group chat orchestrations do not pause for free-form user input on their own; pair them with a `RequestPort` in a custom `WorkflowBuilder` workflow when you need that control between steps. + > [!TIP] > For complete examples with code, see: > - [Sequential orchestration with HITL](./orchestrations/sequential.md#sequential-orchestration-with-human-in-the-loop) diff --git a/agent-framework/workflows/orchestrations/handoff.md b/agent-framework/workflows/orchestrations/handoff.md index 9494a8f9..5ee9da1f 100644 --- a/agent-framework/workflows/orchestrations/handoff.md +++ b/agent-framework/workflows/orchestrations/handoff.md @@ -4,7 +4,7 @@ description: In-depth look at Handoff Orchestrations in Microsoft Agent Framewor author: TaoChenOSU ms.topic: tutorial ms.author: taochen -ms.date: 05/27/2026 +ms.date: 07/16/2026 ms.service: agent-framework zone_pivot_groups: programming-languages --- @@ -18,8 +18,8 @@ zone_pivot_groups: programming-languages | Define Specialized Agents | ✅ | ✅ | ❌ | | | Configure Handoff Rules | ✅ | ✅ | ❌ | | | Run Interactive Workflow | ✅ | ✅ | ❌ | Different patterns per language | - | Autonomous Mode | ❌ | ✅ | ❌ | Python-specific | - | Tool Approval (HITL) | ❌ | ✅ | ❌ | | + | Autonomous Mode | ✅ | ✅ | ❌ | | + | Tool Approval (HITL) | ✅ | ✅ | ❌ | | | Checkpointing | ❌ | ✅ | ❌ | | | Sample Interaction | ✅ | ✅ | ❌ | | | Context Synchronization | ✅ | ✅ | ❌ | Shared section | @@ -180,6 +180,125 @@ triage_agent: This is another math question. I'll route this to the math tutor. math_tutor: I'd be happy to help with calculus integration! Integration is the reverse of differentiation. The basic power rule for integration is: ∫x^n dx = x^(n+1)/(n+1) + C, where C is the constant of integration. ``` +## Autonomous Mode + +By default, handoff orchestration is interactive: when an agent responds without handing off, the workflow returns control to you for the next user input. Enable **autonomous mode** to let an agent keep working without waiting for user input. When an agent does not hand off, the workflow feeds it a continuation prompt and invokes it again, until the agent hands off, a termination condition is met, or the per-agent turn limit is reached. + +Enable it by calling `WithAutonomousMode()` on the handoff builder: + +```csharp +var workflow = AgentWorkflowBuilder.CreateHandoffBuilderWith(triageAgent) + .WithHandoffs(triageAgent, [mathTutor, historyTutor]) + .WithHandoffs([mathTutor, historyTutor], triageAgent) + .WithAutonomousMode() + .Build(); +``` + +By default, each agent runs up to 50 autonomous turns, and each continuation uses the prompt `"User did not respond. Continue assisting autonomously."`. Override the turn limit and prompt as needed: + +```csharp +var workflow = AgentWorkflowBuilder.CreateHandoffBuilderWith(triageAgent) + .WithHandoffs(triageAgent, [mathTutor, historyTutor]) + .WithAutonomousMode(turnLimit: 10, continuationPrompt: "Continue assisting the user.") + .Build(); +``` + +Pass a list of agents to the `agents` parameter to enable autonomous mode for only a subset of participants. Agents not in the list always return control after a single response: + +```csharp +var workflow = AgentWorkflowBuilder.CreateHandoffBuilderWith(triageAgent) + .WithHandoffs(triageAgent, [mathTutor, historyTutor]) + .WithAutonomousMode(agents: [triageAgent]) // Only triageAgent runs autonomously + .Build(); +``` + +Combine autonomous mode with a termination condition to stop the loop when the conversation reaches a certain state: + +```csharp +var workflow = AgentWorkflowBuilder.CreateHandoffBuilderWith(triageAgent) + .WithHandoffs(triageAgent, [mathTutor, historyTutor]) + .WithAutonomousMode(turnLimit: 10) + .WithTerminationCondition(conversation => conversation.Any(m => m.Text?.Contains("RESOLVED") == true)) + .Build(); +``` + +## Advanced: Tool Approval in Handoff Workflows + +Agents in a handoff workflow can use tools that require human approval before they run; useful for sensitive operations such as processing refunds, making purchases, or executing irreversible actions. Wrap the sensitive function with `ApprovalRequiredAIFunction`. When the agent tries to call it, the workflow pauses and emits a `RequestInfoEvent` containing a `ToolApprovalRequestContent`. + +### Define Agents with Approval-Required Tools + +```csharp +ChatClientAgent triageAgent = new(client, + "You are frontline support. Route the customer to the right specialist.", + "triage_agent", + "Routes customers to specialists"); + +ChatClientAgent refundAgent = new(client, + "You process refund requests.", + "refund_agent", + "Handles refund requests", + [new ApprovalRequiredAIFunction(AIFunctionFactory.Create(ProcessRefund))]); +``` + +### Handle User Input and Tool Approval Requests + +Two things can pause a handoff workflow: an agent finishing its turn and waiting for the next user message, and an approval-required tool call. Handle both in the same event loop; respond to a `RequestInfoEvent` approval with `SendResponseAsync`, and supply the next user message when the workflow returns control: + +```csharp +var workflow = AgentWorkflowBuilder.CreateHandoffBuilderWith(triageAgent) + .WithHandoffs(triageAgent, [refundAgent]) + .WithHandoffs([refundAgent], triageAgent) + .Build(); + +List messages = []; + +while (true) +{ + Console.Write("You: "); + string userInput = Console.ReadLine()!; + if (userInput.Equals("exit", StringComparison.OrdinalIgnoreCase)) + { + break; + } + + messages.Add(new(ChatRole.User, userInput)); + + await using StreamingRun run = await InProcessExecution.RunStreamingAsync(workflow, messages); + await run.TrySendMessageAsync(new TurnToken(emitEvents: true)); + + List newMessages = []; + await foreach (WorkflowEvent evt in run.WatchStreamAsync()) + { + // An approval-required tool call pauses the workflow and emits a RequestInfoEvent. + if (evt is RequestInfoEvent requestEvt && + requestEvt.Request.TryGetDataAs(out ToolApprovalRequestContent? approval)) + { + var toolCall = (FunctionCallContent)approval.ToolCall; + Console.Write($"Approve {toolCall.Name}? (y/n): "); + bool approved = (Console.ReadLine() ?? "n").Trim().Equals("y", StringComparison.OrdinalIgnoreCase); + await run.SendResponseAsync(requestEvt.Request.CreateResponse(approval.CreateResponse(approved))); + } + else if (evt is AgentResponseUpdateEvent update) + { + Console.Write(update.Update.Text); + } + else if (evt is WorkflowOutputEvent outputEvt) + { + newMessages = outputEvt.As>()!; + break; + } + } + + // Control returns here after the agent responds without handing off. Merge the new + // messages into the conversation and loop to collect the next user input. + messages.AddRange(newMessages.Skip(messages.Count)); +} +``` + +> [!NOTE] +> Tool approval works with `CreateHandoffBuilderWith()` out of the box; no extra builder configuration is needed. When an agent calls a tool wrapped with `ApprovalRequiredAIFunction`, the workflow automatically pauses and emits a `RequestInfoEvent`. The same `RequestInfoEvent` handling pattern is used across orchestrations; see the [`GroupChatToolApproval` sample](https://github.com/microsoft/agent-framework/tree/main/dotnet/samples/03-workflows/Agents/GroupChatToolApproval) for a complete runnable project. + ::: zone-end ::: zone pivot="programming-language-python" @@ -635,6 +754,8 @@ After broadcasting the response, the participant then checks whether it needs to - **Context Preservation**: Full conversation history is maintained across all handoffs - **Multi-turn Support**: Supports ongoing conversations with seamless agent switching - **Specialized Expertise**: Each agent focuses on their domain while collaborating through handoffs +- **WithAutonomousMode()**: Lets agents continue without waiting for user input, up to a per-agent turn limit or until a termination condition is met +- **Tool Approval (HITL)**: Wrap sensitive tools with `ApprovalRequiredAIFunction`; the workflow pauses and emits a `RequestInfoEvent` with `ToolApprovalRequestContent`, which you answer via `SendResponseAsync` ::: zone-end diff --git a/agent-framework/workflows/orchestrations/sequential.md b/agent-framework/workflows/orchestrations/sequential.md index b2d323cb..b4fe9d78 100644 --- a/agent-framework/workflows/orchestrations/sequential.md +++ b/agent-framework/workflows/orchestrations/sequential.md @@ -5,7 +5,7 @@ zone_pivot_groups: programming-languages author: TaoChenOSU ms.topic: tutorial ms.author: taochen -ms.date: 07/01/2026 +ms.date: 07/16/2026 ms.service: agent-framework --- @@ -19,7 +19,7 @@ ms.service: agent-framework | Set Up the Sequential Orchestration | ✅ | ✅ | ✅ | | | Run the Sequential Workflow | ✅ | ✅ | ✅ | | | Sample Output | ✅ | ✅ | ✅ | | - | Sequential with Human-in-the-Loop | ✅ | ✅ | ✅ | | + | Sequential with Human-in-the-Loop | ✅ | ✅ | ✅ | Python adds a request-info feedback step; C# covers tool approval and links to Handoff for interactive HITL | | Advanced: Mixing Agents with Custom Executors | ❌ | ✅ | ✅ | Python/Go-specific | | Controlling Context Between Agents | ❌ | ✅ | ✅ | Python/Go-specific | | Intermediate Outputs | ❌ | ✅ | ✅ | Python/Go-specific | @@ -207,6 +207,13 @@ await foreach (WorkflowEvent evt in run.WatchStreamAsync()) > [!TIP] > For a complete runnable example of this approval flow, see the [`GroupChatToolApproval` sample](https://github.com/microsoft/agent-framework/tree/main/dotnet/samples/03-workflows/Agents/GroupChatToolApproval). The same `RequestInfoEvent` handling pattern applies to other orchestrations. +### Beyond Tool Approval: Interactive Feedback + +Tool approval lets a human accept or reject a specific tool call, but a sequential orchestration does not include a built-in step to pause for free-form user feedback between agents, and it cannot return control to a previous agent. When an agent needs to interactively ask the user for more information and iterate before continuing; for example, collecting booking details before it calls a reservation tool; use one of the following approaches instead: + +- **[Handoff orchestration](./handoff.md)** is interactive by default: when an agent responds without handing off, control returns to the user for the next input, enabling multi-turn back-and-forth within the orchestration. Restrict each agent to a single handoff target to approximate a sequential flow that still pauses for user input. +- A **custom workflow** built with `WorkflowBuilder` and a [`RequestPort`](../human-in-the-loop.md) lets you send a typed request to the user at any point and route the response back to an executor, which you can place before or after your agents in the pipeline. + ## Key Concepts - **Sequential Processing**: Each agent processes the output of the previous agent in order @@ -216,6 +223,7 @@ await foreach (WorkflowEvent evt in run.WatchStreamAsync()) - **Event Handling**: Monitor agent progress through `AgentResponseUpdateEvent` and completion through `WorkflowOutputEvent` - **Tool Approval**: Wrap sensitive tools with `ApprovalRequiredAIFunction` to require human approval before execution - **RequestInfoEvent**: Emitted when a tool requires approval; contains `ToolApprovalRequestContent` with the tool call details +- **Interactive HITL**: Sequential orchestration covers tool approval; for interactive back-and-forth where an agent gathers more information from the user, use [handoff orchestration](./handoff.md) or a custom `RequestPort` workflow ::: zone-end