十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

CopilotKit Microsoft Teams 频道适配器完整指南:从本地 Playground 到生产级 HITL 机器人

CopilotKit Microsoft Teams 频道适配器完整指南:从本地 Playground 到生产级 HITL 机器人 CopilotKit Microsoft Teams 频道适配器完整指南从本地 Playground 到生产级 HITL 机器人【免费下载链接】CopilotKitThe Frontend Stack for Agents Generative UI. React, Angular, Mobile, Slack, and more. Makers of the AG-UI Protocol项目地址: https://gitcode.com/GitHub_Trending/co/CopilotKit本文以copilotkit/channels-teams包README为核心深入讲解如何把用 CopilotKit Channels 平台无关引擎写好的 Bot 一键接入 Microsoft Teams。你将掌握适配器的安装与 Quickstart、PlatformAdapter契约在 Teams 上的落地方式Ingress/Egress/Streaming/History、Adaptive Card 与原生 Teams JSX 的渲染模型、卡片点击与 Human-in-the-LoopHITL的完整回路、自托管与 Managed Intelligence Channels 两条部署路径以及从POST /api/messages到真实 Teams 租户的全链路配置。一、定位copilotkit/channels-teams是什么copilotkit/channels-teams是 CopilotKit 平台无关 Bot 引擎的Microsoft Teams 平台适配器。它实现了PlatformAdapter接口把 Teams 插进引擎正如copilotkit/channels-slack之于 Slack你只用createChannel写一次 Bothandlers、JSX、工具、context通过添加这个适配器就能运行在 Teams 上。它的底层依赖是Microsoft 365 Agents SDKmicrosoft/agents-hosting——Bot Framework SDK 的继任者。从源码看适配器正是通过CloudAdapter见 adapter.ts和 Express 服务器见 listener.ts承载 Teams 活动的。自托管 vs Managed Intelligence Channels本适配器是自托管路径你的进程持有 Microsoft Teams 凭据、运行 Teams 入口ingress并直接与 Teams 通信。Managed Intelligence Channels则是替代方案由 CopilotKit Intelligence 拥有 provider 边缘签名 ingress/egress、加密凭据存储你的进程不持有任何 Teams 凭据、也不暴露公开的 Teams 端点还能获得持久化线程和带 Channel 健康状态与转录的 Channels 仪表盘。新建 managed app 后浏览器会生成持久化的 Channel 草稿并给出完整授权范围的配置命令npx copilotkitlatest channels add --project-id project-id --channel-id channel-id --adapter teams --provisionManaged 路径创建 Teams 托管的 Bot 与 Entra 身份不需要 Azure Bot手动路径则使用 Teams Developer Portal Entra。两条路径都把 provider 密钥和一次性 app-package 字节留在你的项目之外。两种方式下你的 Bot 代码完全相同——agent、工具、context、命令和 turn handlers 都不变只有传输层不同。自托管适配器仍然完全受支持当你想把 provider 连接放在自己的基础设施内时就选它。二、安装与 Quickstart安装依赖pnpm add copilotkit/channels copilotkit/channels-ui copilotkit/channels-teams最小可用 BotEchoimport { createChannel } from copilotkit/channels; import { teams } from copilotkit/channels-teams; import { CopilotRuntime, CopilotKitIntelligence } from copilotkit/runtime/v2; import { createCopilotNodeListener } from copilotkit/runtime/v2/node; const bot createChannel({ name: support-bot, // project-unique Intelligence Channel name identifyUser: platform, adapters: [teams({ port: 3978 })], }); bot.onMessage(({ thread, message }) thread.post(Echo: ${message.text})); // The runtime owns the channels lifecycle — there is no bot.start(). const runtime new CopilotRuntime({ intelligence: new CopilotKitIntelligence({ // apiUrl and wsUrl default to cloud-hosted CopilotKit Intelligence — override // both together only for a self-hosted deployment. apiKey: process.env.CPK_INTELLIGENCE_API_KEY!, // free tier available }), channels: [bot], }); // Creating the listener starts the Channels connection. const listener createCopilotNodeListener({ runtime }); // Optional: await that activation; once settled, POST /api/messages is listening // on :3978. await listener.channels.ready();几个关键点值得展开Channel 生命周期由 runtime 持有没有bot.start()。创建createCopilotNodeListener({ runtime })即建立 Channel 连接listener.channels.ready()可等待其就绪.stop()拆除。这也是为什么即便不需要 Microsoft 凭据也需要 Intelligence key免费档即可。本地开发零凭据teams({ port: 3978 })不传clientId/clientSecret/tenantId即为匿名本地模式直接对接Microsoft 365 Agents Playgroundnpx microsoft/m365agentsplayground # opens http://localhost:56150Playground 连接到http://127.0.0.1:3978/api/messages提供类 Teams 的聊天 UI 用于测试。完整可运行的 Echo Bot 见 examples/teams接入真实 Teams通过 Azure Bot Service sideloading见 Microsoft Teams 指南。直接照搬示例仓库之外时注意examples/teams里copilotkit/channels用的是workspace:*协议脱离 monorepo 后请替换为^0.2.0或更新版本并从copilotkit/channels/teams子路径导入 Teams API见 examples/teams/README.md。三、PlatformAdapter契约在 Teams 上的落地IngressPOST /api/messages与消息归一化CloudAdapter在POST /api/messages接收 Teams 活动由 Express 服务器承载见 listener.ts。每个message活动被归一化为sink.onTurn(...)。handleActivityadapter.ts会先剔除atbot/atmention频道场景再进入 agent run。服务器还带一个/healthzliveness 探针方便在 tunnel 之后做存活检查。上传文件会作为 attachments 随消息到来buildFileContentParts下载它们file.download.infoURL或data:/https 媒体 URL交给 agent 多模态内容部分——CSV/JSON/text 解码为文本图片和 PDF 作为二进制。这就是上传 CSV → 得到图表能工作的原因download-files.ts。注意 Teams 的局限Teams只在 1:1个人聊天中把上传的文件投递给 Bot需要在 app manifest 中设置supportsFiles: true在channel 或 group chat中 Teams根本不把文件发给 Bot此时适配器通过Microsoft Graph拉取buildChannelFileContentParts见 graph-files.ts需要Files.Read.All与Group.Read.All或 RSCChannelMessage.Read.Group两种 application 权限未配置/未授权时 Bot 仍可用会提示用户把数据内联粘贴。文件下载被刻意放进drive内部而不是 ack 之前执行这样慢下载不会阻塞入站 HTTP turn且转录里记录的是文件的内容而非仅文本后续轮次把它改成柱状图仍能基于数据行动。EgressAdaptive Card 渲染与文本回退结构化/交互式 UI 被渲染成Adaptive Card (1.5)并以 attachment 发送能折叠为纯文本的回复则以普通文本活动发送Echo: hi不该是一张卡片。render()的逻辑adapter.ts是若 IR 含原生 Teams 节点走renderTeamsNativeCard否则若可折叠为纯文本isPlainText发送renderTeamsMarkdown渲染的文本否则渲染为 Adaptive CardrenderAdaptiveCard。两条路径都在发起 turn 的存活TurnContext上发送。引擎会等待整个 turn handler 完成所以一条回复或完整的runAgent()循环在 HTTP 响应关闭前结束。turn 之外的proactive发送则通过捕获的 conversation reference 走CloudAdapter.continueConversation回退。Files outpostFile内联图片postFile向会话发布文件。图片如渲染出的图表 PNG通过data:URI 作为内联 attachment 发送直接在线程里渲染——这是 bot-slackpostFile的对应物adapter.ts。MIME 由文件扩展名推断mimeFromFilename默认回退为image/png。Streaming按消息编辑流式回复文本回复采用by-message-edit方式流式输出Teams 的基线模型先发出首段内容随着 buffer 增长用updateActivity编辑同一条消息节流 串行化见TeamsMessageStreammessage-stream.ts并先触发 typing indicator。此外startTypingHeartbeatadapter.ts每 3.5 秒重发一次 typing 指示因为 Teams 的指示几秒就过期而慢工作下载文件、渲染图表期间没有新内容发出一次性 ping 会留下死寂。原生逐 token 流式是后续增强项。Agent runs 与 HistorycreateRunRenderer把 AG-UI 事件桥接到 Teams每条文本消息按编辑流式输出工具调用与 interrupt 被捕获进入 run loopevent-renderer.ts。Teams 不向 Bot 提供可查询的转录所以适配器维护一个内存版TeamsConversationStoreconversation-store.ts每个会话一份并把它作为种子注入每次 agent run。生产环境应替换为持久化的ConversationStore——注意示例中的会话存储与 pending HITL 审批都是内存态重启即丢失。四、Options 详解teams({ port: 3978, // POST /api/messages port (Playground default) clientId, // Microsoft app id; omit for anonymous local dev clientSecret, // omit for anonymous local dev tenantId, // omit for multi-tenant / anonymous interruptEventNames, // custom-event names treated as agent interrupts });各参数语义对应 types.tsportBotPOST /api/messages端口默认3978Playground 连接的端点。clientId/clientSecret/tenantIdMicrosoft 应用凭据。省略即匿名本地开发Playground要连真实 Teams经 Azure Bot Service则必须提供。tenantId省略表示 multi-tenant 或匿名。interruptEventNames被视为 agent interrupt 的自定义事件名集合默认on_interruptLangGraph 的 AG-UI 适配器发出的名字。files入站文件处理的调优项FileDeliveryConfig大小/数量上限默认值已合理仅在需要放宽或收紧时覆盖。凭据还会从clientId/clientSecret/tenantId环境变量解析M365 Agents SDK 读取的名字——见 adapter.ts 中authConfig的构造逻辑。五、原生 Teams JSX 与 Adaptive Card 渲染在可移植 JSX 集合之外用Teams命名空间表示 Adaptive Card 类型。原生卡片有一个显式的Teams.AdaptiveCard根节点actions 可以是根的子节点也可以是Teams.ActionSet的子节点import { Teams } from copilotkit/channels-teams; await thread.post( Teams.AdaptiveCard fallbackTextDeploy approval Teams.TextBlock textDeploy ready wrap / Teams.ActionSet Teams.Action.Submit keyapprove titleApprove value{{ decision: approve }} onSubmit{({ action }) approve(action.value)} / /Teams.ActionSet /Teams.AdaptiveCard, );序列化器根据实际使用的类型与属性计算卡片版本显式更低的根版本会连同引发最低版本要求的组件与属性一起失败。具名子插槽保持可遍历因此 action 内的 handlers 在 managed delivery 与 action recovery 中幸存。Teams.Raw接受一个经过审查的非交互式 Adaptive Card 对象。生成的 native catalog 标注了38 个稳定 Teams body 类型、7 个稳定 actions、预览条目与支撑节点。host badges 与 catalog 存在性来自 adaptivecards.microsoft.com。Supported 意味着微软将该条目标记为 Teams 可用在真实租户中的验证仍是独立的发布检查。直接 Teams 与 managed Teams 使用同一序列化器与 Bot Framework attachment 形状。六、Card-action 往返与 HITLHuman-in-the-Loop 是整个适配器最有价值的能力之一。工作流如下Agent 的工具 handler 调用await thread.awaitChoice(Card/)把 agent挂起等待人类决定Adaptive CardAction.Submit点击作为 Message activity 到达其 actiondata携带在activity.value中见 interaction.ts 的parseCardActiondecodeInteraction解析不透明的ckActionId 按钮 value路由到sink.onInteraction引擎据此解析awaitChoicewaiter并运行按钮的onClick例如就地编辑 picker 卡片。Ingress 与 interaction 解码通过同一个共享助手conversationKeyOfinteraction.ts派生会话键两条路径必须一致否则 waiter 会被静默搁浅。注意往返中只携带不透明 id 与很小的按钮 value没有 resume-data 走私——持久性由引擎以该 id 为键的 ActionStore 承载。完整的 approve/reject 演示见 examples/teams/app/human-in-the-loop其测试见 confirm-action.test.tsx。异步 turn 移交让审批跨越 Teams turn 窗口有凭据时真实 Teamsingress立即 ack 入站 turnagent 在分离的continueConversation上下文上运行因此awaitChoice挂起可以超越 Teams 的 turn 窗口几分钟后的审批也 OK。机制上runDetachedadapter.ts基于捕获的 conversation reference 打开一个 proactiveTurnContext。这也解释了为什么卡片点击在真实 Teams 中也要走 detached 路径入站点击 turn 的 connector client 以匿名身份创建就地编辑卡片updateActivity即对 Connector 的 PUT会被 401 拒绝而 detached proactive context 使用 app-id 认证。匿名本地 Playground 中continueConversation没有 app idrun 使用入站 turn contextlocalhost 连接会在挂起期间保持打开。Waiter 目前是内存态v1不跨进程重启存活。七、状态与路线图已实现并在 M365 Agents Playground 中验证消息 ingressbot-ui 词汇表的Adaptive Card 渲染Header、Section/Markdown、Fields、Table、Image、Actions/Button、Select、Input、Context裸回复走纯文本路径、表格有 Markdown 回退按编辑流式的文本回复 typing indicatorrunAgent工具调用 / interrupt 捕获卡片 action 往返 HITL见上会话历史update/delete。计划中的后续项架构已为每一项留出空间原生 token 流式通过 SDK 的StreamingResponsequeueInformativeUpdate/queueTextChunk/endStream逐 token 回复替代当前的 post-then-edit 模型持久化 HITL waiters把 pendingawaitChoice状态持久化使审批跨重启存活当前是内存态用户查找Microsoft Graph与任意非图片文件上传经 Teams/Graph file-consent 流程当前postFile处理内联图片。八、连接真实 Microsoft Teams本地 Playground 不需要凭据真实 Teams 需要。高层次路径详见 examples/teams/README.md向 Microsoft 注册 Bot创建 Entra app registration记下 Application (client) ID、Directory (tenant) ID 与 client secret创建使用该 app 的 Azure Bot resource启用Microsoft Teamschannel把messaging endpoint设为https://your-host/api/messages。给 Bot 配置凭据设置clientId/clientSecret/tenantIdM365 Agents SDK 读取的名字。设置后Bot 会 ack 每个 turn 并在 detached context 上运行 agent使 HITL 审批可在几分钟后恢复。构建并上传 app packagepnpm package生成appPackage/appPackage.zip。打包脚本appPackage/package.mjs零依赖从MICROSOFT_APP_ID/CLIENT_ID/clientIdenv 或.env读取 bot id 注入 manifest、校验 manifest并在缺失时自动生成占位图标——提交的manifest.json保持为占位符永不硬编码你的 id。然后在 Teams 中Apps → Manage your apps → Upload a custom app。部署上Bot 是普通 HTTP 服务提供POST /api/messages外加/healthz探针绑定PORT任何能跑 Node 进程的地方都能运行。Teams 是入站 webhook所以服务需要公网 URL。若以 Railway 部署设置 Root Directory 为 repo 根、Build 为pnpm install pnpm --filter teams-example build、Start 为pnpm --filter teams-example start并把packages/**、examples/teams/**、pnpm-lock.yaml、package.json加入 Watch Paths。关键环境变量变量说明OPENAI_API_KEY必需示例用BuiltInAgent缺失则启动即退出OPENAI_MODEL可选默认openai/gpt-5.5CPK_INTELLIGENCE_API_KEY必需Intelligence runtime 持有 Channel 生命周期免费档足够COPILOTKIT_API_KEY为已废弃的别名回退COPILOTKIT_INTELLIGENCE_URL/COPILOTKIT_INTELLIGENCE_WS_URL可选指向自托管 Intelligence必须同时设置——API 与 realtime 是两个独立 hostwebsocket URL 不能靠换 scheme 推导CHANNELS_PORT可选Intelligence runtimeloopback默认 8300clientId/clientSecret/tenantId连真实 Teams 必需九、Exports 全景包的公开 API见 index.ts包括入口teams、TeamsAdapter、TeamsAdapterOptions、TeamsReplyTarget、ConversationKey存储与渲染TeamsConversationStorecreateRunRendererconversationKeyOf/parseCardActionrenderTeamsMarkdownrenderAdaptiveCard/AdaptiveCard/isPlainText/ADAPTIVE_CARD_CONTENT_TYPE预算与流式TEAMS_LIMITS含truncateText/clampArray见 render/budget.ts、TeamsMessageStream服务器createTeamsServer/TeamsServer/TeamsServerConfig文件管线buildFileContentParts/TeamsAttachmentRef/FileDeliveryConfig以及 Graph 侧的buildChannelFileContentParts/GraphCredentials/ChannelMessageRef原生 JSXTeams命名空间、TeamsNativeProps/TeamsRawProps以及从 native-manifest.ts 导出的 9 个 manifest 常量body / element / input / chart / graph / action / layout / preview 等遗留SanitizingHttpAgent已废弃——Channels 默认做 sanitization。十、调试与测试建议仓库为 Teams 适配器配备了较完整的测试可作为行为契约阅读adapter.test.ts适配器行为、interaction.test.ts卡片 action 解码、conversation-store.test.ts、message-stream.test.ts渲染层 render/adaptive-card.test.ts、render/markdown.test.ts、render/budget.test.ts文件管线 download-files.test.ts、graph-files.test.ts原生 JSX 与契约 native-jsx.test.tsx、native-interaction.test.ts、button-action-envelope.contract.test.ts。调试时注意两点一是onTurnError被显式拦截adapter.ts——没有它M365 适配器会把 turn-handler 失败如 Bot Connector 401 表现为 Unknown error typerethrow 成 unhandled rejection 拖垮整个进程把一个坏 turn 变成服务级宕机 重启循环二是若要在你有权限的租户里验证 Graph 链路可运行scripts/verify-graph-channel.ts见其文件头注释。【免费下载链接】CopilotKitThe Frontend Stack for Agents Generative UI. React, Angular, Mobile, Slack, and more. Makers of the AG-UI Protocol项目地址: https://gitcode.com/GitHub_Trending/co/CopilotKit创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表