0Pricing
AI Agents · Урок

Отправка сообщений и содержательных блоков

Обычный текст, 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 — локальная установка не требуется.

Все уроки этого курса

  1. Основы Slack Bolt SDK
  2. Обработка событий и команд со слешем
  3. Отправка сообщений и содержательных блоков
  4. Создание бота для уведомлений команды
← Назад к AI Agents