- BrainTools - https://www.braintools.ru -
Отзывы пользователей — один из самых ценных источников информации о продукте, при этом часто клиенты описывают одну и ту же тему или проблему десятками разных слов. Раньше работать с фидбэком было долго и ресурсоемко, но с появлением Embeddings и LLM это изменилось.

Меня зовут Вова Михайлов, я маркетинговый аналитик в Garage Eight. Мы развиваем международные финтех-продукты и стараемся использовать всю информацию для улучшения наших сервисов, в том числе отзывы.
В этой статье я расскажу, как мы смогли автоматизировать и ускорить аналитику фидбэков с помощью Embeddings и GPT. Наш опыт [1] будет релевантен не только финтеху, но и любым компаниям, которые хотят научиться в реальном времени получать данные из отзывов и сравнивать себя с конкурентами.
I completed the action, but nothing changed on my side.
The information I expected is missing.
Still waiting for everything to update.
The expected update hasn’t come through.
Все эти отзывы относятся к одной теме, при этом различаются по лексике и тональности. Для человека очевидно, что их связывает, а для компьютера это совершенно разные наборы слов.
В существующих приложениях, которые мы используем для сбора отзывов (YouScan, AppTweak), аналитика так и собирается по ключевым словам: найти общие темы можно, но это распределение будет некачественным. Чтобы корректно разобрать их по ключевым категориям или фичам, специалисту понадобятся недели или месяцы. Именно поэтому мы решили собрать свой инструмент и автоматизировать работу с отзывами.
Чтобы разобраться в доступных методах анализа фидбэка, поделюсь ретроспективой — как они менялись с развитием технологий:
Ручной анализ. Сначала аналитики читали фидбэк вручную и раскладывали его по категориям в Excel. Это работало на десятках и сотнях отзывов, но масштабировать дальше было очень ресурсоемко.
Опросы и NPS. Далее компании начали измерять NPS, CSAT и другие метрики удовлетворенности. Это позволило понять уровень лояльности, но не дало объяснений причин проблем.
Keyword Analytics. Потом появился поиск по ключевым словам и Word Cloud. Можно было собирать облака тегов, определять ключевые темы, но проблема была в том, что алгоритм выявлял слова, а не смысл. Отзывы на одну тему, написанные разными словами, оказывались в разных категориях.
Classical NLP. С появлением NLP стали использовать TF-IDF, LDA и Topic Modeling для автоматического поиска тем. Это усовершенствовало группировку, но всё еще не помогало справиться с ключевой проблемой.
Embeddings. Здесь произошла революция: вместо слов начали использовать многомерное векторное представление смысла. Например:
My funds are missing → [0,14, −0,92, 0,33, …].
Deposit never arrived → [0,15, −0,90, 0,31, …].
Благодаря этому в многомерном векторном пространстве отзывы оказываются рядом, хотя общих слов почти нет. Так мы наконец научились искать отзывы из одной темы, написанные в разных выражениях.
LLM. Большие языковые модели стали отличным дополнением: Embeddings хорошо находят группы похожих отзывов, а LLM помогают интерпретировать их и превращать в понятные бизнес-темы.
В своем решении я использовал связку инструментов:
Google Play Scraper → OpenAI Embeddings API → KMeans Clustering → GPT Naming → Meta Clustering → GPT Naming → Business Topics
То есть система сначала понимает смысл отзывов, затем группирует их по смысловой близости и в конце переводит результат на привычный нам язык. Далее расскажу, что происходит на каждом из этапов.
Перед началом кластеризации отзывов мы обрабатываем данные. С появлением современных Embeddings большая часть этой подготовки стала необязательна: теперь модели достаточно хорошо работают самостоятельно с разными языками, опечатками, разговорными выражениями, эмодзи и сокращениями. Именно поэтому в нашем решении предобработка минимальная.
Фактически я почти не делаю классическую NLP-нормализацию: не перевожу отзывы на английский, не делаю лемматизацию, когда все слова приводятся в начальную форму, и не вычищаю агрессивно текст. Изначально мне хотелось проверить, насколько современные Embeddings смогут работать с сырыми данными из стора. Честно говоря, результат оказался лучше ожидаемого: модель достаточно хорошо группирует отзывы по смыслу, даже на смеси разных языков и с опечатками.
Итак, переходим к основной части. В основе Embeddings лежит идея представления текста в виде многомерного вектора признаков. Для этого я использую OpenAI Embeddings API, который преобразует каждый отзыв в вектор размерностью 1536 признаков.
from openai import OpenAI
client = OpenAI(api_key=config.API_KEY)
def get_embedding(text):
response = client.embeddings.create(
model="text-embedding-3-small",
input=text
)
return response.data[0].embedding
df["embedding"] = df["Review"].apply(get_embedding)
После построения Embeddings я использую алгоритм KMeans для поиска плотных областей в семантическом пространстве. KMeans я выбрал по нескольким причинам:
это простой и хорошо изученный алгоритм;
быстро работает на десятках тысяч записей и легко масштабируется;
дает предсказуемый результат;
позволяет контролировать количество тем через параметр K.
Единственная сложность — KMeans требует заранее знать количество кластеров. Именно поэтому пришлось экспериментировать с разными значениями K и смотреть на качество получаемых тем.
Кроме KMeans, я рассматривал еще два метода:
DBSCAN/HDBSCAN. Они не требуют заранее задавать число кластеров, хорошо находят выбросы и часто используются для embedding-space. Но в моих тестах они либо давали слишком много мелких кластеров, либо сильно зависели от параметров.
Agglomerative Clustering. Он хорошо показывает иерархию тем и удобен для последующего объединения похожих кластеров. В итоге именно его я использовал на втором уровне пайплайна для формирования MetaTopic.
В результате получилось так, что KMeans отвечает за поиск локальных смыслов внутри отзывов, а Agglomerative Clustering помогает находить более крупные бизнес-темы поверх уже найденных кластеров.
Интересный момент — появление LLM сильно изменило требования к кластеризации. Раньше нужно было получить «идеальные» кластеры, потому что именно они были конечным результатом анализа. Теперь кластеризация стала промежуточным этапом.
Главное — не идеальная математическая точность, а быстрое объединение семантически похожих отзывов, которые затем LLM может качественно проанализировать, назвать темы, выделить проблемы и сформулировать выводы. Это позволяет использовать более простые и быстрые алгоритмы кластеризации, сохраняя высокое качество итоговой аналитики.
Технически кластеризация реализуется следующими строками:
import numpy as np
from sklearn.cluster import KMeans
X = np.vstack(df["embedding"].values)
kmeans = KMeans(
n_clusters=15,
random_state=42,
n_init="auto"
)
df["ClusterID"] = kmeans.fit [2]_predict(X)
Если на вход KMeans дать 1000 отзывов, то вместо тысячи отдельных комментариев можно получить примерно 10–20 устойчивых тематических кластеров. Например, в нашем случае могут быть такие области:
Verification
Localization
Trust & Security
Product Features
На этом этапе мы уже переходим от текста к смыслу, но сами кластеры пока остаются просто математическими группами похожих векторов.
Чтобы сделать результаты кластеризации понятными человеку, поверх подключается ChatGPT. Модель анализирует примеры отзывов внутри каждого кластера и формирует базу данных с названием темы, описанием проблемы, тональностью отзывов, их количеством и средним рейтингом. Дополнительно для каждого отзыва в базе данных сохраняются дата публикации, рейтинг и тональность, что позволяет анализировать не только сами темы, но и их динамику во времени.
Для обработки данных используем промпт:
prompt = f"""
You are a senior product researcher.
Analyze the customer reviews below.
Identify the single dominant customer topic inside this cluster.
Return ONLY valid JSON:
{{
"cluster_name": "Topic name",
"description": "Short description",
"sentiment": "Positive"
}}
Rules:
- Return exactly one topic.
- Choose the dominant theme.
- Sentiment must be only: Positive, Negative, Mixed.
Reviews:
{reviews_text}
"""
response = client.chat [3].completions.create(
model=config.MODEL_TOPICS,
response_format={"type": "json_object"},
messages=[
{"role": "user", "content": prompt}
],
temperature=0
)
result = json.loads(response.choices[0].message.content)
Таким образом тысячи комментариев превращаются в полноценную модель Voice of Customer. Анализируя модель, мы поняли, что можем использовать ее не только на собственных отзывах, но и на отзывах конкурентов. Так мы смогли выявить их сильные и слабые стороны, а также определить наше положение на рынке с точки зрения [4] клиентов.

Всё получилось: Embeddings определили векторы отзывов, KMeans кластеризовал их, а GPT собрал понятную БД. И тут появилась новая проблема: оказалось, что одинаковые темы могут иметь разные названия:
Need English Language
English Language Request
Lack of English Support
По сути это та же проблема, которую мы решали с помощью векторов: бизнес понимает, что это одна сущность, а модель — нет. Именно поэтому я добавил второй уровень анализа и кластеризации.
На этом этапе для уже найденных GPT тем мы повторно строим Embeddings и с помощью Agglomerative Clustering применяем иерархическую кластеризацию. Это позволяет объединять похожие темы разных компаний в общие группы.
from sklearn.cluster import AgglomerativeClustering
from sklearn.metrics.pairwise import cosine_distances
topic_texts = (
topics["ClusterName"] + "n" + topics["Description"]
).tolist()
topic_embeddings = [
get_embedding(text)
for text in topic_texts
]
X_topics = np.vstack(topic_embeddings)
distance_matrix = cosine_distances(X_topics)
clustering = AgglomerativeClustering(
metric="precomputed",
linkage="average",
distance_threshold=0.35,
n_clusters=None
)
topics["MetaClusterID"] = clustering.fit [5]_predict(distance_matrix)
Затем GPT снова обрабатывает данные и формирует более высокий уровень абстракции — MetaTopic. После этого похожие MetaTopic дополнительно консолидируются в бизнес-категории.
prompt = f"""
You are a product analytics expert.
Below are similar customer topics from different apps.
Create one concise MetaTopic name that represents them.
Return ONLY valid JSON:
{{
"meta_topic": "Meta topic name"
}}
Topics:
{topics_text}
"""
response = client.chat [3].completions.create(
model=config.MODEL_TOPICS,
response_format={"type": "json_object"},
messages=[
{"role": "user", "content": prompt}
],
temperature=0
)
result = json.loads(response.choices[0].message.content)
В результате получается многоуровневая карта смыслов:
Review (Отзыв) → Cluster (Общая тема) → MetaTopic (Более крупная тема) → BusinessTopic (Бизнес-задача).
Например: Need English Language → English Language Support → Multilingual App Support → Localization.
Фактически система перестает работать со словами и начинает работать со смыслами: на входе — тысяча неструктурированных отзывов, на выходе — десяток бизнес-категорий, их тональность, объем обсуждения и динамика изменений во времени. Именно это позволяет использовать отзывы не как набор комментариев, а как источник знаний о продукте, клиентах и рынке.
С помощью нашего решения мы обошли встроенные инструменты аналитики в приложениях для отзывов и смогли автоматизировать работу с ними — то, на что раньше у специалиста ушли бы недели, сейчас обрабатывается за десять минут.
Сейчас мы активно пользуемся внутри инструментом и планируем внедрить чат-бота, который будет дополнительно анализировать и комментировать результаты в модели Voice of Customer.
Огромное преимущество современных возможностей в том, что стоимость такого решения минимальна: мы используем только OpenAI Embeddings API. Адаптировать наше решение под себя можете и вы — я буду рад обсудить фидбэк и ответить на ваши вопросы, задавайте их в комментариях.
Автор: Vladimir_Mihailov
Источник [6]
Сайт-источник BrainTools: https://www.braintools.ru
Путь до страницы источника: https://www.braintools.ru/article/32832
URLs in this post:
[1] опыт: http://www.braintools.ru/article/6952
[2] kmeans.fit: http://kmeans.fit
[3] client.chat: http://client.chat
[4] зрения: http://www.braintools.ru/article/6238
[5] clustering.fit: http://clustering.fit
[6] Источник: https://habr.com/ru/companies/garage8/articles/1057100/?utm_source=habrahabr&utm_medium=rss&utm_campaign=1057100
Нажмите здесь для печати.