Agent Framework Sample Agent - Python

This sample demonstrates how to build an agent using Agent Framework in Python with the Microsoft Agent 365 SDK. It covers:

  • Observability: End-to-end tracing, caching, and monitoring for agent applications
  • Notifications: Services and models for managing user notifications
  • Tools: Model Context Protocol tools for building advanced agent solutions
  • Hosting Patterns: Hosting with Microsoft 365 Agents SDK

This sample uses the Microsoft Agent 365 SDK for Python.

For comprehensive documentation and guidance on building agents with the Microsoft Agent 365 SDK, including how to add tooling, observability, and notifications, visit the Microsoft Agent 365 Developer Documentation.

Prerequisites

To run the template in your local dev machine, you will need:

  • Microsoft Agent 365 SDK
  • Agent Framework (agent-framework-azure-ai)
  • A365 CLI: Required for agent deployment and management.

Python Environment Configuration

Set up the Python virtual environment manually before running the agent or deploy steps:

  1. Install uv:
    • pip install uv
  2. Create a virtual environment:
    • uv venv
  3. Activate the virtual environment:
    • Windows PowerShell: .venv\Scripts\Activate.ps1
    • macOS/Linux: source .venv/bin/activate

Observability S2S export

A365 export is disabled by default. To send traces to Agent 365, set the dedicated OBS credentials and enable the exporter:

ENABLE_A365_OBSERVABILITY_EXPORTER=true
AGENT365_OBS_TENANT_ID=<<YOUR_TENANT_ID>>
AGENT365_OBS_AGENT_ID=<<YOUR_AGENT_INSTANCE_CLIENT_ID>>
AGENT365_OBS_BLUEPRINT_CLIENT_ID=<<YOUR_BLUEPRINT_CLIENT_ID>>
AGENT365_OBS_BLUEPRINT_CLIENT_SECRET=<<YOUR_BLUEPRINT_CLIENT_SECRET>>

AGENT365_OBS_AGENT_ID is the actual runtime agent instance client ID, not the blueprint ID, service-principal object ID, or agent-user ID. ENABLE_A365_OBSERVABILITY_EXPORTER=false disables A365 HTTP export; samples using the Microsoft OpenTelemetry distro can still enrich spans for other exporters.

The sample-local provider is single-instance: one configured tenant and agent instance, plus a separate blueprint secret. It has no managed-identity option and requests tokens from login.microsoftonline.com, so sovereign clouds need provider changes. Multi-instance or multi-tenant deployments should cache per agent/tenant and reuse the hosting connection credential.

Accepted OBS tokens are app-only tokens with idtyp=app, valid nonempty roles, or absent idtyp with nonempty oid == sub; any scp claim is rejected. See Agent 365 observability S2S export for route details and validation commands.

Working with User Identity

On every incoming message, the A365 platform populates activity.from_property with basic user information — always available with no API calls or token acquisition:

Field Description
activity.from_property.id Channel-specific user ID (e.g., 29:1AbcXyz... in Teams)
activity.from_property.name Display name as known to the channel
activity.from_property.aad_object_id Azure AD Object ID — use this to call Microsoft Graph

The sample logs these fields at the start of every message turn and injects the display name into the LLM system instructions for personalized responses.

Running the Agent in Microsoft 365 Agents Playground

  1. First, select the Microsoft 365 Agents Toolkit icon on the left in the VS Code toolbar.
  2. In file env/.env.playground.user, fill in your Azure OpenAI key SECRET_AZURE_OPENAI_API_KEY=<your-key>, endpoint AZURE_OPENAI_ENDPOINT=<your-endpoint>, and deployment name AZURE_OPENAI_DEPLOYMENT_NAME=<your-deployment> if you're using Azure OpenAI.
  3. In file env/.env.playground.user, fill in your OpenAI key SECRET_OPENAI_API_KEY=<your-key> if you're using OpenAI.
  4. In file env/.env.playground, fill in your custom app registration client id CLIENT_APP_ID.
  5. Press F5 to start debugging which launches your agent in Microsoft 365 Agents Playground using a web browser. Select Debug in Microsoft 365 Agents Playground.
  6. You can send any message to get a response from the agent.

Congratulations! You are running an agent that can now interact with users in Microsoft 365 Agents Playground.

Handling Agent Install and Uninstall

When a user installs (hires) or uninstalls (removes) the agent, the A365 platform sends an InstallationUpdate activity — also referred to as the agentInstanceCreated event. The sample handles this in on_installation_update in host_agent_server.py:

Action Description
add Agent was installed — send a welcome message
remove Agent was uninstalled — send a farewell message
if action == "add":
    await context.send_activity("Thank you for hiring me! Looking forward to assisting you in your professional journey!")
elif action == "remove":
    await context.send_activity("Thank you for your time, I enjoyed working with you.")

To test with Agents Playground, use Mock an Activity → Install application to send a simulated installationUpdate activity.

Sending Multiple Messages in Teams

Agent365 agents can send multiple discrete messages in response to a single user prompt in Teams. This is achieved by calling send_activity multiple times within a single turn.

Important: Streaming responses are not supported for agentic identities in Teams. The SDK detects agentic identity and buffers the stream into a single message. Use send_activity directly to send immediate, discrete messages to the user.

The sample demonstrates this in on_message (host_agent_server.py):

# Message 1: immediate ack — reaches the user right away
await context.send_activity("Got it — working on it…")

# Send typing indicator immediately (awaited so it arrives before the LLM call starts).
await context.send_activity(Activity(type="typing"))

# Background loop refreshes the "..." animation every ~4s (it times out after ~5s).
async def _typing_loop():
    try:
        while True:
            await asyncio.sleep(4)
            await context.send_activity(Activity(type="typing"))
    except asyncio.CancelledError:
        pass  # Expected on cancel.

typing_task = asyncio.create_task(_typing_loop())
try:
    response = await agent.process_user_message(...)
    # Message 2: the LLM response
    await context.send_activity(response)
finally:
    typing_task.cancel()
    try:
        await typing_task
    except asyncio.CancelledError:
        pass

Each send_activity call produces a separate Teams message. You can call it as many times as needed to send progress updates, partial results, or a final answer.

Typing Indicators

  • Typing indicators show a "..." progress animation in Teams
  • They have a built-in ~5-second visual timeout and must be refreshed in a loop every ~4 seconds
  • Only visible in 1:1 chats and small group chats — not in channels

Running the Agent

To set up and test this agent, refer to the Configure Agent Testing guide for complete instructions.

For a detailed explanation of the agent code and implementation, see the Agent Code Walkthrough.

Support

For issues, questions, or feedback:

Contributing

This project welcomes contributions and suggestions. Most contributions require you to agree to a Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us the rights to use your contribution. For details, visit https://cla.opensource.microsoft.com.

When you submit a pull request, a CLA bot will automatically determine whether you need to provide a CLA and decorate the PR appropriately (e.g., status check, comment). Simply follow the instructions provided by the bot. You will only need to do this once across all repos using our CLA.

This project has adopted the Microsoft Open Source Code of Conduct. For more information see the Code of Conduct FAQ or contact opencode@microsoft.com with any additional questions or comments.

Additional Resources

Trademarks

Microsoft, Windows, Microsoft Azure and/or other Microsoft products and services referenced in the documentation may be either trademarks or registered trademarks of Microsoft in the United States and/or other countries. The licenses for this project do not grant you rights to use any Microsoft names, logos, or trademarks. Microsoft's general trademark guidelines can be found at http://go.microsoft.com/fwlink/?LinkID=254653.

License

Copyright (c) Microsoft Corporation. All rights reserved.

Licensed under the MIT License - see the LICENSE file for details.