Сценарии использования и решения

Анализ отзывов покупателей с помощью ИИ на локальной LLM: тональность, аспекты и главные жалобы

6 шагов 20 мин чтения 19 просмотров 0
Содержание

Товары собирают сотни отзывов на Shopee, Lazada, Amazon, TikTok Shop и в вашем собственном магазине, часто на нескольких языках. Звёздный рейтинг показывает, что что-то не так, но не говорит, что именно. В этой статье локальная языковая модель читает каждый отзыв и фиксирует его общую тональность, аспекты товара, которые в нём хвалят или критикуют, и конкретные упомянутые проблемы. Затем отчёт по каждому товару показывает самые критикуемые аспекты, отзывы, оценка которых не соответствует тексту, и подготовленную ИИ сводку основных проблем с предложениями по их устранению. Всё работает на вашем собственном сервере, поэтому тексты покупателей не передаются третьим лицам.

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

Шаг 1: Установка инструментов

apt install -y python3-venv
python3 -m venv /opt/reviews
/opt/reviews/bin/pip install ollama numpy scikit-learn
ollama pull qwen2.5:7b

Установка Ollama описана в статье запуск локальной LLM с Ollama. numpy и scikit-learn для этой статьи не нужны, но их использует тематический анализ из соседнего руководства.

Шаг 2: Подготовка отзывов

Выгрузите отзывы в CSV-файл в кодировке UTF-8. Столбцы id и text обязательны; sku, rating (от 1 до 5) и date необязательны, но делают отчёт полезнее:

id,sku,rating,date,text
R1001,LS-01,2,2026-09-14,"The stand wobbles when I type, screws came loose after a week."
R1002,LS-01,5,2026-09-15,"Sturdy, looks great on my desk, easy to fold."
R1003,BT-07,1,2026-09-15,"左耳机三天就没声音了,客服也没回复。"

Шаг 3: Скрипт анализа

cat > /opt/reviews/reviews.py <<'EOF2'
import csv, json, os, sys
from collections import Counter, defaultdict
import ollama

MODEL = os.environ.get("MODEL", "qwen2.5:7b")
LANGUAGE = os.environ.get("REPORT_LANG", "English")       # language of issues, summaries and the report
ASPECTS = ["quality", "size_fit", "function", "durability", "ease_of_use",
           "delivery", "packaging", "price_value", "customer_service", "other"]
SENT = {"type": "string", "enum": ["positive", "neutral", "negative"]}
SCHEMA = {
    "type": "object",
    "properties": {
        "sentiment": SENT,
        "aspects": {"type": "array", "items": {"type": "object", "properties": {
            "aspect": {"type": "string", "enum": ASPECTS}, "sentiment": SENT}, "required": ["aspect", "sentiment"]}},
        "issues": {"type": "array", "items": {"type": "string"}},
        "summary": {"type": "string"},
    },
    "required": ["sentiment", "aspects", "issues", "summary"],
}
SYSTEM = ("You analyse customer reviews for an online shop. The review can be in any language. "
          "Give the overall sentiment, and the sentiment of each product aspect the review actually mentions. "
          f"List concrete problems as short phrases in {LANGUAGE} (empty list if there are none) "
          f"and write a one-sentence summary in {LANGUAGE}. Do not invent anything that is not in the review.")

def ask(system, text, schema=None):
    r = ollama.chat(model=MODEL, format=schema, options={"temperature": 0}, messages=[
        {"role": "system", "content": system}, {"role": "user", "content": text}])
    return r.message.content

src, out = sys.argv[1], "reviews_tagged.csv"
done = {}
if os.path.exists(out):
    with open(out, encoding="utf-8-sig") as f:
        done = {r["id"]: r for r in csv.DictReader(f)}
with open(src, encoding="utf-8-sig") as f:
    reviews = [r for r in csv.DictReader(f) if r.get("text", "").strip()]

fields = ["id", "sku", "rating", "sentiment", "aspects", "issues", "summary", "text"]
new_file = not os.path.exists(out)
with open(out, "a", newline="", encoding="utf-8-sig") as f:
    w = csv.DictWriter(f, fieldnames=fields)
    if new_file:
        w.writeheader()
    for r in reviews:
        if r["id"] in done:
            continue
        d = json.loads(ask(SYSTEM, r["text"][:4000], SCHEMA))
        row = {"id": r["id"], "sku": r.get("sku", ""), "rating": r.get("rating", ""), "sentiment": d["sentiment"],
               "aspects": "; ".join(f"{a['aspect']}:{a['sentiment']}" for a in d["aspects"]),
               "issues": "; ".join(i.strip() for i in d["issues"] if i.strip()), "summary": d["summary"], "text": r["text"]}
        w.writerow(row)
        f.flush()
        done[r["id"]] = row
        print(r["id"], d["sentiment"], flush=True)

by_sku = defaultdict(list)
for row in done.values():
    by_sku[row["sku"] or "-"].append(row)
with open("report.md", "w", encoding="utf-8") as f:
    for sku, rows in sorted(by_sku.items(), key=lambda x: -len(x[1])):
        sent = Counter(r["sentiment"] for r in rows)
        neg = Counter(a.split(":")[0] for r in rows for a in r["aspects"].split("; ") if a.endswith(":negative"))
        issues = [i for r in rows for i in r["issues"].split("; ") if i]
        odd = [r["id"] for r in rows if r["rating"].isdigit() and ((int(r["rating"]) >= 4 and r["sentiment"] == "negative")
               or (int(r["rating"]) <= 2 and r["sentiment"] == "positive"))]
        f.write(f"## {sku}: {len(rows)} reviews\n\n")
        f.write(f"- Positive {sent['positive']}, neutral {sent['neutral']}, negative {sent['negative']}\n")
        f.write("- Most criticised aspects: " + (", ".join(f"{a} ({n})" for a, n in neg.most_common(5)) or "none") + "\n")
        f.write("- Rating does not match the text: " + (", ".join(odd) or "none") + "\n\n")
        if issues:
            f.write(ask(f"You are a product manager. Group these customer complaints into the main problems, "
                        f"most frequent first, and suggest one concrete fix for each. Answer in {LANGUAGE}, briefly.",
                        "\n".join(issues[:300])) + "\n\n")
        print("report:", sku, flush=True)
print("done: reviews_tagged.csv, report.md")
EOF2

JSON-схема заставляет модель отвечать в фиксированной структуре: одна из трёх тональностей, аспекты из вашего списка и список проблем. Обработанные отзывы сохраняются сразу, а при повторном запуске пропускаются, так что анализ можно в любой момент остановить и продолжить или позже дописать новые отзывы в тот же CSV.

Шаг 4: Запуск анализа

cd /data/reviews
REPORT_LANG="Simplified Chinese" nohup /opt/reviews/bin/python /opt/reviews/reviews.py reviews.csv > reviews.log 2>&1 &
tail -f reviews.log

На сервере без GPU 7B-модели нужно от нескольких секунд до нескольких десятков секунд на отзыв в зависимости от его длины; большие выгрузки запускайте на ночь или используйте модель поменьше, например qwen2.5:3b, указав MODEL=qwen2.5:3b. Результаты — reviews_tagged.csv (по строке на каждый отзыв) и report.md (по разделу на каждый товар).

Шаг 5: Разбор результатов

  • Строка Most criticised aspects (самые критикуемые аспекты) показывает, чем товар разочаровывает: проблемы с долговечностью, размером и посадкой, доставкой или упаковкой требуют разных решений и разных команд.
  • В строке Rating does not match the text (оценка не соответствует тексту) перечислены, например, пятизвёздочные отзывы, в которых всё же есть жалобы (часто на доставку), и однозвёздочные, которые на самом деле положительные. И те и другие стоит просмотреть.
  • Сводка от ИИ группирует жалобы в основные проблемы и предлагает решение для каждой; передайте её отделу разработки продукта или поставщику.
  • Отфильтруйте reviews_tagged.csv по товару и негативной тональности, чтобы прочитать исходные отзывы, стоящие за каждой проблемой.

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

Шаг 6: Регулярный анализ

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

Частые вопросы

Насколько точен анализ?

Для понятных отзывов тональность и аспекты определяются надёжно. Сложнее с сарказмом, смешанными отзывами и очень короткими текстами вроде «ok». Прежде чем полагаться на цифры, сверьте результаты для 20 случайных отзывов и оставьте температуру 0, чтобы ответы были стабильными.

Можно ли так же анализировать обращения в поддержку или чаты?

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

Можно ли использовать облачную ИИ-модель?

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

Если проблема не решена, создайте тикет — техническая поддержка IMIDC работает 24/7. Укажите IP сервера, используемую модель, первые строки CSV-файла без персональных данных и текст ошибки, чтобы мы быстрее нашли причину.

Помог ли вам данный ответ?

Похожие руководства