第21章 Slack 集成 Omnigent:在聊天中管理 AI Agent
Slack Omnigent 的 Slack 集成让团队可以直接在 Slack 里与 agent 对话。在频道中 @ 机器人或私信它,就会启动一个 Omnigent 会话;回复实时流入线程,工具审批则以交互式 Approve / Deny 卡片的形式出现,可以就地点击确认。 核心模型很简单:一个 Slack 线程 ↔ 一个 Omnigent 会话。在线程里回复就是延续同一会话,新的 @ 则开启新会话。 机器人连接到由运维者指定的唯一一台 Omnigent 服务器,用户无需输入服务器地址。每位用户仍以自己的 Omnigent 身份认证,会话按用户各自的权限运行,任何共享凭证都不会经过 Slack。 功能一览
通过 @ 机器人(@omnigent help me inspect this failure)或私信来启动会话。 在线程里回复即可继续对话——还是同一个会话,上下文完整。 实时观看回复以 Markdown 格式流式输出,与卡片、通知按时间顺序排列。 直接在线程里用按钮批准或拒绝工具调用(也可以从 web UI 操作——哪边先响应以哪边为准)。 回答 agent 的提问——选择题会渲染成单选/多选按钮,附 Submit 按钮。 查看 agent 的计划:todo 列表会发布一次,并随进度原地更新。 随时用 /omnigent 重新配置(选择 agent、主机和工作区),或用 /omnigent logout 重置。
部署配置(运维者) 机器人是一个 Socket Mode 的 Slack 应用——它主动向 Slack 发起 WebSocket 连接,因此无需公网 HTTP 端点,也无需配置请求 URL。 1. 创建 Slack 应用 快捷方式——用 manifest 直接创建应用。在 api.slack.com/apps → Create New App → From an app manifest 中选择工作区,粘贴仓库中的 integrations/slack/deploy/slack-app-manifest.yaml。这一步会一次性配置好 Socket Mode、Interactivity、机器人所需的全部 scope、事件以及 /omnigent 命令。如果使用 Databricks web-auth,粘贴前先在 manifest 中取消 users:read 和 users:read.email 的注释。manifest 无法生成 token,所以还需完成它做不到的两步——生成 connections:write 应用级 token(Basic Information → App-Level Tokens)并 Install to Workspace 安装机器人 token——之后直接跳到第 3 步即可,只有需要核对 scope 时才回头做第 2 步。 手动配置:
创建 Slack 应用,启用 Socket Mode 和 Interactivity(Socket Mode 同时负责传递按钮/模态框的载荷,无需请求 URL)。 添加下文列出的 OAuth scope 和事件订阅。 添加斜杠命令 /omnigent(Features → Slash Commands)。Socket Mode 下请求 URL 会被忽略,随便填个占位符即可。 将应用安装到工作区。
2. 授权 scope 以下 scope 和事件与 integrations/slack/deploy/slack-app-manifest.yaml 中一致——如果你是用 manifest 创建的应用,这些已经配置好了;如果修改任何一项,请同步修改 manifest。 Bot token scopes(OAuth & Permissions → Bot Token Scopes)——全部必填: Scope 用途 app_mentions:read 接收 @提及——这是机器人加入频道线程的方式。chat:write 发送、删除和流式输出回复(包括 ephemeral 提示)。im:write 打开私信,用于发送配置按钮和登出确认。im:history 读取私信——私信是一等入口。commands 注册并接收 /omnigent。team:read 读取工作区名称,用于标注登录请求。 仅当使用 Databricks web-auth 模式时才额外添加两个 bot scope——该模式下机器人会把用户邮箱签入注册链接,并与 OAuth 认证出的身份做比对;accounts / oidc 模式下无需添加: Scope 用途 users:read 读取用户资料以查询邮箱。users:read.email 读取用户邮箱地址,用于身份绑定。 频道历史 scope——只为机器人会运行的频道类型添加:channels:history(公开频道)、groups:history(私有频道)、mpim:history(群聊私信)。如果只用私信和频道 @提及,这三个都可以省略。 应用级 token scope:connections:write(Socket Mode 连接必需)。 事件订阅(Subscribe to bot events):app_mention、message.im,以及上述启用的频道类型对应的 message.channels / message.groups / message.mpim。 3. 安装并配置机器人 机器人随 Omnigent 的 slack extra 一起分发(独立包 omnigent-slack),需安装在 omni CLI 同一环境中,这样 omni integration slack 才能找到它: uv tool install "omnigent[slack]" # 或从源码检出安装:uv sync --extra slack 配置来自真实的环境变量——机器人自身不读取 .env 文件(与 omni server 一致)。可以直接导出变量,也可以在能注入 .env 的工具下启动机器人(如 uv run --env-file .env omni integration slack)。.env.example 记录了完整变量集,可对照复制: 变量 必填 用途 OMNIGENT_SLACK_BOT_TOKEN 是 机器人 token(xoxb-…)。OMNIGENT_SLACK_APP_TOKEN 是 Socket Mode 所需的应用级 token(xapp-…)。OMNIGENT_SERVER_URL 是 机器人连接的唯一 Omnigent 服务器。OMNIGENT_DEVICE_CLIENT_SECRET 否 与服务器的 OMNIGENT_DEVICE_CLIENT_SECRET 设为相同值,确保只有本机器人能驱动 device-grant 登录。仅限 accounts 模式。OMNIGENT_SLACK_TOKEN_ENCRYPTION_KEY 否 用于加密用户委托 token 的 Fernet 密钥。不设置时 token 仅存于内存,重启后用户需重新认证。OMNIGENT_SLACK_DATABASE_PATH 否 机器人 SQLite 存储路径。默认为 $OMNIGENT_DATA_DIR/omnigent_slack.sqlite3(或 ~/.omnigent/omnigent_slack.sqlite3)。OMNIGENT_SLACK_DEFAULT_AGENT_ID 否 在所有用户的 /omnigent 配置弹窗中预选该 agent id。见「运维者预设配置默认值」。OMNIGENT_SLACK_DEFAULT_HOST_TYPE 否 仅支持 managed——在配置弹窗中预选服务器托管的 managed 沙箱作为主机。其他非空值会导致启动失败。LOG_LEVEL 否 排查 Slack 事件无响应时设为 DEBUG。 用以下命令生成加密密钥: uv run python -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())" 如果 Omnigent 服务器以 Databricks App 方式运行(header/proxy 认证),机器人会改用自带的 Databricks web-auth 流程而非 device/OIDC 登录——所需的额外 OMNIGENT_SLACK_DATABRICKS_* 变量见下文「Databricks Apps web-auth」。 4. 运行 机器人作为后台守护进程由 omni CLI 管理: omni integration slack # 前台运行(Ctrl-C 停止) omni integration slack --background # 后台运行(脱离终端) omni integration slack status # 后台机器人是否在运行? omni integration slack stop # 停止后台机器人 omni integration slack logs # 打印日志路径 omni integration slack logs -f # 跟踪日志 机器人已在运行时执行 --background 是无效操作,会报告现有进程。若后台守护进程已占用端口,前台运行会拒绝启动(需先停止它)。 用户配置 用户首次在未配置的情况下与机器人交互时,机器人会私信发送一个 Set up Omnigent 按钮。点击按钮打开的弹窗会:
校验到运维者服务器的连通性;如果服务器启用了认证,还会引导用户完成登录(见下文)。 让用户选择 agent 和主机(均为必填,选项来自服务器)以及工作区路径——即主机上的绝对目录,每个会话的 runner 从这里启动。默认为主机的家目录。
配置按(Slack 工作区,用户)保存。随时可用 /omnigent 更改 agent、主机或工作区,用 /omnigent logout 吊销 token 并清空所有已保存的设置。 每个新会话都会在所选主机上启动一个全新的 runner,根目录为配置的工作区。 运维者预设配置默认值 如果团队已统一使用某个 agent——或统一使用 managed 沙箱——运维者可以在第 2 步为所有人预选,这样新用户只需直接提交弹窗,而不必对两个自己毫无概念的选项做出决定: 变量 效果 OMNIGENT_SLACK_DEFAULT_AGENT_ID Agent 菜单打开时预选该 agent id——即服务器 GET /v1/agents 返回的某个 id。OMNIGENT_SLACK_DEFAULT_HOST_TYPE 仅限 managed——Host 菜单打开时预选服务器托管的 managed 沙箱。其他非空值会导致启动失败。 两者都像其他变量一样在机器人环境中设置。留空或不设置即无默认值(菜单会像往常一样空白打开)。 这些只是预选项,仅此而已。弹窗照常打开,照常显示完整菜单,也仍需提交——用户可以更改任一选项,最终保存的是提交的内容。 可用性按用户校验,每次渲染弹窗时都会针对该用户自己的登录可见的 agents 和 hosts 重新检查。不可用的默认值——菜单中不存在的 agent id,或未提供沙箱的服务器上的 managed——会让对应菜单保持空白,并在弹窗中加一条警告说明。永远不会自动替换为其他 agent 或主机,而另一菜单中有效的默认值仍会生效。警告会区分「未能确定」和「确定没有」:如果无法检查服务器的沙箱能力,弹窗会说明检查未完成,而不是断言服务器不提供沙箱;超出菜单 100 项上限而未显示的 agent 会报告为「不在菜单中」,而非「服务器上不存在」。 我们刻意不为特定外部主机 id 提供默认值:在启用了认证的服务器上,/v1/hosts 是按所有者隔离的,一个用户的主机不会出现在他人列表中,也就无法被预选。 认证 对于启用了认证的服务器,每位 Slack 用户都在 /omnigent 弹窗内用自己的 Omnigent 身份登录。机器人自动检测服务器的认证模式并选择对应流程:
accounts 模式——OAuth 2.0 Device Authorization Grant(RFC 8628)。弹窗显示一键验证链接(已预填代码)和简短的确认码;用户在浏览器中批准授权页面。授权页在批准前强制重新输入密码——即使用户已登录——以防用户并非本人发起的链接被下意识批准。服务器签发短生命周期的会话级委托 token,并带轮换的 refresh token。服务器必须启用 device grant(OMNIGENT_DEVICE_GRANT_ENABLED=1——默认关闭)。将机器人的 OMNIGENT_DEVICE_CLIENT_SECRET 设为与服务器一致,确保只有本机器人能驱动此流程。 oidc 模式——使用服务器的 cli-login ticket 流程。用户在浏览器中于你的 IdP 登录;机器人获得服务器的常规会话 JWT。没有 refresh token,会话 TTL(默认 8 小时)到期后用户需重新登录。 header / proxy 模式——这种模式下身份由上游代理断言,自动检测无法驱动按用户登录。当服务器以 Databricks App 部署时,设置 OMNIGENT_SLACK_SERVER_AUTH=databricks,机器人会运行自带的 web-auth 流程(见下文)。 其他情况下,请将服务器切换到 accounts / oidc 模式,或将机器人置于同一身份代理之后。
设置了 OMNIGENT_SLACK_TOKEN_ENCRYPTION_KEY 时 token 加密存储;否则仅保存在内存中,重启即丢失(用户重新认证即可)。 Databricks Apps web-auth 当 Omnigent 服务器是 Databricks App 时,其代理会认证每个请求并注入用户身份——而 Socket Mode 事件不经过这样的代理认证,因此 device/OIDC 流程行不通。替代方案是,机器人作为一个 U2M OAuth 应用(authorization code + PKCE,offline_access)运行,通过它以自己 Databricks App 身份提供的注册页面完成认证:
收到 /omnigent 后,机器人查询用户邮箱,生成 PKCE verifier 和一次性 nonce,并向工作区的 /oidc/v1/authorize 发送一个 Sign in with Databricks 链接。 用户登录后,Databricks 携带一次性且绑定 PKCE 的 code 重定向回机器人的 /auth/callback,机器人用它换取 access + refresh token 对。 身份绑定——回调要求 OAuth 认证出的邮箱与签入链接的 Slack 邮箱一致,因此绑定某一用户的链接无法存入他人的 token。不匹配则拒绝。 存储前确认——回调展示一个授权页面,明确列出要关联的身份;只有用户点击确认按钮后 token 才会持久化,随后配置弹窗自动进入下一步。
机器人向服务器出示 access token,代理校验后注入真实身份 header,因此服务器端无需任何改动。token 会静默刷新,用户只需登录一次,而非每小时一次。
通过 OMNIGENT_SLACK_SERVER_AUTH=databricks 加以下变量启用:
变量 必填 用途 OMNIGENT_SLACK_DATABRICKS_CLIENT_ID 是 自定义 U2M OAuth 应用的 client id(公开)。OMNIGENT_SLACK_DATABRICKS_CLIENT_SECRET 是 自定义 U2M OAuth 应用的 client secret。OMNIGENT_SLACK_DATABRICKS_STATE_SECRET 是 签名注册状态的 HMAC 密钥(≥32 字符熵,如 openssl rand -hex 32)。独立于 OAuth client secret。OMNIGENT_SLACK_DATABRICKS_APP_URL 是¹ 机器人自身 Databricks App 的公网基础 URL——注册链接和 OAuth 重定向的基础地址。非平台注入,且该 URL 首次部署后才存在,因此需在第二次部署时接入。OMNIGENT_SLACK_DATABRICKS_SCOPES 否 请求的 scopes(空格分隔)。默认 all-apis;必须是服务器应用 scopes 的超集。openid + offline_access 始终会附加。
¹ 启动时不强制校验——没有它机器人也能启动,但在设置之前不会签发注册链接。注册 web 服务器始终绑定 DATABRICKS_APP_PORT(按惯例为 8000,平台注入);没有覆盖选项。
将自定义 OAuth 应用的 redirect URI 注册为
工具审批渲染为 Approve / Deny 卡片,附带待执行操作的预览。 选择题渲染为单选或多选按钮,附 Submit 按钮。 自由文本输入无法用按钮收集,因此机器人改为发送 web UI 链接——该轮对话保持活跃,你在那里回答后自动继续。
卡片对整个频道可见,但只有线程所有者能操作。从 web UI 回答也可以:请求在任何一处被解决后,Slack 中的卡片会自动结束。 与团队共用频道 Slack 频道是多用户的,因此机器人强制实行按线程所有者模式:线程属于启动它的人。如果他人在你的线程中回复,只会收到一条私信提示,让他们另开线程——他们绝不会加入你的会话。 如果机器人还在回复时你向线程发消息,它会私信请你稍等(或改用 web UI 继续)。向空闲线程发消息则直接继续对话。 支持与不支持 支持
频道 @提及、私信、私有频道、群聊私信(需对应 scope) Markdown 回复实时流式输出,严格按时间顺序 工具审批卡片和选择题表单,可在 Slack 或 web UI 中回答 accounts 和 oidc 模式服务器的按用户身份,以及无认证服务器 以 Databricks App 部署的 header/proxy 服务器的按用户身份(通过机器人 web-auth 流程) 实时更新的 todo/计划消息、策略拒绝通知、产物文件通知 /omnigent(配置/重设)和 /omnigent logout 斜杠命令
不支持
Databricks Apps 之外的 header/proxy 认证模式——自动检测无法驱动按用户登录;请使用 accounts/oidc 模式或将机器人置于同一身份代理之后。 Slack 中的自由文本输入——通过 web UI 链接处理,而非线程内表单。 多服务器——机器人只连接运维者固定的唯一一台服务器。 多用户线程——会话属于启动线程的用户。 Slack 侧在对话中途排队消息——等回复结束后再发,或改用 web UI 继续。
故障排查
机器人不响应私信——确认已添加 im:history scope 和 message.im 事件订阅,然后重新安装应用。
无法连接你的服务器——机器人会回复提示运行 /omnigent 重新配置。
没有主机在线——机器人会回复需要在目标机器上执行的命令:omni host --server