Knowledge Base Indexing Principles
How DB-GPT indexes an uploaded document โ from raw file to searchable indexes. Intended for product / design readers. No code, but the mechanisms match the implementation.
What "indexing" means hereโ
Indexing happens at document-sync time, not at chat time. Chat only retrieves; it never re-indexes. The pipeline for every document:
upload โโโบ load file โโโบ chunk โโโบ embed โโโบ persist into one or more indexes
โ
โโโบ optionally build graph(s)
A knowledge space declares which indexes to build via index_methods (a string list). Three index
methods are selectable; "structural index" and "code graph" are capabilities layered on top
(structural index is built at retrieve time; the code/heading graph is built on top of the
KnowledgeGraph method or for GIT_REPO spaces).
| Index entry point | Code name (index_methods) | Built at sync time? | What it gives you |
|---|---|---|---|
| Vector index | VectorStore | yes | semantic similarity ranking |
| Keyword index | FullText | yes | exact keyword / BM25 hits |
| Knowledge-graph index | KnowledgeGraph | yes | graph-traversal over entities, headings, code |
| Structural index | (retrieve-time tree) | no โ rebuilt at query time | markdown-header tree / parent-child navigation |
| Code graph | (layered on KnowledgeGraph / GIT_REPO) | yes | AST of code files as function / class nodes |
In short: vector, keyword, knowledge-graph are the three persisted index methods. Structural index and code graph are two extra shapes of indexing that DB-GPT builds on top of those.
1. Structural index โ navigate by document structureโ
What it isโ
A view over the already-chunked document that arranges chunks into a tree by their Markdown
heading level (H1 โ H2 โ H3 โฆ). Every chunk already carries Header1/Header2/... metadata from
chunking (see Metadata pipeline below); the structural index simply organises those chunks into a
tree so retrieval can walk up to a parent section and then back down to its children โ instead of
treating every chunk as an isolated island.
Why it's a separate concept (even though it has no own index method)โ
During indexing, DB-GPT persists the heading path on each chunk but does not persist the tree.
At retrieve time the retriever reconstructs the tree from those HeaderN fields in memory and
walks it ("find this leaf chunk, then expand its whole parent section, then rank"). So the "structural
index" is really structural metadata + a tree-view retriever, not a separate store.
Principle (how it's built + used)โ
markdown file chunking writes HeaderN on every chunk
โโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
# Prompt Cache Design โโโบ chunk A { H1: "Prompt Cache Design" }
## Core Principle โโโบ chunk B { H1: "Prompt Cache", H2: "Core Principle" }
### Hit Condition โโโบ chunk C { H1..H3: "Hit Condition" }
## Three Strategies โโโบ chunk D { H1: "Prompt Cache", H2: "Three Strategies" }
โ
retrieve-time โผ organise into tree by H1>H2>H3
โโโ H1 Prompt Cache โโโ
โ โ
H2 Core Principle H2 Three Strategies
โ โ
H3 Hit Condition (chunk D)
(chunk C)
- Best for: "what's in this whole section?", "summarise chapter 3", parent-context expansion.
- Note: for non-Markdown documents there is no heading hierarchy, so the structural tree degrades to a flat list โ its value is concentrated on Markdown / structured docs.
2. Knowledge-graph index โ relational retrievalโ
This is the richest index, and internally it is not one graph but several that all get built when
the KnowledgeGraph method is enabled:
enable index_methods = KnowledgeGraph
โ
โโโ (a) LLM triplet graph semantic: (entity) -predicate- (entity)
โโโ (b) documentโparagraph graph structural: document โ chunk โ chunk (include / next)
โโโ (c) Markdown heading graph structural: file โ H1 โ H2 โ H3 (contains)
โโโ (d) code graph structural: repository โ file โ function/class (defines)
(a) and (b) live in the knowledge-graph store (TuGraph / Neo4j / Memgraph); (c) and (d) both land in the code-graph tables and share one builder.
2a. LLM triplet-extraction graph (semantic)โ
Each chunk is sent to an LLM with an extraction prompt that asks for (subject, predicate, object)
triplets. The returned triplets are upserted into the graph store as entity -edge- entity. This is
the classic "knowledge graph from text": it captures facts and relations that pure keyword/vector
search cannot.
chunk text: "Anthropic's prompt cache reuses the KV matrix to cut cost"
โ LLM triplet extraction prompt
โผ
(Anthropic) โโhasโโโถ (Prompt Cache)
(Prompt Cache) โโreusesโโโถ (KV matrix)
(Prompt Cache) โโcutsโโโถ (cost)
Retrieval traverses edges: an entity hit expands to its neighbours, and each edge knows which chunk
it came from (via a _chunk_id edge property) so answers can still be cited back to source.
2b. Documentโparagraph graphโ
Independently of triplets, the KG store also builds a structural skeleton:
document -include- chunk, chunk -include- chunk (parent/child when chunks nest), and
chunk -next- chunk (reading order). This lets retrieval hop from an entity โ the chunk that
contains it โ the document โ neighbouring chunks. (When the community-summary variant is enabled, it
also runs community detection and summarises each community with an LLM.)
2c. Markdown heading graphโ
For .md files under a KnowledgeGraph-method space, the builder reconstructs each file's content
from its stored chunks, scans ^(#{1,6})\s+ heading lines (skipping headings inside fenced code
blocks), and emits heading vertices connected by contains edges:
file โ H1 โ H2 โ H3. This is the graph analogue of the structural index above โ queryable via edge
traversal instead of an in-memory tree.
2d. Code graph (ไปฃ็ ๅพ่ฐฑ)โ
This is the part most worth understanding. The same RepoGraphBuilder that builds the Markdown
heading graph is also able to build a graph from source code, and it is what powers code-level
retrieval over a GIT_REPO space (or over code files uploaded into a KnowledgeGraph-method space).
Principle โ parse with an AST, not with regex:
walk repo
repository โโcontainsโโโถ file
โ language inferred from extension
โโโโโโโโโโโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ โ โ
.py/.java/.js/.ts/ .md/.markdown other languages
.go/.rs/.c/.cpp (heading graph 2c) (regex fallback)
โ โ
โผ parse with tree-sitter โผ regex def/class lines
function_definition / class_definition / ... file โโdefinesโโโถ function|class
โ
โผ for each AST target node
create a vertex: type=function|class|method|class|interface|impl|struct|enum|trait
name, file_path, start_line, end_line, language
add edge: file โโdefinesโโโถ <that vertex>
- Nodes created:
repository,file, and code nodesfunction/class/method/interface/impl/struct/enum/trait. - Edges created by the builder:
contains(repoโfile, fileโheading) anddefines(fileโcode node). - Parser: tree-sitter for Python / Java / JavaScript /
TypeScript / Go / Rust / C / C++. Each target AST node type (e.g.
function_definition,class_definition,method_declaration,interface_declaration,struct_item) is mapped to a semantic node type. Languages without a tree-sitter grammar here fall back to a regexdef/classscan that still emitsfile -defines-> function|classedges. - What it buys you: "where is
apply_anthropic_cache_controldefined?", "list all methods of classPromptCache" โ answered by vertex lookup +definesedge traversal, instead of fuzzy vector match.
Honest caveat about edges. The retrieval layer also supports
CALLS/INHERITS/IMPLEMENTS/IMPORTS/REFERENCESedges (for call-chain and class-hierarchy queries). The currentRepoGraphBuilder, however, only emitscontainsanddefines. So call-chain and inheritance traversals only return data when those edges were produced by another builder; for graphs built purely by this builder they will be empty. Call it out when designing features that depend on call graphs.
3. Vector index โ semantic similarityโ
chunk โโembedding modelโโโบ [0.12, -0.34, 0.56, โฆ] (e.g. 1024-d) โโโบ vector store
query โโembedding modelโโโบ query vector โโcosine similarityโโโบ top-K chunks
Principleโ
Each chunk is run through an embedding model that maps text to a high-dimensional vector; the vector + the chunk's metadata are bulk-upserted into a vector database (Chroma / Milvus / PGVector / Qdrant / Weaviate / โฆ) as chosen by the space config. At query time the question is embedded the same way and the store returns the chunks whose vectors are nearest (cosine / inner-product) to the query vector.
Why it's the defaultโ
Vector search catches paraphrase: "caching mechanism" still retrieves "prompt cache" because the vectors live nearby even though no words match. It doesn't need any structural information about the document and works on every file type.
Weaknessโ
A vector averages a whole chunk into one point โ if the chunk mixes two topics it blurs; if the exact term matters (an API name, an error code) it may miss. That's why keyword + vector are usually enabled together.
4. Keyword index โ exact-term match (BM25)โ
chunk โโindexed as text in search engineโโโบ BM25 term index
query โโtokenizeโโโบ "terms" โโBM25 score over chunksโโโบ top-K chunks
Principleโ
Chunks are stored verbatim as text documents in a full-text search engine (Elasticsearch with BM25
scoring; k1 / b tunable). Retrieval tokenises the query and returns chunks ranked by BM25 score โ
i.e. by term frequency ร inverse document frequency, with length normalisation. No embedding, no LLM,
pure lexical matching.
When it winsโ
Precise identifiers: apply_anthropic_cache_control, ERR_CONN_REFUSED, a config key. Vector search
may dilute such exact tokens into "similar" but wrong neighbours; BM25 returns the exact hit and
ranks it at the very top.
Weaknessโ
It cannot match synonyms or paraphrase ("caching" will not find "cache" reliably once tokenised, and definitely won't find "็ผๅญ"). It is purely complementary to the vector index.
How the four combine at retrieval timeโ
user question
โ
โโโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโโ
โผ โผ โผ
vector keyword (BM25) knowledge graph
semantic exact term / structural / code
hits hits hits
โ โ โ
โโโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโโ
โผ
fusion + rerank (structural tree can expand parents here)
โ
โผ
Top-K chunks โโโบ into the LLM prompt โโโบ cited answer
Chunking & metadata pipeline (the foundation under all four)โ
All four indexes operate on the same chunks, so chunking quality dominates retrieval quality.
Chunking strategies
- Markdown header split (
H1โH2โH3): preserves semantic boundary + heading path โ best for tech docs. - Size split (fixed chars): for plain text without structure.
- Separator split (paragraph / newline / table cell): for tabular / unstructured content.
Metadata written on every chunk during indexing (used later by every index and by citation tracing):
upload chunking storage
โโโโโโ โโโโโโโโ โโโโโโโ
file_name: prompt.md โ chunk_id: 101 โ vector: [0.12, ...]
file_path: /docs/... โ doc_name: prompt.md โ content: "Prefix..."
doc_type: DOCUMENT โ chunk_type: text โ meta_info: {
index_methods: โ file_path: /docs/... โ "Header1": "Prompt Cache",
[Vector, FullText, โ header_path: H1>H2>H3 โ "Header2": "Core Principle",
KG] โ "Header3": "Hit Condition"
โ }
Key design principlesโ
- Three persisted index methods โ
VectorStore,FullText,KnowledgeGraphโ combinable per space. - Structural index is metadata + a retrieve-time tree, not a separate store; it relies on the
HeaderNwritten during chunking. - The knowledge-graph index is a family of graphs โ LLM triplets, documentโparagraph skeleton, Markdown headings, and the code graph (AST) โ that share the build path.
- Code graph parses with tree-sitter, emitting
function/classnodes +definesedges; it is what makes code-level question-answering precise. Mind that call/inheritance edges are not populated by the current builder. - One chunking, four indexes โ chunk quality and the
HeaderNmetadata underpin everything; chunk per doc type (Markdownโheaders, textโsize, tableโseparator). - Index/chat decoupled โ indexing runs at sync time; chat only retrieves.