从写代码,到创作下一幕

探索 字节跳动 - 火山方舟 的 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 构建 AI 会议助手,在通话过程中记录笔记。它可作为会议记录工具的基础,包含流式语音转文本、结构化笔记输出,以及可追溯至转写原文的问答。

在通话期间保持应用打开。它会为每位发言人实时生成字幕,持续整理决议、行动项、负责人和截止日期,并能回答有关会议内容的问题,附上指向发言时刻的链接。可以在小型网页应用中使用麦克风或播放示例会议,也可以在终端处理音频文件。

你将学到什么

  • 在浏览器中用 AudioWorklet 以 16 kHz PCM 采集麦克风音频,经服务器中转,使 API 密钥留在服务端

  • 通过 WebSocket 将音频传给语音转文本服务,利用中间结果、最终结果、diarize 生成的说话人标签和 Smart Turn,避免把发言中的短暂停顿当作轮次结束

  • 将笔记保存为结构化输出,让 Grok 在每个发言轮次结束后依据新增行更新笔记,并流式接收发生变化的条目,使用 json 事件

  • 让回答提供可跳转到原始转写行的引用

  • 在音频、字幕和笔记同时传输时保持页面响应流畅

运行示例

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

Bash

cd examples/meeting-to-action-items/typescript
npm install
npm run web

打开 http://localhost:3000 并点击 Play a sample meeting,或点击 Start listening 使用麦克风。页面左侧实时滚动显示字幕并标记每位发言人,右侧陆续填入决议、行动项、负责人和截止日期。一旦 Grok 根据对话判断出发言人的姓名,就会补全姓名。提问“我们就定价做了什么决定?”等问题时,答案会链接到相关原文行。点击时间即可跳转。选择 Stop 会结束会议,将笔记与转写保存至 output/,格式为 Markdown。页面还会显示本次费用和下载笔记按钮。

示例是 Maya 和 Sam 的一分钟规划会议,分别使用两种内置声音。首次播放时,应用会通过文本转语音进行录制,并保存至 output/sample-meeting.wav。网页使用和麦克风相同的采集流程播放样例,因此你可以边听边看转写。要更改会议,请编辑 sample-meeting.json 并删除 WAV 文件。

要从终端转写录音,可传入音频文件:

Bash

npm start -- ./your-meeting.wav

不传参数则播放示例会议。它以正常播放速度流式发送音频,模拟麦克风,实时打印每行转写和每处笔记变更,最后将笔记保存到 output/<name>-notes.md。支持直接读取 16 位单声道 PCM 的 WAV 文件;要读取其他格式,需要安装 ffmpeg。

运行示例约需 75 秒,其中音频时长为 68 秒,最后一次修订笔记需要额外几秒。实测运行约花费 0.04 美元:转写不到半美分,笔记约 3 美分,每次提问不到 1 美分。首次录制示例还需约 1.4 美分。流式转写每小时 0.20 美元,大部分费用来自 Grok 修订笔记使用的 token,每次修订约三分之一美分。

工作原理

  1. transcribe() 位于 src/transcribe.ts 中,将音频流式发送到 wss://api.x.ai/v1/stt。SDK 仅封装了批量转写,因此这里使用原生 WebSocket。Node 内置的 WebSocket 支持 Authorization 请求头,浏览器则不支持,所以密钥保留在服务器上。请求启用中间结果、说话人标签和 Smart Turn,并将结果整理为每个发言轮次一行:

    • 中间结果约每秒返回一次,包含截至当前听到的词,但不标明发言人,因此页面会在独立一行暂时显示,直到收到最终结果。

    • 最终结果会为每个词标注发言人。当说话人切换,或 Smart Turn 判定本轮发言结束时,当前行结束。轮次末尾的结果会重复该轮的所有词,已经添加的词会被跳过。

    • Smart Turn 为每次停顿给出 0 至 1 的分数,衡量发言是否已结束。实测句中停顿的分数也可能高达 0.82,因此仅在分数超过 0.9 或静音超过 3 秒时结束轮次。即使阈值为 0.5,也不能在每次发言人切换时准确划分轮次,因此说话人区分也用于判断何时换行。

  2. startMeeting() 位于 src/meeting.ts 中,每当一行结束便让 Grok 修订笔记。同一时间只运行一次修订,其间结束的行会纳入下一次修订,因此笔记最多落后一次修订。如果因限流等原因失败,所涉及各行会留给下一次处理。

  3. reviseNotes() 位于 src/notes.ts 中,该函数会向 grok-4.7 发送 JSON 格式的已有笔记和新增行,同时附上新增行之前的三行,以便识别对话中被叫到名字的人。它接收完整笔记的结构化输出,包括发言人姓名、决议、含负责人和截止日期的行动项,以及摘要。Grok 保留每项的 ID,因此将截止日期从周五改为周四时会更新原条目,页面会将旧日期加上删除线。笔记流式返回时,json 事件会提供截至当前的笔记;每当 Grok 写出新增或变更的条目,就会立即报告。示例使用较低推理强度:一次测试中,相同修订在低强度下耗时 2.5 秒,在默认强度下耗时 6.7 秒,结果相同。由于仅发送新增行,修订成本随笔记长度增长,不随转写全文增长。

  4. answer() 向 Grok 发送带行号的转写和问题,并要求以类似以下形式引用原文行:[12]。新转写始终追加在末尾,问题排在转写之后,因此只要为会议设置 prompt_cache_key,每次提问都能复用提示词开头的缓存。

每次 Grok 调用的费用取自 API 返回的 usage.cost_usd。转写流不报告费用,因此根据音频时长计算。

src/server.ts 是一个小型 node:http 服务器。网页打开 /api/meeting 以接收服务器发送事件并获取会议 ID,再通过 WebSocket 将音频发送到 /api/audio,同时传入该 ID。Node 未内置 WebSocket 服务器,因此这一段使用 ws 包。这是 SDK 以外唯一的依赖。服务器将每个音频块转发给转写流,并以服务器发送事件返回字幕、笔记修订和变更条目。关闭音频连接会结束会议:服务器通知 API 音频已结束,等待最后的转写,完成并保存笔记。问题会发送到 /api/answer,由该端点流式返回答案。每个 API 调用都获得一个 AbortSignal,网页关闭时会触发中止。

public/index.html 使用原生 HTML、CSS 和 JavaScript。AudioWorklet 在音频线程中采集麦克风或正在播放的示例音频,通过求平均值降采样至 16 kHz,每次向网页提交 100 毫秒的 16 位 PCM 音频供发送。每个事件仅更新对应的内容,如一行转写或一条笔记,所以即使上传音频时还在接收字幕和笔记,页面也能及时更新。src/index.ts 在终端完成相同的工作。