如何使用 upstash + supabase 做一个 AI 书签管理器
- Authors
- @SLIPPERTOPIA
推特上点赞、收藏了很多有趣的推文,想着哪天有需要了回头再看。 但点赞内容与日俱增,历史收藏却从来没有翻阅过,这些推文就静静躺在收藏夹里吃灰,吃了数年之久,像极了我在桌面角落里的 Kindle。
再后来有一日想查找一条推文,但碍于 twitter 的搜索功能过于鸡肋,没法直接对个人收藏夹进行搜索,只能采用笨方法从上往下逐条翻阅。越翻越觉得很多内容过于精彩,放在收藏夹里吃灰着实可惜,于是就萌生了自己做一个 AI 书签管理器的想法。
放在之前,这是一个大项目,要略懂前端、熟悉后端、了解 DevOps、数据库,最好还得懂点 NLP。好在现在有 LLM 加持,结合 cursor,使用 NextJS 直接一把梭。
技术选型
如果主要功能是标签收集及检索,那这就是一个典型的召回重排项目,核心需求就是:如何从数据库中召回和当前 query 最相关的文档,并确定各个文档的优先级顺序。
在 LLM 流行之前,一个可行方案是通过对比学习(contrast learning)训练一个 SimCSE 网络,训练数据集要包含若干 (<query, doc>, 1)
对(大量标注是跑不了的),应用时将 query 向量化,再通过相似度检索召回。LLM 流行之后,一个讨巧的做法是通过 LLM (e.g., OpenAI text-embedding-3-large) 直接生成 embedding,再通过相似度检索召回。毕竟很多 LLM 在预训练过程中已经通过大量文本习得了强大的语义编码能力,可以直接用于生成上下文嵌入,因此可以省去大量训练成本。
我使用的嵌入模型是 bge-large,并结合 upstash 的 Vector Database 进行向量存储与搜索。Upstash vector database 的一个优势是支持直接将文本进行向量化存储,不需要先通过模型向量化再写入数据库。同时在检索时也封装了向量化+相似度检索的流程,用户只需要输入文本,就可以直接获取到向量化后的结果,过程非常丝滑。
后端使用 supabase 的 postgres 数据库,存储文章元信息,如链接、摘要、标签等。最终部署在赛博菩萨 cloudflare page 上。
实现
存储
拿到链接后,第一步是获取链接内容,并进行向量化存储。根据公开链接获取内容主要有三种方式:
- LangChain WebBaseLoader
- API
- playwright
普通网页内容的获取直接使用了 LangChain 的 WebBaseLoader,获得文本之后,通过 LLM 进行摘要总结。爬取的文本可能包含一些广告内容、网站信息(如版权声明),但通过 LLM 进行摘要总结后,这些不重要的内容都会被过滤掉。
推特文本获取
从马老板买下推特后,推特的 API 也更新升级了。虽然价格贵了,但接口并没有变得更好用。 并且,推特对第三方应用和爬虫采取了更加严格的限制措施,在不登录的情况下,想通过脚本获取推文是比较困难的。因此在推特内容获取上,我花了不少时间。目前有两套方案:
- 使用 Coze API 获取内容,这个方案获取内容成功率很高,不过需要使用 Coze API 。
- playwright 模拟浏览器操作。这个方案简单暴力,但是非常吃资源。
第一个方案主要借用 Coze 商店中的 twitter link parser 插件,这个插件实际上是通过 twitter 的 API 获取内容的。插件按调用次数收费,但价格还算合适,1 元钱可以调用插件 350 次左右。
但这个插件有一个小问题,通过插件返回的推文内容有时候是不完整的,如果原文是长文本(Twitter 会员没有 140 字限制),那返回的内容就只有一部分。截断的内容对下游摘要总结任务有一定影响,因此这种情况下还是要使用 playwright 方案。
Playwright 作为兜底方案,主要用于获取通过 API 及 WebBaseLoader 获取失败的内容。这种通过无头浏览器操作获取内容的方式,可以直接读取几乎所有公开链接的文本内容,但缺点也很明显,计算资源消耗大且读取速度慢。我在一台 1C1G 的 VPS 上部署了 playwright 服务,实测获取一条推文耗时在 10 秒左右。而且不能同时读取多个推文,不然很容易就内存不足。
生成 Embedding
如上所述,向量化采用了 upstash 的 bge-m3 模型,主要考虑到中英文内容都有,采用多语言模型更合适。 Upstash 支持的模型并不多,除了 bge 系列(bge-large, bge-small, bge-medium)之外, 还有 UAE, bert-uncased 等,但对中文的支持都不太友好。
检索
检索方案也比较简单,使用 upstash 的 Vector Database 支持相似度检索,通过点积计算向量间相似度,过滤掉低于阈值(0.7)的结果,再通过元信息从 supabase 取出对应链接及摘要。
可优化点
文本获取提速
作为兜底方案的 playwright 速度太慢,如果短时间内需要获取大量内容,就不得不等待前面的链接内容获取完毕。 需要考虑使用更快捷的文本获取方案。
提高召回准确性
召回与准确性不可兼得,目前设定的相似度阈值是 0.7,使用过程中经常返回一些关联度不高的结果,且对 query 输入较为敏感。
缓存优化
每次检索时都先将 query 进行向量化,然后进行相似度检索,反应延迟有点高(1~3秒),我期望做到秒级响应。后续考虑使用缓存优化。
小结
我几日体验下来,感觉还是挺实用的。多数情况下,通过几个关键词,都能在 top3 中命中目标内容。 后面继续观察一下,存储内容多了之后,是否能保持住这个效果。