В McKinsey & Company подсчитали, что 67% компаний в ближайшие три года планируют увеличить инвестиции в ИИ-технологии. При этом российские компании в основном применяют генеративные нейросети в маркетинге и рекламе — а это 66% внедрений.

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

Раньше традиционные рекламные ИИ-инструменты позволяли составлять профиль потенциального клиента и показывать ему рекламу на основе его предполагаемых интересов. Сегодня ИИ может генерировать лиды с обратной стороны: сначала анализировать колоссальный объем данных о пользователях — пол, возраст, сообщения в публичных чатах и прочее. А затем с помощью LLM-модели и модели машинного обучения анализировать эти текстовые данные, выявлять интересы клиентов и предлагать им наиболее релевантный продукт/услугу.

Зачем разрабатывать свой ИИ для лидогенерации

Бизнес может воспользоваться готовыми ИИ-решениями для улучшения лидогенерации. Но разработка собственного искусственного интеллекта дает несколько преимуществ:

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

Как правило, вертикально-интегрированные компании создают свои модели с нуля или на основе модели открытого доступа. В итоге они получают более глубоко проработанный продукт — чат-бота или интеллектуальную систему.

Например, компания Perplexity создала свою поисковую систему и чат-бота, который может общаться с пользователями на различные темы. Обладая глубокой экспертизой в машинном обучении, компания сама готовила данные и дорабатывала архитектуру на основе языковых моделей GPT-4о и Claude 3. Принцип работы чат-бота похож на ChatGPT. Но ChatGPT решает более общие рабочие задачи, а Perplexity предлагает максимально персонализированного ассистента.

Разработка сценария для лидогенерации

Внедрять ИИ-систему для лидогенерации нужно в несколько этапов.

Первый этап — взаимодействие с бизнесом

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

Второй этап — это подготовка тренировочного датасета

Датасет — это структурированный набор данных для отладки лидов. Первый набор данных мы загружаем в нейросети из множества имеющихся сообщений. Затем нейросеть выбирает данные, максимально релевантные заявленному продукту бизнеса.

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

Третий этап — дообучение модели

На основе подобранного датасета разработчики дообучают модель. Для этого можно пользоваться двумя основными инструментами:

  • DSPy — инструмент автоматического тюнинга промтов от Стэнфордской NLP-школы. С помощью данного инструмента на собранном датасете можно в автоматическом режиме подобрать нужные слова для LLM-модели. Это те слова, которые бизнес ввел текстом на первом этапе. Инструмент DSPy позволяет уточнить эти запросы, добавляя нужные примеры и слова в описании. Также он исключает лишние слова, которые мешают LLM-модели правильно классифицировать лида.
  • LoRA — low-rank adaptation — этот инструмент используется для доработки open source-моделей (открытый исходный код, который можно дорабатывать). LoRA поверх основной модели добавляет несколько параметров, и позволяет на основе подобранного датасета автоматически дообучать саму модель.

Работа модели состоит из двух частей:

  1. Собственно вычисление весов модели, т.е. ее первоначальное обучение. Под весами понимается огромная матрица из чисел, которые перемножаются для получения нужного результата. В процессе обучения модели подбираются нужные числа и параметры в огромной матрице.
  2. Дообучение: из матрицы с миллиардами параметров отбирают и дообучают нужное количество параметров. Таким образом веса приводятся в состояние, когда сгенерированные моделью ответы будут подходить под задачу.

Четвертый этап — запуск модели

Разработчики оценивают результат, возвращаются на предыдущие этапы и при необходимости дорабатывают модель.

На этом этапе оценивают не столько технические, сколько бизнес-показатели. Например, типичный пакет сообщений пользователей чата состоит из 100 тыс. сообщений. После их анализа разработчики отбирают условно 2 тыс. лидов, которые написали сообщения, релевантные товару/услуге бизнеса. Далее передает информацию по этим лидам бизнесу, а sales-команды связываются с потенциальными клиентами и доводят их до сделки.

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

Пятый этап — подведение итогов

На финальном этапе происходит калибровка модели с реальностью: насколько результат совпадает с изначальным запросом бизнеса. Здесь оценивают в том числе количество и качество лидов.

Фреймворки и библиотеки для создания чат-ботов

Сегодня есть большое количество библиотек для создания виртуальных ассистентов. Их можно разделить на несколько типов.

  1. Библиотеки для работы с промтами. Промты — это определенный кусок текста, который выдается на вход LLM-модели. Этот тип библиотек позволяет превращать естественный язык (сообщения клиента) в узконаправленные виды данных. Задача LLM-модели состоит в том, чтобы превратить полученные данные в число, а задача фреймворков — выделить это число из текста и предоставить разработчику.
  2. Библиотеки для создания ИИ-агентов, которые могут действовать самостоятельно и в кооперации. Эти библиотеки также принимают на вход LLM-модели текстовые описания, но позволяют уже на более высоком уровне задавать поведение мини-ботов. Разработчику нужно не просто задать искомый тип данных, а предоставить боту инструменты, с помощью которых он будет реализовывать задачу из основного промта.
  3. Библиотеки для ресерча. Они создают fine-tuning модели с помощью инструментов DSPy и LoRA, о которых говорилось выше. Как правило, разработчики такими глубокими исследованиями не занимаются. Это узкая задача MО-специалистов.

Пример

Давайте представим, что мы создаем простого виртуального ассистента для ресторана, который может помочь клиентам с заказом пиццы.

Библиотеки для работы с промтами (на примере Microsoft Instructor):

В этом примере мы используем библиотеку Microsoft Instructor для создания структурированного промта для заказа пиццы. Мы определяем класс PizzaOrder, который описывает структуру заказа пиццы, и затем используем этот класс для извлечения информации из текстового запроса пользователя.

from instructor import OpenAISchema
from pydantic import Field
from typing import List
import OpenAI
from instructor import patch

# Патчим клиент OpenAI для использования Instructor
patch()

class PizzaOrder(OpenAISchema):
    size: str = Field(..., description="Размер пиццы: маленькая, средняя или большая")
    toppings: List[str] = Field(..., description="Список топпингов для пиццы")
    extra_cheese: bool = Field(..., description="Нужен ли дополнительный сыр")

# Настройка OpenAI клиента (замените 'your-api-key' на ваш настоящий ключ API)
client = OpenAI.OpenAI(api_key='your-api-key')

# Функция для обработки заказа пиццы
def process_pizza_order(user_input: str) -> PizzaOrder:
    response = client.chat.completions.create(
        model="gpt-3.5-turbo",
        response_model=PizzaOrder,
        messages=[
            {"role": "system", "content": "Вы - ассистент по заказу пиццы. Извлеките информацию о заказе из сообщения клиента."},
            {"role": "user", "content": user_input}
        ]
    )
    return response

# Пример использования
user_input = "Я хочу большую пиццу с пепперони и грибами, и да, добавьте побольше сыра!"
order = process_pizza_order(user_input)

print(f"Размер пиццы: {order.size}")
print(f"Топпинги: {', '.join(order.toppings)}")
print(f"Дополнительный сыр: {'Да' if order.extra_cheese else 'Нет'}")

Теперь давайте разберем этот пример подробнее:

1. Импорт и настройка:

  • Мы импортируем необходимые компоненты из библиотеки Instructor и OpenAI.
  • Функция patch() из Instructor модифицирует клиент OpenAI, чтобы он мог работать с моделями Pydantic.

2. Определение схемы данных:

  • Мы создаем класс PizzaOrder, который наследуется от OpenAISchema. Это ключевой момент в использовании Instructor.
  • Каждое поле класса определено с использованием Field из Pydantic, что позволяет добавить описание для каждого поля. Эти описания помогут модели понять, какую информацию нужно извлечь.

3. Настройка OpenAI-клиента:

  • Мы создаем экземпляр клиента OpenAI, используя API ключ.

4. Функция обработки заказа:

  • process_pizza_order принимает пользовательский ввод в виде строки.
  • Внутри функции мы используем client.chat.completions.create() для отправки запроса к модели GPT.
  • Ключевой момент: мы указываем response_model=PizzaOrder. Это говорит Instructor, что мы ожидаем ответ в формате нашего класса PizzaOrder.

5. Использование модели:

  • Мы отправляем системное сообщение, которое инструктирует модель действовать как ассистент по заказу пиццы.
  • Пользовательский ввод передается как сообщение от пользователя.

6. Обработка результата:

  • Instructor автоматически преобразует ответ модели в экземпляр класса PizzaOrder.
  • Мы можем обращаться к полям этого объекта напрямую (order.size, order.toppings, order.extra_cheese).

Преимущество использования Instructor в том, что он автоматически преобразует неструктурированный текстовый ответ модели в структурированный объект Python. Это избавляет нас от необходимости парсить текст вручную и уменьшает вероятность ошибок.

Важно отметить, что Instructor работает, модифицируя стандартное поведение клиента OpenAI. Когда мы указываем response_model, Instructor добавляет специальные инструкции в промты, которые говорят модели, как нужно структурировать ответ. Затем он парсит ответ модели и создает объект указанного класса.

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

Библиотеки для создания ИИ-агентов (на примере LangChain):

В этом примере мы создаем агента с использованием LangChain, который может отвечать на вопросы о меню и ценах на пиццу. Агент использует два инструмента: один для получения меню, другой для расчета цены заказа.

from LangChain.agents import Tool, AgentExecutor, LLMSingleActionAgent
from LangChain.prompts import StringPromptTemplate
from LangChain import OpenAI, LLMChain
from typing import List, Union
from LangChain.schema import AgentAction, AgentFinish
import re

# 1. Определение инструментов (tools)
def get_menu():
    return "Меню: Маргарита, Пепперони, Четыре сыра, Вегетарианская"

def calculate_price(order: str):
    base_price = 10
    if "большая" in order.lower():
        base_price += 5
    return f"Цена заказа: {base_price} $"

tools = [
    Tool(
        name="Меню",
        func=get_menu,
        description="Получить список доступных пицц"
    ),
    Tool(
        name="Расчет цены",
        func=calculate_price,
        description="Рассчитать цену заказа"
    )
]

# 2. Определение промта
template = """Вы ассистент по заказу пиццы. Используйте следующие инструменты для помощи клиенту:

{tools}

Используйте следующий формат:

Вопрос: вопрос, на который нужно ответить
Мысли: вы всегда должны думать о том, что делать дальше
Действие: действие для выполнения, должно быть одним из [{tool_names}]
Вход для действия: вход в действие
Наблюдение: результат действия
... (этот процесс повторяется N раз, если нужно)
Мысли: Теперь я знаю окончательный ответ
Финальный ответ: окончательный ответ на первоначальный вопрос

Вопрос: {input}
{agent_scratchpad}"""

# 3. Создание кастомного промта
class CustomPromptTemplate(StringPromptTemplate):
    template: str
    tools: List[Tool]
   
    def format(self, **kwargs) -> str:
        intermediate_steps = kwargs.pop("intermediate_steps")
        thoughts = ""
        for action, observation in intermediate_steps:
            thoughts += action.log
            thoughts += f"\nНаблюдение: {observation}\nМысли: "
        kwargs["agent_scratchpad"] = thoughts
        kwargs["tools"] = "\n".join([f"{tool.name}: {tool.description}" for tool in self.tools])
        kwargs["tool_names"] = ", ".join([tool.name for tool in self.tools])
        return self.template.format(**kwargs)

prompt = CustomPromptTemplate(
    template=template,
    tools=tools,
    input_variables=["input", "intermediate_steps"]
)

# 4. Создание парсера вывода
class CustomOutputParser:
    def parse(self, llm_output: str) -> Union[AgentAction, AgentFinish]:
        if "Финальный ответ:" in llm_output:
            return AgentFinish(
                return_values={"output": llm_output.split("Финальный ответ:")[-1].strip()},
                log=llm_output,
            )
       
        action_match = re.search(r"Действие: (\w+).*?Вход для действия: (.*)", llm_output, re.DOTALL)
        if not action_match:
            raise ValueError(f"Could not parse LLM output: `{llm_output}`")
        action = action_match.group(1).strip()
        action_input = action_match.group(2)
        return AgentAction(tool=action, tool_input=action_input.strip(" ").strip('"'), log=llm_output)

# 5. Настройка LLM и создание агента
llm = OpenAI(temperature=0)
llm_chain = LLMChain(llm=llm, prompt=prompt)
tool_names = [tool.name for tool in tools]
agent = LLMSingleActionAgent(
    llm_chain=llm_chain,
    output_parser=CustomOutputParser(),
    stop=["\nНаблюдение:"],
    allowed_tools=tool_names
)

# 6. Создание и запуск агента
agent_executor = AgentExecutor.from_agent_and_tools(agent=agent, tools=tools, verbose=True)

# 7. Пример использования
result = agent_executor.run("Какие у вас есть пиццы и сколько стоит большая пицца?")
print(result)

Теперь давайте разберем этот пример подробнее:

1. Определение инструментов (tools):

  • Мы определяем две функции: get_menu() и calculate_price().
  • Эти функции оборачиваются в объекты Tool, которые агент может использовать.
  • Каждый инструмент имеет имя, функцию и описание, которое помогает агенту понять, когда использовать этот инструмент.

2. Определение шаблона промта:

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

3. Создание кастомного промта:

  • Класс CustomPromptTemplate наследуется от StringPromptTemplate.
  • Метод format отвечает за заполнение шаблона промта.
  • Он обрабатывает промежуточные шаги, форматирует информацию об инструментах и подготавливает «черновик» агента.

4. Создание парсера вывода:

  • Класс CustomOutputParser отвечает за интерпретацию вывода языковой модели.
  • Он определяет, завершил ли агент задачу (возвращая AgentFinish) или нужно выполнить еще одно действие (возвращая AgentAction).
  • Использует регулярные выражения для извлечения информации о действии и его входных данных.

5. Настройка LLM и создание агента:

  • Мы создаем экземпляр языковой модели OpenAI.
  • Создаем LLMChain, который связывает языковую модель с нашим промтом.
  • Создаем агента, используя LLMSingleActionAgent, который выполняет одно действие за раз.

6. Создание и запуск агента:

  • AgentExecutor объединяет агента и инструменты.
  • Он управляет процессом выполнения действий агентом и использования инструментов.

7. Пример использования:

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

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

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

Библиотеки для ресерча (Hugging Face Transformers с LoRA):

Преимущества использования LoRA:

  • Эффективность: LoRA позволяет настраивать большие модели с меньшим количеством обучаемых параметров.
  • Гибкость: можно быстро переключаться между различными адаптациями одной базовой модели.
  • Производительность: адаптированные модели часто показывают производительность, близкую к полностью дообученным моделям, но с меньшими затратами на обучение.

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

import torch
from datasets import load_dataset
from transformers import AutoTokenizer, AutoModelForCausalLM, TrainingArguments, Trainer, DataCollatorForLanguageModeling
from peft import get_peft_model, LoraConfig, TaskType
import os

# 1. Загрузка модели и токенизатора
model_name = "gpt2"
tokenizer = AutoTokenizer.from_pretrained(model_name)
model = AutoModelForCausalLM.from_pretrained(model_name)

# 2. Конфигурация LoRA
peft_config = LoraConfig(
    task_type=TaskType.CAUSAL_LM,
    r=8,  # ранг матрицы адаптации
    lora_alpha=32,  # параметр масштабирования
    lora_dropout=0.1,  # вероятность dropout
    bias="none",
    target_modules=["c_attn", "c_proj"]  # целевые модули для адаптации
)

# 3. Применение LoRA к модели
model = get_peft_model(model, peft_config)

# 4. Подготовка данных
def tokenize_function(examples):
    return tokenizer(examples["text"], padding="max_length", truncation=True, max_length=128)

# 5. Создание игрушечного датасета (в реальности, вам нужно будет подготовить свой датасет)
pizza_dataset = {
    "train": [
        {"text": "Я хочу заказать большую пепперони пиццу"},
        {"text": "Сколько стоит средняя маргарита?"},
        {"text": "Можно ли добавить дополнительный сыр на вегетарианскую пиццу?"},
        # Добавьте больше примеров здесь
    ],
    "test": [
        {"text": "У вас есть пицца четыре сыра?"},
        {"text": "Какие у вас есть варианты для вегетарианцев?"},
        # Добавьте больше примеров здесь
    ]
}

dataset = load_dataset("dict", data_files=pizza_dataset)
tokenized_datasets = dataset.map(tokenize_function, batched=True)

# 6. Настройка обучения
training_args = TrainingArguments(
    output_dir="./results",
    num_train_epochs=3,
    per_device_train_batch_size=8,
    per_device_eval_batch_size=8,
    warmup_steps=500,
    weight_decay=0.01,
    logging_dir="./logs",
    logging_steps=10,
    evaluation_strategy="steps",
    eval_steps=500,
    save_steps=1000,
    load_best_model_at_end=True,
)

# 7. Инициализация Trainer
trainer = Trainer(
    model=model,
    args=training_args,
    train_dataset=tokenized_datasets["train"],
    eval_dataset=tokenized_datasets["test"],
    data_collator=DataCollatorForLanguageModeling(tokenizer=tokenizer, МОm=False),
)

# 8. Запуск fine-tuning
trainer.train()

# 9. Сохранение модели
peft_model_id = "pizza-bot-lora"
model.save_pretrained(peft_model_id)

# 10. Загрузка и использование fine-tuned модели
from peft import PeftModel, PeftConfig

config = PeftConfig.from_pretrained(peft_model_id)
model = AutoModelForCausalLM.from_pretrained(config.base_model_name_or_path)
model = PeftModel.from_pretrained(model, peft_model_id)

# 11. Пример использования fine-tuned модели
input_text = "Я хочу заказать пиццу"
input_ids = tokenizer.encode(input_text, return_tensors="pt")
with torch.no_grad():
    output = model.generate(input_ids, max_length=50, num_return_sequences=1)
print(tokenizer.decode(output[0], skip_special_tokens=True))

Теперь давайте разберем этот пример подробно:

1. Загрузка модели и токенизатора:

  • Мы используем предобученную модель GPT-2 и соответствующий токенизатор из библиотеки Hugging Face Transformers.

2. Конфигурация LoRA:

  • LoRA (Low-Rank Adaptation) — это метод эффективной настройки больших языковых моделей.
  • Мы настраиваем LoRA, указывая ранг матрицы адаптации ®, параметр масштабирования (lora_alpha), вероятность dropout и целевые модули для адаптации.
  • LoRA позволяет настраивать модель, изменяя только небольшое количество параметров, что делает процесс более эффективным по сравнению с полной настройкой.

3. Применение LoRA к модели:

  • Функция get_peft_model применяет конфигурацию LoRA к нашей базовой модели.

4. Подготовка данных:

  • Мы определяем функцию tokenize_function, которая будет использоваться для токенизации наших текстовых данных.

5. Создание датасета:

  • В этом примере мы создаем небольшой датасет. В реальном проекте вам нужно будет подготовить более обширный датасет, релевантный вашей задаче.
  • Мы используем load_dataset для загрузки данных в формат, с которым может работать Hugging Face.

6. Настройка обучения:

  • Мы настраиваем параметры обучения, такие как количество эпох, размер батча, шаги для логирования и оценки, и т.д.

7. Инициализация Trainer:

  • Класс Trainer из Hugging Face упрощает процесс обучения, инкапсулируя многие детали.

8. Запуск fine-tuning:

  • Мы запускаем процесс дообучения модели на нашем датасете.

9. Сохранение модели:

  • После обучения мы сохраняем адаптированную модель.

10. Загрузка и использование fine-tuned модели:

  • Мы показываем, как загрузить сохраненную модель с адаптацией LoRA.

11. Пример использования fine-tuned модели:

  • Мы демонстрируем, как использовать настроенную модель для генерации текста по заданному началу.

Как выбрать фреймворк?

При выборе программной системы следует учитывать следующие критерии.

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

Второе — насколько библиотека поддерживаема. Как правило, на GitHub есть информация о том, насколько модель поддерживается, как давно велась работа над этим проектом. Если авторы не обновляют библиотеку, то это тревожный сигнал: наверняка вам понадобится много времени на ее интеграцию и поддержку.

Третье — насколько инструмент популярен и стандартизирован, т.е. общепринят в разработке. Есть библиотеки, которые компании создают для себя и выкладывают в общий доступ. Например, PWA, LangChain — известные фреймфорки для работы с промтами и LLM-моделями. В их надежности можно быть уверенным.

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

Команда для разработки ИИ-технологий

Размер вашей ИТ-команды будет зависть от уровня сложности ИИ-проектов.

Если это прикладные решения, связанные с использованием небольшой fine-tuning-модели, то будет достаточно команды из пяти человек. Разработчики справятся с задачей программирования поведения чат-ботов с помощью уже известных библиотек. В команде может не быть узких МО-специалистов, поскольку применяются готовые LM-модели через API (программные интерфейсы) более крупных ИТ-компаний.

Если нужно разворачивать LM-модели на своих мощностях, то компании потребуется команда из 20 разработчиков и собственная технологическая база — дата-центры и серверы. Это как раз компании, которые предоставляют другим разработчикам доступ к своим библиотекам и фреймворкам.

Для более глубокой технической экспертизы в LM-разработке нужна команда из 50-100 специалистов машинного обучения, которые глубоко разбираются в архитектуре своей модели, могут ее дорабатывать и дообучать на более высоком уровне. Речь идет о вертикально-интегрированных компаниях, которые создают свои модели с нуля.

Разработка собственного искусственного интеллекта для лидогенерации — это сложная и затратная задача. Но если компания обладает достаточными ресурсами, то индивидуальные ИИ-решения помогут ей улучшить маркетинговую стратегию и повысить эффективность продаж, поскольку ИИ может автоматически собирать данные о потенциальных клиентах, анализировать их поведение и предлагать товары/услуги на основе их интересов. Это повышает степень эмоционального взаимодействия между бизнесом и клиентом, а следовательно и конверсии в покупку.

Егор Копылов, основатель проекта Topspace