Руководство: отправка и получение почты

Первый рабочий сценарий диалогового агента.

В этом руководстве разобран полный практический цикл, когда агент ведёт переписку. Разделы «Основные концепции» описывают отдельные вызовы API; здесь показано, как связать их в рабочий диалоговый цикл.

Основа: HTML и текст

Как напоминание из документации по сообщениям: лучше всегда передавать и html, и text. Так письмо нормально отображается в разных клиентах и заметно лучше доходит до получателя (доставляемость).

1# Always provide both html and text when possible
2client.inboxes.messages.send(
3 inbox_id="outreach@agentinbox.space",
4 to=["potential-customer@example.com"],
5 subject="Following up",
6 text="Hi Jane,\n\nThis is a plain-text version of our email.",
7 html="<p>Hi Jane,</p><p>This is a <strong>rich HTML</strong> version of our email.</p>",
8 labels=["outreach-campaign"]
9)

Диалоговый цикл

Типичная задача агента — проверить ящик на ответы и ответить. Эффективнее всего для этого подходят вебхуки, но можно собрать и простой опрос (polling).

Ниже логика агента на опросе.

1

1. Найти цепочку, на которую нужно ответить

Сначала нужно понять, в каких разговорах есть новые сообщения, на которые агент ещё не ответил. Удобно помечать такие письма метками. Можно получить список цепочек в ящике с меткой unreplied.

1# Find all threads in this inbox that are marked as unreplied
2threadsRes = client.threads.list(
3 labels = ["unreplied"]
4)
5if threadsRes.count == 0:
6 print("No threads need a reply.")
7else:
8 # Let's work on the first unreplied thread
9 thread_to_reply_to = threadsRes.thread[0]
2

2. Взять ID последнего сообщения в цепочке

Чтобы ответить в переписке, нужно ответить на самое последнее сообщение в цепочке. По ID цепочки можно получить объект с полным списком сообщений; ID нужного — у последнего сообщения в списке.

1# Get the full thread object to access its messages
2thread_details = client.threads.get(thread_to_reply_to.thread_id)
3
4# The last message in the list is the one we want to reply to
5last_message = thread_details.messages[-1]
6message_id_to_reply_to = last_message.message_id

Используйте last_message.extracted_text (или extracted_html), если нужен только новый текст ответа без цитируемой истории.

3

3. Отправить ответ и обновить метки

Когда есть message_id, можно отправить ответ агента. Хорошая практика — сразу обновить метки у исходного сообщения: снять unreplied и добавить replied, чтобы агент не ответил дважды на одно письмо.

1# Send the reply
2client.inboxes.messages.reply(
3 inbox_id="info@patternautomation.com",
4 message_id=message_id_to_reply_to,
5 text="This is our agent's helpful reply!"
6)
7
8# Update the labels on the original message
9client.inboxes.messages.update(
10 inbox_id="info@patternautomation.com",
11 message_id=message_id_to_reply_to,
12 add_labels=["replied"],
13 remove_labels=["unreplied"]
14)
Обработка в реальном времени через вебхуки

В продакшене опрос неэффективен. Надёжнее обрабатывать входящие ответы через вебхуки: Agent Inbox сразу уведомит агента о новом сообщении, и ответ можно строить в реальном времени.

Как настроить вебхуки →

Отложенная отправка

Вместо мгновенной отправки можно запланировать письмо на будущее — удобно для рабочих часов или равномерного аутрича.

Создайте черновик с полем send_at — дальше Agent Inbox отправит письмо в указанное время.

1from datetime import datetime, timedelta
2
3# Schedule for tomorrow at 9 AM UTC
4send_time = (datetime.utcnow() + timedelta(days=1)).replace(hour=9, minute=0, second=0)
5
6client.inboxes.drafts.create(
7 inbox_id="outreach@agentinbox.space",
8 to=["prospect@example.com"],
9 subject="Quick question about your workflow",
10 text="Hi, I noticed you're using...",
11 html="<p>Hi, I noticed you're using...</p>",
12 send_at=send_time.isoformat() + "Z"
13)

Подробнее об отложенной отправке — отмена, перенос, список запланированных черновиков и условные напоминания — на странице Черновики.