跳到正文
Kale的数字花园
目录 文章
/11 分钟/8 阅读/0 评论

Amazon 退货评论智能分类报表 — 面试备战手册


零、简历精简版(可直接粘贴到简历)

Amazon 退货评论智能分类报表平台

项目角色:方案设计 + 项目主导 + 核心开发 + 架构设计

技术栈:Java/Go、MySQL、MongoDB、ClickHouse、Kafka、GPT Batch API、CDC

项目描述:搭建 Amazon 退货评论的 AI 智能分类打标与多源数据报表分析平台,通过 GPT 对非结构化退货评论进行自动归类与中文翻译,结合订单/结算数据生成退货分析报表,替代原有 MySQL 千万级多表 Join 的低效查询方案。

核心工作与产出:

  • 设计并落地全链路数据流:Amazon 报告拉取 → MongoDB 中转 → MySQL 清洗 → GPT 打标 → ClickHouse 报表,覆盖数据从采集到可视化的完整生命周期
  • 采用 GPT Batch API + 请求体缓存(prompt+评论+企业枚举值 hash)替代单条串行请求,大幅降低 LLM API 调用成本;设计 24 小时兜底重试机制保障数据不丢失
  • 将报表存储从 MySQL 迁移至 ClickHouse ReplacingMergeTree,按企业维度水平分表 + 按天下单时间分区,多字段聚合查询延迟从 1 分钟+ 降至 1 秒以内
  • 设计 CDC 监听 MySQL binlog → Kafka → ClickHouse 的增量写入链路,保证评论打标完成后实时推送至报表层

一、项目口述(面试自我介绍时用)

这个项目主要是为了解决 Amazon 退货评论无法自动归类的问题。用户退货时填写的评论是自由文本,带有主观意愿,比如"买小了"、"质量不好"、"袖口太松"这类,硬编码根本没办法分类。我们引入 GPT 对评论进行智能分类,同时翻译成中文,最终结合订单和结算数据生成退货分析报表。

我在项目中的角色是全栈式的——方案设计、项目主导、核心开发、架构设计都有参与。整体链路是:定时任务每 8 小时拉取 Amazon 退货报告,源数据先落 MongoDB,再定时清洗到 MySQL,评论字段单独存一张评论表。然后定时刷评论表,按企业维度每次拿 20 条评论,加上企业维护的分类枚举和 prompt,以 function calling 的方式请求 GPT。为了降低调用成本,我设计了请求体缓存加 Batch API 的方案——重复评论走缓存,不同评论攒够 10 条写入文件流通过 Batch API 批量请求。打标完成后落库 MySQL,通过 CDC 监听 binlog 写 Kafka,最终增量写入 ClickHouse 的 ReplacingMergeTree 表。

这个项目中我个人觉得最有价值的两个决策:一是把 GPT 请求从单条串行改为批处理 + 缓存,省了真金白银;二是把报表存储从 MySQL 换到 ClickHouse,查询性能从 1 分钟以上降到了 1 秒以内,解决了千万级数据多表 Join 的瓶颈。当然,也有一些当时没考虑到的点,比如 OPTIMIZE TABLE 应该按分区粒度合并而不是全表合并,这些是我后续会优化的方向。

flowchart LR
    A["Amazon 退货报告每 8 小时拉取"] --> B["MongoDB保存源数据"]
    B --> C["清洗任务标准化字段"]
    C --> D["MySQL订单 / 评论 / 结算数据"]
    D --> E["评论打标任务按企业取 20 条"]
    E --> F{"请求体缓存prompt + 评论 + 枚举 hash"}
    F -->|"命中"| G["复用历史分类结果"]
    F -->|"未命中"| H["GPT Batch API每 10 条提交"]
    H --> I["打标结果回写 MySQL"]
    G --> I
    I --> J["CDC 监听 binlog"]
    J --> K["Kafka"]
    K --> L["ClickHouseReplacingMergeTree"]
    L --> M["退货分析报表1 秒内查询"]

二、面试官可能会问的问题 & 参考回答

模块一:ClickHouse ReplacingMergeTree 设计与查询优化

Q1:为什么选 ClickHouse?为什么用 ReplacingMergeTree?

之前的方案是 MySQL 做千万级数据的多表 Join 查询,延迟超过 1 分钟,用户体验非常差。我们调研后发现报表场景主要是多字段聚合查询,读多写少,这正是 ClickHouse 列存 + 向量化执行引擎的强项。选 ReplacingMergeTree 是因为我们的评论数据存在重复写入的情况(比如重新打标),ReplacingMergeTree 可以在后台异步合并时自动去重,不需要我们在写入层做复杂的幂等控制。

Q2:ReplacingMergeTree 的去重原理是什么?

去重是在后台异步的 merge 过程中触发的,基于 ORDER BY 的排序键来判断重复,不是 PARTITION BY 的分区键。我们的排序键是 (store, amazon_order_id, platform_sku),这三者组合在业务上可以唯一标识一条评论。ver 字段我用的是打标时间戳,merge 时会保留 ver 值最大的那行。所以正常流程下,重新打标后的数据因为时间戳更大,会被保留。手动 OPTIMIZE TABLE FINAL 可以强制合并,但生产上不建议频繁使用,因为全表合并非常吃 IO。

flowchart TB
    A["重新打标产生新结果"] --> B["向 ClickHouse 再插入一行"]
    B --> C["ORDER BY 键相同store + amazon_order_id + platform_sku"]
    C --> D["ver 使用打标时间戳"]
    D --> E{"后台 merge 是否完成"}
    E -->|"未完成"| F["短时间可能同时存在新旧两行"]
    E -->|"已完成"| G["保留 ver 最大的新结果"]
    F --> H["查询层可用 GROUP BY / argMax 兜底"]
    F --> I["必要时 OPTIMIZE PARTITION"]
    G --> J["报表看到最新分类结果"]

Q3:在 merge 还没完成、数据还没真正去重的"延迟窗口期",报表查询怎么处理?

说实话,我当时的方案是依赖后台自动 merge,没有做额外的查询层防护。但这是一个可以优化的点——在 SQL 层面可以加 SELECT ... GROUP BY ORDER BY id DESC LIMIT 1 的方式来保证查询结果的唯一性,或者把 OPTIMIZE 的粒度从全表缩小到按分区合并(OPTIMIZE PARTITION),因为重新打标涉及的评论的下单时间是已知的,只对那个分区做合并就可以了。

Q4:表的分表和分区策略是怎样的?

按企业维度做了水平分表,每张表的表名 + 企业 ID 做了隔离。分区键是按订单的下单时间,按天分区。这样的设计是为了查询时可以快速定位到某个企业某个时间范围的数据,避免全表扫描。

模块二:GPT Batch API 与缓存设计

Q5:批处理方案具体是怎么做的?为什么是 10 条一个 batch?

我设计了两层降本机制:第一层是请求体缓存,把 hash(prompt + 评论原文 + 企业分类枚举) 作为缓存 key,相同组合直接复用历史结果。第二层是 Batch API,每攒够 10 条评论写入文件流,提交给 GPT 的 Batch API 异步处理。选择 10 条是一个经验值,需要在等待时间和单次请求成本之间做权衡——批太小单次请求的 overhead 占比高,批太大等待时间太长。具体来说(面试时可补充具体测试数据)我测试过不同 batch 大小对等待时间和费用的影响,10 条在成本和时效之间达到了比较优的平衡。

Q6:Batch API 是异步的,结果怎么回写?失败了怎么办?

提交 batch 请求后,会把请求记录存放在 MySQL 的一张请求记录表中,记录里包含评论 ID 和 batch 请求 ID。后台有定时任务定时扫描这个表,轮询 GPT 获取 batch 请求结果。处理成功后根据评论 ID 写入打标结果表,然后触发 CDC 走后续流程。如果 batch API 回写失败或者丢失了请求,24 小时内会有兜底任务将此条 batch 请求记录标记为失败,并将这些评论的是否打标字段重置为否,打标任务会重新触发。

sequenceDiagram
    participant Job as 打标任务
    participant DB as MySQL请求记录表
    participant Cache as 请求体缓存
    participant GPT as GPT Batch API
    participant Poll as 结果轮询任务

    Job->>DB: 扫描未打标评论
    Job->>Cache: 按 hash 查询缓存
    alt 缓存命中
        Cache-->>Job: 返回历史分类结果
        Job->>DB: 直接写入打标结果
    else 缓存未命中
        Job->>DB: 记录 commentId 和 batchRequestId
        Job->>GPT: 每 10 条提交 batch 文件
        Poll->>GPT: 定时轮询 batch 结果
        GPT-->>Poll: 返回成功 / 部分失败 / 超时
        Poll->>DB: 成功项写结果,失败项等待兜底重试
    end

Q7:缓存命中是怎么判断的?不同企业的相同评论能共享缓存吗?

缓存 key 是 hash(prompt + 评论 + 企业分类枚举),在这个维度下 hash 结果相同,缓存可以共享。因为 prompt 和分类枚举是企业维度的配置,所以即使两个不同企业的用户写了完全相同的评论,只要他们的 prompt 或分类配置不同,就不会命中对方的缓存。当企业配置发生变更时,该企业的缓存会被全部清空。

flowchart LR
    A["评论原文"] --> D["计算缓存 key"]
    B["企业 prompt"] --> D
    C["企业分类枚举"] --> D
    D["hash(prompt + 评论 + 枚举)"] --> E{"缓存是否命中"}
    E -->|"命中"| F["复用历史分类和翻译结果"]
    E -->|"未命中"| G["进入 GPT Batch API"]
    G --> H{"打标是否成功"}
    H -->|"成功"| I["写入缓存"]
    H -->|"失败"| J["不写缓存等待重试或人工处理"]
    K["企业配置变更"] --> L["清空该企业相关缓存"]

Q8:如果 batch 里 10 条有 2 条解析失败怎么处理?

8 条成功的正常写缓存和打标结果,2 条失败的不写缓存。失败的那 2 条会被兜底任务重新触发。因为缓存是评论维度的,那 2 条失败的评论没有缓存,所以不会命中脏数据。

模块三:数据一致性与幂等性

Q9:重新打标场景下,数据一致性怎么保证?

重新打标时,评论记录不会被更新,而是往 ClickHouse 中重新插入一条新的打标数据。因为新数据的 ver(打标时间戳)一定大于旧的时间戳,所以 merge 后会保留新的打标结果。重新打标任务完成后,代码会执行 OPTIMIZE TABLE FINAL 进行手动合并来强制去重。

Q10:缓存写入时机是什么?会不会出现"缓存里存的是错误结果"的情况?

缓存只在打标成功后才写入。打标失败不会写缓存。所以不会出现缓存里存脏数据的情况。

模块四:LLM 工程化

Q11:GPT 输出的准确率怎么保证?99.8% 是怎么来的?

准确率是通过产品经理调试 prompt 来提高的,目前本地测试可以达到 99.8% 的成功率。但我也意识到本地测试集和真实生产流量可能存在差距——本地测试集比较干净,而真实评论可能有各种噪声。这块我计划上线后补充线上质量监控,比如枚举值校验、随机抽检等机制。

Q12:如果上线后 GPT 的分类准确率突然下降,怎么排查?

这块我当时确实没有深入设计过监控方案。如果让我现在来设计,我会做几件事:第一,在线上每次 GPT 返回后做一次枚举值校验,确认分类值在企业枚举范围内;第二,每天随机抽检 1% 的打标结果推送到人工复核队列;第三,对 prompt 做版本化管理,每次变更版本号 +1,并把 prompt_version 和 gpt_model_version 记录到打标结果里,这样能定位是 prompt 变更还是模型行为变化导致的准确率下降;第四,设置告警规则,比如枚举值命中率低于 98% 持续 1 小时就告警。

flowchart TB
    A["GPT 返回分类结果"] --> B["枚举值校验"]
    B --> C{"分类值是否合法"}
    C -->|"否"| D["标记异常进入重试或人工复核"]
    C -->|"是"| E["写入打标结果"]
    E --> F["记录 prompt_versiongpt_model_version"]
    E --> G["每日随机抽检 1%"]
    G --> H["人工复核队列"]
    H --> I["沉淀准确率样本"]
    I --> J{"命中率是否低于阈值"}
    J -->|"是"| K["告警并定位prompt / 模型 / 数据分布"]
    J -->|"否"| L["持续监控"]

模块五:架构设计与全链路

Q13:整个数据链路是什么样的?

整体链路:定时任务每 8 小时拉取 Amazon 退货报告 → 源数据落 MongoDB → 定时清洗到 MySQL(评论字段单独存评论表)→ 定时刷评论表,按企业维度拿 20 条评论 + 企业 prompt + 分类枚举,通过 GPT 智能分类打标 → 打标完成后落库 MySQL → CDC 监听 MySQL binlog 写 Kafka → 增量写入 ClickHouse ReplacingMergeTree 表。

Q14:你的 CDC 是怎么实现的?

CDC 有单独的项目负责,这块我在后续的项目面试中会详细聊。简单来说就是监听评论表的 binlog,打标完成后发送 Kafka 消息到 ClickHouse 的报表服务。

sequenceDiagram
    participant MySQL as MySQL打标结果表
    participant CDC as CDC监听服务
    participant Kafka as Kafka Topic
    participant Report as 报表写入服务
    participant CH as ClickHouse
    participant UI as 报表查询

    MySQL->>CDC: binlog 产生 INSERT / UPDATE
    CDC->>CDC: 解析评论打标变更事件
    CDC->>Kafka: 投递增量消息
    Kafka->>Report: 消费打标结果
    Report->>CH: 写入 ReplacingMergeTree
    UI->>CH: 按企业和日期聚合查询
    CH-->>UI: 返回退货分类报表

三、架构扩展与优化方向(加分项)

Q:你觉得这个系统最大的瓶颈在哪里?

我认为有几个可以优化的方向:

  1. OPTIMIZE 粒度优化:当前重新打标后做 OPTIMIZE TABLE FINAL 是全表合并,数据量大时耗时很长。应该改为 OPTIMIZE PARTITION 只合并涉及的那个分区。
  2. LLM 输出质量监控:当前缺少线上的 GPT 输出质量监控体系,建议补充枚举值校验、随机抽检、prompt 版本化管理。
  3. 批量部分失败的重试幂等性:如果 GPT 对同一条评论的解析一直失败(稳定失败),重试多少次都没用,应该加失败次数上限,超过后转人工审核通道。
  4. 查询层唯一性保障:在 merge 延迟窗口期,SQL 层面可以加 GROUP BY + ORDER BY + LIMIT 1 保证查询结果的唯一性,而不是完全依赖后台 merge。
  5. 量化指标完善:batch 大小选择的具体测试数据、GPT 费用对比(优化前后省了多少钱)、日增/总数据量等指标需要进一步补充。

四、技术要点速查表

技术点 关键词 面试话术要点
ReplacingMergeTree 异步去重、ver 机制、merge 触发 去重基于 ORDER BY 排序键,不是分区键;ver 用打标时间戳保证新数据覆盖旧数据
查询性能优化 MySQL → ClickHouse 千万级多表 Join 从 1 分钟+ 降到 1 秒以内
Batch API 降本 请求体缓存 + 文件流批量提交 hash(prompt+评论+企业枚举)做缓存;每 10 条攒批;24 小时兜底重试
缓存设计 缓存命中、缓存失效 仅打标成功后写缓存;企业配置变更清空该企业全部缓存
CDC 增量写入 binlog 监听 → Kafka → ClickHouse 打标完成后触发 CDC,增量写入报表层
水平分表 企业维度隔离 表名 + 企业 ID 分表,按下单时间按天分区
LLM 工程化 function calling、prompt 调试 99.8% 本地测试成功率;需补充线上监控(枚举校验 + 抽检 + prompt 版本管理)
数据一致性 重新打标、幂等性 重新打标插入新记录,ver 更大覆盖旧数据;OPTIMIZE 强制合并

五、面试自评总结

维度 评分(/10) 说明
架构设计能力 9 全链路设计完整,技术选型务实
中间件深度 8 ReplacingMergeTree 原理扎实,边界场景可加强
LLM 工程能力 7 Batch API + 缓存落地不错,缺线上监控设计
数据一致性意识 8 缓存写入时机 + 兜底重试合理
成本意识 9 批处理 + 缓存组合拳降本效果显著
量化意识 6 有"1 分钟→1 秒"数据,其他指标待补充

综合定级:高级开发工程师 / 初级架构师

核心卖点:

  • 全链路 Owner(方案设计 → 核心开发 → 架构落地)
  • 查询性能从 1 分钟降到 1 秒以内(量化成果突出)
  • GPT 调用成本大幅降低(需补充具体金额数据)
  • 全链路异常兜底机制完善
相关阅读

27 分钟

质检模块业务全景与现状梳理

数据来源:7份技术方案文档 + 代码库实际代码扫描 质检模块从0到1经历了以下迭代,每一版都是前一版的改造和扩展: 材料问题、工艺问题、破损问题、二次工艺、印绣花工艺、规格问题、版型问题、外观问题、包装问题、功能问题、性能问题 Contro

面试#简历项目10 阅读 · 0 评论

评论需要登录。生产环境使用 GitHub 登录,本地可前往 登录页 使用开发登录。

还没有评论,来抢沙发。