mcpField
M
m365-agents-py

m365-agents-py

github.com/sickn33Agent 工作流48.7k49 次安装今天更新
代码仓库

关于 m365-agents-py

面向使用 Microsoft Agents SDK 为 Microsoft 365、Teams 和 Copilot Studio 构建企业 Agent 的 Python 技能,涵盖 aiohttp 托管、消息路由、流式响应和 MSAL 身份验证,并提示版本核验与前置条件。

主要功能

  1. 01基于 aiohttp 托管 Agent
  2. 02AgentApplication 消息路由
  3. 03使用 Azure OpenAI 流式响应
  4. 04MSAL 身份验证与 OAuth 路由
  5. 05Copilot Studio 客户端用法

适用场景

  1. 01构建 Microsoft 365 或 Teams Agent
  2. 02添加受 OAuth 保护的消息处理器
  3. 03流式传输 Azure OpenAI 响应
  4. 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())

最佳实践

  1. 使用 microsoft_agents 导入前缀(使用下划线而非点号)。
  2. MemoryStorage 仅用于开发;生产环境请使用 BlobStorage 或 CosmosDB。
  3. 始终使用 load_configuration_from_env(environ) 加载 SDK 配置。
  4. 在 aiohttp Application 中间件中加入 jwt_authorization_middleware。
  5. 使用 MsalConnectionManager 进行基于 MSAL 的身份验证。
  6. 使用流式响应时,在 finally 代码块中调用 end_stream()。
  7. 对受 OAuth 保护的路由,在消息装饰器上使用 auth_handlers 参数。
  8. 将机密保存在环境变量中,而不是源代码中。

参考文件

文件内容
references/acceptance-criteria.md导入路径、托管管道、流式响应、OAuth 和 Copilot Studio 模式

参考链接

资源URL
Microsoft 365 Agents SDKhttps://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

适用场景

当需要执行概述中描述的工作流或操作时,可使用此技能。

限制

  • 仅当任务明确符合上述范围时,才使用此技能。
  • 不要将输出视为环境特定验证、测试或专家审核的替代品。
  • 如果缺少必需的输入、权限、安全边界或成功标准,请停止并请求澄清。

评论

支持 Markdown