ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Karakeep(原 Hoarder)架构解析:Web 应用 + SQLite 队列 + 三类 Worker 的协作模型

Karakeep(原 Hoarder)架构解析:Web 应用 + SQLite 队列 + 三类 Worker 的协作模型 Karakeep原 Hoarder架构解析Web 应用 SQLite 队列 三类 Worker 的协作模型【免费下载链接】hoarderA self-hostable bookmark-everything app (links, notes and images) with AI-based automatic tagging and full text search项目地址: https://gitcode.com/GitHub_Trending/ho/hoarderKarakeep 是一款可自托管的收藏一切应用链接、笔记与图片均可收藏其核心架构用一个简单而清晰的模型支撑起完整的收藏流水线Web 应用负责用户交互与数据存储后台 Worker 负责抓取、AI 标注与索引。本文以官方架构文档为主体结合仓库源码逐步拆解这套架构Web 层为何选择 Next.js SQLite任务队列如何在 SQLite 之上运转以及 Crawling抓取、OpenAIAI 打标、Indexing全文索引三类 Worker 各自的职责与调用链。读完本文你将能理解 Karakeep 从保存一个链接到可搜索、可自动打标全过程的底层工作方式也能在部署时准确判断每个容器与配置项的作用。整体架构一览官方架构文档docs/docs/08-development/04-architecture.md给出了整幅架构图文档用三个要点概括了整套架构Webapp基于 Next.js 构建使用 SQLite 存储数据Workers消费 SQLite 任务队列中的任务并执行共有三种任务类型Crawling使用运行在 workers 容器中的无头 Chrome 浏览器抓取链接内容OpenAI调用 OpenAI API 推断内容标签Indexing将内容索引到 Meilisearch以便搜索时快速检索。下面逐层展开并结合源码验证每个组件在仓库中的真实实现。Web 应用层Next.js SQLiteWeb 应用是整个系统唯一的门面。它接收用户的操作保存书签、编辑标签、搜索等把数据落到 SQLite并把异步工作转交给任务队列。Next.js 前端/服务端位于 apps/web包含仪表盘、阅读器、设置、订阅管理等页面通过 tRPC 暴露 API路由定义见 packages/trpc/routers。SQLite 数据层位于 packages/db/schema.ts使用 Drizzle ORM 定义users、bookmarks、bookmarkLinks、tags、assets等表。以用户表为例其中已经可以看到架构与后续 Worker 的联动用户级别有browserCrawlingEnabled是否允许浏览器抓取、autoTaggingEnabled是否开启自动打标、autoSummarizationEnabled是否自动总结等字段这些字段直接决定某个书签的抓取/推理任务是否会被触发。SQLite 同时承担了两份职责一是书签等业务数据的持久化存储二是任务队列的存储介质。这也正是轻量自托管定位的体现——不需要单独部署 Redis 或 Postgres一个数据文件即可运行整套系统相关迁移脚本位于 packages/db/drizzle。SQLite 任务队列三类任务统一调度文档强调Workers 消费 SQLite 任务队列中的任务。队列机制的定义位于 packages/shared/queueing.ts它抽象了Queue入队、统计、取消与Runner消费任务、重试、完成/失败回调两个核心接口Queue.enqueue(payload, options)支持幂等键、优先级priority、延迟执行delayMs、分组groupId等入队选项RunnerFuncs每个 Worker 需要实现run执行任务主体、onComplete成功后回调如更新数据库状态、onError失败后回调可区分重试剩余次数RunnerOptions包含轮询间隔默认 1000ms、任务超时timeoutSecs与并发数concurrency。队列实现本身是插件化的默认的轻量实现基于 SQLitequeue-liteque插件位于 packages/plugins/queue-liteque也提供基于 Restate 的分布式实现packages/plugins/queue-restate。从源码结构看SQLite 队列是开箱即用的默认方案这与文档SQLite 任务队列的描述完全一致。各类任务的队列在 packages/shared 中统一注册如LinkCrawlerQueue、OpenAIQueue、SearchIndexingQueue并在 apps/workers/index.ts 中按名称启动对应 Workercrawler、inference、search分别对应文档提到的三类任务且支持通过环境变量WORKERS_ENABLED_WORKERS/WORKERS_DISABLED_WORKERS精确控制启用哪些 Worker。任务类型一Crawling —— 无头 Chrome 抓取链接抓取任务是流水线的第一环用户在 Web 端保存一个链接后系统需要真正读到页面内容才能为后续打标与索引提供素材。浏览器环境文档明确指出抓取依赖运行在 workers 容器中的 headless chrome。这一设计在 docker/docker-compose.yml 中体现得很直观单独起一个chrome服务ghcr.io/karakeep-app/karakeep-chrome通过BROWSER_WEB_URL: http://chrome:9222暴露给主容器主容器通过该地址连接远程 Chrome。Chrome 以--disable-gpu、--disable-dev-shm-usage等参数运行适合容器化场景。浏览器连接的初始化与管理在 apps/workers/workers/crawler/browser.ts使用playwright-extrachromium建立全局浏览器连接并集成了 Ghostery 广告拦截器ghostery/adblocker-playwright、Stealth 反检测插件、磁盘 Cookie 加载以及浏览器上下文的自动回收机制。抓取主流程抓取 Worker 的队列编排在 apps/workers/workers/crawlerWorker.ts探测内容类型先对 URL 做探测probe.ts判断目标是网页、PDF 还是图片PDF/图片会被降级为资产型书签asset bookmark处理域名限流检查checkDomainRateLimit按域名维度限流被限流时抛出QueueRetryAfterError并附带随机抖动延迟避免惊群效应源码中注释为防止 thundering herd正式抓取调用crawlAndParseUrl执行浏览器抓取、截图、整页归档、PDF 存储等抓取实现位于 apps/workers/workers/crawler/crawlPage.tsHTML 解析在子进程中完成parseSubprocess.ts成功后编排后续任务enqueuePostCrawlJobs是架构中最能体现协作模型的函数——一次抓取成功后会根据配置把后续工作分别入队EmbeddingsQueue/OpenAIQueue打标、OpenAIQueue总结、SearchIndexingQueue重建索引、VideoWorkerQueue视频下载可选以及触发crawledWebhook。相关配置抓取行为高度可配置相关变量定义在 packages/shared/config.ts前缀CRAWLER_*/BROWSER_*常见项包括BROWSER_WEB_URL/BROWSER_WEBSOCKET_URL远程 Chrome 连接地址CRAWLER_HEADLESS_BROWSER是否启用无头浏览器默认 trueCRAWLER_JOB_TIMEOUT_SEC单个抓取任务超时默认 60 秒CRAWLER_NUM_WORKERS抓取并发数默认 1CRAWLER_STORE_SCREENSHOT/CRAWLER_FULL_PAGE_SCREENSHOT/CRAWLER_STORE_PDF控制截图与 PDF 归档行为CRAWLER_ENABLE_ADBLOCKER/CRAWLER_ENABLE_AUTOCONSENT控制广告拦截与 cookie 同意弹窗自动处理。任务类型二OpenAI —— 内容标签推断抓取完成后内容会被送入推理 Worker 做 AI 打标以及可选的自动总结这是 Karakeep基于 AI 的自动打标卖点的核心实现。推理 Worker 位于 apps/workers/workers/inference/inferenceWorker.ts它消费OpenAIQueue中的任务按type分发到两个执行函数tag→runTaggingtagging.ts将书签的标题、正文、元数据等内容拼装成 prompt 发送给 LLM要求模型返回标签列表响应通过 zod 的openAIResponseSchema校验为{ tags: string[] }生成结果遵循用户的tagStyle偏好如小写连字符、驼峰等字段见 packages/db/schema.ts 的tagStyle枚举summarize→runSummarizationsummarize.ts对书签内容生成摘要写入书签的summary字段。值得注意的实现细节runTagging中定义了RELEVANT_TAG_TRUNCATE_LENGTH 1000会把已有关联标签截断后拼入上下文防止无关标签膨胀推理上下文同时任务完成后会通过triggerSearchReindex触发搜索索引重建保证新打的标签能立即被搜索到——再次印证了三类 Worker 之间的联动关系。推理 Worker 还会在完成/失败时更新书签的状态字段taggingStatus、summarizationStatusWeb 端可以据此展示AI 处理中/已完成的状态。推理配置同样集中在 packages/shared/config.tsOPENAI_API_KEY、OPENAI_BASE_URL、INFERENCE_TEXT_MODEL默认gpt-5.6-luna、INFERENCE_ENABLE_AUTO_TAGGING默认 true、INFERENCE_ENABLE_AUTO_SUMMARIZATION默认 false、INFERENCE_NUM_WORKERS等同时也支持OLLAMA_BASE_URL即可以对接本地 Ollama 而无需 OpenAI 账号。任务类型三Indexing —— Meilisearch 全文索引抓取、打标完成后书签内容最终要被索引进 Meilisearch这是搜索快的保证。文档明确指出这一任务类型的存在其实现位于 apps/workers/workers/searchWorker.ts消费SearchIndexingQueue中的任务支持index写入/更新与delete删除两种操作runIndex会从 SQLite 聚合一个书签的完整搜索文档BookmarkSearchDocument链接的 URL/标题/描述/正文、资产的文本内容、纯文本书签的正文、笔记、摘要、作者、发布时间以及标签列表文档结构与索引接口定义在 packages/shared/search.ts其中addDocuments支持批量写入但重试轮次runNumber 0会关闭批处理以提高可靠性若 Meilisearch 未配置Worker 会直接跳过并记录日志Search is not configured, nothing to do now。Meilisearch 的接入同样是插件化的默认实现是search-meilisearch插件packages/plugins/search-meilisearch/index.ts在MeiliSearchProvider.isConfigured()成立时自动注册为搜索提供方。部署层面docker-compose 中运行getmeili/meilisearch容器并通过MEILI_ADDR: http://meilisearch:7700供主容器连接。从搜索接口SearchOptions含过滤、排序、分页和 apps/web 中的搜索页面可以看出Web 端的搜索请求最终会落到 Meilisearch 的全文检索这也是 SQLite 与 Meilisearch 分工的体现SQLite 管权威数据Meilisearch 管检索加速。部署拓扑与三类任务的协作闭环把三类 Worker 放在一次保存链接的完整生命周期里整个协作闭环非常清晰用户在 Next.js Web 应用中保存一个链接书签记录写入 SQLite同时LinkCrawlerQueue入队Crawling Worker通过远程 Chrome 抓取页面解析出正文、元数据、截图等写回 SQLite 与对象存储抓取成功后自动把OpenAI打标/总结与Indexing任务入队OpenAI Worker调用 LLM 生成标签/摘要并写回 SQLiteIndexing Worker汇总书签的正文、摘要、标签构建搜索文档写入 Meilisearch用户在 Web 端搜索时请求通过 tRPC 转发到 Meilisearch 完成全文检索。这一整套流程在单机 docker-compose 部署中即可跑通docker/docker-compose.yml 包含 web、chrome、meilisearch 三个服务充分体现轻量自托管的架构取舍默认不引入外部队列与搜索引擎却通过插件层队列、搜索、向量库、存储等见 packages/plugins保留了向更强组件升级的路径。架构的演化不止三类 Worker值得一提的是原架构文档写于项目早期描述的是三类任务从 apps/workers/index.ts 的workerBuilders可以看到如今的 Worker 体系已在此基础上扩展出更多类型embeddings向量嵌入用于语义搜索、video视频下载、feedRSS 订阅刷新、assetPreprocessing资产预处理、webhookWebhook 投递、ruleEngine自动化规则引擎、backup定时备份、adminMaintenance管理维护、import导入轮询等。它们复用同一套队列 Runner框架因此在架构图上依然是Workers 消费队列并执行任务的同一模型——这正是该架构最具扩展性的地方新增一个 Worker 类型只需实现run/onComplete/onError并注册队列即可无缝融入现有流水线。小结Karakeep 的架构用最小的组件集合实现了收藏 — 抓取 — AI 打标 — 全文搜索的完整闭环Next.js SQLite 保证单机可跑、易于自托管SQLite 承载任务队列让异步处理无需额外中间件三类核心 WorkerCrawling / OpenAI / Indexing通过队列解耦、通过配置联动彼此独立又可编排。对于希望二次开发或深入理解其内部机制的人来说沿着 apps/workers 的 Worker 目录、packages/shared/queueing.ts 的队列抽象、packages/db/schema.ts 的数据模型以及 packages/shared/config.ts 的配置中心这四条主线阅读即可完整掌握这套架构的全貌。【免费下载链接】hoarderA self-hostable bookmark-everything app (links, notes and images) with AI-based automatic tagging and full text search项目地址: https://gitcode.com/GitHub_Trending/ho/hoarder创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表