☰
如何实现SSE流式推送与并行抓取?TinyFish Cookbook之game-buying-guide完整深度解析
2026/10/8 13:12:05 网站建设 项目流程

如何实现SSE流式推送与并行抓取?TinyFish Cookbook之game-buying-guide完整深度解析

【免费下载链接】tinyfish-cookbookA collection of sample apps and recipes built with the TinyFish web agent. Open-source examples for you to learn & build!项目地址: https://gitcode.com/gh_mirrors/ti/tinyfish-cookbook

本文带你完整拆解 TinyFish Cookbook 中的开源示例game-buying-guide:它是一个 AI 游戏比价助手,通过SSE 流式推送+11 路并行抓取,让 10 个浏览器 Agent 同时访问 Steam、Epic、GOG 等游戏商店并实时回传价格分析结果。下面从架构、并发、流式协议到客户端解析,一次讲透它的代码设计。

一、它是什么?一个能"同时逛 11 个商店"的 AI 比价工具

game-buying-guide 的目标很直接:输入一个游戏名,它立刻派出 11 个 TinyFish 浏览器 Agent并行执行:

  • 🎮10 个商店 Agent:Steam、Epic Games、GOG、PlayStation、Xbox、Nintendo eShop、Humble Bundle、Green Man Gaming、Fanatical、CDKeys
  • 📉1 个 SteamDB Agent:专门抓取该游戏的历史最低价格,判断"现在是买还是等"

每个 Agent 返回结构化 JSON(现价、折扣、评分、buy_now / wait / consider建议),最先完成的平台最先显示在页面上——这正是 SSE 流式推送的价值所在。

二、总体架构:三个 API 路由组成流水线

项目结构见 game-buying-guide/README.md,核心只有 3 个 API 路由:

路由职责是否流式
discover-platforms/route.ts返回 10 个商店的搜索 URL(硬编码模板,无需 LLM)❌ 普通 JSON
analyze-platform/route.ts单个商店的 Agent 分析✅ SSE 流式
steamdb-price-history/route.tsSteamDB 历史最低价分析✅ SSE 流式

设计亮点:平台发现用硬编码 URL 模板(route.ts#L4-L55),省掉一次 LLM 调用的等待;只有真正"需要看网页"的环节才交给浏览器 Agent。整个项目无数据库、无缓存,纯内存实时抓取。

三、并行抓取核心:Promise.allSettled 同时发起 11 个任务

客户端状态全部集中在 hooks/use-game-search.ts 这个自定义 Hook 里。search函数是并行抓取的入口:

// 所有平台 Agent + SteamDB 并行执行 await Promise.allSettled([ ...platforms.map((platform) => analyzePlatform(platform, gameTitle)), analyzeSteamDB(gameTitle), ]);

见 use-game-search.ts#L186-L189。

这里有两个关键选择:

  1. Promise.allSettled而非Promise.all:任何一个商店 Agent 超时或失败,都不会拖垮其余 10 个任务,失败的那张卡片单独显示错误状态。
  2. 每个任务独立持有AbortController(见 use-game-search.ts#L22-L23):用户重新搜索或点击重置时,可以精确中止某一路 SSE 连接,避免旧请求污染新结果。

四、SSE 服务端实现:ReadableStream + 四行事件头

analyze-platform路由的精髓只有几十行。它用ReadableStream构造响应体,把 Agent 事件逐条编码为 SSE 格式(data: {JSON}\n\n)写入:

const stream = new ReadableStream({ async start(controller) { const send = (payload) => controller.enqueue(encoder.encode(sseData(payload))); // 把 TinyFish 事件流逐条转成 SSE 帧发送…… }, });

完整实现见 route.ts#L70-L118。响应头中有 4 个保证流式体验的关键设置:

Content-Type: text/event-stream Cache-Control: no-cache, no-transform Connection: keep-alive X-Accel-Buffering: no ← 禁止 Nginx 类代理缓冲,数据即刻下发

另外注意 route.ts#L4-L5 的maxDuration = 300:浏览器 Agent 爬取慢,必须把云函数超时时间拉长到 5 分钟,否则流会在中途被掐断。

三种 SSE 事件类型

客户端只会收到 4 种帧,全部由服务端事件映射而来:

SSE 帧类型来源(TinyFish SDK 事件)用途
STREAMING_URLEventType.STREAMING_URL拿到 Agent 浏览器的实时画面地址
STATUSEventType.PROGRESS更新卡片上的"正在做什么"文案
COMPLETEEventType.COMPLETE+RunStatus.COMPLETED返回解析后的价格分析 JSON
ERROR异常分支携带错误信息,前端标记卡片为失败

五、最大亮点:STREAMING_URL 驱动的"实时浏览器预览"

STREAMING_URL事件是这套架构最出彩的部分:TinyFish 会把 Agent 浏览器的实时画面地址流式推给前端,前端直接用<iframe>加载,用户就能亲眼看到 AI 正在哪个商店页面上翻找价格。

  • 卡片内嵌预览:components/agent-card.tsx 中运行中的卡片带一块 128px 高的实时窗口
  • 点击可放大为悬浮窗:components/live-browser-preview.tsx 用 Framer Motion 实现展开/收起

上图是同一 Cookbook 仓库中 fast-qa 示例应用的截图——同样基于 TinyFish Agent + 流式回传,能看到"边执行、边更新"的交互质感,game-buying-guide 的每张卡片都是这种体验。

六、客户端 SSE 解析:手写 30 行解析器

前端没有用EventSource(它不支持 POST),而是手写了一个极简解析器,核心逻辑在 use-game-search.ts#L40-L74:

  1. response.body.getReader()按块读取响应体;
  2. 用TextDecoder解码,buffer 保留最后一个不完整行(SSE 帧可能被网络拆包);
  3. 按\n切行,只处理以data:开头的帧;
  4. JSON.parse后按type字段分发到对应的状态更新。

解析失败直接catch跳过坏块,保证流不被一条脏数据中断。所有数据结构定义在 lib/types.ts:AgentStatus(每路任务状态机:pending → running → complete/error)与PlatformAnalysis(比价结果字段)。

七、一个容易被忽略的细节:COMPLETED ≠ 任务成功

服务端对结果做了双重校验(见 route.ts#L85-L96):

  • 状态校验:RunStatus.COMPLETED只代表"浏览器没崩",不代表真的取到了价格;
  • 内容校验:Agent 可能返回带 ```json 围栏的字符串,需先清洗再JSON.parse;解析不出来时回退到兜底对象(recommendation: "consider"),前端永远不会白屏。

这是所有 Agent 应用的通用经验:永远校验内容,而不只是状态。

八、本地运行这份示例

git clone https://gitcode.com/gh_mirrors/ti/tinyfish-cookbook cd tinyfish-cookbook/game-buying-guide # 需要 Node.js 22.x npm install # 创建 .env.local,填入 TINYFISH_API_KEY npm run dev

打开 http://localhost:3000,输入任意游戏名,即可看到 11 张卡片从"运行中"逐步变为"完成",实时画面、进度文案、比价结果依次流式呈现。

总结:这套架构最值得抄的 4 个点

  • ⚡并行用allSettled:N 路任务互不牵连,失败只影响单卡
  • 📡SSE 用ReadableStream手写:POST 场景下比EventSource更灵活,客户端解析器仅需 30 行
  • 🖥️STREAMING_URL事件转发 iframe:把"AI 在干活"变成可视过程,体验远超转圈 loading
  • ✅COMPLETED 后仍校验内容:Agent 结果永远需要清洗 + 兜底

完整源码可直接参考 game-buying-guide/ 目录;更多基于 TinyFish 的实战示例,都在 Cookbook 仓库的 README.md 中有索引。

【免费下载链接】tinyfish-cookbookA collection of sample apps and recipes built with the TinyFish web agent. Open-source examples for you to learn & build!项目地址: https://gitcode.com/gh_mirrors/ti/tinyfish-cookbook

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询