0Pricing
AI Agents · 课时

发送消息与富文本块

纯文本、Markdown、Block Kit JSON——设置 Slack 消息格式。

发送消息与富文本块 是 CoddyKit 上的免费 AI Agents 课时。 这是第 3 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 AI Agents 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 AI Agents 课程共包含 4 节课。

使用 say() 发送简单文本消息

发布 Slack 消息最简单的方式是 say(text)。文本支持 mrkdwn——Slack 的 Markdown 变体。您可以使用 *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 用于构建丰富、可交互消息的 UI 框架。您无需使用纯文本,而是通过类型化的区块组合消息:section、header、divider、actions、context。区块作为列表传入 say() 或 chat_postMessage() 的 blocks 参数。

@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。请为主要 CTA 使用 style: 'primary',为具有破坏性的操作使用 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

Slack 的 mrkdwn 支持 Markdown 的一部分功能。代理消息常用的格式选项包括:粗体、斜体、代码、链接、频道提及、用户提及和列表。请使用这些选项,让 AI 生成的内容在 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 中查找频道 ID,也可以右键点击频道进行查找。使用 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)

快速检查:区块类型

测试您对 Slack Block Kit 的理解。

丰富消息回顾

现在,您的代理可以发布专业且可交互的 Slack 消息:

  • say(text) — 简单的 mrkdwn 文本;支持 *bold*、_italic_、`code` 和提及
  • say(blocks=[...]) — 使用 Block Kit 构建结构化布局
  • 标题 — 大型分区标题(仅支持 plain_text)
  • 分区 — 带可选字段(键值网格)或一个附加元素的文本正文
  • 分隔线 — 水平分隔线
  • 操作 — 按钮和交互式元素的容器
  • 上下文 — 底部的简短元数据文本
  • respond(response_type='ephemeral') — 仅对触发操作的用户可见的消息
  • chat_update(ts=...) — 使用新内容更新之前发布的消息

常见问题解答

「发送消息与富文本块」课时是免费的吗?

是的 — 「发送消息与富文本块」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 AI Agents 课程的其余内容,请升级到 CoddyKit PRO。 AI Agents 课程共包含 4 节课。

「发送消息与富文本块」这节课中我会学到什么?

纯文本、Markdown、Block Kit JSON——设置 Slack 消息格式。 你通过在浏览器中直接运行的动手代码来练习 AI Agents,全天候 AI 导师会在你学习这节课的过程中回答你的问题。

学习 AI Agents 需要有经验吗?

无需任何先前经验。CoddyKit 上的 AI Agents 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 3 节课,共 4 节。

「发送消息与富文本块」课时需要多长时间?

大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。

我能在这节 AI Agents 课中编写并运行代码吗?

能。每节 AI Agents 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。

此课程中的所有课时

  1. Slack Bolt SDK 基础
  2. 监听事件与斜杠命令
  3. 发送消息与富文本块
  4. 构建团队通知机器人
← 返回 AI Agents