从写代码,到创作下一幕

探索 字节跳动 - 火山方舟 的 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 新闻简报,以简短的语音摘要播报关注话题的最新动态。同一方法也适用于其他定时调研任务:按固定时间范围搜索、记录已报道内容,并限制每次运行的支出。

选择几个话题,应用每天早晨会在 𝕏 和网页上搜索上次简报之后的新进展,跳过已播报的新闻,将其余内容整理为约两分钟的语音,并附上每条新闻的来源帖子和文章。网页版像播客一样播放当前话题,同时由 Grok 调研下一话题;终端版可按计划运行。

你将学到

  • 用 X Search 的以下两个参数指定搜索时段:from_date 和 to_date

  • 记录每期简报的内容,让 Grok 判断一条新闻是新事件、后续进展还是已经报道过的消息

  • 根据引用只保留搜索实际返回的帖子和文章

  • 实时显示每次搜索,使用 SDK 的 server_tool_call 事件

  • 依据 API 对每个请求报告的费用控制单次运行支出

  • 使用 client.voice.speak() 和少量语音标签为各部分配音,同时调研下一话题

  • 通过 cron 定时运行,或使用 --every

运行示例

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

Bash

cd examples/topics-to-morning-briefing/typescript
npm install
npm run web

打开 http://localhost:3000,点击 Brief me。页面初始话题来自 topics.txt,包括 SpaceX、一级方程式和 AI 芯片。点击 × 可移除话题,也可以输入新话题并按回车。页面类似播客播放器,会实时展示各话题的搜索过程,再列出新闻及对应的 𝕏 帖子和文章。第一个话题准备好便开始播放,高亮当前话题,逐词标出朗读进度。再次点击 Brief me 时,若已报道的新闻没有进展,应用会告诉你没有新消息。起始页面列出已记住的内容,可用 Forget them 清空。点击 Stop 或关闭页面即可取消本次运行。

在终端制作简报的方法如下:

Bash

npm start

默认播报 topics.txt 中的话题,每行一个,也可以传入话题,例如 npm start -- "SpaceX" "Formula 1"。两个版本都将简报保存到 output/<time>/briefing.mp3,已报道的内容保存到 output/memory.json。终端版还会将含全部来源链接的节目说明写入 briefing.md,与 MP3 位于同一目录。

示例实测三个话题需一至两分钟,API 繁忙时最长约五分钟。费用约 0.45 美元:X Search 和网页搜索合计约 0.20 美元,token 约 0.20 美元,语音约 0.03 美元。没有新动态时费用约减半。X Search 按获取的帖子数计费,每 1,000 条 5 美元;网页搜索每 1,000 次调用 5 美元;语音每百万字符 15 美元。

定时运行

如果继续处理新话题预计会超过费用上限,应用便会停止启动新话题。上限默认为 1 美元,可通过 --max-cost 调整。网页版在 Cost cap 菜单中设置。要每天早晨 7 点生成简报,可用 crontab -e 添加如下条目。cron 执行命令时使用较短的 PATH,因此请将 /path/to/node/bin 替换为 dirname "$(which node)" 输出的目录:

Text

0 7 * * * cd /path/to/xai-cookbook/examples/topics-to-morning-briefing/typescript && PATH="/path/to/node/bin:$PATH" npm start -- --max-cost 0.75 >> "$HOME/morning-briefing.log" 2>&1

也可在终端持续运行并指定 --every。每次简报开始后,程序会在指定间隔后再次生成简报,直到按下 Ctrl+C:

Bash

npm start -- --every 1d

工作原理

共享代码位于 src/briefing.ts,记忆逻辑位于 src/memory.ts。makeBriefing() 依次调研各个话题,通过回调报告每一步,并在调研下一话题的同时为当前话题配音:

  1. loadMemory() 读取 output/memory.json,其中列出各期简报的时间、费用、调研话题和已报道新闻。searchWindow() 为每个话题划定从上次报道当天(首次运行则从昨天)到今天的搜索时段。X Search 以 UTC 整日为单位,而 to_date 不计入范围,所以将结束日期设为明天才能涵盖今天。X Search 文档称范围包含 to_date,但示例实测将 to_date 设为今天查询最新帖子时,没有返回今天的帖子。

  2. researchTopic() 流式发出一个 grok-4.7 请求,使用 xSearch({ from_date, to_date }) 和 webSearch() 并在 text.format 中设置 JSON Schema。提示词列出该话题过去一周报道过的新闻和上次简报的发布时间。Grok 为找到的新闻分别标记“新事件”“后续进展”或“旧闻”,附上来源帖子、文章和主播口播稿。由于 X Search 只接受日期,Grok 会根据上次播报时间判断哪些进展是新发生的,并经常用 𝕏 的 since: 运算符将查询精确到分钟。server_tool_call 事件实时报告各次搜索。citation 事件收集搜索返回的全部 URL;没有匹配 URL 的帖子或文章会被丢弃。主要工作由搜索完成,因此请求采用较低的推理强度。max_turns 限制每个话题的搜索轮数,idleTimeout 防止停滞的数据流阻塞定时任务。

  3. 每个响应会通过 usage.cost_in_usd_ticks 返回包含搜索在内的费用,SDK 将其换算为美元并存入 usage.cost_usd。只有在请求结束后才能知道费用,不能处理到一半再停下话题。因此,应用按本次迄今最贵话题的费用估算,下个话题预计不超预算才会开始。若尚未处理任何话题,则使用上次简报的平均话题费用。片尾会报出被跳过的话题;下次简报将向前扩大这些话题的搜索范围。

  4. recordClip() 会在每个话题的脚本写好后立即调用 client.voice.speak() 配音。Grok 可使用少量语音标签,例如在新闻之间插入 [pause],或在某个词上使用 <emphasis>。语音 API 会把不能识别的标签念出来,所以先通过 SDK 的 stripInvalidSpeechTags() 移除无效标签。Grok 偶尔也会在脚本中留下引用标记,如 <citation id="web:23"/>,语音合成会将其读作“citation ID web 23”,因此用 stripCitations() 清除。语音 API 不报告费用,故按字符单价计算。片头和片尾文案由程序直接生成。

  5. 所有话题完成后,程序会将片段合并为一个 MP3,并由 remember() 把新事件和后续进展添加到记忆文件。中途停止不会改动记忆。

src/server.ts 会运行 makeBriefing() 来处理页面提交的话题,并通过服务器发送事件逐步报告进度。由于每次都会重写记忆文件,同时只运行一期简报。服务器给每个请求传入 AbortSignal,因此关闭页面或点击 Stop 都会取消进行中的任务。服务器只提供本次写入的音频文件,按字节范围传输以支持浏览器跳转。public/index.html 使用原生 HTML 和 JavaScript 显示这些事件,按顺序播放各片段。src/index.ts 在终端输出相同步骤并写入节目说明。

如需更换声音,请修改 VOICE,位置在 src/briefing.ts。client.voice.list() 可列出内置声音。