处理消息

通过使用代理 365 SDK,代理可以处理平台活动事件,例如安装和卸载,并在单个轮次内发送多个离散消息。 本文介绍在代理处理请求时响应用户并确保用户知情的关键模式。

处理代理安装和卸载事件

当用户在 Teams 或其他代理 365 托管频道中安装或卸载代理时,平台会发送活动 InstallationUpdate (也称为 agentInstanceCreated 事件)。 代理可以处理这些事件,以在安装时发送欢迎消息,并在卸载时发送告别消息。

Action Description
add 用户安装代理
remove 用户卸载代理

与通知处理程序不同,处理程序 InstallationUpdate 不需要身份验证,因为安装或卸载事件在用户具有活动会话之前或之后触发。

注册安装和卸载处理程序

在代理的初始化中为 InstallationUpdate 活动类型注册活动处理程序:

@agent_app.activity("installationUpdate")
async def on_installation_update(context: TurnContext, state: TurnState):
    action = context.activity.action
    from_prop = context.activity.from_property
    logger.info(
        "InstallationUpdate received — Action: '%s', DisplayName: '%s', UserId: '%s'",
        action or "(none)",
        getattr(from_prop, "name", "(unknown)") if from_prop else "(unknown)",
        getattr(from_prop, "id", "(unknown)") if from_prop else "(unknown)",
    )
    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.")

Activity.action 是在安装代理或"add"卸载代理时设置为"remove"的字符串。 Activity.from_property 是包含用户标识的 ChannelAccount 实例。

发送多条消息

代理 365 代理可以发送多个离散消息,以响应单个用户提示。 为此,请多次在单个轮次内调用 SendActivityAsync (.NET)、 send_activity (Python)或 sendActivity (JavaScript)。

重要

Teams 不支持代理标识的流式处理响应。 SDK 将检测代理标识并将流缓冲到单个消息中。 使用 SendActivityAsyncsend_activitysendActivity 直接向用户发送即时离散消息。

以下示例演示了在 LLM 响应之前发送即时确认的模式:

@agent_app.activity("message")
async def on_message(context: TurnContext, state: TurnState):
    # Message 1: immediate ack — reaches the user right away
    await context.send_activity("Got it — working on it…")

    # ... LLM processing ...

    # Message 2: the LLM response
    await context.send_activity(response)

此示例通过在 LLM 响应之前发送即时确认来演示 (on_message) 中的此模式。

每次调用sendActivitysend_activitySendActivityAsync时,都会创建一条单独的消息。 可以根据需要多次调用它来发送进度更新、部分结果或最终答案。

正在输入指示

输入指示器在 Teams 中显示 ... 进度动画:

  • 它们有一个大约 5 秒的内置视觉超时,并且必须在一个循环中每隔 4 秒刷新一次。
  • 它们仅在一对一聊天和小组聊天中可见,不在频道中。

代理每隔四秒循环发送键入指示器,以维持 ... 动画的活动状态,同时 LLM 处理请求。

# 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(...)
    await context.send_activity(response)
finally:
    typing_task.cancel()
    try:
        await typing_task
    except asyncio.CancelledError:
        pass