深入解析 LlamaIndex KeywordTableIndex:基于关键词表的轻量级索引与查询原理

发布时间:2026/9/12 13:43:01
深入解析 LlamaIndex KeywordTableIndex:基于关键词表的轻量级索引与查询原理
深入解析 LlamaIndex KeywordTableIndex基于关键词表的轻量级索引与查询原理【免费下载链接】llama_indexLlamaIndex is the document processing platform for AI项目地址: https://gitcode.com/GitHub_Trending/ll/llama_indexKeywordTableIndex 是 LlamaIndex 中一种基于关键词映射的索引结构其设计灵感来自哈希表hash table构建时从文本块中提取关键词查询时再从查询语句中提取关键词并回表检索。本文以仓库内 keyword_table 模块 README 为骨架结合llama-index-core的源码实现完整讲解其索引构建流程、default/simple/rake三种查询模式、核心参数、复杂度与成本特征并给出可直接运行的代码示例帮助你在无需向量化模型或强算力的场景下快速搭建基于关键词匹配的检索问答能力。KeywordTableIndex 是什么一种哈希表式的倒排索引在 LlamaIndex 的索引家族中KeywordTableIndex走的是一条与传统向量索引如VectorStoreIndex截然不同的路线它不计算文本的稠密向量而是将每个文本块chunk表示为若干关键词再用一张关键词 → 文本块 ID 集合的表来组织数据。从源码看这张表的数据结构定义在 data_structs.pydataclass class KeywordTable(IndexStruct): A table of keywords mapping keywords to text chunks. table: Dict[str, Set[str]] field(default_factorydict) def add_node(self, keywords: List[str], node: BaseNode) - None: Add text to table. for keyword in keywords: if keyword not in self.table: self.table[keyword] set() self.table[keyword].add(node.node_id)table是一个从关键词key到文本块 ID 集合value的映射语义上等价于搜索引擎中的倒排索引给定一个关键词可以立刻定位到所有包含它的文本块而无需遍历全部文档。KeywordTable还提供了node_ids全部文本块 ID、keywords全部关键词、size关键词数量等便捷属性供检索与调试使用。这种设计的价值在于不需要嵌入embedding模型默认模式下依赖 LLM 提取关键词simple/rake模式甚至可以完全不调用模型构建与查询透明可解释关键词表可以直接查看与审计适合关键词特征明显的语料如代码片段、结构化短文本、FAQ 列表等。索引构建流程分块 → 提取关键词 → 写入关键词表KeywordTableIndex的构建过程在其基类BaseKeywordTableIndex中实现核心代码位于 base.py。整体流程分三步分块chunking输入文档先被切分为更小的文本块。README 明确说明索引构建时first takes in a dataset of text documents as input, and chunks them up into smaller document chunks。关键词提取对每个文本块调用关键词提取器默认是 LLM抽取出与该块相关的关键词集合。README 特别指出关键词可以是短语如new york city而不仅是单词。写入关键词表将关键词与该文本块 ID 建立映射写入KeywordTable。对应地_add_nodes_to_index的实现base.py逐节点提取关键词并调用add_nodedef _add_nodes_to_index(self, index_struct, nodes, show_progressFalse): nodes_with_progress get_tqdm_iterable(nodes, show_progress, Extracting keywords from nodes) for n in nodes_with_progress: keywords self._extract_keywords(n.get_content(metadata_modeMetadataMode.LLM)) index_struct.add_node(list(keywords), n)需要注意两个实现细节关键词提取基于n.get_content(metadata_modeMetadataMode.LLM)的内容即默认以 LLM 视角可读的节点文本为准支持异步版本_async_add_nodes_to_index当use_asyncTrue时通过run_async_tasks并行执行适合节点数量较大的场景。默认关键词提取模板默认模式使用 LLM 提取关键词模板定义在 default_prompts.pySome text is provided below. Given the text, extract up to {max_keywords} keywords from the text. Avoid stopwords. --------------------- {text} --------------------- Provide keywords in the following comma-separated format: KEYWORDS: keywordsLLM 的输出需以KEYWORDS:开头随后是逗号分隔的关键词列表。解析逻辑位于 utils.py 的extract_keywords_given_response去除前后空白、剥离start_token前缀、按逗号切分并统一转为小写。测试 test_utils.py 给出了明确的解析行为验证例如response KEYWORDS: foo, bar, foobar keywords extract_keywords_given_response(response, start_tokenKEYWORDS:) assert keywords {foo, bar, foobar}一个容易被忽略的细节是解析得到的关键词集合还会经过expand_tokens_with_subtokens处理indices/utils.py——多词短语会被拆分为子词同时过滤停用词。例如foo bar会被扩展为{foo bar, foo, bar}。这保证了查询时无论用户命中短语还是短语中的单个词都能检索到对应文本块是提升召回率的关键机制。max_keywords_per_chunk控制每个文本块的关键词数量BaseKeywordTableIndex.__init__暴露了max_keywords_per_chunk: int 10参数并在初始化时通过partial_format(max_keywords...)将最大值注入到提取模板中与 LLM 提取质量、表大小和查询成本直接相关值越大单个文本块的召回覆盖面越广但关键词表越大、构建成本越高值越小构建更轻量但可能遗漏低频但关键的信息。三种关键词提取引擎GPT / Simple(Regex) / RAKEkeyword_table模块提供了三个索引类对应三种关键词提取引擎全部继承自BaseKeywordTableIndex通过重写_extract_keywords实现差异化init.py 统一导出索引类提取引擎依赖源码位置KeywordTableIndexLLMGPT 等关键词提取需要配置 LLMbase.pySimpleKeywordTableIndex正则表达式 停用词过滤无额外依赖simple_base.pyRAKEKeywordTableIndexRAKE 关键词提取器nltk、rake_nltkrake_base.pyGPT 模式默认KeywordTableIndex._extract_keywords调用self._llm.predict(keyword_extract_template, texttext)再通过extract_keywords_given_response解析输出base.py。异步场景使用apredict。该模式能理解语义、提取多词短语如new york city质量最高但构建与查询都需要调用 LLM产生 token 成本。旧版命名GPTKeywordTableIndex作为兼容别名保留。Simple正则模式SimpleKeywordTableIndex完全不调用模型。其simple_extract_keywords实现utils.py用正则\w切出全部单词 token转为小写过滤globals_helper.stopwords中的停用词再通过Counter.most_common(max_keywords)取出现频率最高的关键词tokens [t.strip().lower() for t in re.findall(r\w, text_chunk)] if filter_stopwords: tokens [t for t in tokens if t not in globals_helper.stopwords] token_counts Counter(tokens) keywords [keyword for keyword, count in token_counts.most_common(max_keywords)]该模式零成本、零依赖、速度快但只能处理单词级别的高频词无法理解语义也无法识别多词短语。适合对构建成本敏感、语料关键词分布明显的场景。RAKE 模式RAKEKeywordTableIndex使用业界流行的 RAKERapid Automatic Keyword Extraction算法。实现utils.py依赖nltk与rake_nltk两个库未安装时会抛出明确的ImportError提示pip install nltk/pip install rake_nltkr Rake( sentence_tokenizernltk.tokenize.sent_tokenize, word_tokenizernltk.tokenize.wordpunct_tokenize, ) r.extract_keywords_from_text(text_chunk) keywords r.get_ranked_phrases()[:max_keywords]RAKE 通过分析词共现与词频权重来评分关键词能够产出带排序的多词短语。当expand_with_subtokensTrue时短语同样会被拆分为子词以提升召回。相比 GPT 模式它不产生 token 成本相比 Simple 模式它保留了短语级别的语义粒度是低成本 中等质量的折中选择。查询流程关键词匹配、排序截断与 create-and-refine 应答README 定义了三种查询模式query modedefault、simple、rake分别对应三个 Retriever 类由BaseKeywordTableIndex.as_retriever统一分发base.pyretriever_modedefault→KeywordTableGPTRetrieverLLM 提取查询关键词retriever_modesimple→KeywordTableSimpleRetriever正则提取retriever_moderake→KeywordTableRAKERetrieverRAKE 提取三种 Retriever 共用同一套检索核心逻辑BaseKeywordTableRetriever._retrieveretrievers.py# go through text chunks in order of most matching keywords chunk_indices_count: Dict[str, int] defaultdict(int) keywords [k for k in keywords if k in self._index_struct.keywords] for k in keywords: for node_id in self._index_struct.table[k]: chunk_indices_count[node_id] 1 sorted_chunk_indices sorted( chunk_indices_count.keys(), keylambda x: chunk_indices_count[x], reverseTrue, ) sorted_chunk_indices sorted_chunk_indices[: self.num_chunks_per_query] sorted_nodes self._docstore.get_nodes(sorted_chunk_indices)流程与 README 描述完全一致提取查询关键词从查询语句中提取关键词模式不同提取器不同回表匹配只保留关键词表中存在的关键词逐个查表累加每个文本块命中的关键词数量排序截断按命中关键词数从高到低排序截断前num_chunks_per_query默认 10个文本块作为候选组装应答对候选文本块采用_create and refine先创建后精炼范式生成答案——先用第一个文本块作为上下文构造初始答案再依次喂入后续文本块进行精炼精炼的结果可能是保持原答案、小幅修改或整体重写。这种先粗召回、再逐块精炼的方式在候选块数量可控的前提下兼顾了答案质量。README 特别强调查询阶段对 LLM 的调用次数被限制为 O(d)其中d num_chunks_per_query这直接决定了单次查询的 LLM 成本上限。查询侧的关键词提取模板默认查询模式使用独立于构建模板的DEFAULT_QUERY_KEYWORD_EXTRACT_TEMPLATEdefault_prompts.py它在提示词中明确要求提取最适合用于检索问题答案的关键词以提升查询关键词的命中质量A question is provided below. Given the question, extract up to {max_keywords} keywords from the text. Focus on extracting the keywords that we can use to best lookup answers to the question. Avoid stopwords. --------------------- {question} --------------------- Provide keywords in the following comma-separated format: KEYWORDS: keywords核心参数速查以下参数贯穿索引构建与查询阶段默认值与语义均以当前仓库源码为准参数默认值作用位置说明max_keywords_per_chunk10构建时base.py每个文本块最多提取的关键词数注入构建模板keyword_extract_templateDEFAULT_KEYWORD_EXTRACT_TEMPLATE构建时构建关键词提取的提示模板可自定义query_keyword_extract_templateDEFAULT_QUERY_KEYWORD_EXTRACT_TEMPLATE查询时查询关键词提取的提示模板可自定义max_keywords_per_query10查询时retrievers.py从查询中最多提取的关键词数num_chunks_per_query10查询时retrievers.py每次查询最多检索的文本块数量即 README 中的duse_asyncFalse构建时是否异步提取关键词show_progressFalse构建时是否显示 tqdm 进度条retriever_modedefault查询时default/simple/rake三选一其中max_keywords_per_query与num_chunks_per_query共同决定单次查询的召回范围与 LLM 调用开销max_keywords_per_query控制匹配广度num_chunks_per_query控制送入精炼流程的候选块数量。完整使用示例基础用法默认 GPT 模式from llama_index.core import KeywordTableIndex, SimpleDirectoryReader # 构建索引 documents SimpleDirectoryReader(data).load_data() index KeywordTableIndex.from_documents(documents) # 查询 query_engine index.as_query_engine() response query_engine.query(question text)切换查询模式与调整参数from llama_index.core import KeywordTableIndex index KeywordTableIndex.from_documents(documents) # 使用 simple 正则模式检索不调用 LLM 提取查询关键词 simple_engine index.as_query_engine( retriever_modesimple, max_keywords_per_query5, num_chunks_per_query6, ) response simple_engine.query(When was New York City founded?) # 使用 rake 模式 rake_engine index.as_query_engine(retriever_moderake) response rake_engine.query(question text)无 LLM 的纯本地方案Simple / RAKE 索引如果希望构建与查询全程不依赖 LLM可分别使用SimpleKeywordTableIndex与RAKEKeywordTableIndexfrom llama_index.core.indices.keyword_table import ( SimpleKeywordTableIndex, RAKEKeywordTableIndex, ) # 正则模式零依赖 index SimpleKeywordTableIndex.from_documents(documents) response index.as_query_engine().query(question text) # RAKE 模式需先安装 nltk 与 rake_nltk index RAKEKeywordTableIndex.from_documents(documents) response index.as_query_engine().query(question text)RAKE 模式依赖安装可参考 rake_base.py 与 utils.py 中的错误提示pip install nltk、pip install rake_nltk。增量插入与删除索引构建完成后还可以通过基类实现进行增量维护base.py_insert(nodes)对新节点重新提取关键词并追加到关键词表_delete_node(node_id)从所有关键词对应的节点集合中移除该节点当某个关键词下不再有节点时该关键词会一并从表中删除。复杂度与成本分析README 的 FAQ 部分给出了明确的复杂度结论这里结合源码逐一解读最坏情况查询耗时为O(k * c)其中k是提取出的关键词数量c是每次查询涉及的文本块数量。从_retrieve的实现看耗时主要来自两处对每个关键词遍历其映射的文本块 ID 累加计数以及对命中块按计数排序。关键词表本身就是哈希映射结构因此单次查表是常数级操作。LLM 调用次数被严格限制为O(d)其中d num_chunks_per_query。由于 _create and refine 范式对每个候选文本块执行一次精炼调用候选块数量上限即 LLM 调用上限。README 给出的参考当num_chunks_per_query10时单次查询的估算成本约为$0.40。需要说明的是这是原 README 基于当时的模型定价给出的粗略估算实际费用取决于所用 LLM 的型号与上下文长度参数调小如num_chunks_per_query3可显著降低单次查询成本。与向量索引的取舍KeywordTableIndex的查询在关键词层面精确匹配不存在向量相似度计算的开销也不依赖嵌入模型其代价是语义泛化能力弱于向量索引——查询语句必须与文档中的关键词存在字面交集才能召回。因此它更适合关键词特征明确的语料以及低成本、可解释、快速上线的检索场景。源码导航与进一步阅读如需深入理解或二次开发可按以下路径继续探索当前仓库模块入口与导出keyword_table/init.py含全部索引类、Retriever 类及旧版兼容别名索引基类与默认 GPT 实现keyword_table/base.py检索与精炼流程keyword_table/retrievers.py三种关键词提取算法keyword_table/utils.py关键词表数据结构data_structs.py默认提示模板default_prompts.py单元测试tests/indices/keyword_table/test_base.py、test_retrievers.py、test_utils.py其中 test_utils.py 覆盖了关键词解析与子词扩展的行为断言测试目录中的用例是理解各模式行为边界的捷径它们以可断言的方式固定了关键词解析规则、检索排序逻辑与算法输出格式是比 README 更精确的行为规范。【免费下载链接】llama_indexLlamaIndex is the document processing platform for AI项目地址: https://gitcode.com/GitHub_Trending/ll/llama_index创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考