打造一个产品评论分析AI工具

客户评论是公司可访问的最丰富的产品反馈来源之一。它们揭示了客户在购买后如何实际体验产品,他们喜欢什么,什么让他们沮丧,他们期望什么,以及产品在哪里不足。

这种反馈特别有价值,因为它是自发的。客户自然会谈及对他们最重要的功能。当大规模分析客户评论时,它们可以揭示哪些方面持续推动正面情感,哪些领域造成不满,以及哪些功能在不同客户群体中收到混合反应。

团队可以监控客户情感是否在产品更新后改善,新推出的功能是否被积极接受,或者竞争对手是否在某个领域超越他们。

挑战在于评论数据集通常很大、混乱、多语言,并分布在多个渠道。手动阅读数千条评论缓慢、难以扩展,而且往往不一致。重要的痛点可能会被遗漏,特别是当评论来自多个国家、产品、市场和竞争对手时。

对于大型企业,问题变得更加复杂。公司不仅对自家产品的反馈感兴趣,还希望了解客户如何看待同类竞争产品。比较各品牌的优势和劣势可以揭示差异化机会,突出竞争对手的优势,并为产品路线图决策提供信息。

在本文中,我们将开发一个产品评论智能仪表板,接收一家公司及其竞争对手的客户评论。该工具允许用户定义产品方面(例如构建质量、易用性等),并运行情感分析管道,以结构化格式为每个方面提取正面、负面、中性和混合情感,以及证据引用和置信度分数。

本项目使用的示例(合成)数据集包含3,000条消费音频耳机产品评论,涵盖三个品牌系列、10种不同型号,从入门级耳塞到高端工作室耳机。评论包含五个零售渠道,覆盖七个欧洲市场。评论以相应的当地语言撰写,评分从1到5星,评论日期跨越数年。

该工具可以轻松用于任何其他数据集,只需少量定制。

产品评论智能的用户界面

完整的注释代码和所有设置说明都在 GitHub仓库中。

让我们开始深入。

1. 情感分析

第一步是从团队已经获取产品评论的地方收集评论,例如开源网络爬虫(如Python的BeautifulSoup库)、市场导出、零售商门户、第三方评论服务、内部评论计划,或Amazon评论爬取API。我不会在本文中重点介绍评论爬取。

抓取的评论应整合到单个Excel表中,如下所示。你的评论表中不需要所有这些列。此工具中的情感分析管道需要三列必填:review_idreview_titlereview_text。上传的文件可以包含任意数量的附加列。它们将按原样保留,并在处理完成后合并回丰富后的输出。

该工具使用的示例数据集片段

在运行情感分类管道之前,决定应从评论中提取和分析哪些方面很重要。这些方面因产品类别而异。

对于消费电子,客户可能会谈论构建质量、电池续航、音质、舒适度、连接性或性价比。对于软件产品,重要方面可能是易用性、可靠性、客户支持、定价或性能。家用电器可能引入完全不同的维度,如能源效率、安装、耐用性或清洁性能。

在整体评论层面分析情感很有用,但通常不够。一位客户可能 overall 留下四星评论,同时对电池续航或舒适度表示沮丧。另一位客户可能不喜欢价格但仍然称赞音质。只看整体评分可能会隐藏这些类型的权衡。

这就是方面级情感分析重要的原因。它让公司确切了解哪些产品功能推动正面情感,哪些造成不满,哪些领域收到混合反应。

此管道中的一个关键设计决策是使用封闭方面分类法。而不是允许模型为每条评论发明任意方面名称,它被迫从预定义的一组已批准方面中选择,这确保相似反馈按相同方面分组。它还使下游仪表板更容易解释、比较和跨产品、国家和竞争对手基准。

为耳机示例数据集定义的方面

默认方面分类法在backend/sentiments_prompts.py中定义,也可以在用户界面的情感分析部分更改,如下所示:

用户界面中方面分类法的部分快照

情感管道由在backend/sentiment_prompts.py中定义为SYSTEM_PROMPT的系统提示驱动。build_system_prompt()函数在每次管道运行前在运行时将方面分类法注入其中:

SYSTEM_PROMPT = """你是一个产品评论情感分析师。

## 任务
分析产品评论并提取:
1. 整体情感(正面/负面/中性/混合)
2. 带证据的方面级情感(直接引用)

## 方面分类法(封闭集 - 仅使用这些)
{aspect_list}

## 规则(必须遵循)
1. 不要发明分类法之外的方面
2. 不要改写证据 - 直接从评论文本引用
3. 不要分配没有证据的方面
4. 优先选择较少、高置信度的方面,而不是许多弱方面
5. 仅对整体_sentiment使用mixed
6. 按分类法中列出的方式返回方面名称

## 语言处理
- 以原始语言分析评论
- 以原始语言返回证据引用
- 检测并报告语言代码(en, de, fr, es, it, nl等)
"""

USER_PROMPT_TEMPLATE = """分析以下{count}条产品评论。

{dataset_context}
按提供的相同顺序返回恰好{count}条评论结果。

评论:
{reviews}
"""

提示明确要求模型直接从评论文本中带来证据引用,而不是改写,这使结果可审计。此外,鼓励模型只提取少数高置信度方面,而不是分配每个可能的类别。

评论批量发送到LLM。我为此数据集使用了200的批量大小。

当情感分类管道完成时,它向评论表添加五列:语言、整体情感、整体置信度、方面的JSON结构,以及提取的方面数量。请参阅示例review_titlereview_text的示例输出。

review_title = "Absolutely love these!"
review_text = "These are hands down the best headphones I've owned. The Basix Wireless Buds B30 delivers on every front. ANC blocks out everything perfectly. Excellent craftsmanship. Will buy again."
language = "en"
overall_sentiment = "positive"
overall_confidence = 0.99
aspect_json = [{"aspect": "SOUND_QUALITY", "sentiment": "positive", "evidence": "These are hands down the best headphones I've owned.", "confidence": 0.93}, {"aspect": "NOISE_CANCELLATION", "sentiment": "positive", "evidence": "ANC blocks out everything perfectly.", "confidence": 0.99}, {"aspect": "BUILD_DURABILITY", "sentiment": "positive", "evidence": "Excellent craftsmanship.", "confidence": 0.95}]
aspect_count = 3

2. 仪表板工作原理

仪表板由四个主要页面组成,每个页面从情感分类管道生成的同一数据文件(sentiment_enriched.xlsx)读取数据。

2.1 分析仪表板

分析仪表板是主要监控页面。它围绕"按...分析"控制组织,将整个页面范围限定到一个维度(例如国家、供应商、产品等)。

对于选定的范围(或总体),后端将评论DataFrame过滤到该段,然后按整体情感(正面、负面、中性)聚合所有方面级证据,将原始客户引用片段分组在每个方面名称下。这种结构化证据被组装成一个提示并发送到LLM,后者返回三个类型化字段:客户 consistently 欣赏什么、主要抱怨,以及需要改进的关键领域。

通过将结构化证据发送到LLM为选定的产品生成的洞察摘要

结果被持久化到input/product_summaries.json,键为dimension → value(例如source → Shopzone)。在后续访问时,GET /summaries/filter首先检查缓存,如果摘要已存在则立即返回。LLM仅在首次加载或用户想要"刷新摘要"时调用。

分析仪表板还有两个趋势图表,显示评分和情感随时间的变化,每个都有多个视图模式(例如7天滚动平均值、每日线、堆叠情感组成)。方面频率图表显示客户提及最多的产品维度,按正面和负面情感细分。

每个图表都包含一个解释选项,将可见的图表数据发送到LLM并返回自然语言业务解释,识别趋势、指出模式和建议操作,而无需用户自己制定提示。

随时间变化的评分和情感趋势

图表数据由两个后端文件提供。analytics.py包含所有数据塑造逻辑。它将评论DataFrame聚合为时间序列数组(评分趋势、带有预计算的7天和30天滚动平均值的情感趋势)和方面频率计数,按正面和负面情感细分。main.py将其作为三个API端点公开:GET /analytics/data调用analytics.py并返回针对维度和值(例如country = "US")的完整图表负载;POST /interpret/chart接收图表的可见数据作为纯文本提示,将其转发到LLM,并流式返回叙述性解释。

可选择时段的图表缩放视图以分析趋势
将图表的可见数据发送到LLM并获得叙述性解释
方面频率分析图,附有用LLM解释的选项

2.2 方面基准

方面基准页面是为比较而非监控而构建的。不是孤立查看一个数据切片,它允许用户将多个段并排放置在雷达图上,跨维度比较方面级正面情感分数。

这很有用,例如当需要在分析仪表板中发现的模式需要更具体地检查时,例如了解一个产品是否在价格价值方面 consistently 低于竞争对手,或某个市场是否比其他市场对舒适度评分更负面。基准范围可以设置为总体、产品、国家或来源(供应商),多个选择可以叠加在同一图表上。

main.py中的GET /analytics/aspect-benchmark API端点返回所选范围的雷达图数据。main.py中的POST /interpret/chart端点接收雷达图的可见数据。它直接将其传递给LLM并返回叙述性解释,识别哪些方面最强和最弱,系列在哪里分化,以及该模式对产品或市场策略意味着什么。

两个供应商(来源)的竞争基准 

2.3 询问AI代理

询问代理页面是仪表板的自然语言分析层。它不是要求用户手动组合过滤器或从多个可视化中推断模式,而是允许直接针对当前加载的情感数据集提出问题,例如某个市场主导的抱怨是什么,评分是否随时间改善,哪些方面推动负面情感,或两个段如何比较。

代理仅在已经由情感管道生成的结构化评论数据集上操作,因此其答案基于可用证据,而不是开放式的模型回忆。

代理在data_agent.py中实现。在运行时,run_data_qa()构建一个OpenAI Agents SDK运行器,附加工具集,并传入当前用户问题以及最近的对话上下文。

代理将情感数据集加载到pandas中。不是要求模型直接从原始上下文中回答,而是代理需要通过显式、类型化的工具进行推理。这些工具在pandas中实现实际数据操作并返回结构化JSON式结果。当前工具集如下。

AI代理使用的工具

对于工具集未覆盖的查询或分析,代理生成代码片段,run_pandas_code工具在具有受限内置函数的沙盒环境中执行。

代理初始化和运行如下:

      agent = Agent(    
        name="Product Review Data QA Runner",    
        instructions=_build_runner_system_prompt(),    
        tools=[    
            get_schema,    
            get_aspect_summary,    
            aggregate_reviews,    
            find_reviews,    
            explain_negative_drivers,    
            get_time_trends,    
            compare_segments,    
            detect_anomalies,    
            explain_sentiment_drivers,    
            statistical_comparison,    
            analyze_correlations,    
            search_review_text,    
            get_top_keywords,    
            get_aspect_cooccurrence,    
            run_pandas_code,    
        ],    
    )    
         
    result = await Runner.run(    
        starting_agent=agent,    
        input=_build_input_messages(question, history),    
        max_turns=10,    
        run_config=run_config,    
    )

data_agent.py中的_build_runner_system_prompt()构建定义代理行为的系统提示。提示组织为八个命名部分:范围、工具选择、多步推理、何时使用run_pandas_coderun_pandas_code规则、方面名称处理、响应风格和数据完整性。

def _build_runner_system_prompt() -> str:
    return (
        "You are Review Copilot, an expert data analyst for the product review sentiment dataset.\n\n"

        "## Scope\n"
        "Answer ANY question that involves this dataset's columns: ratings, sentiments, aspects, "
        "products, countries, sources, review text, or review dates. "
        "This includes - but is not limited to - averages, medians, percentiles, counts, fractions, "
        "trends, comparisons, distributions, correlations, word frequencies, and custom statistics. "
        "If the question asks about the data in any way, it is in scope.\n"
        "Respond with 'I cannot answer this question, because it is not relevant to the product review sentiment data.' "
        "ONLY when the question has no connection to the dataset whatsoever (e.g. cooking recipes, weather forecasts, geography trivia).\n\n"

        "## Tool Selection - use the most specific tool available\n"
        "1. Counts, averages, rates grouped by a field -> aggregate_reviews\n"
        "2. Aspect frequencies and sentiment breakdown -> get_aspect_summary\n"
        "3. Trend over time (rating or sentiment) -> get_time_trends\n"
        "4. Side-by-side comparison of segments -> compare_segments\n"
        "5. Statistical difference between two groups -> statistical_comparison\n"
        "6. Which dimension predicts a metric -> analyze_correlations\n"
        "7. Statistical outliers in a dimension -> detect_anomalies\n"
        "8. What drives positive or negative sentiment -> explain_sentiment_drivers\n"
        "9. Negative aspect drivers for a specific product -> explain_negative_drivers\n"
        "10. Keyword or phrase search in review text -> search_review_text\n"
        "11. Most frequent words in review text -> get_top_keywords\n"
        "12. Which aspects appear together in reviews -> get_aspect_cooccurrence\n"
        "13. Fetch individual review examples -> find_reviews (only when user asks for examples or quotes)\n"
        "14. Dataset schema and field values -> get_schema\n"
        "15. Custom analysis not covered by any above -> run_pandas_code (last resort only)\n\n"

        "## Multi-step reasoning\n"
        "- Break complex questions into sub-questions and call tools in sequence.\n"
        "- Use the output of one tool to inform parameters of the next.\n"
        "- Before using any product name, aspect name, or field value in a tool call, verify it exists "
        "by calling get_schema first. Never assume a name is valid - always confirm.\n"
        "- If a tool returns an error or empty result, call get_schema to discover the correct identifiers "
        "and retry with exact values from the schema.\n"
        "- Never conclude that data does not exist without first verifying the exact names via get_schema.\n"
        "- For 'why' questions: first get the quantitative answer, then explain it using aspect evidence.\n\n"

        "## When to use run_pandas_code\n"
        "Use run_pandas_code whenever the question requires ANY of the following - "
        "these cases CANNOT be handled by dedicated tools:\n"
        "- Median, percentile, or any statistic other than mean/count/rate\n"
        "- Word count, character count, or text-length metrics per review\n"
        "- Cross-field boolean filters (e.g. rating=5 AND sentiment=negative)\n"
        "- Custom ratios or derived metrics (e.g. 5-star to 1-star ratio per product)\n"
        "- Aspect-level sentiment filtering: when the question is about reviews where a specific "
        "aspect has negative/positive sentiment (not the overall review sentiment). "
        "Access this via aspects_json, which contains a list of dicts with keys: "
        "'aspect' (name string) and 'sentiment' ('positive'/'negative'/'neutral').\n"
        "- Any computation combining multiple columns in a way no single tool supports\n"
        "Use dedicated tools (1-14) when they fully cover the question. "
        "NEVER refuse a valid dataset question - use run_pandas_code if no dedicated tool fits.\n\n"

        "## run_pandas_code rules\n"
        "- Always assign the final answer to a variable named `result`.\n"
        "- Available in sandbox: df (DataFrame), pd, np, re, Counter.\n"
        "- Key columns: review_text, overall_sentiment, rating, product_name, country, source, "
        "review_date, aspects_json.\n"
        "- Before arithmetic on any column, convert with pd.to_numeric(..., errors='coerce').\n"
        "- For string comparisons, apply .str.lower() on both sides.\n"
        "- Never hardcode product names, aspect names, or field values - always derive them from "
        "the data (e.g. use df['product_name'].unique() if you need the list of products).\n\n"

        "## Aspect name handling\n"
        "- Aspects in tools use UPPERCASE_WITH_UNDERSCORES (e.g. SOUND_QUALITY, NOISE_CANCELLATION).\n"
        "- In aspects_json (for run_pandas_code), aspect names may be lowercase - compare with .lower().\n"
        "- Always call get_schema to find the exact aspect names present in this dataset before filtering.\n\n"

        "## Response style\n"
        "- Lead with the direct answer in plain language.\n"
        "- Weave numbers into prose; do not dump raw tables in text.\n"
        "- Add 1-3 sentences of interpretation when it adds value.\n"
        "- For small samples (< 20 reviews), state the sample is small.\n"
        "- If a comparison exists, name the winner and the gap plainly.\n"
        "- If a trend exists, state whether it is improving, worsening, or stable.\n"
        "- Never mention tools, JSON, schemas, or internal processing to the user.\n"
        "- If data is insufficient, say so clearly instead of guessing.\n\n"

        "## Data integrity\n"
        "- Use ONLY tool outputs; never invent or estimate values.\n"
        "- Cite specific numbers from tool results.\n"
        "- Use conversation history to resolve references like 'that product', 'it', or 'the same source'.\n"
    )

下图给出了代理架构的高级视图。

使用12个工具回答评论/情感数据问题的AI代理架构

以下快照显示了"询问代理"页面的一个问答。

询问代理页面的快照

2.4 评论浏览器

评论浏览器页面是应用程序的行级检查层。虽然仪表板和基准视图总结了许多评论的模式,但评论浏览器允许用户直接检查底层记录。用户可以通过业务维度、情感和提取的方面过滤丰富的评论表,然后打开单个行来阅读原始评论文本以及结构化情感输出。

评论/情感数据的行视图。选定的过滤器:产品代码、整体情感和方面。选中行的评论详情(部分视图)显示在"评论详情"中

3. 潜在扩展

该工具可以通过多种方式扩展。例如:

  • 自动化评论摄取: 计划连接器直接从Amazon、Google或Trustpilot API拉取评论,删除手动上传步骤
  • 多类别方面分类法: 允许每个产品类别有不同的方面集,因此同一工具可以在单一部署中处理音频产品、家用电器和软件
  • 方面级趋势监控: 跟踪特定方面的情感分数如何随时间变化,当超过阈值时发出警报
  • 导出和报告: 从当前仪表板状态生成计划的PDF或幻灯片报告,推送到电子邮件或Slack
  • 用户身份验证和多租户数据集: 允许不同团队上传和探索自己的评论数据集,而无需共享单个全局数据集

原文链接: I Built a Product Review Intelligence Tool Powered by an AI Agent

汇智网翻译整理,版权归作者所有,转载需标明出处