关于 m365-agents-py
面向使用 Microsoft Agents SDK 为 Microsoft 365、Teams 和 Copilot Studio 构建企业 Agent 的 Python 技能,涵盖 aiohttp 托管、消息路由、流式响应和 MSAL 身份验证,并提示版本核验与前置条件。
主要功能
- 01基于 aiohttp 托管 Agent
- 02AgentApplication 消息路由
- 03使用 Azure OpenAI 流式响应
- 04MSAL 身份验证与 OAuth 路由
- 05Copilot Studio 客户端用法
适用场景
- 01构建 Microsoft 365 或 Teams Agent
- 02添加受 OAuth 保护的消息处理器
- 03流式传输 Azure OpenAI 响应
- 04直接连接 Copilot Studio 引擎
MCP 相关
该技能建议使用 microsoft-docs MCP 核验最新 SDK API 和身份验证选项。
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ sickn33/ agentic-awesome-skills --skill m365-agents-py安装后在 Claude Code 中输入/m365-agents-py确认已加载。
详细介绍
支持材料可用性
此目录副本包含下方的内嵌指南,但不包含以下支持路径。这些是外部前置条件,并非随附文件:
<support-root>/references/acceptance-criteria.md
使用依赖这些路径的步骤前,请索取原始支持材料或现有安装;此目录未提供经过验证的下载来源。请根据该安装解析每个路径,检查其内容,并确认适用的许可证及执行前提。在所需文件存在之前,不要运行命令,也不要声称已遵循引用的流程。不要伪造缺失的脚本,也不要将名称相同但无关的文件视为等效文件。此处已完整说明的独立步骤,可在用户授权范围内继续执行;对于任何依赖材料的步骤,请报告其当前不可用。
Microsoft 365 Agents SDK (Python)
使用 Microsoft Agents SDK、aiohttp 托管、AgentApplication 路由、流式响应和基于 MSAL 的身份验证,为 Microsoft 365、Teams 和 Copilot Studio 构建企业级 Agent。
实现前
- 使用 microsoft-docs MCP 验证 AgentApplication、start_agent_process 和身份验证选项的最新 API 签名。
- 在 PyPI 上确认计划使用的 microsoft-agents-* 软件包版本。
重要通知 - 导入变更
⚠️ 重大变更:近期更新已将 Python 导入结构从
microsoft.agents改为microsoft_agents(使用下划线而非点号)。
安装
pip install microsoft-agents-hosting-core
pip install microsoft-agents-hosting-aiohttp
pip install microsoft-agents-activity
pip install microsoft-agents-authentication-msal
pip install microsoft-agents-copilotstudio-client
pip install python-dotenv aiohttp
环境变量(.env)
CONNECTIONS__SERVICE_CONNECTION__SETTINGS__CLIENTID=<client-id>
CONNECTIONS__SERVICE_CONNECTION__SETTINGS__CLIENTSECRET=<client-secret>
CONNECTIONS__SERVICE_CONNECTION__SETTINGS__TENANTID=<tenant-id>
# Optional: OAuth handlers for auto sign-in
AGENTAPPLICATION__USERAUTHORIZATION__HANDLERS__GRAPH__SETTINGS__AZUREBOTOAUTHCONNECTIONNAME=<connection-name>
# Optional: Azure OpenAI for streaming
AZURE_OPENAI_ENDPOINT=<endpoint>
AZURE_OPENAI_API_VERSION=<version>
AZURE_OPENAI_API_KEY=<key>
# Optional: Copilot Studio client
COPILOTSTUDIOAGENT__ENVIRONMENTID=<environment-id>
COPILOTSTUDIOAGENT__SCHEMANAME=<schema-name>
COPILOTSTUDIOAGENT__TENANTID=<tenant-id>
COPILOTSTUDIOAGENT__AGENTAPPID=<app-id>
代码片段要求使用固定版本的 SDK,并采用各章节所示的设置和导入方式;重复的 /me 处理程序是可选方案,不应同时注册。请使用合成消息和获授权的身份;保留中间件验证,不要通过日志或云端跟踪暴露机密、私密轮次内容或令牌。Azure OpenAI 模型字段应配置为实际部署名称,而非臆测的公开模型 ID。
核心工作流:由 aiohttp 托管的 AgentApplication
import logging
from os import environ
from dotenv import load_dotenv
import aiohttp
from aiohttp.web import Request, Response, Application, run_app
from microsoft_agents.activity import load_configuration_from_env
from microsoft_agents.hosting.core import (
Authorization,
AgentApplication,
TurnState,
TurnContext,
MemoryStorage,
)
from microsoft_agents.hosting.aiohttp import (
CloudAdapter,
start_agent_process,
jwt_authorization_middleware,
)
from microsoft_agents.authentication.msal import MsalConnectionManager
# Enable logging
ms_agents_logger = logging.getLogger("microsoft_agents")
ms_agents_logger.addHandler(logging.StreamHandler())
ms_agents_logger.setLevel(logging.INFO)
# Load configuration
load_dotenv()
agents_sdk_config = load_configuration_from_env(environ)
# Create storage and connection manager
STORAGE = MemoryStorage()
CONNECTION_MANAGER = MsalConnectionManager(**agents_sdk_config)
ADAPTER = CloudAdapter(connection_manager=CONNECTION_MANAGER)
AUTHORIZATION = Authorization(STORAGE, CONNECTION_MANAGER, **agents_sdk_config)
# Create AgentApplication
ApplicationType = AgentApplication[TurnState]
AGENT_APP = ApplicationType(
storage=STORAGE, adapter=ADAPTER, authorization=AUTHORIZATION, **agents_sdk_config
)
@AGENT_APP.conversation_update("membersAdded")
async def on_members_added(context: TurnContext, _state: TurnState):
await context.send_activity("Welcome to the agent!")
@AGENT_APP.activity("message")
async def on_message(context: TurnContext, _state: TurnState):
await context.send_activity(f"You said: {context.activity.text}")
@AGENT_APP.error
async def on_error(context: TurnContext, error: Exception):
await context.send_activity("The agent encountered an error.")
# Server setup
async def entry_point(req: Request) -> Response:
agent: AgentApplication = req.app["agent_app"]
adapter: CloudAdapter = req.app["adapter"]
return await start_agent_process(req, agent, adapter)
APP = Application(middlewares=[jwt_authorization_middleware])
APP.router.add_post("/api/messages", entry_point)
APP["agent_configuration"] = CONNECTION_MANAGER.get_default_connection_configuration()
APP["agent_app"] = AGENT_APP
APP["adapter"] = AGENT_APP.adapter
if __name__ == "__main__":
run_app(APP, host="localhost", port=environ.get("PORT", 3978))
AgentApplication 路由
import re
from microsoft_agents.hosting.core import (
AgentApplication, TurnState, TurnContext, MessageFactory
)
from microsoft_agents.activity import Activity, ActivityTypes
ApplicationType = AgentApplication[TurnState]
AGENT_APP = ApplicationType(
storage=STORAGE, adapter=ADAPTER, authorization=AUTHORIZATION, **agents_sdk_config
)
# Welcome handler
@AGENT_APP.conversation_update("membersAdded")
async def on_members_added(context: TurnContext, _state: TurnState):
await context.send_activity("Welcome!")
# Regex-based message handler
@AGENT_APP.message(re.compile(r"^hello$", re.IGNORECASE))
async def on_hello(context: TurnContext, _state: TurnState):
await context.send_activity("Hello!")
# Simple string message handler
@AGENT_APP.message("/status")
async def on_status(context: TurnContext, _state: TurnState):
await context.send_activity("Status: OK")
# Auth-protected message handler
@AGENT_APP.message("/me", auth_handlers=["GRAPH"])
async def on_profile(context: TurnContext, state: TurnState):
token_response = await AGENT_APP.auth.get_token(context, "GRAPH")
if token_response and token_response.token:
# Use token to call Graph API
await context.send_activity("Profile retrieved")
# Invoke activity handler
@AGENT_APP.activity(ActivityTypes.invoke)
async def on_invoke(context: TurnContext, _state: TurnState):
invoke_response = Activity(
type=ActivityTypes.invoke_response, value={"status": 200}
)
await context.send_activity(invoke_response)
# Fallback message handler
@AGENT_APP.activity("message")
async def on_message(context: TurnContext, _state: TurnState):
await context.send_activity(f"Echo: {context.activity.text}")
# Error handler
@AGENT_APP.error
async def on_error(context: TurnContext, error: Exception):
await context.send_activity("An error occurred.")
使用 Azure OpenAI 的流式响应
from openai import AsyncAzureOpenAI
from microsoft_agents.activity import SensitivityUsageInfo
CLIENT = AsyncAzureOpenAI(
api_version=environ["AZURE_OPENAI_API_VERSION"],
azure_endpoint=environ["AZURE_OPENAI_ENDPOINT"],
api_key=environ["AZURE_OPENAI_API_KEY"]
)
@AGENT_APP.message("poem")
async def on_poem_message(context: TurnContext, _state: TurnState):
# Configure streaming response
context.streaming_response.set_feedback_loop(True)
context.streaming_response.set_generated_by_ai_label(True)
context.streaming_response.set_sensitivity_label(
SensitivityUsageInfo(
type="https://schema.org/Message",
schema_type="CreativeWork",
name="Internal",
)
)
context.streaming_response.queue_informative_update("Starting a poem...\n")
# Stream from Azure OpenAI
streamed_response = await CLIENT.chat.completions.create(
model="gpt-4o",
messages=[
{"role": "system", "content": "You are a creative assistant."},
{"role": "user", "content": "Write a poem about Python."}
],
stream=True,
)
try:
async for chunk in streamed_response:
if chunk.choices and chunk.choices[0].delta.content:
context.streaming_response.queue_text_chunk(
chunk.choices[0].delta.content
)
finally:
await context.streaming_response.end_stream()
OAuth / 自动登录
@AGENT_APP.message("/logout")
async def logout(context: TurnContext, state: TurnState):
await AGENT_APP.auth.sign_out(context, "GRAPH")
await context.send_activity(MessageFactory.text("You have been logged out."))
@AGENT_APP.message("/me", auth_handlers=["GRAPH"])
async def profile_request(context: TurnContext, state: TurnState):
user_token_response = await AGENT_APP.auth.get_token(context, "GRAPH")
if user_token_response and user_token_response.token:
# Use token to call Microsoft Graph
async with aiohttp.ClientSession() as session:
headers = {
"Authorization": f"Bearer {user_token_response.token}",
"Content-Type": "application/json",
}
async with session.get(
"https://graph.microsoft.com/v1.0/me", headers=headers
) as response:
if response.status == 200:
user_info = await response.json()
await context.send_activity(f"Hello, {user_info['displayName']}!")
Copilot Studio 客户端(直连引擎)
import asyncio
from msal import PublicClientApplication
from microsoft_agents.activity import ActivityTypes, load_configuration_from_env
from microsoft_agents.copilotstudio.client import (
ConnectionSettings,
CopilotClient,
)
# Token cache (local file for interactive flows)
class LocalTokenCache:
# See samples for full implementation
pass
def acquire_token(settings, app_client_id, tenant_id):
pca = PublicClientApplication(
client_id=app_client_id,
authority=f"https://login.microsoftonline.com/{tenant_id}",
)
token_request = {"scopes": ["https://api.powerplatform.com/.default"]}
accounts = pca.get_accounts()
if accounts:
response = pca.acquire_token_silent(token_request["scopes"], account=accounts[0])
return response.get("access_token")
else:
response = pca.acquire_token_interactive(**token_request)
return response.get("access_token")
async def main():
settings = ConnectionSettings(
environment_id=environ.get("COPILOTSTUDIOAGENT__ENVIRONMENTID"),
agent_identifier=environ.get("COPILOTSTUDIOAGENT__SCHEMANAME"),
)
token = acquire_token(
settings,
app_client_id=environ.get("COPILOTSTUDIOAGENT__AGENTAPPID"),
tenant_id=environ.get("COPILOTSTUDIOAGENT__TENANTID"),
)
if not token:
raise RuntimeError("Token acquisition failed")
copilot_client = CopilotClient(settings, token)
# Start conversation
conversation_id = None
act = copilot_client.start_conversation(True)
async for action in act:
if action.conversation:
conversation_id = action.conversation.id
if action.text:
print(action.text)
# Ask question
if not conversation_id:
raise RuntimeError("No conversation ID returned")
replies = copilot_client.ask_question("Hello!", conversation_id)
async for reply in replies:
if reply.type == ActivityTypes.message:
print(reply.text)
asyncio.run(main())
最佳实践
- 使用
microsoft_agents导入前缀(使用下划线而非点号)。 MemoryStorage仅用于开发;生产环境请使用 BlobStorage 或 CosmosDB。- 始终使用
load_configuration_from_env(environ)加载 SDK 配置。 - 在 aiohttp Application 中间件中加入
jwt_authorization_middleware。 - 使用
MsalConnectionManager进行基于 MSAL 的身份验证。 - 使用流式响应时,在 finally 代码块中调用
end_stream()。 - 对受 OAuth 保护的路由,在消息装饰器上使用
auth_handlers参数。 - 将机密保存在环境变量中,而不是源代码中。
参考文件
| 文件 | 内容 |
|---|---|
| references/acceptance-criteria.md | 导入路径、托管管道、流式响应、OAuth 和 Copilot Studio 模式 |
参考链接
| 资源 | URL |
|---|---|
| Microsoft 365 Agents SDK | https://learn.microsoft.com/en-us/microsoft-365/agents-sdk/ |
| GitHub 示例(Python) | https://github.com/microsoft/Agents-for-python |
| PyPI 软件包 | https://pypi.org/search/?q=microsoft-agents |
| 与 Copilot Studio 集成 | https://learn.microsoft.com/en-us/microsoft-365/agents-sdk/integrate-with-mcs |
适用场景
当需要执行概述中描述的工作流或操作时,可使用此技能。
限制
- 仅当任务明确符合上述范围时,才使用此技能。
- 不要将输出视为环境特定验证、测试或专家审核的替代品。
- 如果缺少必需的输入、权限、安全边界或成功标准,请停止并请求澄清。