Создание клиентской электронной почты Agentic AI

Read this article in:

В процессе построения агентной системы ИИ я сначала построю «агентическую» и «системную» части, очень мало касаясь ИИ. Для этого есть несколько причин. Аудитория является одной из них. Я хочу точно знать, что происходит и почему. Я думаю, что ИИ в основном не нужен для (1) принятия решений и (2) принятия решений для меня на данный момент. Видишь? Автоматизация полезнее искусственного интеллектаДля некоторых рассуждений в свободном стиле на эту тему.

Программное обеспечение можно рассматривать как животное. Мышцы, скелет и остальная часть структуры проходят долгий путь, задолго до того, как интеллект животного становится полезным или желательным. Большую часть времени я брал быка или мула над обезьяной для работы. Итак, давайте сначала разовьем структуру - мышцы и кости - прежде чем мы разовьем мозг.

У меня уже есть проект системы, которую можно преобразовать в агентическую. Вообще-то, у меня их два. Одним из них является почтовый клиент. Другой — торговый бот — см. Создание агентской торговой платформы.

У меня есть почтовый клиент (на самом деле целая платформа электронной почты), и он может быть преобразован в агентский. Это означает, что клиент электронной почты будет отвечать на электронные письма автоматически. Как бы это было весело! Я удивлен, что не делал этого раньше. На самом деле, я много лет работал над этим проектом, делая очень длительные перерывы. И другие люди и компании также работают над этим, я уверен, что сложные агентские почтовые клиенты уже существуют, с открытым исходным кодом или запатентованы. Я сделаю свое, как упражнение, так и потому, что оно мне нужно. И я не просто отдам работу кому-то другому и просто заплачу за API. Нет, я буду делать работу сам и владеть готовым продуктом.

В системе электронной почты части (0) получают и анализируют электронные письма, (0) отслеживают предыдущие разговоры и всю историю разговора с этим свинцом и лидитом, (0) генерируют ответное электронное письмо (возможно, через богатый шаблон), (0) и следуют за намерением перенести свинец вниз по воронке продаж.

* * * * * * *

Ладно, начнем. Мы перейдем от простого к сложному, чтобы сэкономить время и головную боль. Давайте рассмотрим архитектуру торговой платформы, поскольку она уже почти готова. Также мы будем делать это в рубине на рельсах в основном по личным предпочтениям. Если вы начинаете с нуля, я, вероятно, должен порекомендовать python, так как так много кода ИИ является python. И я определенно рекомендую вам использовать чистый javascript, потому что он намного яснее и легче.

Допустим, установка уже сделана. Мы вернемся к переделке установки, как только установим общий шаг времени. Теперь предположим, что все уже работает.

Проще говоря, почтовый агент делает следующее:

 Upon receiving an email,
   Evaluate if any action needs to be taken.
     Examples are: add/remove tags, auto-respond, or (importantly) take an office action.
     Evaluation is semantically equivalent to taking the action.

Это также звучит довольно просто. Здесь я говорю, что решение о том, что действие должно быть предпринято, равносильно принятию этого действия. Это звучит разумно для меня, и это течет в духе рубина на рельсах: метод тот же, что и результат вызова метода. Где же тогда агентическая часть?

* * * * * * *

Давайте посмотрим на существующие агентные системы, чтобы увидеть, как они построены. Многие из них являются телефонными системами: скажем, вы звоните в свой банк, но телефон пытается поговорить с вами без участия представителя службы поддержки клиентов. Как работают эти системы? Я пропущу материал на естественном языке: анализ голоса, генерация голоса и LLM были очень хорошо сделаны гигантами отрасли, и я не могу ни переизобрести этот материал, ни действительно улучшить его. Я буду использовать доступные процедуры LLM и синтеза / анализа. Я предполагаю, что простой английский текст является универсальным интерфейсом. Это может быть расширено в yaml или json, который снова выглядит как минимальный семантический контейнер, но гораздо более структурированный, чем английский. Интерфейс yaml/json затем преобразуется в код, особенно для интеграции API, но этот интерфейс также может быть чистым и понятным. Мне не нужен ИИ для написания кода разъема API, просто скажем, что он может быть написан людьми.

контекст

Старомодная агентическая система имеет контекст для пользователя: она знает, кто пользователь, что в его учетной записи, и что пользователь сказал или сделал ранее, так или иначе. Для меня эта информация представляет собой структурированные данные Leadset. Каждый человек (адрес электронной почты) принадлежит своей компании (лидсет), и все много конвергенций можно найти, ссылаясь на этого человека (электронная почта). Соответствующая информация также извлекается из всех разговоров всех коллег (компании, лидета). Это было реализовано.

намерение

Старомодная агентическая система работает на умысел. Система считывает намерение клиента (например: проверить баланс, сообщить о краже карты, произвести платеж или совершить самоубийство с помощью правительства). А пока давайте закодируем все возможные намерения. Затем система предпринимает одно из возможных действий. Кажется, что доступное действие очень тесно коррелирует с намерением. Возможно, каждое намерение соответствует одному или нескольким действиям. Как написать это в псевдокоде?

 ## won't quite work:
 Upon receiving an email,
   Decide the intent (confirmable/overrideable), then
     Take action relevant to the intent.

Однако моя система электронной почты не является чат-ботом для обслуживания клиентов. Клиент электронной почты не реагирует на намерения отправителя - у него есть намерения. Система должна иметь свои собственные цели. Я пока называю идею воронкой продаж. Поскольку все это делается для получения дохода, я в первую очередь подключаю это к продажам.

Несколько прагматических определений почтовых ботов

Давайте создадим несколько определений почтовых ботов, которые имеют смысл и прагматичны. Давайте разработаем одну или несколько воронок продаж, или действий, которые может предпринять агентный бот. Давайте сделаем это уже, на простом английском. Помните, что мы можем преобразовать это в yml/json, затем в Ruby-интерфейс, а затем в фактическую реализацию кода. Вот так. Некоторые агенты, в которых я уже нуждаюсь:

  • День рожденияBot
  • SpamCocktailBot
  • Электронная почта FluffBot
  • Холодный звонокBot

И определения псевдокода для каждого отдельно.

День рожденияBot

 BirthdayBot
   When it's someone's birthday as determined by tags and the clock,
     Generate an original birthday letter and send it.

Холодный звонокBot

 ColdcallBot
   For each tagged lead (in the campaign)
     periodically (every day)
       If I bothered them 0 times (n_impressions),
         send the main pitch.
         mark that I've bothered them 1 time.
       If I bothered them 1 time,
         send the 2nd reminder
         mark that I've bothered them 2 times.
       If I bothered them 2 times,
         send the 3rd sales pitch
         mark that I've bothered them 2 times.
       If I bothered them 3 times,
         remove tag, add tag `closed`
   When receiving an email in a relevant conversation,
     delete previously scheduled reminders
     remove tag, add tag `closing-manual`

SpamCocktailBot

 SpamCocktailBot
   When receiving an email from a spam lead (new or existing),
     Delete all previous follow-ups to this lead.
     Create a semi-meaningful reply and send it.
     Schedule to follow up in 2 days.

Электронная почта FluffBot

 EmailFluffBot
   Periodically for each lead tagged to be conversed with,
     Taking previous conversations into account,
     Send them a generated email based on the general intent of the bot.

* * * * * * *

Как я буду отслеживать, сколько раз я обращался к лидеру? Это будет конкретное название для кампании за лидерство. Мы отмечаем все в названии, и хотя это само по себе не гарантирует уникальность названия, оно очень близко к этому и очень семантично и полезно. Примером названия кампании является «20260320-директор» или «20260320 KY директор холодный звонок» или что-то подобное, где я обращаюсь ко всем лицам, принимающим решения на уровне директоров в Кентукки, сегодня (отличается от того, чтобы делать то же самое завтра). Seach lead будет иметь конфигурационный парам:

 this_lead.memory ||= {}
 this_lead.memory['20260320 KY director coldcall'] ||= {}
 this_lead.memory['20260320 KY director coldcall']['n_impressions'] ||= 0
 this_lead.memory['20260320 KY director coldcall']['n_impressions'] += 1

Слово «память» выше ничего не значит, но есть также «this lead.config», который является отдельным, и элементы памяти не должны быть в корне. Память — это контекст, но я уже использую слово контекст в другом месте.

А отправкой основного, 2-го и 3-го полей являются EmailTemplates. Этот объект уже построен, и у него есть контекст для свинца, поэтому это транзакционное электронное письмо с богатым шаблонированием и переменной заменой, такой как имя свинца.

Намерение может быть легко реализовано с помощью тегов (которые уже существуют). Иметь теги, такие как «intent-they-salespitch-me» принадлежит тегу «intents», назначить этот тег руководителю или разговору, и там вы идете, это было сделано.

Возможные намерения в электронной почте:

  • Меня продают
  • Они беспокоят меня без причины
  • спам
  • Автоматический... периодический
  • близкий друг
  • Настоящий человек
  • потенциальный клиент
  • рекрутер

Итак, я могу назначить намерение руководителю и разговору, просто пометив их.

Еще кое-что. Нам нужно концептуализировать воронку продаж. Смотрите статью на Воронка продаж: определение, модели и этапы.

* * * * * * *

Это хорошая идея, чтобы записать слизни, которые вы планируете использовать заранее, на этапе планирования, а не менять их позже. Существует мало валидации слизней, поэтому вы должны быть точны в том, как вы получаете доступ к объектам. Я люблю начинать каждый слизень с даты в формате yyymmdd или yyyymm. Это хорошо работало для меня, особенно потому, что сортировка по алфавиту также сортирует по дате.

Вам нужен каждый шаблон электронной почты. Шаблоны это:

  • 20260320 - холодный вызов
  • 20260320-coldcall-intro-1
  • 20260320-coldcall-intro-2

Тебе нужен каждый слизняк. Они:

  • 20260320 - холодный звонок
  • 20260320 - холодный звонок

Слизняк для этой кампании может быть: «20260320-холодный звонок». Обратите внимание, что я избегаю пробелов в слизняках. Слизни должны быть закодированы.

Электронная почта

Таким образом, мой агентный почтовый бот отправит последовательность писем холодным лидерам. Давайте напишем содержание для электронных писем. Эта воронка предназначена для технических лиц, принимающих решения, руководителей, таких как технический директор в своих компаниях. Во-первых, бот представится и попросит разрешения продолжить. Затем намерение будет захвачено, что является одним из: (1) «без остановки», (2) «да-продолжение» и (3) «нет ответа». Если они ответят положительно, «да-продолжить», на самом деле я должен уже переключить процесс на ручной, так как это будет заинтересованный клиент. Фактически, если один человек ответит «да-продолжение», это приведет к успешному выполнению этой задачи. Если они отвечают «без остановки», бот отправляет отличное последнее сообщение «tmpl-fin-neg». При отсутствии ответа отправляется сообщение tmpl-intro-2. После этого при отсутствии ответа отправляется сообщение tmpl-fin-neg. Это полный бот и полная воронка. Давайте посмотрим.

Существует два способа создания шаблонов электронной почты. Вы можете написать их сами и спасти. Или вы можете использовать GPT, чтобы написать и сохранить их. Эта статья о боте электронной почты, а не о разговорном LLM-агенте, поэтому я пропущу второй, более сложный вариант и просто напишу шаблон с нуля. Вообще-то, я все еще использую GPT, но буду копировать текст вручную.

 20260320-coldcall-intro
   prompt: Write a cold-approach email directed at CTO's and other technical decision-makers of technology companies, asking them if they'd like to hear about the technology services (such as software development or feature implementation) that Wasya Co offers.

Тема: Изучение технологических решений для [наименование компании]

Привет (первое имя),

Я надеюсь, что это сообщение найдет вас хорошо. Я обращаюсь к Wasya Co, где мы помогаем технологическим компаниям ускорить свои проекты с помощью экспертной разработки программного обеспечения и индивидуальной реализации функций.

Я хотел бы понять ваши текущие технологические приоритеты и выяснить, могут ли наши услуги помочь вашей команде в их более эффективном достижении. Вы готовы к короткому разговору, чтобы обсудить это?

С нетерпением жду ваших мыслей,

(Ваше имя)
[Ваша позиция]
Wasya Co.
[Контактная информация]

 20260320-coldcall-intro-1
   prompt: write a follow-up to the previous, if he doesn't reply in 2 days

Тема: Оказание технической поддержки [название компании]

Привет (первое имя),

Я хотел продолжить свое предыдущее электронное письмо о том, как Wasya Co может помочь в разработке программного обеспечения и реализации функций.

Если вы готовы к быстрому разговору, я буду рад обсудить, как наши решения могут помочь вашей команде достичь своих целей быстрее и эффективнее. Пожалуйста, дайте мне знать время, которое работает для вас, или я буду рад отправить вам краткую информацию для ознакомления.

С нетерпением жду услышать от вас,

(Ваше имя)
[Ваша позиция]
Wasya Co.
[Контактная информация]

 20260320-coldcall-intro-2
   prompt: write a final, closing follow-up saying to reach out to me if anything changes

Тема: Последнее наблюдение - Вася Технологическая поддержка

Привет (первое имя),

Я хотел в последний раз поговорить о разработке программного обеспечения и услугах по внедрению функций Wasya Co. Я понимаю, что сейчас не самое подходящее время.

Если ваши приоритеты изменятся или вы захотите узнать, как мы можем поддержать вашу команду в будущем, пожалуйста, не стесняйтесь обращаться к нам - я буду рад связаться, когда это удобно для вас.

Спасибо за ваше время и внимание,

(Ваше имя)
[Ваша позиция]
Wasya Co.
[Контактная информация]

Создайте шаблоны электронной почты. Мы пропустим сообщение «tmpl-fin-neg», так как пока молчание будет служить так же хорошо.

Я буду комбинировать рубиновый код с использованием интерфейса. UI специально вмещает блоки кода в определенных местах. Хотя он менее безопасен, чем точка и щелчок, он предлагает мне два преимущества. Во-первых, мне не нужно представлять весь пользовательский интерфейс, который, я думаю, мне понадобится в будущем. Просто бесплатные обратные вызовы проходят очень долгий путь, и для технического (экспертного) пользователя этого будет достаточно. В нижней строке система будет предлагать несколько периодов выполнения (например, вы сможете писать либо javascript, либо Ruby-код, по своему усмотрению), а также пользовательский интерфейс, чтобы вообще не писать код. Во-вторых, это дает мне огромную гибкость. До тех пор, пока корректные переменные открыты (и они есть), я могу быть очень утонченным в том, что я делаю с ними.

create email templates

Нажмите на ссылку «[+]», чтобы начать создавать новый шаблон электронной почты.

Обратите внимание, что субъект, как и тело, допускает переменные шаблона erb. Перечислены доступные переменные.

rich text editor

Нажмите на ссылку RTEditor в нижнем колонтитуле, чтобы открыть богатый текстовый редактор в отдельном окне.

rich text editor

Причина, по которой богатый текстовый редактор не встроен в форму, заключается в том, что как технический пользователь, вы можете поместить в тело электронной почты больше, чем HTML-теги. В частности, вы можете использовать шаблон erb (ruby) для вставки динамических переменных в текст. Доступные переменные перечислены в справочной информации. Например, вы можете написать:

 Hello <%= @lead.name -%>,

Чтобы персонализировать приветствие. Чтобы это сделать, в настоящее время вы должны написать простой текст (который может включать HTML-код или другой код). Но все же можно писать насыщенный текст в редакторе, предоставленном отдельно, затем копировать-вставить источник в форму шаблона электронной почты.

Закончите вставлять значения и нажмите Сохранить.

finish and click save
Машинные государства

Текущими состояниями машины являются:

  • "Начни"
  • "без остановки"
  • "без ответа-1"
  • "без ответа-2"
  • "да-продолжение"
  • "фин"

Государственные машины и государство, хотя и не упомянутые этим термином в этой статье, относятся к запоминаемому состоянию. Состояние лидера сохраняется повсюду, и у лидера есть один государственный словарь. Если вы сохранили что-то в памяти этого лидера год назад, оно все равно будет там. Поэтому вы можете или не хотите разделять переменные для этого кампаина на его собственный хеш:

## if you don't anticipate conflicts:
lead.memory['current_state'] = 'no-response-1'

## if you want to compartmentalize:
lead.memory['20260320-coldcall'] ||= {}
lead.memory['20260320-coldcall']['current_state'] = 'no-response-1'

который на самом деле коррелирует с переменной «this lead.memory ['20260320 KY Director Coldcall'] ['n impressions']» как таковой:

n impressions:Current state:0(начало)-(без остановки)1 1(без ответа-1)2 2(без ответа-2)-(да-продолжение)3(фин)

Я буду использовать current state, а не n impressions, поскольку я считаю, что это более точно. Или я могу использовать оба. Теперь давайте реализуем этот бот на платформе wco email. После внедрения мы загрузим около 100 потенциальных клиентов в качестве пробных данных и запустим кампанию!

<<

OfficeActionTemplate (OAT)

Итак, давайте разберем бота, который состоит из двух частей. Одним из них является OfficeActionTemplate, который отправляет указанные выше электронные письма. Другой - EmailFilter, который действует на ответ от руководителя.

Давайте создадим ОАТ, который является картофелем и мясом этого бота / кампании. В Pseudocode это может выглядеть примерно так:

 OfficeActionTemplate
   if leads.count == 0
     report to self: the campaign has ended
     this_oat.unschedule()
   For each lead in the tag `a20260320-active-leads`,
     case lead.memory['current_state']
     when
       (begin)
         send: a20260320-tmpl-intro
         lead.memory['current_state'] = no-response-1
       (no-stop)
       (no-response-1)
         send: a20260320-tmpl-intro-1
         lead.memory['current_state'] = no-response-2
       (no-response-2)
         send: a20260320-tmpl-intro-2
         lead.memory['current_state'] = no-stop
         lead.tags.delete a20260320-active-leads
         lead.tags.add    a20260320-done-leads
       (yes-continue)
       (fin)
   next_at( Time.now + 2.days )

И код:

tag = Wco::Tag.find_by({ slug: '20260320-coldcall-active' })
leads = tag.leads
if leads.count == 0
  @oat.update({ status: 'inactive' }) ## oat == office action template
  ApplicationMailer.notify_self({ subject: "Campaign 20260320-coldcall-active has finished." })
else
  leads.each do |lead|
    lead.memory['current_state'] ||= 'begin'
    lead.memory['n_impressions'] ||= 0

    case lead.memory['current_state']
    when 'begin':
      WcoEmail::EmailAction.create({ 
        lead: lead,
        template: EAT.find_by({ slug: '20260320-tmpl-begin' }), ## EAT == EmailActionTemplate
        perform_at: Time.now })
      lead.memory['current_state'] = 'no-response-1'

    when 'no-stop':
      lead.remove_tag tag

    when 'yes-continue':
      lead.remove_tag tag
      ApplicationMailer.notify_self({ 
        subject: "Campaign 20260320-coldcall-active lead #{lead.id} replied yes-continue!", 
        body: "Lead: #{lead.inspect} 

" }) when 'fin': lead.remove_tag tag when 'no-response-1': WcoEmail::EmailAction.create({ lead: lead, template: EAT.find_by({ slug: '20260320-tmpl-cont-1' }), ## EAT == EmailActionTemplate perform_at: Time.now }) lead.memory['current_state'] = 'no-response-2' when 'no-response-2': WcoEmail::EmailAction.create({ lead: lead, template: EAT.find_by({ slug: '20260320-tmpl-cont-2' }), perform_at: Time.now }) lead.memory['current_state'] = 'fin' else: ApplicationMailer.notify_self({ subject: "Campaign 20260320-coldcall-active something went wrong with lead #{lead.id}.", body: "Lead: #{lead.inspect}

Oat: #{@oat.inspect}" }) end end end

Давайте вставим его в интерфейс. Это немного сложно и технично, однако создание пользовательского интерфейса без кода будет еще сложнее и будет сделано только в будущем, если будет продемонстрирован достаточный спрос клиентов.

OAT UI

Давайте перейдем к созданию фильтра электронной почты.

Фильтр электронной почты

EmailFilter является еще одной частью этого бота. Ответы на них (если таковые имеются) будут даны.

 EmailFilter
   if lead.tags.include? a20260320-active-leads
     intent = induce_intent()
     lead.memory['current_state'] = intent
     case intent:
     when 'yes-continue':
       notify self: Email.find('this-lead-is-interested', lead:, )
     when 'no-stop':
       lead.tags.delete a20260320-active-leads
       lead.tags.add    a20260320-done-leads
     default:
       notify_self: Email.find('for-review', lead:, )

И это в значительной степени так. Единственное, что осталось сделать, это поместить 100 потенциальных клиентов в этот пробный тег ... и запустить его!

Вот как импортировать электронные письма в тег:

<<

И чтобы запустить кампанию, просто создайте OfficeAction из шаблона.

Отлично! Теперь, когда он запущен, позволяет контролировать процесс. Я добавил себя в качестве одного из этих лидеров, поэтому я собираюсь проверить рабочий процесс, участвуя по обе стороны кампании. Я отвечу как «да», так и «нет», чтобы проверить рабочий процесс.

<<

Вышеупомянутый рабочий процесс является примером того, как настроить агента электронной почты в системе wco email. Мы на самом деле не использовали ИИ для этого, только индукцию намерения.

comments

Please login to post comments:
On 2026-04-19 admin said:

Many technologists rush to add AI into routine email workflows, but automation and well-structured rules often deliver far more reliability and transparency than agentic AI. Sometimes, a 'dumb' system is all a real business needs.