从写代码,到创作下一幕

探索 字节跳动 - 火山方舟 的 AI 编程与视频创作活动。

火山方舟 Agent Plan 与 Coding Plan 活动,价格 ¥9.9 起

Agent Plan & Coding Plan

一站体验多款热门模型,为 AI 编程与智能体开发提供更多选择。新用户可联系(微信: goo_lvyouyou)免费体验 9.9 agent plan。

查看套餐优惠
Seedance 2.5 视频创作活动:30 秒视频、多模态参考、局部编辑

Seedance 2.5

让创意,跃然成片。探索 30 秒视频、多模态参考与局部编辑,把脑海中的画面变成下一支作品。

探索视频创作

从用户评论中提取主题

查看 Markdown

此应用使用 Grok API 分析客户反馈,从大量评论或支持工单中提取主题。它演示如何利用 Batch API 进行分块处理和汇总(map-reduce),以处理单个请求容纳不下的文本。

提供一份含有应用评论或支持工单的 CSV,应用便能找出人们抱怨什么、希望获得什么、喜欢什么。每个主题都列出提及它的评论数、数量的增减趋势,以及可跳转至对应数据行的引文。Grok 先从样本中提取候选主题,再通过 Batch API 按这些主题给每条评论打标签,最后合并主题并命名。可通过小型网页应用或终端运行。

你将学到

  • 对超过单个请求容量的文本执行 map-reduce:先从样本中找出候选主题,再通过多个小请求给每条评论打标签,最后统计标签并合并主题

  • 将请求写入 JSONL 文件,使用 client.files.upload() 上传,再基于该文件创建批次,调用 client.batches.create({ input_file_id })

  • 轮询批次,一有结果便读取,无需等到整个批次完成

  • 程序崩溃或页面关闭后,仅凭保存的 ID 即可继续处理原批次,也可调用以下方法取消批次:client.batches.cancel()

  • 如何在批处理与实时请求之间取舍,以及批处理为何在不占用速率配额方面更具优势

  • 编写 JSON Schema:为每条评论设置一个属性,用 enum 限定主题 ID,从而确保每条评论都有标签且每个标签都对应真实主题;使用 $defs 复用定义,减少输入 token 数量

  • 通过服务器发送事件从 Node 服务端向网页实时传送进度

运行示例

需要 Node.js 22.13 或更高版本。将 API 密钥放在 .env(仓库根目录),或导出环境变量 XAI_API_KEY。

Bash

cd examples/reviews-to-themes/typescript
npm install
npm run web

打开 http://localhost:3000,点击 Run on the sample,也可以先选择自己的 CSV。页面是一个反馈分析仪表盘。顶部显示三个步骤,下面的进度条随评论标注推进。左侧实时显示 Grok 找到的候选主题及其不断更新的评论数。右侧显示 Grok 的推理过程,随后按标注进度逐条展示评论,并高亮匹配的词。完成后按提及次数为主题排序,各主题附趋势、平均评分和引文。点击主题可按周查看评论及其对应的原始数据行,点击引文可跳至原行。点击 Stop 可取消本次运行及对应批次。

sample-reviews.csv 包含 993 条虚构的预算管理应用评论,时间跨度为 12 周。样本刻意包含一些主题:银行同步故障日益频繁、价格上涨、广受欢迎的新规划功能及其引发的崩溃,以及已经修复的登录问题。src/make-sample.ts 设定每周每个主题对应的评论数,再由 Grok 撰写评论,方便核对应用的分析结果。要生成新样本,请运行 node --env-file-if-exists=../../../.env --experimental-strip-types src/make-sample.ts。

自备 CSV 至少需要一个文本列,列名可为 text、review、body、comment、content、message 或 feedback。添加 date 列可分析趋势,添加 rating、stars 或 score 列可计算平均评分。Node 没有内置 CSV 解析器,因此 src/themes.ts 内含一个小型解析器。

在终端查找主题时,可传入 CSV 文件;未传入时使用示例数据。

Bash

npm start -- reviews.csv

主题会保存到 output/<name>/ 目录,文件名为 themes.md 和 themes.json。每条评论及其主题会写入 reviews.csv。可添加 --model 选择标注评论的模型,添加 --live 直接发送实时请求,或添加 --new 从头运行。

批次在 SpaceXAI 端运行,因此本地进程停止后仍会继续执行。批次一创建,其 ID 就会写入 run.json(输出目录内)。终端版在等待时中断,可以再次执行相同命令,接着处理原批次。使用网页版时,关闭页面会让服务器停止等待;重新打开页面即可选择恢复处理。

标注占了大部分工作量。示例需要 40 个请求,每个处理 25 条评论;一万条评论需要 400 个请求。应用默认使用 grok-4.3 批量提交,也可以选择 grok-4.20-0309-non-reasoning 或 grok-4.20-0309-reasoning。批处理请求可享折扣,适用于这三个模型;更大的优势在于这些请求不计入速率限制。实时请求则消耗团队的每分钟 token 配额,大量并发时可能收到 429 错误,必须等待。

grok-4.7 能力最强,但本示例所用的批处理不支持它。包含该模型的批次会被取消,并提示“Model grok-4.7 is not supported for batch processing”。选择此模型后,应用会实时发送请求,每次并发八个。其余模型也支持实时模式。查找候选主题和给最终主题命名各需一个请求,因此这两步始终实时执行,使用 grok-4.7。

批次最长可能需要 24 小时,不过本示例测试过的每个批次都在 40 秒内完成。示例数据运行一次约需一至三分钟,按公布价格计算约 0.23 美元,几乎全部用于标注评论。页面和终端都会显示根据 API 返回数据计算的本次费用。

工作原理

共享代码位于 src/themes.ts。analyze() 对评论进行 map-reduce 处理,解决单个请求容量不足的问题,并在运行时报告每一步:

  1. findCandidates() 向 grok-4.7 提供均匀分布在整个时间段内的 150 条评论,以结构化输出流式返回候选主题。每项都有 ID、名称和一句话的判定标准。流中的 json 事件在下一主题开始时报告上一个主题,reasoning 事件则展示 Grok 的推理过程。生成主题列表只需较低的推理强度。

  2. labelRequest() 每 25 条评论构建一个请求,其 JSON Schema 为每条评论设置必填属性,确保没有遗漏。每条评论会从 enum 所列的候选 ID 中选出提及的主题,并附上体现各主题的原词。Schema 同样计入输入 token,因此各属性通过 $defs 和 $ref 复用定义,将每个请求的输入从约 3,900 个 token 减至 1,600 个。

  3. submitBatch() 将请求写入 JSONL 文件,每行一个,包含 custom_id 及 /v1/responses 请求体。随后使用 client.files.upload() 上传文件,再使用 client.batches.create({ input_file_id }) 基于该文件启动批次。文件会原样提交给 API,不会补上 SDK 的默认值,因此每个请求体均显式设置 store: false。

  4. waitForBatch() 每三秒轮询一次 client.batches.get()。每当有新请求完成,就通过 client.batches.results() 读取新增结果。每项结果都包含 JSON 格式的标签,并通过 cost_in_usd_ticks 给出费用,每 100 亿个计费单位相当于一美元。SDK 的 client.batches.wait() 会一直等待到没有待处理请求,但从文件创建的批次在最初几秒仍在读取文件,这时请求数为零。因此,waitForBatch() 还会等待批次请求数达到文件中的请求总数。labelLive() 则自行发送同样的请求,每次并发八个。

  5. nameThemes() 负责汇总(reduce)。全部评论标注完成后,向 grok-4.7 发送各候选主题、评论数和若干引文,Grok 将实际描述同一主题的候选项合并,为最终主题命名。summarize() 按周统计每个主题的评论数,比较最近四周与之前四周,并从最新的评论中选取能逐字对应原文的引文。

resume() 会加载 run.json,并为已保存的批次执行最后两步;cancel() 通过 client.batches.cancel() 终止批次。所有 API 调用均通过 SDK 发出。

src/server.ts 为网页运行 analyze() 或 resume(),并以服务器发送事件报告每一步。EventSource 只能发送 GET 请求,因此页面先将 CSV 上传至 /api/uploads,然后打开 /api/run 并传入返回的 ID。服务器会将 AbortSignal 传给每次 API 调用,页面关闭时中止调用,但保留批次继续运行,便于稍后恢复。Stop 还会向 /api/stop 发送 POST 请求,以取消批次。服务器只提供本次运行保存的文件。public/index.html 使用原生 HTML 和 JavaScript,以仪表盘的形式展示事件,并将运行 ID 保存在 localStorage 中,以便提示用户恢复批次。src/index.ts 在终端中执行相同的工作。