如何实现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.ts | SteamDB 历史最低价分析 | ✅ 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。
这里有两个关键选择:
Promise.allSettled而非Promise.all:任何一个商店 Agent 超时或失败,都不会拖垮其余 10 个任务,失败的那张卡片单独显示错误状态。- 每个任务独立持有
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_URL | EventType.STREAMING_URL | 拿到 Agent 浏览器的实时画面地址 |
STATUS | EventType.PROGRESS | 更新卡片上的"正在做什么"文案 |
COMPLETE | EventType.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:
response.body.getReader()按块读取响应体;- 用
TextDecoder解码,buffer 保留最后一个不完整行(SSE 帧可能被网络拆包); - 按
\n切行,只处理以data:开头的帧; 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),仅供参考