[{"data":1,"prerenderedAt":1617},["ShallowReactive",2],{"header-counts":3,"playbook-onboarding\u002Frag-pipeline-build":6,"footer-counts":1616},{"tools":4,"reviews":5},65,7,{"id":7,"title":8,"body":9,"category":1599,"cover":1600,"description":1601,"extension":1602,"meta":1603,"navigation":224,"path":1604,"published":1605,"relatedTools":1606,"seo":1610,"stem":1611,"tags":1612,"updated":1605,"__hash__":1615},"playbook\u002Fplaybook\u002Fonboarding\u002Frag-pipeline-build.md","RAG 管道从零搭建：文档问答系统实战",{"type":10,"value":11,"toc":1572},"minimark",[12,16,32,36,47,50,139,143,175,179,183,325,329,388,395,399,615,619,762,765,768,772,925,929,932,1032,1036,1039,1304,1307,1310,1396,1399,1403,1409,1415,1426,1430,1434,1445,1449,1453,1464,1468,1472,1486,1489,1568],[13,14,15],"h2",{"id":15},"适用场景",[17,18,19,23,26,29],"ul",{},[20,21,22],"li",{},"公司内部知识库问答（HR 政策、技术文档、FAQ）",[20,24,25],{},"法律\u002F医疗文档检索 + 问答",[20,27,28],{},"产品手册智能客服",[20,30,31],{},"个人笔记\u002F文献智能检索",[13,33,35],{"id":34},"rag-架构总览","RAG 架构总览",[37,38,44],"pre",{"className":39,"code":41,"language":42,"meta":43},[40],"language-text","文档 → 切分 → Embedding → 存入向量数据库\n                                    ↓\n用户提问 → Embedding → 检索 Top-K → 重排序 → LLM 生成回答\n","text","",[45,46,41],"code",{"__ignoreMap":43},[13,48,49],{"id":49},"技术选型",[51,52,53,69],"table",{},[54,55,56],"thead",{},[57,58,59,63,66],"tr",{},[60,61,62],"th",{},"组件",[60,64,65],{},"选型",[60,67,68],{},"理由",[70,71,72,84,95,106,117,128],"tbody",{},[57,73,74,78,81],{},[75,76,77],"td",{},"框架",[75,79,80],{},"LlamaIndex",[75,82,83],{},"比 LangChain 更专注 RAG",[57,85,86,89,92],{},[75,87,88],{},"Embedding",[75,90,91],{},"BGE-large-zh-v1.5",[75,93,94],{},"中文效果最佳，开源免费",[57,96,97,100,103],{},[75,98,99],{},"向量数据库",[75,101,102],{},"ChromaDB",[75,104,105],{},"轻量，原型够用",[57,107,108,111,114],{},[75,109,110],{},"重排序",[75,112,113],{},"bge-reranker-large",[75,115,116],{},"显著提升准确率",[57,118,119,122,125],{},[75,120,121],{},"LLM",[75,123,124],{},"GPT-4o",[75,126,127],{},"性价比好，支持国内中转",[57,129,130,133,136],{},[75,131,132],{},"文档解析",[75,134,135],{},"Unstructured",[75,137,138],{},"支持 PDF\u002FWord\u002FHTML",[13,140,142],{"id":141},"第一步环境准备","第一步：环境准备",[37,144,148],{"className":145,"code":146,"language":147,"meta":43,"style":43},"language-bash shiki shiki-themes github-light github-dark","pip install llama-index chromadb sentence-transformers unstructured\n","bash",[45,149,150],{"__ignoreMap":43},[151,152,155,159,163,166,169,172],"span",{"class":153,"line":154},"line",1,[151,156,158],{"class":157},"sScJk","pip",[151,160,162],{"class":161},"sZZnC"," install",[151,164,165],{"class":161}," llama-index",[151,167,168],{"class":161}," chromadb",[151,170,171],{"class":161}," sentence-transformers",[151,173,174],{"class":161}," unstructured\n",[13,176,178],{"id":177},"第二步文档加载与切分","第二步：文档加载与切分",[180,181,182],"p",{},"切分是 RAG 效果的决定性因素。",[37,184,188],{"className":185,"code":186,"language":187,"meta":43,"style":43},"language-python shiki shiki-themes github-light github-dark","from llama_index.core import SimpleDirectoryReader\nfrom llama_index.core.node_parser import SentenceSplitter\n\n# 1. 加载文档\ndocuments = SimpleDirectoryReader(\".\u002Fdocs\").load_data()\n\n# 2. 切分\nsplitter = SentenceSplitter(\n    chunk_size=512,      # 每块 512 token\n    chunk_overlap=50,    # 重叠 50 token，保证上下文连贯\n)\nnodes = splitter.get_nodes_from_documents(documents)\n","python",[45,189,190,206,219,226,233,251,256,261,272,291,308,314],{"__ignoreMap":43},[151,191,192,196,200,203],{"class":153,"line":154},[151,193,195],{"class":194},"szBVR","from",[151,197,199],{"class":198},"sVt8B"," llama_index.core ",[151,201,202],{"class":194},"import",[151,204,205],{"class":198}," SimpleDirectoryReader\n",[151,207,209,211,214,216],{"class":153,"line":208},2,[151,210,195],{"class":194},[151,212,213],{"class":198}," llama_index.core.node_parser ",[151,215,202],{"class":194},[151,217,218],{"class":198}," SentenceSplitter\n",[151,220,222],{"class":153,"line":221},3,[151,223,225],{"emptyLinePlaceholder":224},true,"\n",[151,227,229],{"class":153,"line":228},4,[151,230,232],{"class":231},"sJ8bj","# 1. 加载文档\n",[151,234,236,239,242,245,248],{"class":153,"line":235},5,[151,237,238],{"class":198},"documents ",[151,240,241],{"class":194},"=",[151,243,244],{"class":198}," SimpleDirectoryReader(",[151,246,247],{"class":161},"\".\u002Fdocs\"",[151,249,250],{"class":198},").load_data()\n",[151,252,254],{"class":153,"line":253},6,[151,255,225],{"emptyLinePlaceholder":224},[151,257,258],{"class":153,"line":5},[151,259,260],{"class":231},"# 2. 切分\n",[151,262,264,267,269],{"class":153,"line":263},8,[151,265,266],{"class":198},"splitter ",[151,268,241],{"class":194},[151,270,271],{"class":198}," SentenceSplitter(\n",[151,273,275,279,281,285,288],{"class":153,"line":274},9,[151,276,278],{"class":277},"s4XuR","    chunk_size",[151,280,241],{"class":194},[151,282,284],{"class":283},"sj4cs","512",[151,286,287],{"class":198},",      ",[151,289,290],{"class":231},"# 每块 512 token\n",[151,292,294,297,299,302,305],{"class":153,"line":293},10,[151,295,296],{"class":277},"    chunk_overlap",[151,298,241],{"class":194},[151,300,301],{"class":283},"50",[151,303,304],{"class":198},",    ",[151,306,307],{"class":231},"# 重叠 50 token，保证上下文连贯\n",[151,309,311],{"class":153,"line":310},11,[151,312,313],{"class":198},")\n",[151,315,317,320,322],{"class":153,"line":316},12,[151,318,319],{"class":198},"nodes ",[151,321,241],{"class":194},[151,323,324],{"class":198}," splitter.get_nodes_from_documents(documents)\n",[326,327,328],"h3",{"id":328},"切分策略选择",[51,330,331,343],{},[54,332,333],{},[57,334,335,338,341],{},[60,336,337],{},"策略",[60,339,340],{},"chunk_size",[60,342,15],{},[70,344,345,356,366,377],{},[57,346,347,350,353],{},[75,348,349],{},"小块",[75,351,352],{},"256",[75,354,355],{},"FAQ、短问答",[57,357,358,361,363],{},[75,359,360],{},"中块",[75,362,284],{},[75,364,365],{},"通用文档（推荐起步值）",[57,367,368,371,374],{},[75,369,370],{},"大块",[75,372,373],{},"1024",[75,375,376],{},"长文档、技术手册",[57,378,379,382,385],{},[75,380,381],{},"按段落",[75,383,384],{},"不固定",[75,386,387],{},"保持语义完整",[180,389,390,394],{},[391,392,393],"strong",{},"关键","：chunk_overlap 设 chunk_size 的 10%，避免切断语义。",[13,396,398],{"id":397},"第三步embedding-与入库","第三步：Embedding 与入库",[37,400,402],{"className":185,"code":401,"language":187,"meta":43,"style":43},"from llama_index.embeddings.huggingface import HuggingFaceEmbedding\nfrom llama_index.vector_stores.chroma import ChromaVectorStore\nimport chromadb\n\n# 1. 用 BGE 中文模型\nembed_model = HuggingFaceEmbedding(\n    model_name=\"BAAI\u002Fbge-large-zh-v1.5\",\n    max_length=512,\n)\n\n# 2. 创建向量数据库\ndb = chromadb.PersistentClient(path=\".\u002Fchroma_db\")\nchroma_collection = db.get_or_create_collection(\"docs\")\nvector_store = ChromaVectorStore(chroma_collection=chroma_collection)\n\n# 3. 构建索引\nfrom llama_index.core import StorageContext, VectorStoreIndex\nstorage_context = StorageContext.from_defaults(vector_store=vector_store)\nindex = VectorStoreIndex(nodes, embed_model=embed_model, storage_context=storage_context)\n",[45,403,404,416,428,435,439,444,454,467,478,482,486,491,511,527,546,551,557,569,588],{"__ignoreMap":43},[151,405,406,408,411,413],{"class":153,"line":154},[151,407,195],{"class":194},[151,409,410],{"class":198}," llama_index.embeddings.huggingface ",[151,412,202],{"class":194},[151,414,415],{"class":198}," HuggingFaceEmbedding\n",[151,417,418,420,423,425],{"class":153,"line":208},[151,419,195],{"class":194},[151,421,422],{"class":198}," llama_index.vector_stores.chroma ",[151,424,202],{"class":194},[151,426,427],{"class":198}," ChromaVectorStore\n",[151,429,430,432],{"class":153,"line":221},[151,431,202],{"class":194},[151,433,434],{"class":198}," chromadb\n",[151,436,437],{"class":153,"line":228},[151,438,225],{"emptyLinePlaceholder":224},[151,440,441],{"class":153,"line":235},[151,442,443],{"class":231},"# 1. 用 BGE 中文模型\n",[151,445,446,449,451],{"class":153,"line":253},[151,447,448],{"class":198},"embed_model ",[151,450,241],{"class":194},[151,452,453],{"class":198}," HuggingFaceEmbedding(\n",[151,455,456,459,461,464],{"class":153,"line":5},[151,457,458],{"class":277},"    model_name",[151,460,241],{"class":194},[151,462,463],{"class":161},"\"BAAI\u002Fbge-large-zh-v1.5\"",[151,465,466],{"class":198},",\n",[151,468,469,472,474,476],{"class":153,"line":263},[151,470,471],{"class":277},"    max_length",[151,473,241],{"class":194},[151,475,284],{"class":283},[151,477,466],{"class":198},[151,479,480],{"class":153,"line":274},[151,481,313],{"class":198},[151,483,484],{"class":153,"line":293},[151,485,225],{"emptyLinePlaceholder":224},[151,487,488],{"class":153,"line":310},[151,489,490],{"class":231},"# 2. 创建向量数据库\n",[151,492,493,496,498,501,504,506,509],{"class":153,"line":316},[151,494,495],{"class":198},"db ",[151,497,241],{"class":194},[151,499,500],{"class":198}," chromadb.PersistentClient(",[151,502,503],{"class":277},"path",[151,505,241],{"class":194},[151,507,508],{"class":161},"\".\u002Fchroma_db\"",[151,510,313],{"class":198},[151,512,514,517,519,522,525],{"class":153,"line":513},13,[151,515,516],{"class":198},"chroma_collection ",[151,518,241],{"class":194},[151,520,521],{"class":198}," db.get_or_create_collection(",[151,523,524],{"class":161},"\"docs\"",[151,526,313],{"class":198},[151,528,530,533,535,538,541,543],{"class":153,"line":529},14,[151,531,532],{"class":198},"vector_store ",[151,534,241],{"class":194},[151,536,537],{"class":198}," ChromaVectorStore(",[151,539,540],{"class":277},"chroma_collection",[151,542,241],{"class":194},[151,544,545],{"class":198},"chroma_collection)\n",[151,547,549],{"class":153,"line":548},15,[151,550,225],{"emptyLinePlaceholder":224},[151,552,554],{"class":153,"line":553},16,[151,555,556],{"class":231},"# 3. 构建索引\n",[151,558,560,562,564,566],{"class":153,"line":559},17,[151,561,195],{"class":194},[151,563,199],{"class":198},[151,565,202],{"class":194},[151,567,568],{"class":198}," StorageContext, VectorStoreIndex\n",[151,570,572,575,577,580,583,585],{"class":153,"line":571},18,[151,573,574],{"class":198},"storage_context ",[151,576,241],{"class":194},[151,578,579],{"class":198}," StorageContext.from_defaults(",[151,581,582],{"class":277},"vector_store",[151,584,241],{"class":194},[151,586,587],{"class":198},"vector_store)\n",[151,589,591,594,596,599,602,604,607,610,612],{"class":153,"line":590},19,[151,592,593],{"class":198},"index ",[151,595,241],{"class":194},[151,597,598],{"class":198}," VectorStoreIndex(nodes, ",[151,600,601],{"class":277},"embed_model",[151,603,241],{"class":194},[151,605,606],{"class":198},"embed_model, ",[151,608,609],{"class":277},"storage_context",[151,611,241],{"class":194},[151,613,614],{"class":198},"storage_context)\n",[13,616,618],{"id":617},"第四步检索-重排序","第四步：检索 + 重排序",[37,620,622],{"className":185,"code":621,"language":187,"meta":43,"style":43},"from llama_index.core.postprocessor import SentenceTransformerRerank\n\n# 重排序模型——显著提升检索质量\nreranker = SentenceTransformerRerank(\n    model=\"BAAI\u002Fbge-reranker-large\",\n    top_n=3,  # 重排序后取前 3\n)\n\n# 检索器\nretriever = index.as_retriever(similarity_top_k=10)  # 先粗检索 10 条\n\n# 检索 + 重排序\nnodes = retriever.retrieve(\"年假怎么请？\")\nreranked = reranker.postprocess_nodes(nodes, query_str=\"年假怎么请？\")\n",[45,623,624,636,640,645,655,667,683,687,691,696,720,724,729,743],{"__ignoreMap":43},[151,625,626,628,631,633],{"class":153,"line":154},[151,627,195],{"class":194},[151,629,630],{"class":198}," llama_index.core.postprocessor ",[151,632,202],{"class":194},[151,634,635],{"class":198}," SentenceTransformerRerank\n",[151,637,638],{"class":153,"line":208},[151,639,225],{"emptyLinePlaceholder":224},[151,641,642],{"class":153,"line":221},[151,643,644],{"class":231},"# 重排序模型——显著提升检索质量\n",[151,646,647,650,652],{"class":153,"line":228},[151,648,649],{"class":198},"reranker ",[151,651,241],{"class":194},[151,653,654],{"class":198}," SentenceTransformerRerank(\n",[151,656,657,660,662,665],{"class":153,"line":235},[151,658,659],{"class":277},"    model",[151,661,241],{"class":194},[151,663,664],{"class":161},"\"BAAI\u002Fbge-reranker-large\"",[151,666,466],{"class":198},[151,668,669,672,674,677,680],{"class":153,"line":253},[151,670,671],{"class":277},"    top_n",[151,673,241],{"class":194},[151,675,676],{"class":283},"3",[151,678,679],{"class":198},",  ",[151,681,682],{"class":231},"# 重排序后取前 3\n",[151,684,685],{"class":153,"line":5},[151,686,313],{"class":198},[151,688,689],{"class":153,"line":263},[151,690,225],{"emptyLinePlaceholder":224},[151,692,693],{"class":153,"line":274},[151,694,695],{"class":231},"# 检索器\n",[151,697,698,701,703,706,709,711,714,717],{"class":153,"line":293},[151,699,700],{"class":198},"retriever ",[151,702,241],{"class":194},[151,704,705],{"class":198}," index.as_retriever(",[151,707,708],{"class":277},"similarity_top_k",[151,710,241],{"class":194},[151,712,713],{"class":283},"10",[151,715,716],{"class":198},")  ",[151,718,719],{"class":231},"# 先粗检索 10 条\n",[151,721,722],{"class":153,"line":310},[151,723,225],{"emptyLinePlaceholder":224},[151,725,726],{"class":153,"line":316},[151,727,728],{"class":231},"# 检索 + 重排序\n",[151,730,731,733,735,738,741],{"class":153,"line":513},[151,732,319],{"class":198},[151,734,241],{"class":194},[151,736,737],{"class":198}," retriever.retrieve(",[151,739,740],{"class":161},"\"年假怎么请？\"",[151,742,313],{"class":198},[151,744,745,748,750,753,756,758,760],{"class":153,"line":529},[151,746,747],{"class":198},"reranked ",[151,749,241],{"class":194},[151,751,752],{"class":198}," reranker.postprocess_nodes(nodes, ",[151,754,755],{"class":277},"query_str",[151,757,241],{"class":194},[151,759,740],{"class":161},[151,761,313],{"class":198},[326,763,764],{"id":764},"为什么要重排序",[180,766,767],{},"Embedding 检索快但不够精准。先粗检索 10 条，再用重排序模型精选 3 条，准确率提升 20-30%。",[13,769,771],{"id":770},"第五步生成回答","第五步：生成回答",[37,773,775],{"className":185,"code":774,"language":187,"meta":43,"style":43},"from llama_index.llms.openai import OpenAI\n\nllm = OpenAI(model=\"gpt-4o\", temperature=0)\n\n# 构建 query engine\nquery_engine = index.as_query_engine(\n    llm=llm,\n    similarity_top_k=10,\n    node_postprocessors=[reranker],\n    response_mode=\"compact\",  # 紧凑模式，省 token\n)\n\n# 提问\nresponse = query_engine.query(\"年假怎么请？需要提前多久申请？\")\nprint(response.response)\n",[45,776,777,789,793,824,828,833,843,853,864,874,889,893,897,902,917],{"__ignoreMap":43},[151,778,779,781,784,786],{"class":153,"line":154},[151,780,195],{"class":194},[151,782,783],{"class":198}," llama_index.llms.openai ",[151,785,202],{"class":194},[151,787,788],{"class":198}," OpenAI\n",[151,790,791],{"class":153,"line":208},[151,792,225],{"emptyLinePlaceholder":224},[151,794,795,798,800,803,806,808,811,814,817,819,822],{"class":153,"line":221},[151,796,797],{"class":198},"llm ",[151,799,241],{"class":194},[151,801,802],{"class":198}," OpenAI(",[151,804,805],{"class":277},"model",[151,807,241],{"class":194},[151,809,810],{"class":161},"\"gpt-4o\"",[151,812,813],{"class":198},", ",[151,815,816],{"class":277},"temperature",[151,818,241],{"class":194},[151,820,821],{"class":283},"0",[151,823,313],{"class":198},[151,825,826],{"class":153,"line":228},[151,827,225],{"emptyLinePlaceholder":224},[151,829,830],{"class":153,"line":235},[151,831,832],{"class":231},"# 构建 query engine\n",[151,834,835,838,840],{"class":153,"line":253},[151,836,837],{"class":198},"query_engine ",[151,839,241],{"class":194},[151,841,842],{"class":198}," index.as_query_engine(\n",[151,844,845,848,850],{"class":153,"line":5},[151,846,847],{"class":277},"    llm",[151,849,241],{"class":194},[151,851,852],{"class":198},"llm,\n",[151,854,855,858,860,862],{"class":153,"line":263},[151,856,857],{"class":277},"    similarity_top_k",[151,859,241],{"class":194},[151,861,713],{"class":283},[151,863,466],{"class":198},[151,865,866,869,871],{"class":153,"line":274},[151,867,868],{"class":277},"    node_postprocessors",[151,870,241],{"class":194},[151,872,873],{"class":198},"[reranker],\n",[151,875,876,879,881,884,886],{"class":153,"line":293},[151,877,878],{"class":277},"    response_mode",[151,880,241],{"class":194},[151,882,883],{"class":161},"\"compact\"",[151,885,679],{"class":198},[151,887,888],{"class":231},"# 紧凑模式，省 token\n",[151,890,891],{"class":153,"line":310},[151,892,313],{"class":198},[151,894,895],{"class":153,"line":316},[151,896,225],{"emptyLinePlaceholder":224},[151,898,899],{"class":153,"line":513},[151,900,901],{"class":231},"# 提问\n",[151,903,904,907,909,912,915],{"class":153,"line":529},[151,905,906],{"class":198},"response ",[151,908,241],{"class":194},[151,910,911],{"class":198}," query_engine.query(",[151,913,914],{"class":161},"\"年假怎么请？需要提前多久申请？\"",[151,916,313],{"class":198},[151,918,919,922],{"class":153,"line":548},[151,920,921],{"class":283},"print",[151,923,924],{"class":198},"(response.response)\n",[326,926,928],{"id":927},"prompt-优化","Prompt 优化",[180,930,931],{},"默认 prompt 是英文的，中文场景建议自定义：",[37,933,935],{"className":185,"code":934,"language":187,"meta":43,"style":43},"from llama_index.core import PromptTemplate\n\nqa_prompt = PromptTemplate(\"\"\"\n你是一个文档问答助手。请根据以下检索到的文档片段回答问题。\n如果文档中没有相关信息，明确说\"文档中未找到相关信息\"，不要编造。\n\n文档片段：\n{context_str}\n\n问题：{query_str}\n\n回答：\n\"\"\")\n\nquery_engine.update_prompts({\"response_synthesis_prompt\": qa_prompt})\n",[45,936,937,948,952,965,970,975,979,984,989,993,1001,1005,1010,1017,1021],{"__ignoreMap":43},[151,938,939,941,943,945],{"class":153,"line":154},[151,940,195],{"class":194},[151,942,199],{"class":198},[151,944,202],{"class":194},[151,946,947],{"class":198}," PromptTemplate\n",[151,949,950],{"class":153,"line":208},[151,951,225],{"emptyLinePlaceholder":224},[151,953,954,957,959,962],{"class":153,"line":221},[151,955,956],{"class":198},"qa_prompt ",[151,958,241],{"class":194},[151,960,961],{"class":198}," PromptTemplate(",[151,963,964],{"class":161},"\"\"\"\n",[151,966,967],{"class":153,"line":228},[151,968,969],{"class":161},"你是一个文档问答助手。请根据以下检索到的文档片段回答问题。\n",[151,971,972],{"class":153,"line":235},[151,973,974],{"class":161},"如果文档中没有相关信息，明确说\"文档中未找到相关信息\"，不要编造。\n",[151,976,977],{"class":153,"line":253},[151,978,225],{"emptyLinePlaceholder":224},[151,980,981],{"class":153,"line":5},[151,982,983],{"class":161},"文档片段：\n",[151,985,986],{"class":153,"line":263},[151,987,988],{"class":283},"{context_str}\n",[151,990,991],{"class":153,"line":274},[151,992,225],{"emptyLinePlaceholder":224},[151,994,995,998],{"class":153,"line":293},[151,996,997],{"class":161},"问题：",[151,999,1000],{"class":283},"{query_str}\n",[151,1002,1003],{"class":153,"line":310},[151,1004,225],{"emptyLinePlaceholder":224},[151,1006,1007],{"class":153,"line":316},[151,1008,1009],{"class":161},"回答：\n",[151,1011,1012,1015],{"class":153,"line":513},[151,1013,1014],{"class":161},"\"\"\"",[151,1016,313],{"class":198},[151,1018,1019],{"class":153,"line":529},[151,1020,225],{"emptyLinePlaceholder":224},[151,1022,1023,1026,1029],{"class":153,"line":548},[151,1024,1025],{"class":198},"query_engine.update_prompts({",[151,1027,1028],{"class":161},"\"response_synthesis_prompt\"",[151,1030,1031],{"class":198},": qa_prompt})\n",[13,1033,1035],{"id":1034},"第六步评估","第六步：评估",[326,1037,1038],{"id":1038},"检索质量评估",[37,1040,1042],{"className":185,"code":1041,"language":187,"meta":43,"style":43},"# 准备测试集：问题 + 正确答案所在文档\ntest_cases = [\n    {\"question\": \"年假怎么请？\", \"relevant_doc_id\": \"hr_policy_003\"},\n    {\"question\": \"报销流程是什么？\", \"relevant_doc_id\": \"finance_007\"},\n]\n\n# 计算 Recall@K\ndef eval_retrieval(query_engine, test_cases, k=5):\n    hits = 0\n    for tc in test_cases:\n        nodes = query_engine.retrieve(tc[\"question\"])\n        retrieved_ids = [n.node.metadata[\"doc_id\"] for n in nodes[:k]]\n        if tc[\"relevant_doc_id\"] in retrieved_ids:\n            hits += 1\n    return hits \u002F len(test_cases)\n\nrecall = eval_retrieval(query_engine, test_cases, k=5)\nprint(f\"Recall@5: {recall:.1%}\")\n",[45,1043,1044,1049,1059,1085,1107,1112,1116,1121,1140,1150,1164,1179,1206,1223,1234,1251,1255,1274],{"__ignoreMap":43},[151,1045,1046],{"class":153,"line":154},[151,1047,1048],{"class":231},"# 准备测试集：问题 + 正确答案所在文档\n",[151,1050,1051,1054,1056],{"class":153,"line":208},[151,1052,1053],{"class":198},"test_cases ",[151,1055,241],{"class":194},[151,1057,1058],{"class":198}," [\n",[151,1060,1061,1064,1067,1070,1072,1074,1077,1079,1082],{"class":153,"line":221},[151,1062,1063],{"class":198},"    {",[151,1065,1066],{"class":161},"\"question\"",[151,1068,1069],{"class":198},": ",[151,1071,740],{"class":161},[151,1073,813],{"class":198},[151,1075,1076],{"class":161},"\"relevant_doc_id\"",[151,1078,1069],{"class":198},[151,1080,1081],{"class":161},"\"hr_policy_003\"",[151,1083,1084],{"class":198},"},\n",[151,1086,1087,1089,1091,1093,1096,1098,1100,1102,1105],{"class":153,"line":228},[151,1088,1063],{"class":198},[151,1090,1066],{"class":161},[151,1092,1069],{"class":198},[151,1094,1095],{"class":161},"\"报销流程是什么？\"",[151,1097,813],{"class":198},[151,1099,1076],{"class":161},[151,1101,1069],{"class":198},[151,1103,1104],{"class":161},"\"finance_007\"",[151,1106,1084],{"class":198},[151,1108,1109],{"class":153,"line":235},[151,1110,1111],{"class":198},"]\n",[151,1113,1114],{"class":153,"line":253},[151,1115,225],{"emptyLinePlaceholder":224},[151,1117,1118],{"class":153,"line":5},[151,1119,1120],{"class":231},"# 计算 Recall@K\n",[151,1122,1123,1126,1129,1132,1134,1137],{"class":153,"line":263},[151,1124,1125],{"class":194},"def",[151,1127,1128],{"class":157}," eval_retrieval",[151,1130,1131],{"class":198},"(query_engine, test_cases, k",[151,1133,241],{"class":194},[151,1135,1136],{"class":283},"5",[151,1138,1139],{"class":198},"):\n",[151,1141,1142,1145,1147],{"class":153,"line":274},[151,1143,1144],{"class":198},"    hits ",[151,1146,241],{"class":194},[151,1148,1149],{"class":283}," 0\n",[151,1151,1152,1155,1158,1161],{"class":153,"line":293},[151,1153,1154],{"class":194},"    for",[151,1156,1157],{"class":198}," tc ",[151,1159,1160],{"class":194},"in",[151,1162,1163],{"class":198}," test_cases:\n",[151,1165,1166,1169,1171,1174,1176],{"class":153,"line":310},[151,1167,1168],{"class":198},"        nodes ",[151,1170,241],{"class":194},[151,1172,1173],{"class":198}," query_engine.retrieve(tc[",[151,1175,1066],{"class":161},[151,1177,1178],{"class":198},"])\n",[151,1180,1181,1184,1186,1189,1192,1195,1198,1201,1203],{"class":153,"line":316},[151,1182,1183],{"class":198},"        retrieved_ids ",[151,1185,241],{"class":194},[151,1187,1188],{"class":198}," [n.node.metadata[",[151,1190,1191],{"class":161},"\"doc_id\"",[151,1193,1194],{"class":198},"] ",[151,1196,1197],{"class":194},"for",[151,1199,1200],{"class":198}," n ",[151,1202,1160],{"class":194},[151,1204,1205],{"class":198}," nodes[:k]]\n",[151,1207,1208,1211,1214,1216,1218,1220],{"class":153,"line":513},[151,1209,1210],{"class":194},"        if",[151,1212,1213],{"class":198}," tc[",[151,1215,1076],{"class":161},[151,1217,1194],{"class":198},[151,1219,1160],{"class":194},[151,1221,1222],{"class":198}," retrieved_ids:\n",[151,1224,1225,1228,1231],{"class":153,"line":529},[151,1226,1227],{"class":198},"            hits ",[151,1229,1230],{"class":194},"+=",[151,1232,1233],{"class":283}," 1\n",[151,1235,1236,1239,1242,1245,1248],{"class":153,"line":548},[151,1237,1238],{"class":194},"    return",[151,1240,1241],{"class":198}," hits ",[151,1243,1244],{"class":194},"\u002F",[151,1246,1247],{"class":283}," len",[151,1249,1250],{"class":198},"(test_cases)\n",[151,1252,1253],{"class":153,"line":553},[151,1254,225],{"emptyLinePlaceholder":224},[151,1256,1257,1260,1262,1265,1268,1270,1272],{"class":153,"line":559},[151,1258,1259],{"class":198},"recall ",[151,1261,241],{"class":194},[151,1263,1264],{"class":198}," eval_retrieval(query_engine, test_cases, ",[151,1266,1267],{"class":277},"k",[151,1269,241],{"class":194},[151,1271,1136],{"class":283},[151,1273,313],{"class":198},[151,1275,1276,1278,1281,1284,1287,1290,1293,1296,1299,1302],{"class":153,"line":571},[151,1277,921],{"class":283},[151,1279,1280],{"class":198},"(",[151,1282,1283],{"class":194},"f",[151,1285,1286],{"class":161},"\"Recall@5: ",[151,1288,1289],{"class":283},"{",[151,1291,1292],{"class":198},"recall",[151,1294,1295],{"class":194},":.1%",[151,1297,1298],{"class":283},"}",[151,1300,1301],{"class":161},"\"",[151,1303,313],{"class":198},[326,1305,1306],{"id":1306},"回答质量评估",[180,1308,1309],{},"用 LLM 自动评估回答质量：",[37,1311,1313],{"className":185,"code":1312,"language":187,"meta":43,"style":43},"eval_prompt = f\"\"\"\n请评估以下回答的质量，打分 1-5：\n问题：{question}\n检索到的文档：{retrieved_docs}\n回答：{answer}\n\n评分标准：\n5 — 完全正确，基于文档\n3 — 部分正确，有遗漏\n1 — 错误或编造\n\"\"\"\n",[45,1314,1315,1327,1332,1344,1356,1368,1372,1377,1382,1387,1392],{"__ignoreMap":43},[151,1316,1317,1320,1322,1325],{"class":153,"line":154},[151,1318,1319],{"class":198},"eval_prompt ",[151,1321,241],{"class":194},[151,1323,1324],{"class":194}," f",[151,1326,964],{"class":161},[151,1328,1329],{"class":153,"line":208},[151,1330,1331],{"class":161},"请评估以下回答的质量，打分 1-5：\n",[151,1333,1334,1336,1338,1341],{"class":153,"line":221},[151,1335,997],{"class":161},[151,1337,1289],{"class":283},[151,1339,1340],{"class":198},"question",[151,1342,1343],{"class":283},"}\n",[151,1345,1346,1349,1351,1354],{"class":153,"line":228},[151,1347,1348],{"class":161},"检索到的文档：",[151,1350,1289],{"class":283},[151,1352,1353],{"class":198},"retrieved_docs",[151,1355,1343],{"class":283},[151,1357,1358,1361,1363,1366],{"class":153,"line":235},[151,1359,1360],{"class":161},"回答：",[151,1362,1289],{"class":283},[151,1364,1365],{"class":198},"answer",[151,1367,1343],{"class":283},[151,1369,1370],{"class":153,"line":253},[151,1371,225],{"emptyLinePlaceholder":224},[151,1373,1374],{"class":153,"line":5},[151,1375,1376],{"class":161},"评分标准：\n",[151,1378,1379],{"class":153,"line":263},[151,1380,1381],{"class":161},"5 — 完全正确，基于文档\n",[151,1383,1384],{"class":153,"line":274},[151,1385,1386],{"class":161},"3 — 部分正确，有遗漏\n",[151,1388,1389],{"class":153,"line":293},[151,1390,1391],{"class":161},"1 — 错误或编造\n",[151,1393,1394],{"class":153,"line":310},[151,1395,964],{"class":161},[13,1397,1398],{"id":1398},"常见问题与优化",[326,1400,1402],{"id":1401},"问题-1检索不到相关文档","问题 1：检索不到相关文档",[180,1404,1405,1408],{},[391,1406,1407],{},"原因","：切分太碎，语义丢失。",[180,1410,1411,1414],{},[391,1412,1413],{},"解决","：",[17,1416,1417,1420,1423],{},[20,1418,1419],{},"增大 chunk_size（512 → 1024）",[20,1421,1422],{},"加 chunk_overlap",[20,1424,1425],{},"用 parent-child 切分（检索小块，返回大块）",[326,1427,1429],{"id":1428},"问题-2回答不基于文档幻觉","问题 2：回答不基于文档（幻觉）",[180,1431,1432,1414],{},[391,1433,1413],{},[17,1435,1436,1439,1442],{},[20,1437,1438],{},"prompt 强制要求\"只基于文档回答\"",[20,1440,1441],{},"temperature 设 0",[20,1443,1444],{},"检查检索结果是否相关（不相关就回答\"未找到\"）",[326,1446,1448],{"id":1447},"问题-3中文检索效果差","问题 3：中文检索效果差",[180,1450,1451,1414],{},[391,1452,1413],{},[17,1454,1455,1458,1461],{},[20,1456,1457],{},"用 BGE 系列中文 Embedding 模型",[20,1459,1460],{},"不要用 OpenAI 的 Embedding（中文效果一般）",[20,1462,1463],{},"加重排序模型",[326,1465,1467],{"id":1466},"问题-4速度慢","问题 4：速度慢",[180,1469,1470,1414],{},[391,1471,1413],{},[17,1473,1474,1477,1480,1483],{},[20,1475,1476],{},"Embedding 用 GPU",[20,1478,1479],{},"向量数据库加 HNSW 索引",[20,1481,1482],{},"减少 similarity_top_k（10 → 5）",[20,1484,1485],{},"LLM 用流式输出",[13,1487,1488],{"id":1488},"生产部署清单",[17,1490,1493,1502,1508,1514,1520,1526,1532,1538,1544,1550,1556,1562],{"className":1491},[1492],"contains-task-list",[20,1494,1497,1501],{"className":1495},[1496],"task-list-item",[1498,1499],"input",{"disabled":224,"type":1500},"checkbox"," 文档解析支持 PDF\u002FWord\u002FHTML\u002FMarkdown",[20,1503,1505,1507],{"className":1504},[1496],[1498,1506],{"disabled":224,"type":1500}," 切分策略测试过（chunk_size 调优）",[20,1509,1511,1513],{"className":1510},[1496],[1498,1512],{"disabled":224,"type":1500}," Embedding 模型选定并部署",[20,1515,1517,1519],{"className":1516},[1496],[1498,1518],{"disabled":224,"type":1500}," 向量数据库持久化",[20,1521,1523,1525],{"className":1522},[1496],[1498,1524],{"disabled":224,"type":1500}," 重排序模型集成",[20,1527,1529,1531],{"className":1528},[1496],[1498,1530],{"disabled":224,"type":1500}," LLM prompt 优化（中文 + 防幻觉）",[20,1533,1535,1537],{"className":1534},[1496],[1498,1536],{"disabled":224,"type":1500}," 检索质量评估（Recall@K > 80%）",[20,1539,1541,1543],{"className":1540},[1496],[1498,1542],{"disabled":224,"type":1500}," 回答质量评估（人工抽检）",[20,1545,1547,1549],{"className":1546},[1496],[1498,1548],{"disabled":224,"type":1500}," 流式输出（用户体验）",[20,1551,1553,1555],{"className":1552},[1496],[1498,1554],{"disabled":224,"type":1500}," 缓存层（常见问题缓存回答）",[20,1557,1559,1561],{"className":1558},[1496],[1498,1560],{"disabled":224,"type":1500}," 日志记录（问题 + 检索结果 + 回答）",[20,1563,1565,1567],{"className":1564},[1496],[1498,1566],{"disabled":224,"type":1500}," 限流 + 鉴权",[1569,1570,1571],"style",{},"html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}",{"title":43,"searchDepth":221,"depth":221,"links":1573},[1574,1575,1576,1577,1578,1581,1582,1585,1588,1592,1598],{"id":15,"depth":208,"text":15},{"id":34,"depth":208,"text":35},{"id":49,"depth":208,"text":49},{"id":141,"depth":208,"text":142},{"id":177,"depth":208,"text":178,"children":1579},[1580],{"id":328,"depth":221,"text":328},{"id":397,"depth":208,"text":398},{"id":617,"depth":208,"text":618,"children":1583},[1584],{"id":764,"depth":221,"text":764},{"id":770,"depth":208,"text":771,"children":1586},[1587],{"id":927,"depth":221,"text":928},{"id":1034,"depth":208,"text":1035,"children":1589},[1590,1591],{"id":1038,"depth":221,"text":1038},{"id":1306,"depth":221,"text":1306},{"id":1398,"depth":208,"text":1398,"children":1593},[1594,1595,1596,1597],{"id":1401,"depth":221,"text":1402},{"id":1428,"depth":221,"text":1429},{"id":1447,"depth":221,"text":1448},{"id":1466,"depth":221,"text":1467},{"id":1488,"depth":208,"text":1488},"onboarding","\u002Fog\u002Fplaybook\u002Frag-pipeline-build.png","用 LlamaIndex + ChromaDB + GPT-4o 搭一个生产级 RAG 系统——文档切分策略、Embedding 选型、检索优化、重排序、回答生成、评估指标，含完整代码和踩坑记录。","md",{},"\u002Fplaybook\u002Fonboarding\u002Frag-pipeline-build","2026-06-21",[1607,1608,1609],"agent\u002Fplatform\u002Fdify","agent\u002Fplatform\u002Ffastgpt","coding\u002Flocal\u002Follama",{"title":8,"description":1601},"playbook\u002Fonboarding\u002Frag-pipeline-build",[1613,80,99,1614],"RAG","文档问答","iDBgSssiFFBzdFhaAXgu0ePo29Ov3xLYoHWpnnCPo04",{"tools":4,"reviews":5,"playbooks":293,"news":263},1782316489336]