Разработка AI-бота для Slack на базе LLM
Slack Bot с LLM встраивает AI прямо в рабочий процесс команды: ответы на вопросы в каналах, суммаризация тредов, AI-помощник в DM. Технически — Slack Bolt SDK (Python/Node.js) + обработка событий через Socket Mode или webhooks.
Настройка приложения Slack
# Создать Slack App на api.slack.com/apps
# Включить: Socket Mode, Bot Token Scopes: app_mentions:read, channels:history,
# chat:write, im:history, im:write, reactions:write
# Event Subscriptions: app_mention, message.im, message.channels
from slack_bolt.async_app import AsyncApp
from slack_bolt.adapter.socket_mode.async_handler import AsyncSocketModeHandler
from anthropic import AsyncAnthropic
app = AsyncApp(token="SLACK_BOT_TOKEN")
anthropic_client = AsyncAnthropic(api_key="ANTHROPIC_API_KEY")
# Хранилище историй диалогов (в production — Redis/PostgreSQL)
conversation_history: dict[str, list] = {}
Обработка упоминаний и DM
@app.event("app_mention")
async def handle_mention(event, say, client):
"""Реагирует на @botname в каналах"""
text = event["text"]
user_id = event["user"]
thread_ts = event.get("thread_ts") or event["ts"]
# Убираем упоминание бота из текста
import re
clean_text = re.sub(r'<@[A-Z0-9]+>', '', text).strip()
# Получаем историю треда если есть
history = await get_thread_history(client, event["channel"], thread_ts)
# Добавляем emoji "думает"
await client.reactions_add(
channel=event["channel"],
name="thinking_face",
timestamp=event["ts"],
)
response_text = await generate_response(clean_text, history, user_id)
await say(text=response_text, thread_ts=thread_ts)
# Убираем emoji
await client.reactions_remove(
channel=event["channel"],
name="thinking_face",
timestamp=event["ts"],
)
@app.event("message")
async def handle_dm(event, say, client):
"""Обрабатывает личные сообщения"""
if event.get("channel_type") != "im":
return
user_id = event["user"]
text = event.get("text", "")
channel_id = event["channel"]
history = conversation_history.get(channel_id, [])
history.append({"role": "user", "content": text})
response = await generate_response(text, history[-10:], user_id)
history.append({"role": "assistant", "content": response})
conversation_history[channel_id] = history[-20:]
await say(text=response)
async def generate_response(text: str, history: list, user_id: str) -> str:
"""Генерирует ответ через Claude"""
messages = history + [{"role": "user", "content": text}] if history else [{"role": "user", "content": text}]
response = await anthropic_client.messages.create(
model="claude-sonnet-4-5",
max_tokens=2048,
system="""Ты — AI-ассистент для команды разработчиков в Slack.
Отвечай кратко и по существу. Используй Markdown (поддерживается Slack).
Для кода используй блоки \`\`\`.""",
messages=messages,
)
return response.content[0].text
Суммаризация тредов
@app.shortcut("summarize_thread")
async def summarize_thread(ack, shortcut, client, logger):
"""Shortcut для суммаризации треда (правая кнопка на сообщении)"""
await ack()
channel_id = shortcut["channel"]["id"]
message_ts = shortcut["message"]["ts"]
thread_ts = shortcut["message"].get("thread_ts") or message_ts
# Получаем все сообщения треда
thread_response = await client.conversations_replies(
channel=channel_id,
ts=thread_ts,
)
messages_text = "\n".join([
f"<{m['user']}>: {m['text']}"
for m in thread_response.get("messages", [])
if m.get("text")
])
# Суммаризируем
summary_response = await anthropic_client.messages.create(
model="claude-haiku-4-5",
max_tokens=512,
messages=[{
"role": "user",
"content": f"Суммаризируй этот тред в 3-5 пунктов:\n\n{messages_text}"
}]
)
# Открываем modal с результатом
await client.views_open(
trigger_id=shortcut["trigger_id"],
view={
"type": "modal",
"title": {"type": "plain_text", "text": "AI Суммаризация"},
"blocks": [{
"type": "section",
"text": {"type": "mrkdwn", "text": summary_response.content[0].text}
}]
}
)
Slash команды
@app.command("/ask")
async def ask_command(ack, command, say):
"""Команда /ask <вопрос> в любом канале"""
await ack()
question = command["text"]
if not question:
await say("Использование: `/ask ваш вопрос`")
return
response = await generate_response(question, [], command["user_id"])
await say(text=f"**Вопрос:** {question}\n\n**Ответ:** {response}")
@app.command("/summarize")
async def summarize_command(ack, command, say, client):
"""Суммаризировать последние N сообщений канала"""
await ack()
# Получаем последние 20 сообщений
history_response = await client.conversations_history(
channel=command["channel_id"],
limit=20,
)
# ... суммаризируем
Запуск
async def main():
handler = AsyncSocketModeHandler(app, "SLACK_APP_TOKEN")
await handler.start_async()
if __name__ == "__main__":
asyncio.run(main())
Практический кейс: команда разработки 25 человек
Боль: разработчики постоянно спрашивали в Slack про внутренние процессы, конфиги, онбординг. Одни и те же вопросы.
Решение: бот с доступом к корпоративной базе знаний (Notion + Confluence через MCP/API).
Результат: 65% вопросов бот отвечает автономно. Время ожидания ответа: от часов → секунды.
Сроки
- Базовый бот (DM + mentions): 2–3 дня
- Суммаризация тредов + shortcuts: 2–3 дня
- Slash команды + модальные окна: 2–3 дня
- Интеграция с базой знаний: 1 неделя







