跳转到内容
搜索文档

Slack Agent 示例

最后更新 查看 MarkdownAgent 设置

部署你的第一个 Slack Agent

本指南将展示如何在 Cloudflare Workers 上构建并部署 AI 驱动的 Slack bot,它可以:

  • 响应私信
  • 在频道中被 @提及时回复
  • 在线程中保持对话上下文
  • 使用 AI 生成智能回复

你的 Slack Agent 将是多租户应用,即单次部署可服务多个 Slack 工作区。每个工作区拥有由 Agents SDK 驱动的独立 Agent 实例与专用存储。

完整示例代码见此处

前提条件

开始前,你需要:

1. 创建 Slack App

首先,创建 Agent 用于与 Slack 交互的新 Slack App:

  1. 前往 api.slack.com/apps 并选择 Create New App(创建新应用)
  2. 选择 From scratch(从零开始)
  3. 为应用命名(例如 "My AI Assistant")并选择工作区。
  4. 选择 Create App(创建应用)

配置 OAuth 与权限

在 Slack App 设置中,前往 OAuth & Permissions(OAuth 与权限) 并添加以下 Bot Token Scopes(Bot 令牌范围)

  • chat:write — 以 bot 身份发送消息
  • chat:write.public — 无需加入频道即可发送消息
  • channels:history — 查看公开频道中的消息
  • app_mentions:read — 接收 @提及
  • im:write — 发送私信
  • im:history — 查看私信历史

启用 Event Subscriptions

部署 Agent 后你将配置 Event Subscriptions URL。现在请先在 Slack App 设置中前往 Event Subscriptions(事件订阅) 并做好启用准备。

订阅以下 bot 事件:

  • app_mention — bot 被 @提及时
  • message.im — 发给 bot 的私信

暂时不要启用。部署后再启用。

获取 Slack 凭据

从 Slack App 设置中收集以下值:

  1. Basic Information(基本信息) > App Credentials(应用凭据)
    • Client ID(客户端 ID)
    • Client Secret(客户端密钥)
    • Signing Secret(签名密钥)

请妥善保存 — 下一步会用到。

2. 创建 Slack Agent 项目

  1. 为 Slack Agent 创建新项目:
npm create cloudflare@latest -- my-slack-agent
  1. 进入项目目录:
cd my-slack-agent
  1. 安装所需依赖:
npm install agents openai

3. 设置环境变量

  1. 在项目根目录创建 .env 文件用于本地开发密钥:
touch .env
  1. 将凭据添加到 .env
SLACK_CLIENT_ID="your-slack-client-id"
SLACK_CLIENT_SECRET="your-slack-client-secret"
SLACK_SIGNING_SECRET="your-slack-signing-secret"
OPENAI_API_KEY="your-openai-api-key"
OPENAI_BASE_URL="https://gateway.ai.cloudflare.com/v1/YOUR_ACCOUNT_ID/YOUR_GATEWAY/openai"
  1. 更新 wrangler.jsonc 以配置 Agent:
{
	"$schema": "./node_modules/wrangler/config-schema.json",
	"name": "my-slack-agent",
	"main": "src/index.ts",
	// Set this to today's date
	"compatibility_date": "2026-08-17",
	"compatibility_flags": [
		"nodejs_compat"
	],
	"durable_objects": {
		"bindings": [
			{
				"name": "MyAgent",
				"class_name": "MyAgent",
				"script_name": "my-slack-agent"
			}
		]
	},
	"migrations": [
		{
			"tag": "v1",
			"new_classes": [
				"MyAgent"
			]
		}
	]
}
"$schema" = "./node_modules/wrangler/config-schema.json"
name = "my-slack-agent"
main = "src/index.ts"
# Set this to today's date
compatibility_date = "2026-08-17"
compatibility_flags = [ "nodejs_compat" ]

[[durable_objects.bindings]]
name = "MyAgent"
class_name = "MyAgent"
script_name = "my-slack-agent"

[[migrations]]
tag = "v1"
new_classes = [ "MyAgent" ]

4. 创建 Slack Agent

  1. 首先在 src/slack.ts 创建基础 SlackAgent 类。该类处理 OAuth、request 验证与事件路由。完整实现见 GitHub

  2. src/index.ts 创建 Agent 实现:

import { env } from "cloudflare:workers";
import { SlackAgent } from "./slack";
import { OpenAI } from "openai";

const openai = new OpenAI({
	apiKey: env.OPENAI_API_KEY,
	baseURL: env.OPENAI_BASE_URL,
});

type SlackMsg = {
	user?: string;
	text?: string;
	ts: string;
	thread_ts?: string;
	subtype?: string;
	bot_id?: string;
};

function normalizeForLLM(msgs: SlackMsg[], selfUserId: string) {
	return msgs.map((m) => {
		const role = m.user && m.user !== selfUserId ? "user" : "assistant";
		const text = (m.text ?? "").replace(/<@([A-Z0-9]+)>/g, "@$1");
		return { role, content: text };
	});
}

export class MyAgent extends SlackAgent {
	async generateAIReply(conversation: SlackMsg[]) {
		const selfId = await this.ensureAppUserId();
		const messages = normalizeForLLM(conversation, selfId);

		const system = `You are a helpful AI assistant in Slack.
Be brief, specific, and actionable. If you're unsure, ask a single clarifying question.`;

		const input = [{ role: "system", content: system }, ...messages];

		const response = await openai.chat.completions.create({
			model: "gpt-4o-mini",
			messages: input,
		});

		const msg = response.choices[0].message.content;
		if (!msg) throw new Error("No message from AI");

		return msg;
	}

	async onSlackEvent(event: { type: string } & Record<string, unknown>) {
		// Ignore bot messages and subtypes (edits, joins, etc.)
		if (event.bot_id || event.subtype) return;

		// Handle direct messages
		if (event.type === "message") {
			const e = event as unknown as SlackMsg & { channel: string };
			const isDM = (e.channel || "").startsWith("D");
			const mentioned = (e.text || "").includes(
				`<@${await this.ensureAppUserId()}>`,
			);

			if (!isDM && !mentioned) return;

			const conversation = await this.fetchConversation(e.channel);
			const content = await this.generateAIReply(conversation);
			await this.sendMessage(content, { channel: e.channel });
			return;
		}

		// Handle @mentions in channels
		if (event.type === "app_mention") {
			const e = event as unknown as SlackMsg & {
				channel: string;
				text?: string;
			};
			const thread = await this.fetchThread(e.channel, e.thread_ts || e.ts);
			const content = await this.generateAIReply(thread);
			await this.sendMessage(content, {
				channel: e.channel,
				thread_ts: e.thread_ts || e.ts,
			});
			return;
		}
	}
}

export default MyAgent.listen({
	clientId: env.SLACK_CLIENT_ID,
	clientSecret: env.SLACK_CLIENT_SECRET,
	slackSigningSecret: env.SLACK_SIGNING_SECRET,
	scopes: [
		"chat:write",
		"chat:write.public",
		"channels:history",
		"app_mentions:read",
		"im:write",
		"im:history",
	],
});

5. 本地测试

启动开发服务器:

npm run dev

Agent 现运行于 http://localhost:8787

配置 Slack Event Subscriptions

Agent 本地运行后,需要将其暴露给 Slack。使用 Cloudflare Tunnel 创建安全隧道:

npx cloudflared tunnel --url http://localhost:8787

将输出类似 https://random-subdomain.trycloudflare.com 的公开 URL。

返回 Slack App 设置:

  1. 前往 Event Subscriptions(事件订阅)

  2. Enable Events 切换为 On

  3. 输入 Request URL:https://random-subdomain.trycloudflare.com/slack

  4. Slack 会发送验证 request — 若 Agent 运行正确,应显示 Verified

  5. Subscribe to bot events(订阅机器人事件) 下添加:

    • app_mention
    • message.im
  6. 选择 Save Changes(保存更改)

将应用安装到 Slack

在浏览器中访问 http://localhost:8787/install。将重定向到 Slack 授权页。选择 Allow(允许) 将应用安装到工作区。

授权后,浏览器应显示 "Successfully registered!"。

测试 Agent

打开 Slack。然后:

  1. 向 bot 发送私信 — 应收到 AI 生成的回复。
  2. 在频道中 @提及 bot(例如 @My AI Assistant hello)— 应在线程中回复。

若一切正常,即可部署到生产环境!

6. 部署到生产环境

  1. 部署前,将 secret 添加到 Cloudflare:
npx wrangler secret put SLACK_CLIENT_ID
npx wrangler secret put SLACK_CLIENT_SECRET
npx wrangler secret put SLACK_SIGNING_SECRET
npx wrangler secret put OPENAI_API_KEY
npx wrangler secret put OPENAI_BASE_URL
  1. 部署 Agent:
npx wrangler deploy

部署后将获得类似以下生产 URL:

https://my-slack-agent.your-account.workers.dev

更新 Slack Event Subscriptions

返回 Slack App 设置:

  1. 前往 Event Subscriptions(事件订阅)
  2. 将 Request URL 更新为生产 URL:https://my-slack-agent.your-account.workers.dev/slack
  3. 选择 Save Changes(保存更改)

分发应用

Agent 部署后,可与他人共享:

  • 单工作区:通过 https://my-slack-agent.your-account.workers.dev/install 安装。
  • 公开分发:将应用提交到 Slack App Directory

每个安装应用的工作区都将获得具有专用存储的隔离 Agent 实例。

工作原理

使用 Durable Objects 的多租户

Slack Agent 使用 Durable Objects 为每个 Slack 工作区提供隔离、有状态的实例:

  • 每个工作区的 team_id 用作 Durable Object ID。
  • 每个 Agent 实例在 KV 存储中保存自己的 Slack access token。
  • 对话按需从 Slack API 获取。
  • 所有 Agent 逻辑在隔离、一致的环境中运行。

OAuth 流程

Agent 处理 Slack 的 OAuth 2.0 流程:

  1. 用户访问 /install > 重定向到 Slack 授权。
  2. 用户选择 Allow(允许) > Slack 重定向到 /accept 并携带 authorization code。
  3. Agent 用 code 交换 access token。
  4. Agent 将 token 存储在工作区的 Durable Object 中。

事件处理

Slack 发送事件时:

  1. Request 到达 /slack 端点。
  2. Agent 使用 HMAC-SHA256 验证 request 签名。
  3. Agent 将事件路由到正确工作区的 Durable Object。
  4. onSlackEvent 方法处理事件并生成响应。

自定义 Agent

更改 AI model

src/index.ts 中更新 model:

const response = await openai.chat.completions.create({
	model: "gpt-4o", // 或其他 model
	messages: input,
});

添加对话 memory

在 Durable Object 存储中保存对话历史:

async storeMessage(channel: string, message: SlackMsg) {
  const history = await this.ctx.storage.kv.get(`history:${channel}`) || [];
  history.push(message);
  await this.ctx.storage.kv.put(`history:${channel}`, history);
}

响应特定关键词

onSlackEvent 中添加自定义逻辑:

async onSlackEvent(event: { type: string } & Record<string, unknown>) {
  if (event.type === "message") {
    const e = event as unknown as SlackMsg & { channel: string };

    if (e.text?.includes("help")) {
      await this.sendMessage("Here's how I can help...", {
        channel: e.channel
      });
      return;
    }
  }

  // ... rest of your event handling
}

使用其他 LLM 提供商

将 OpenAI 替换为 Workers AI

import { Ai } from "@cloudflare/ai";

export class MyAgent extends SlackAgent {
	async generateAIReply(conversation: SlackMsg[]) {
		const ai = new Ai(this.ctx.env.AI);
		const response = await ai.run("@cf/meta/llama-3-8b-instruct", {
			messages: normalizeForLLM(conversation, await this.ensureAppUserId()),
		});
		return response.response;
	}
}

后续步骤

相关资源

这篇文档对您有帮助吗?