跪拜 Guibai
← All articles
Database · Frontend

Feeding a 1.5M-Character Martial-Arts Epic into a Vector Database, Chapter by Chapter

By bonechips ·
Read original on juejin.cn ↗ Google Translate ↗ Alt translation

RAG demos often skip the ingestion plumbing, but chunk boundaries, overlap, and metadata schema are where real retrieval quality is won or lost. A 50-character overlap is the difference between finding "凌波微步" and missing it entirely, and chapter-level metadata turns a semantic haystack into a structured, citable knowledge base.

Summary

The pipeline starts with LangChain's EPubLoader, which parses the novel's internal chapter markers to produce one Document per chapter instead of one monolithic blob. A RecursiveCharacterTextSplitter then breaks each chapter into 500-character chunks, with a 50-character overlap that prevents martial-arts move names from being severed across chunk boundaries. Each chunk is sent to an embedding API in parallel via Promise.all, and the resulting 1024-dimension vectors land in a Milvus collection that also stores book ID, chapter number, and chunk index.

An IVF_FLAT index with 1,024 clusters makes similarity search sub-linear, so queries scan only nearby clusters rather than the entire dataset. The collection schema deliberately carries structured metadata alongside the vectors, so a retrieval result can report not just relevant text but its exact chapter location. The entire ingestion runs chapter by chapter to keep memory usage low and to preserve chapter-level provenance on every chunk.

Takeaways
EPubLoader with splitChapters: true parses EPUB chapter markers to produce one Document per chapter, preserving structural boundaries that a flat text dump would lose.
RecursiveCharacterTextSplitter configured at chunkSize: 500 and chunkOverlap: 50 prevents named entities from being cut at chunk edges; the overlap guarantees any 4-character term appears intact in at least one chunk.
Processing chapter by chapter avoids loading the entire book into memory and attaches a chapter_num field to every chunk for structured retrieval.
Promise.all parallelizes embedding API calls for all chunks in a chapter, then a single client.insert() batch-writes the vectors to Milvus, cutting network round-trips.
An IVF_FLAT index with nlist: 1024 clusters queries in O(log n) by searching only nearby clusters, making millisecond response times feasible on hundreds of thousands of vectors.
The Milvus collection schema includes book_id, chapter_num, and index fields so retrieval results carry structured provenance, not just raw text.
Conclusions

Chunk overlap is often treated as a tuning knob, but for domain-specific text like wuxia novels where multi-character named entities are dense, it becomes a hard correctness requirement — without it, martial-arts move names simply disappear from the index.

The decision to embed chapter-level metadata directly into the vector database schema, rather than keeping it in a separate relational store, means a single query can return both semantic matches and their exact book location without a join.

Running embedding requests in parallel per chapter is a pragmatic middle ground: it avoids the latency of sequential calls without the complexity of full-streaming backpressure that a book-wide fan-out would require.

Concepts & terms
RecursiveCharacterTextSplitter
A LangChain text splitter that breaks documents by recursively splitting on a hierarchy of separators (paragraphs, sentences, words) until chunks fit within a specified size, with configurable overlap between adjacent chunks.
IVF_FLAT index
Inverted File Flat index: a vector index that partitions the dataset into nlist clusters via K-Means, then at query time searches only the nearest clusters rather than the full dataset, trading a small accuracy loss for a large speed gain.
Embedding dimension (1024)
The length of the fixed-size vector that an embedding model outputs for any input text. A 1024-dimension vector means each text chunk is represented as 1,024 floating-point numbers capturing its semantic meaning.
Source: juejin.cn ↗ Google Translate ↗ Backup ↗