跪拜 Guibai
← All articles
Agent

A Two-Step RAG Walkthrough: From a Handwritten Story to Crawling Juejin Articles

By 为你学会写情书 ·
Read original on juejin.cn ↗ Google Translate ↗ Alt translation

The pipeline shows how few moving parts a working RAG system actually needs — a loader, a splitter, an embedder, a vector store, and a prompt template — and that the same code runs against any OpenAI-compatible API, including Chinese models. For a developer prototyping a doc-Q&A feature, the whole thing fits in a single script.

Summary

RAG stops LLMs from hallucinating by retrieving relevant text before generation. The pipeline starts with a Cheerio-based web loader that scrapes only the article body via a CSS selector, then hands the raw text to RecursiveCharacterTextSplitter. That splitter first breaks on sentence-ending punctuation and greedily merges sentences until a chunk nears 400 characters, with a 50-character overlap to keep context from being severed at boundaries.

Vectors land in MemoryVectorStore, which runs cosine similarity between the question embedding and every stored chunk, returning the top K matches. Those chunks get spliced into a prompt template, and the LLM answers from the provided context rather than from stale training data. The same code works with Qwen, DeepSeek, or any domestic model that exposes an OpenAI-compatible endpoint — swap the base URL and key, nothing else changes.

A comparison table lays out five LangChain splitters side by side. RecursiveCharacterTextSplitter is the only one that cuts on semantic boundaries first and then merges; the others use fixed windows that can slice through mid-sentence, making the recursive variant the default choice for most RAG setups.

Takeaways
CheerioWebBaseLoader with a CSS selector strips navigation and comments, pulling only the article body paragraphs into the pipeline.
RecursiveCharacterTextSplitter cuts on sentence-ending punctuation first, then greedily merges sentences until a chunk approaches chunkSize, so chunks stay semantically intact.
chunkOverlap prevents information from being cut in half at chunk boundaries by duplicating a few characters across adjacent chunks.
MemoryVectorStore keeps all vectors in RAM and uses cosine similarity to rank chunks; it is meant for prototyping, not production.
Domestic models like Qwen and DeepSeek expose OpenAI-compatible endpoints, so changing baseURL and apiKey is the only code change needed.
The full RAG chain is seven steps: load, split, embed, store, retrieve, augment the prompt, and generate.
Conclusions

The article treats RecursiveCharacterTextSplitter's 'cut-then-merge' strategy as the key differentiator from other splitters, which is a useful mental model for debugging chunk quality.

Running the entire vector store in memory with MemoryVectorStore makes the example instantly reproducible without infrastructure, but the article never flags the obvious scaling limit — it works for one article, not a corpus.

The OpenAI-compatible API note is a practical bridge: Western developers often assume LangChain.js ties them to OpenAI, but the ecosystem has converged on that interface.

Concepts & terms
RAG (Retrieval-Augmented Generation)
A pattern that retrieves relevant documents or chunks before generation, stuffing them into the prompt so the LLM answers from supplied context instead of relying solely on its training data.
RecursiveCharacterTextSplitter
A LangChain splitter that first breaks text on a prioritized list of separators (e.g., periods, commas), then greedily merges pieces until a chunk nears chunkSize, preserving sentence boundaries.
Cosine similarity
A measure of vector closeness ranging from -1 to 1; in RAG retrieval, a user question is embedded and compared against stored document vectors, with scores near 1 indicating high semantic similarity.
MemoryVectorStore
An in-memory vector database in LangChain that stores embeddings in RAM and performs similarity search; suitable for demos and prototyping but not for persistent or large-scale use.
Source: juejin.cn ↗ Google Translate ↗ Backup ↗