pguso/rag-from-scratch:用 node-llama-cpp 把 RAG 的每一环拆开写一遍
Demystify RAG by building it from scratch. Local LLMs, no black boxes - real understanding of embeddings, vector search, retrieval, and context-augmented generation.
秒懂
- 它是什么?
- 这是一个教学型 JavaScript 仓库,用本地 LLM 和手写向量检索把 RAG 流水线拆成十来个可单独运行的示例。适合想搞清楚 embedding、分块、重排到底在做什么的工程师,不适合直接拿去做生产检索服务。
- 适合谁用?
- 如果你已经会用 LangChain 或 LlamaIndex 拼出一条 RAG 链,但说不清 top-k 是怎么算出来的、chunk 重叠为什么会影响召回,这个仓库值得按 examples/00 到 06 的顺序跑一遍,重点看 03_hybrid_search 和 04_multi_query_retrieval 里 RRF 的融合权重。如果你的目标是在下个季度上线一个面向真实用户的问答服务,它不合适:仓库里只有 in-memory 向量存储,没有持久化、并发和索引重建的设计,README 也没有给出任何压测或规模数字。
- 能商用吗?
- 可以。MIT 是宽松许可证:你可以使用、修改并销售基于它的软件,只需保留版权和许可证声明。
- 还在维护吗?
- 活跃度在下降。仓库最近一次提交在 6 个月前。
- 用什么语言写的?
- 主要是 JavaScript(依据 GitHub 的语言统计)。
以上回答依据项目的 GitHub 数据(最近同步于 2026年9月15日)和我们的分析,不构成法律意见。
开源项目深度解析
它解决的是理解问题,不是部署问题
多数 RAG 教程的形态是:装一个框架,调三行 API,得到一个能回答问题的机器人。跑通了,但中间发生了什么仍然不清楚。pguso/rag-from-scratch 走的是相反的路,README 开头就写明目标是 no black boxes、no cloud APIs,把 embedding、向量检索、重排、查询改写这些环节逐个用 JavaScript 写出来。它面向的读者是已经写过 Node.js、知道什么是向量但没亲手实现过余弦相似度的人。仓库的目录结构本身就是课程大纲,examples/00_how_rag_works 号称用不到 70 行代码跑通一条最简端到端流程,后面每一节再把其中一个环节展开。这种编排的代价是它不提供任何封装好的入口函数,你不会在这里找到一个能 import 的 RAG 类。
十步流水线在目录里怎么落地
README 把 RAG 拆成十步:知识需求定义、数据加载、文本切分、embedding、向量存储、检索、检索后重排、查询预处理与向量归一化、上下文增强、生成。对应的代码分散在 examples 下,编号从 00 到 06,中间有跳号,比如 01 在 README 里没有对应条目,02 是数据加载。数据流是单向的:原始文本经 02_data_loading 读入并规范化,03_text_splitting_and_chunking 切成带重叠的块,04_intro_to_embeddings 下的 02_generate_embeddings 把块转成向量,05_building_vector_store/01_in_memory_store 把向量和元数据存进内存结构并实现最近邻搜索,06_retrieval_strategies 下再分四个子目录分别做基础检索、查询预处理、混合检索和多查询检索。每个示例目录都固定配三份文件:example.js 是代码,CODE.md 逐函数讲解,CONCEPT.md 讲背后的概念。这个三件套的约定比代码本身更有价值,因为它把实现和动机分开了。
本地推理这条线由 node-llama-cpp 承担
仓库的 topics 里有 node-llama-cpp,README 也强调 no cloud APIs,说明模型推理走的是本地 GGUF 路线。这意味着两件事。第一,你需要自己准备模型权重文件,仓库不负责分发,README 正文里也没有给出具体的下载地址或推荐模型名,这一点在动手前必须先解决。第二,node-llama-cpp 是原生绑定,安装过程可能涉及本地编译,在 Windows 或缺少构建工具链的环境里失败是常见情况。生成和 embedding 两处都可能依赖它,具体哪个示例用哪个模型,需要打开对应目录的 CODE.md 才能确认,README 层面没有统一说明。把推理放在本地的好处是整条链路可观测,你能打印出每一次检索命中的原文块;代价是首次跑通的环境成本明显高于调一个云端 API。
检索部分才是这个仓库的重心
06_retrieval_strategies 下面四个子目录的排列顺序体现了作者对难度的判断。01_basic_retrieval 只做 top-k 相似度取回,README 提到同目录下还有一个 showcase.js,把前面学过的环节串起来演示。02_query_preprocessing 在向量化之前清洗查询,README 列出的手段包括归一化、去停用词和查询清洗,目标是让向量更稳定。03_hybrid_search 把向量相似度和 BM25 这类关键词信号加权合并,README 的说法是平衡语义相似度与传统检索信号。04_multi_query_retrieval 最复杂,用 LLM 把复合问题拆成子查询,并行检索后用 reciprocal rank fusion 或加权方式融合,还要处理去重和排序。这里的取舍很清楚:混合检索和 RRF 都需要调权重,而权重没有普适值。教学仓库给出的是可运行的默认值,不是调优结论,照搬参数到自己的语料上大概率要重调。
内存向量存储是一个明确的天花板
05_building_vector_store 目前只提供了 01_in_memory_store 一个实现。这意味着每次进程启动都要重新加载文档、重新分块、重新生成 embedding,没有任何持久化层。对于教学这没问题,因为你能完整看到索引是怎么构建的;但对于任何有真实数据量的场景,这是硬约束。仓库没有提供磁盘索引、增量更新或索引重建的示例,README 也没有讨论向量数量增长后线性扫描的耗时问题。另一个没有覆盖的方向是多模态或结构化数据,整条流水线假设输入是纯文本。如果你的文档里有大量表格或扫描件,这个仓库帮不上忙,它连 OCR 或表格解析的入口都没有。
和 LangChain、LlamaIndex 的差别在抽象层级
拿它和 LangChain 的 RetrievalQA 链对比,差别不是功能多少,而是抽象放在哪一层。LangChain 把加载器、切分器、向量存储、检索器都做成可替换的接口,你写的是配置和组合,代价是出问题时要在多层封装之间定位。这个仓库把每一层都摊平成几十行可读的 JavaScript,你能直接看到余弦相似度是怎么算的、chunk 边界是怎么切的,代价是所有东西都要自己接。两者不是替代关系:更常见的用法是先在这个仓库里把机制跑明白,再回到框架里用现成组件,此时你至少知道那些组件在替你做什么。如果你需要的是生产级的向量数据库连接器、重试、限流和可观测性,这里一样都没有。
MIT 许可与跟进成本
仓库使用 MIT 许可,代码可以自由使用、修改和再分发,只需保留版权声明。需要注意许可只覆盖这个仓库自身的代码,不覆盖你自行下载的模型权重,那些模型通常有各自的许可条款,商用前要单独确认,这一点仓库没有代为说明。维护方面,最后一次 push 是 2026 年 3 月,没有发布过正式 release,也没有版本号可依赖。教学仓库的更新节奏通常跟着依赖走,node-llama-cpp 的 API 变动会直接影响示例能否运行。如果你打算把某段示例代码抄进自己的项目,要接受它没有语义化版本、没有变更日志这个事实,升级时只能靠 diff 对比。
编辑结论
如果你已经会用 LangChain 或 LlamaIndex 拼出一条 RAG 链,但说不清 top-k 是怎么算出来的、chunk 重叠为什么会影响召回,这个仓库值得按 examples/00 到 06 的顺序跑一遍,重点看 03_hybrid_search 和 04_multi_query_retrieval 里 RRF 的融合权重。如果你的目标是在下个季度上线一个面向真实用户的问答服务,它不合适:仓库里只有 in-memory 向量存储,没有持久化、并发和索引重建的设计,README 也没有给出任何压测或规模数字。动手前先确认两件事:node-llama-cpp 能否在你的机器上编译通过,以及你打算用的 embedding 模型文件从哪里下载、放在哪个路径。
社区笔记