
1. 为什么你的 RAG 查询又慢又贵从 chunk 与 top_k 说起如果你正在做 RAG 应用大概率遇到过这种场景文档塞进去几百页用户问一句「这份合同里违约金怎么算」模型要么答非所问要么把整段无关内容都塞进上下文token 哗哗地烧响应还慢。问题往往不在模型本身而在检索链路——LlamaIndex 优化 LLM 查询的核心就是把「找什么」和「给模型看多少」这两件事控制住。LlamaIndex 是一个专门为 LLM 应用设计的数据框架它能帮你把散落的文档切分、向量化、建索引再通过检索器把最相关的片段喂给大模型。它适合谁适合已经跑通一个 demo、但发现效果不稳定或成本失控的 RAG 开发者。这篇教程不讲空泛概念直接带你从文档切分一路走到检索调优中间会给出可复制的配置片段并用对比实验验证 chunk 大小和 top_k 对结果的影响。我试过把同一份技术文档用不同参数跑一遍答案质量差异能到「能用」和「不能用」的区别。所以下面每一步我都会说明为什么这么设以及你可以怎么改。先明确整条链路文档 → 切分Node Parser→ 向量索引VectorStoreIndex→ 检索器Retriever→ 响应合成器Response Synthesizer→ 最终答案。查询优化发生在检索器和合成器这两层但前提是切分和索引得先合理。2. 前置准备TaoToken 接入与 LlamaIndex 环境搭建在写代码前先把模型访问这一层搞定。LlamaIndex 默认走 OpenAI 接口协议所以只要有一个兼容 OpenAI 的 Base URL 和 Key就能直接对接。这里用 TaoToken 作为模型访问入口它的接口地址是https://taotoken.net/api兼容 OpenAI 的/v1/chat/completions和/v1/embeddingsLlamaIndex 里配置api_base即可。先建虚拟环境避免依赖冲突python -m venv venv source venv/bin/activate # Windows 用 venv\Scripts\activate pip install llama-index llama-index-embeddings-openai llama-index-llms-openai安装完成后设置环境变量。注意这里 Base URL 用 TaoToken 的 API 地址Key 从控制台获取export TAOTOKEN_API_KEY你的Key export TAOTOKEN_BASE_URLhttps://taotoken.net/api如果你更习惯用.env文件也可以TAOTOKEN_API_KEYsk-xxxx TAOTOKEN_BASE_URLhttps://taotoken.net/apiKey 的获取入口在控制台的 API Keys 页面登录后新建一个即可。模型 ID 方面对话模型可以用gpt-4o-mini这类通用模型嵌入模型用text-embedding-3-small两者在 TaoToken 上都走同一个 Base URL。接下来在 Python 里初始化全局配置。LlamaIndex 的Settings对象可以统一管理 LLM 和嵌入模型避免每个组件单独传参import os from llama_index.core import Settings from llama_index.llms.openai import OpenAI from llama_index.embeddings.openai import OpenAIEmbedding api_key os.environ[TAOTOKEN_API_KEY] base_url os.environ[TAOTOKEN_BASE_URL] Settings.llm OpenAI( modelgpt-4o-mini, api_keyapi_key, api_basebase_url, temperature0.1, ) Settings.embed_model OpenAIEmbedding( modeltext-embedding-3-small, api_keyapi_key, api_basebase_url, )这里temperature0.1是为了让答案更稳定RAG 场景不需要模型发挥创意。api_base指向 TaoToken 的 API 地址LlamaIndex 会自动拼接/v1/embeddings和/v1/chat/completions。如果你用的是 Claude Code 这类编码工具做辅助开发也可以把 Base URL 和 Key 配到对应工具的 settings 里但本文聚焦 LlamaIndex 本身。配置完成后建议先跑一个最小请求验证连通性再进入索引构建。3. 可复制配置文档切分、向量索引与检索器参数这一节是整篇的核心所有片段都可以直接复制到你的项目里改。先准备一份测试文档我用一段 Markdown 技术说明代替你也可以换成自己的 PDF 或 txt。3.1 文档切分chunk_size 与 overlap 的取舍切分决定了检索的最小单元。切太大一个节点里混多个主题检索精度下降切太小上下文断裂模型拿到的信息不完整。LlamaIndex 的SentenceSplitter是默认选择它按句子边界切尽量不破坏语义from llama_index.core.node_parser import SentenceSplitter from llama_index.core import SimpleDirectoryReader documents SimpleDirectoryReader(./data).load_data() splitter SentenceSplitter( chunk_size512, chunk_overlap64, paragraph_separator\n\n, ) nodes splitter.get_nodes_from_documents(documents) print(f切分后节点数: {len(nodes)})chunk_size512是 token 数不是字符数。chunk_overlap64让相邻块有重叠避免关键句正好被切断。实测下来技术文档用 512 比较稳法律合同可以降到 256因为条款粒度更细。3.2 向量索引持久化与相似度策略建索引时把节点存进去并指定相似度计算方式from llama_index.core import VectorStoreIndex, StorageContext from llama_index.core.vector_stores import SimpleVectorStore vector_store SimpleVectorStore() storage_context StorageContext.from_defaults(vector_storevector_store) index VectorStoreIndex( nodes, storage_contextstorage_context, show_progressTrue, ) index.storage_context.persist(persist_dir./storage)persist会把索引和向量落盘下次直接load_index_from_storage加载不用重新嵌入省时间和费用。相似度默认是余弦SimpleVectorStore支持在检索时传similarity_top_k。3.3 检索器与响应合成器top_k 与 mode 配置检索器负责从索引里捞候选节点合成器负责把候选拼成 prompt 给 LLM。两者一起配置from llama_index.core.retrievers import VectorIndexRetriever from llama_index.core.query_engine import RetrieverQueryEngine from llama_index.core.response_synthesizers import get_response_synthesizer retriever VectorIndexRetriever( indexindex, similarity_top_k4, ) synthesizer get_response_synthesizer( response_modecompact, text_qa_templateNone, ) query_engine RetrieverQueryEngine( retrieverretriever, response_synthesizersynthesizer, )similarity_top_k4表示取最相似的 4 个节点。response_modecompact会先把节点压缩再合成适合节点多但上下文有限的情况如果节点少且都相关可以用refine逐条精炼。这两个参数是查询优化的主要旋钮。如果你想把配置写成 JSON 方便版本管理可以这样{ chunk_size: 512, chunk_overlap: 64, similarity_top_k: 4, response_mode: compact, embed_model: text-embedding-3-small, llm_model: gpt-4o-mini, api_base: https://taotoken.net/api }这份配置可以直接被你的加载脚本读取改参数不用动代码。4. 验证请求对比 chunk 大小与 top_k 的实际效果配置写完必须验证否则你不知道参数是不是拍脑袋定的。下面这段脚本会跑三组对比不同 chunk_size 和不同 top_k输出答案和耗时。import time from llama_index.core import VectorStoreIndex, Settings from llama_index.core.node_parser import SentenceSplitter from llama_index.core.retrievers import VectorIndexRetriever from llama_index.core.query_engine import RetrieverQueryEngine from llama_index.core.response_synthesizers import get_response_synthesizer def build_engine(documents, chunk_size, top_k): splitter SentenceSplitter(chunk_sizechunk_size, chunk_overlap64) nodes splitter.get_nodes_from_documents(documents) index VectorStoreIndex(nodes) retriever VectorIndexRetriever(indexindex, similarity_top_ktop_k) synthesizer get_response_synthesizer(response_modecompact) return RetrieverQueryEngine(retrieverretriever, response_synthesizersynthesizer) query 违约金的上限是多少 for chunk_size in [256, 512, 1024]: for top_k in [2, 4, 6]: engine build_engine(documents, chunk_size, top_k) start time.time() response engine.query(query) elapsed time.time() - start print(fchunk{chunk_size} top_k{top_k} 耗时{elapsed:.2f}s) print(f答案: {response}\n)跑完后你会看到类似规律chunk 太小256时 top_k 需要调大才能覆盖答案但噪声也变多chunk 太大1024时 top_k2 可能就够但单次请求 token 多、延迟高。512 top_k4 通常是平衡点。这个对比动作建议你在自己的数据上跑一遍因为文档类型不同最优值会漂移。验证成功的标志是答案里包含你预期的关键信息且耗时在可接受范围。如果答案总是「根据提供的信息无法回答」说明检索没捞到相关节点先调 top_k 或检查切分是否把答案切碎了。5. 常见报错排查401、local proxy failed 与 reading choices接入过程中最容易卡在几个固定报错上这里逐个拆。401 UnauthorizedKey 没传对或 Base URL 写错。检查api_key是否带了多余空格api_base是否是https://taotoken.net/api而不是带/v1的完整路径LlamaIndex 会自己拼。如果用的是环境变量确认os.environ里确实读到了值。local proxy failed / connection error通常是网络层问题不是代码问题。确认你的运行环境能正常访问taotoken.net如果是容器内运行检查 DNS 和出网策略。不要用任何非官方的网络工具直接走标准 HTTPS 即可。Error reading choices / KeyError choices说明返回体不是预期的 OpenAI 格式。常见原因是 Base URL 拼错导致请求打到了别的端点或者模型 ID 写错。把model改成 TaoToken 支持的 ID比如gpt-4o-mini再试一次。OAuth / token 过期如果你用的是带 OAuth 的工具链Key 可能有时效。重新在控制台生成一个 API Key替换环境变量后重启进程。嵌入维度不匹配换嵌入模型后旧索引不能用必须重建。text-embedding-3-small是 1536 维换模型要重新VectorStoreIndex。排查顺序建议先单独用 curl 测 Base URL 通不通再测 Python SDK最后测 LlamaIndex。这样能快速定位是网络、SDK 还是框架层的问题。6. 把查询优化固化到你的工作流参数调好后别每次手动跑。把索引持久化、配置外置、查询封装成函数是让 RAG 稳定运行的关键。索引落盘后启动时直接加载from llama_index.core import load_index_from_storage, StorageContext storage_context StorageContext.from_defaults(persist_dir./storage) index load_index_from_storage(storage_context)这样每次查询省掉嵌入开销响应更快。配置用前面的 JSON 管理改 top_k 不用改代码。查询封装成带日志的函数记录每次的 top_k、耗时和答案长度方便后续回归对比。如果你要长期跑编码类或 Agent 类任务可以考虑用 Coding Plan 这类按量方案控制成本只是验证模型效果的话模型对话页面更轻量。接入文档里有完整的 Base URL 和参数说明遇到报错先对照文档排查。最后给一个实用技巧把用户 query 先做一次改写再检索比如把「违约金怎么算」改写成「合同违约金 上限 计算方式」检索命中率会明显提升。LlamaIndex 的HyDEQueryTransform或简单的 LLM 改写都能做这一步的收益往往比调 top_k 更大。