Отправка сообщений и содержательных блоков
Обычный текст, Markdown, JSON Block Kit — форматирование сообщений Slack.
«Отправка сообщений и содержательных блоков» — бесплатный урок AI Agents на CoddyKit. Это урок 3 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения AI Agents, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс AI Agents содержит 4 уроков всего.
Простые текстовые сообщения с say()
Самый простой способ отправить сообщение в Slack — использовать say(text). Текст поддерживает mrkdwn — вариант разметки Markdown в Slack. Используйте *bold*, _italic_, ~strike~, `code` и упоминания, например <@USERID>.
@app.event('app_mention')
def handle_mention(event, say):
user = event['user']
# Simple text response with mrkdwn formatting
say(
text=(
f'Hello <@{user}>!\n'
'*Agent Report:*\n'
'- Tasks completed: `42`\n'
'- Errors: `0`\n'
'- _Runtime: 3.2 seconds_'
),
mrkdwn=True # enabled by default
)
# Slack mentions
say(f'<@{user}> your request is being processed')
say('Posting to <!channel>: all hands meeting tomorrow')Введение в Block Kit
Block Kit — это интерфейсная платформа Slack для создания насыщенных и интерактивных сообщений. Вместо обычного текста вы составляете сообщения из типизированных блоков: section, header, divider, actions, context. Блоки передаются списком в параметр blocks функции say() или chat_postMessage().
@app.event('app_mention')
def handle_mention(event, say):
blocks = [
{
'type': 'header',
'text': {'type': 'plain_text', 'text': 'Agent Status Report'}
},
{
'type': 'divider'
},
{
'type': 'section',
'text': {
'type': 'mrkdwn',
'text': '*Status:* Running\n*Tasks:* 42 completed'
}
}
]
say(
text='Agent Status Report', # fallback for notifications
blocks=blocks
)Блоки разделов с текстовыми полями
Блок раздела наиболее универсален. Он может отображать текст (mrkdwn или plain_text), список пар «ключ — значение» или дополнительный элемент (кнопку, изображение, меню дополнительных действий). Используйте fields для пар «ключ — значение», расположенных рядом, — это особенно удобно для информационных панелей.
def build_task_summary_blocks(tasks):
blocks = [
{
'type': 'header',
'text': {'type': 'plain_text', 'text': 'Daily Task Summary'}
},
{
'type': 'section',
'text': {
'type': 'mrkdwn',
'text': f'Processed *{len(tasks)} tasks* today.'
}
},
{
'type': 'section',
'fields': [
{'type': 'mrkdwn', 'text': f'*Completed:*\n{sum(1 for t in tasks if t["status"]=="done")}'},
{'type': 'mrkdwn', 'text': f'*Failed:*\n{sum(1 for t in tasks if t["status"]=="error")}'},
{'type': 'mrkdwn', 'text': f'*Pending:*\n{sum(1 for t in tasks if t["status"]=="pending")}'},
{'type': 'mrkdwn', 'text': f'*Avg Time:*\n3.2s'}
]
}
]
return blocks
# --- demo ---
tasks = [
{'status': 'done'}, {'status': 'done'}, {'status': 'error'}, {'status': 'pending'}
]
blocks = build_task_summary_blocks(tasks)
for b in blocks:
print(b)
Блоки заголовков и разделителей
Используйте блоки заголовков для крупных названий разделов, а блоки разделителей — для визуального отделения частей сообщения. Блок заголовка поддерживает только plain_text (без mrkdwn). Объединяйте их, чтобы создавать хорошо структурированные отчёты.
def build_report_message(title, sections):
blocks = []
# Header
blocks.append({
'type': 'header',
'text': {'type': 'plain_text', 'text': title, 'emoji': True}
})
for section_title, content in sections:
# Divider between sections
blocks.append({'type': 'divider'})
# Section header as bold mrkdwn
blocks.append({
'type': 'section',
'text': {'type': 'mrkdwn', 'text': f'*{section_title}*\n{content}'}
})
return blocks
blocks = build_report_message(
title='Weekly Agent Report',
sections=[
('Emails Processed', '142 emails classified, 38 replies drafted'),
('Tasks Completed', '89 tasks completed, 3 failures logged')
]
)
# --- demo ---
for b in blocks:
print(b)
Блоки действий с кнопками
Блоки действий содержат интерактивные элементы, например кнопки. У каждой кнопки есть action_id (используется для направления нажатий соответствующим обработчикам), text и необязательное value с данными. Используйте style: 'primary' для основной CTA и style: 'danger' для действий, которые могут привести к потере данных.
def build_approval_message(task_id, task_description):
blocks = [
{
'type': 'section',
'text': {
'type': 'mrkdwn',
'text': f'*Task ready for approval:*\n{task_description}'
}
},
{
'type': 'actions',
'elements': [
{
'type': 'button',
'text': {'type': 'plain_text', 'text': 'Approve'},
'style': 'primary',
'action_id': 'approve_task',
'value': task_id
},
{
'type': 'button',
'text': {'type': 'plain_text', 'text': 'Reject'},
'style': 'danger',
'action_id': 'reject_task',
'value': task_id
}
]
}
]
return blocks
# --- demo ---
blocks = build_approval_message('task_42', 'Deploy backend v2.3 to production')
for b in blocks:
print(b)
Блоки контекста для метаданных
Блоки контекста отображают небольшой вторичный текст внизу сообщения — это идеально подходит для таких метаданных, как временные метки, источники или сведения о версии агента. Они поддерживают mrkdwn и изображения для небольших значков.
import datetime
def add_context_footer(blocks, agent_version='v1.2'):
timestamp = datetime.datetime.now().strftime('%Y-%m-%d %H:%M UTC')
blocks.append({
'type': 'context',
'elements': [
{
'type': 'mrkdwn',
'text': f'Generated by Agent {agent_version} | {timestamp}'
}
]
})
return blocks
# Full message with context footer
blocks = [
{
'type': 'section',
'text': {'type': 'mrkdwn', 'text': 'Analysis complete. See results below.'}
}
]
blocks = add_context_footer(blocks)
print(f'Message has {len(blocks)} blocks')mrkdwn в текстовых полях
mrkdwn в Slack поддерживает часть возможностей Markdown. Основные варианты форматирования сообщений агента: полужирный и курсивный текст, код, ссылки, упоминания каналов, упоминания пользователей и списки. Используйте их, чтобы сделать содержимое, созданное ИИ, удобным для чтения в Slack.
def format_ai_response_as_mrkdwn(title, bullet_points, code_snippet=None):
lines = [f'*{title}*']
for point in bullet_points:
lines.append(f'• {point}')
if code_snippet:
lines.append(f'```{code_snippet}```') # code block
return '\n'.join(lines)
content = format_ai_response_as_mrkdwn(
title='Security Issues Found',
bullet_points=[
'SQL injection risk in `user_search()` function',
'Hardcoded API key in `config.py` line 42',
'Missing HTTPS on login endpoint'
],
code_snippet='SELECT * FROM users WHERE id = " + userId + "\n# ^ UNSAFE: use parameterized queries'
)
print(content)Эфемерные сообщения с respond()
Эфемерные сообщения видит только пользователь, запустивший действие, но не другие участники канала. Используйте их для обновлений состояния, сообщений об ошибках и подтверждений, которыми не нужно загромождать канал. Они доступны только через respond() (команды с косой чертой) или chat_postEphemeral().
@app.command('/check-status')
def handle_status(ack, respond, body, client):
ack()
user_id = body['user_id']
channel_id = body['channel_id']
# Ephemeral: only the user who ran /check-status sees this
respond(
text='Checking agent status...',
response_type='ephemeral'
)
status = get_agent_status()
# Or use chat_postEphemeral for more control
client.chat_postEphemeral(
channel=channel_id,
user=user_id,
text=f'Agent Status: {status}',
blocks=build_status_blocks(status)
)Обновление сообщений после отправки
После публикации сообщения его можно обновить с помощью client.chat_update(), передав канал и временную метку сообщения (ts). Это удобно для обновления хода выполнения: сначала отправьте сообщение «Обработка…», а после завершения обновите его результатом.
@app.command('/analyze')
def handle_analyze(ack, say, respond, body, client):
ack()
text = body.get('text', '')
channel = body['channel_id']
# Post initial message
initial = client.chat_postMessage(
channel=channel,
text='Analyzing... this may take a moment.'
)
message_ts = initial['ts']
# Do the work
import threading
def do_work():
result = slow_ai_analysis(text)
# Update the original message with the result
client.chat_update(
channel=channel,
ts=message_ts,
text=f'Analysis complete: {result}',
blocks=build_result_blocks(result)
)
threading.Thread(target=do_work, daemon=True).start()Отправка сообщений в определённые каналы
Используйте client.chat_postMessage(channel=channel_id, text=...), чтобы отправить сообщение в любой канал, к которому у вашего бота есть доступ. Найдите идентификаторы каналов в Slack API или щёлкнув по каналу правой кнопкой мыши. Используйте conversations_list(), чтобы программно находить каналы по имени.
def post_alert_to_channel(client, channel_name, alert_message):
# Look up channel ID by name
result = client.conversations_list(
types='public_channel,private_channel',
limit=200
)
channel_id = None
for ch in result['channels']:
if ch['name'] == channel_name:
channel_id = ch['id']
break
if not channel_id:
print(f'Channel #{channel_name} not found')
return None
# Post the alert
response = client.chat_postMessage(
channel=channel_id,
text=alert_message,
unfurl_links=False, # don't expand URLs
unfurl_media=False
)
print(f'Posted to #{channel_name}: ts={response["ts"]}')
return response
# --- demo: minimal stand-in for the Slack client ---
class _FakeClient:
def conversations_list(self, **kwargs):
return {'channels': [{'name': 'alerts', 'id': 'C123'}, {'name': 'general', 'id': 'C456'}]}
def chat_postMessage(self, **kwargs):
print(f"[slack] postMessage to {kwargs['channel']}: {kwargs['text']}")
return {'ts': '1699999999.000100'}
post_alert_to_channel(_FakeClient(), 'alerts', 'Disk usage above 90% on web-1')
Шаблон построителя сообщений Block Kit
Используйте функцию-построитель, которая принимает данные и возвращает список блоков. Это отделяет форматирование сообщений от бизнес-логики и позволяет повторно использовать блоки в разных обработчиках событий.
def build_alert_blocks(level, title, details, link=None):
level_emoji = {'info': ':information_source:',
'warning': ':warning:', 'error': ':x:'}.get(level, '')
blocks = [
{
'type': 'header',
'text': {'type': 'plain_text', 'text': f'{level_emoji} {title}'}
},
{
'type': 'section',
'text': {'type': 'mrkdwn', 'text': details}
}
]
if link:
blocks.append({
'type': 'actions',
'elements': [{
'type': 'button',
'text': {'type': 'plain_text', 'text': 'View Details'},
'url': link,
'action_id': 'view_details'
}]
})
import datetime
blocks.append({
'type': 'context',
'elements': [{'type': 'mrkdwn',
'text': datetime.datetime.now().strftime('%Y-%m-%d %H:%M UTC')}]
})
return blocks
# --- demo ---
blocks = build_alert_blocks('warning', 'High latency', 'p95 latency is 3.2s', link='https://dash.example.com')
for b in blocks:
print(b)
Быстрая проверка: типы блоков
Проверьте, насколько хорошо вы поняли Block Kit Slack.
Итоги: насыщенные сообщения
Теперь ваш агент может отправлять в Slack профессиональные интерактивные сообщения:
- say(text) — простой текст в формате mrkdwn; поддерживает *жирный текст*, _курсив_, `code` и упоминания
- say(blocks=[...]) — Block Kit для структурированной компоновки
- заголовок — крупное название раздела, только plain_text
- раздел — основной текст с необязательными полями в виде таблицы «ключ — значение» или одним дополнительным элементом
- разделитель — горизонтальная линия-разделитель
- действия — контейнер для кнопок и интерактивных элементов
- контекст — небольшой текст с метаданными внизу
- respond(response_type='ephemeral') — сообщение, видимое только пользователю, запустившему действие
- chat_update(ts=...) — обновление ранее опубликованного сообщения новым содержимым
Часто задаваемые вопросы
Урок «Отправка сообщений и содержательных блоков» бесплатный?
Да — полный текст урока «Отправка сообщений и содержательных блоков» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс AI Agents, подпишись на CoddyKit PRO. Курс AI Agents содержит 4 уроков всего.
Чему я научусь в уроке «Отправка сообщений и содержательных блоков»?
Обычный текст, Markdown, JSON Block Kit — форматирование сообщений Slack. Ты практикуешь AI Agents с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.
Нужен ли мне опыт, чтобы начать AI Agents?
Предыдущий опыт не требуется. AI Agents на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 3 из 4.
Сколько времени занимает урок «Отправка сообщений и содержательных блоков»?
Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.
Можно ли писать и запускать код в этом уроке AI Agents?
Да. Каждый урок AI Agents включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.
Все уроки этого курса
- Основы Slack Bolt SDK
- Обработка событий и команд со слешем
- Отправка сообщений и содержательных блоков
- Создание бота для уведомлений команды