
Sikhbani.ai 如何在 AI 问答中处理长文本经文RAG 检索增强生成落地拆解这次我们来看一个比较特别的 AI 问答项目Sikhbani.ai英文副标题是 Ask the Sri Guru Granth Sahib。简单说它做的事是让你用自然语言向一部体量巨大的宗教经典 Sri Guru Granth Sahib 提问AI 根据经文内容给出带出处的回答。这类“面向特定文本集的问答系统”在技术上有很典型的共性长文本索引、精准检索、引用溯源、控制幻觉。跟通用聊天机器人不同它不能靠模型“背答案”必须真正去翻原文。这篇文章不打算只讲这个网站本身而是把它当成一个 RAGRetrieval-Augmented Generation检索增强生成落地的具体案例来拆。我们会从架构设计、数据准备、向量检索、答案生成、接口调用、效果验证、性能优化几个方向展开。即使你没有读过 Sri Guru Granth Sahib也不影响理解这套技术方案如果你正在做古籍、规范文档、法律条令或企业知识库的问答系统这篇文章可以直接参考。1. 核心能力速览先给规格。这个项目的公开信息不算丰富下面的能力表一部分来自项目公开介绍一部分是同类 RAG 问答系统的通用能力推演标注清楚再往下看。能力项说明项目类型特定文本集问答 AI面向 Sri Guru Granth Sahib 的检索增强问答核心架构RAG不是靠模型背诵经文而是先检索原文再生成回答输入方式自然语言提问覆盖信仰理解、经文出处、概念解释等输出方式文本回答理想设计应携带经文引用/章节出处索引对象长文本经文含英文译文、原文音译等多语种版本技术难点长文本切分、语义检索、多语种 embedding、引用溯源、控制幻觉部署方式在线 Web 服务自建版本可走本地部署是否支持 API在线服务通常提供访问入口自建 RAG 可用 FastAPI/Flask 暴露接口是否支持批量任务离线索引阶段支持批量文档处理问答阶段可做批量提问脚本显存需求取决于选用模型纯 API 调用无需 GPU本地部署 7B~14B 模型建议 8G~16G 显存适合场景宗教经典、古籍、规范手册、企业知识库、法律条文的问答检索这里要先说明一个容易踩坑的点RAG 问答的质量上限不取决于大模型而取决于检索质量。经文这种文本有大量相似句式和重复短语如果只用简单的关键词搜索会把大量不相关内容捞上来如果只用向量相似度又会因为语义接近但主题不同而答非所问。后面会专门讲怎么处理这类问题。2. 适用场景与使用边界Sikhbani.ai 这类“经典问答”项目本质上解决的是下面几类需求想快速定位某段经文想了解某句话在经典中的位置想对比不同章节对同一主题的表述以及希望 AI 在回答时给出原文依据而不是凭空发挥。这套思路非常适合以下场景宗教经典与哲学文本检索问答。企业规章制度、产品文档、客服知识库问答。法律条文、监管文件的精准检索。学术文献和古籍整理。内部技术文档的语义搜索。但它也有明确边界。第一它不能替代专业解读。经文含义往往依赖语境、历史背景和历代注疏RAG 只能把相关原文捞出来并组织成通顺回答不能保证理解完全正确。第二它不适合需要对原文做创造性阐释的任务。第三存在幻觉风险再好的检索也挡不住生成阶段“自由发挥”。这里面还有一个非常现实的合规问题。任何涉及宗教经典的 AI 项目都要特别注意几点经文内容的版权和翻译版权必须确认清楚不要对教义做价值判断输出内容需要标注来源尽量避免把 AI 生成的内容当作权威结论。做同类型知识问答时记得在界面上加一条说明“AI 回答仅供参考请以原文和权威解经为准。”这不是套话是降低风险的实际手段。同时如果后面要接入人脸、声音、版权素材相关能力授权问题更要前置处理。本项目的核心是文本问答相对单纯但凡是做知识类 AI版权边界永远优先于技术实现。3. RAG 问答系统的架构拆解先从工程角度拆一下 Sikhbani.ai 这类项目的大致结构帮助你建立一个完整的技术认知。无论前端界面长什么样后端都跑在下面这条链路上。用户提问 - 查询改写 - 向量检索 关键词检索 - 重排序 - 拼装提示词 - LLM 生成 - 引用校验 - 返回结果每个环节都有它要解决的问题查询改写。用户的问题往往是口语化的比如“什么是谦卑”如果直接拿这句话去向量检索效果一般。更稳妥的做法是先让大模型把问题改写成适合检索的查询词或生成多个候选查询提高召回。混合检索。两类方法结合向量检索语义相似把用户提问向量化然后和经文向量的向量库做相似度计算找出语义最接近的片段。适合“意思相同但表达不一样”的情况。关键词检索BM25/TF-IDF适合精确查找术语、人名、章节名。经文里有大量专名比如“Guru Nanak”“Amritsar”“Sabad”关键词检索能精准命中。重排序。向量检索返回 Top 20 到 Top 50 个候选片段再由一个更精细的模型如 cross-encoder重新打分挑出最相关的 Top 5 到 Top 8 个片段作为大模型生成的参考材料。这一步对回答质量提升非常明显。拼装提示词。把检索到的片段和用户问题按固定模板拼起来。模板里要有明确的角色设定、引用格式要求、以及“没有找到相关内容时就如实说明”的约束。生成。模型根据提示词中的原文片段生成回答。这个阶段容易发生幻觉所以上下文里必须强调“只能根据提供的片段回答不要自行补充经文细节”。引用校验。生成之后程序再检查回答中的引用编号是否确实对应检索片段。如果模型写了一个不存在的引用就把这句话标记为低置信度或者直接过滤掉。从实现难度来说最容易被忽略的是重排序和引用校验。很多人搭完向量检索就觉得完成了结果回答经常“看着相关实则不对”。重排序能显著改善这种问题。4. 环境准备与前置条件如果你要自建类似 Sikhbani.ai 的经文问答系统环境准备按下面这套来走。先给通用清单再给一份可直接套用的 Python 依赖。4.1 通用检查清单操作系统Linux / Windows / macOS 均可生产环境建议 Linux。Python 版本3.10建议 3.11。模型选择调用云端 APIOpenAI、Claude、国产大模型 API 均可无需显卡。本地部署Llama、Qwen、ChatGLM 等 7B 到 14B 的模型显卡建议 8G 到 16G 显存。Embedding 模型BGE、M3E 等中文/多语种向量模型本地跑 CPU 也够用。向量数据库Milvus、Qdrant、Chroma、Elasticsearch 任选小规模直接用 Chroma。检索库BM25 可以用 Elasticsearch也可以直接安装 rank_bm25 在内存里跑。磁盘空间文本语料不占空间几十万字也就几十 MB模型单独算7B 量化模型约 4G 到 8G原始权重 14G 以上。4.2 Python 依赖参考pip install chromadb pip install sentence-transformers pip install rank-bm25 pip install langchain-core pip install openai pip install fastapi pip install uvicorn这套组合适合快速做原型验证。Chroma 负责向量存储sentence-transformers 生成向量rank-bm25 做关键词检索FastAPI 用来暴露接口。如果数据量到百万级再把 Chroma 换成 Milvus。5. 数据准备与经文向量化这是整个项目中最占用工时、也最影响效果的部分。Sri Guru Granth Sahib 的文本结构复杂包含 1430 页、约 6000 首圣诗英译和原文并存还带章节编号和作者标记。把这种文本直接丢进大模型是不现实的必须切分、清洗、索引。5.1 文本切分策略切分长文本是 RAG 系统的第一个坑。切得太短语义不完整切得太长向量表示被稀释检索噪音上升。宗教经典尤其难处理因为它的原文经常按诗节组织一个诗节就是完整的语义单元。推荐策略按语义单元优先字符数兜底。# 伪代码演示经文切分的核心思路 def split_gurbani_text(text, max_chars800, overlap100): sections [] # 按诗节/小节标记先切 parts re.split(r\n\n|\u0964|\u0A64, text) # 章节分割符根据语料实际情况调整 current for part in parts: if len(current) len(part) max_chars: current part \n\n else: sections.append(current.strip()) current part \n\n if current.strip(): sections.append(current.strip()) return sections注意 overlap 参数。相邻片段之间留 100 字左右的重叠可以防止语义被边界切断。比如一个句子前半段在上一片段、后半段在下一片段检索时因为重叠至少能保证其中一个片段是完整的。切分完成后每个片段需要保留元数据包括章节号、作者、页码、原文与译文的对应关系。元数据在后续引用溯源时非常重要。5.2 向量化与写入向量库Embedding 模型建议选多语种模型。经文同时涉及旁遮普语原文、英文译文甚至可能有中文翻译单一语言的 embedding 模型效果会打折扣。from sentence_transformers import SentenceTransformer model SentenceTransformer(BAAI/bge-m3) texts [经文片段1, 经文片段2, 经文片段3] embeddings model.encode(texts, normalize_embeddingsTrue) import chromadb client chromadb.PersistentClient(path./gurbani_db) collection client.get_or_create_collection( namesikh_scripture, metadata{hnsw:space: cosine} ) ids [fseg_{i} for i in range(len(texts))] collection.add( embeddingsembeddings.tolist(), documentstexts, metadatas[{chapter: 1, author: Guru Nanak, page: 1} for _ in texts], idsids )这段代码用的是 BGE-M3 模型对多语种和多长度文本支持相对好。如果你用的是其他 embedding 模型记得替换模型名并在小规模语料上先验证检索效果再全量索引。5.3 关键词索引向量检索负责“找语义接近”但还要维护一份 BM25 索引负责“找术语精确匹配”。这里用 rank_bm25 做内存版即可适合几十万片段以内的规模。from rank_bm25 import BM25Okapi tokenized_sections [doc.split() for doc in texts] bm25 BM25Okapi(tokenized_sections) def bm25_search(query, top_k5): tokenized_query query.split() scores bm25.get_scores(tokenized_query) top_indices sorted(range(len(scores)), keylambda i: scores[i], reverseTrue)[:top_k] return [texts[i] for i in top_indices]BM25 在跑中文或旁遮普语时需要先分词英文可以直接 split。生产环境里如果数据量大建议换 Elasticsearch它自带 BM25 实现也支持分词器插件。6. 检索与生成实现一个经文问答接口数据索引完成后接下来把查询链路串起来用一个 FastAPI 服务暴露问答接口。这里给出一个完整的、可直接改用的实现示例。6.1 查询链路核心代码import requests from fastapi import FastAPI from pydantic import BaseModel app FastAPI() # 假设已准备好 vector_collection 和 bm25 # vector_collection 是 chromadb collection # bm25 是 BM25Okapi 实例 class AskRequest(BaseModel): question: str top_k: int 5 def hybrid_retrieve(question, top_k5, vector_weight0.7): # 向量检索 query_embedding embed_model.encode([question], normalize_embeddingsTrue)[0] vec_result vector_collection.query( query_embeddings[query_embedding.tolist()], n_resultstop_k * 4 ) # BM25 检索 bm25_docs bm25_search(question, top_ktop_k * 4) # 合并分数简化版直接按排名加权 merged {} for i, (doc_id, doc, metadata) in enumerate( zip(vec_result[ids][0], vec_result[documents][0], vec_result[metadatas][0]) ): merged[doc_id] { doc: doc, meta: metadata, score: vector_weight * (1.0 / (i 1)) } for i, (doc_id, doc) in enumerate(bm25_docs): if doc_id in merged: merged[doc_id][score] (1 - vector_weight) * (1.0 / (i 1)) else: merged[doc_id] { doc: doc, meta: {}, score: (1 - vector_weight) * (1.0 / (i 1)) } ranked sorted(merged.items(), keylambda x: x[1][score], reverseTrue) return ranked[:top_k]这段混合检索逻辑的重点是向量检索召回更多候选BM25 负责把精确术语命中拉高然后在内存里做加权合并。实际项目中这个合并逻辑可以替换成更专业的重排序模型比如 bge-reranker效果会更好。6.2 提示词拼装与调用 LLMdef build_prompt(question, contexts): context_text \n\n.join( f[{i1}] {c[doc]}\n来源{c[meta].get(chapter, 未知)} for i, c in enumerate(contexts) ) system ( 你是一位精通锡克教经典的助手。请严格根据提供的经文片段回答问题。 回答必须引用对应编号的经文片段例如 [1][2]。 如果提供的片段不足以回答请直接说‘根据提供的经文我无法回答这个问题’。 不要补充经文片段之外的信息。 ) user f经文片段\n{context_text}\n\n用户问题{question} return system, user def generate_answer(question, contexts, llm_api_key, llm_endpointhttps://api.openai.com/v1/chat/completions): system, user build_prompt(question, contexts) payload { model: gpt-4o-mini, messages: [ {role: system, content: system}, {role: user, content: user} ], temperature: 0.3, max_tokens: 1000 } headers { Authorization: fBearer {llm_api_key}, Content-Type: application/json } resp requests.post(llm_endpoint, jsonpayload, headersheaders, timeout60) return resp.json()[choices][0][message][content]temperature 设置 0.3 是比较平衡的选择。太低会让回答变得机械太高又会增加幻觉风险。如果你接入的是本地模型把请求地址改成本地服务的 OpenAI 兼容端点即可。6.3 引用合规检查模型返回的回答里[1] 这种引用标识不一定可靠必须做一次程序化校验。简单做法解析回答中的引用编号检查是否超出本次检索结果的序号范围超出就剔除并给出提示。import re def validate_citations(answer, top_k): citations set(re.findall(r\[(\d)\], answer)) valid [c for c in citations if 1 int(c) top_k] invalid [c for c in citations if c not in valid] if invalid: answer f\n\n以下引用未能验证已移除{invalid} for c in invalid: answer answer.replace(f[{c}], ) return answer这是最基础的校验。更严谨的方案是在生成时要求模型输出结构化 JSON包含回答正文、引用编号、置信度三个字段然后程序逐个校验。这样下游系统可以自行决定是否展示低置信度内容。7. 功能测试与效果验证部署完成后不建议直接上线。这里给出一套验证 RAG 问答效果的测试方案尤其要围绕“经文问答”和“幻觉控制”两个方向。7.1 基础问答测试先跑几个基础测试用例验证链路是否通。curl -X POST http://127.0.0.1:8000/ask \ -H Content-Type: application/json \ -d {question: What does the Guru Granth Sahib say about ego?, top_k: 5}预期返回结构{ answer: 关于 ego我执经文提到……[1][2], contexts: [ { text: ……原文片段……, chapter: Ang 123, score: 0.86 } ] }判断标准回答是否引用了真实存在的经文片段引用编号是否对应上下文回答中的说法是否能在引用片段里找到直接依据。7.2 幻觉专项测试这是 RAG 问答最核心的测试推荐做以下几类测试类型示例问题判断标准模糊问题“你如何看待人生意义”如果经文片段无直接对应应回复无法基于提供的经文回答而不是编造混合问题“Guru Nanak 和 Guru Arjan 都谈过什么关于爱”回答需要区分两位作者的原文不能合并成一句无出处的话翻译对应“请给出某段原文及其英文翻译”必须严格对应同一个片段不能把不同章节的译文拼在一起负向测试“经文里有提到汽车吗”模型应回答“未检索到相关内容”而不是给出类比解释张冠李戴“这段是谁写的”检索元数据必须能追溯到具体作者推荐每轮测试记录三个指标检索准确率、引用准确率、回答相关度。检索准确率指检索结果中确实包含正确段落的比例引用准确率指回答中标注的引用是否真实对应检索片段回答相关度则要人工打分1 到 5 分。如果连续多轮测试回答相关度低于 4 分优先检查检索质量而不是换更大的大模型。7.3 长文本与多轮追问经文问答经常涉及多轮追问。比如用户先问“什么是 Simran”再追问“和 meditation 有什么区别”。这种场景下检索组件要把上文的语义融合进来把“这两者的区别”定向到经文里相关的定义段落。实现上可以在查询改写阶段把历史对话一并交给大模型生成一个包含上下文的独立检索 query。8. 接口 API 与批量任务设计RAG 问答系统上线后通常不只是网页访问还会面临接口 API 和批量任务的需求。这里分两部分说明。8.1 API 接口设计建议至少提供三个接口接口路径方法功能主要参数/askPOST单条问答question,top_k/ask/batchPOST批量问答questions,max_batch_size/searchPOST纯检索不生成回答query,top_k/search接口很重要很多应用场景只需要检索不需要生成。比如前端想展示“相关经文卡片”直接调用检索接口即可既能减少大模型调用成本又能避免幻觉。8.2 批量任务与队列设计批量问答的逻辑不复杂核心是控制并发、记录进度、处理失败。import asyncio import json async def process_batch(questions, batch_size5): results [] for i in range(0, len(questions), batch_size): batch questions[i:ibatch_size] batch_results await asyncio.gather( *(ask_one(q) for q in batch), return_exceptionsTrue ) for q, r in zip(batch, batch_results): if isinstance(r, Exception): results.append({question: q, error: str(r), status: failed}) else: results.append({question: q, answer: r, status: success}) return results批量任务还需要考虑以下问题失败重试LLM 接口偶发超时很正常要对单个问题做最多 3 次重试重试间隔指数退避。成本控制批量任务会迅速消耗 token建议设置每日调用上限并在任务前估算 token。结果落盘每完成一条就把结果写入 JSONL 文件防止中途崩溃导致全丢。限流如果调用第三方大模型 API要在本地做令牌桶限流避免触发 API 限速。8.3 Python 批量调用示例import requests import json url http://127.0.0.1:8000/ask/batch payload { questions: [ What does Sikhism say about service?, What is the concept of Naam in the Guru Granth Sahib?, Which chapters mention compassion? ], max_batch_size: 2 } response requests.post(url, jsonpayload, timeout300) with open(batch_output.jsonl, w, encodingutf-8) as f: for item in response.json()[results]: f.write(json.dumps(item, ensure_asciiFalse) \n) print(batch done, saved to batch_output.jsonl)离线索引阶段同样要批量处理。给所有经文文本建索引时建议按章节分片处理每处理完一章写入一次向量库并保存进度避免因为中途 OOM 导致全部重来。9. 资源占用与性能观察这类 RAG 项目的性能瓶颈不固定取决于你把哪个环节放在本地。9.1 本地部署模型时的资源观察如果你选择本地跑 embedding 模型和大模型重点观察这些指标Embedding 模型BGE-M3 这类模型在 CPU 上也能跑但批量编码时会被 CPU 占满单条编码耗时几十到几百毫秒。GPU 上会快很多显存占用通常在 1G 到 2G 左右。大模型推理7B 模型量化后显存占用约 4G 到 8G14B 模型约 10G 到 16G。具体数字以实际模型和量化方式为准。向量检索Chroma 在内存中检索几十万片段的规模下单次查询延迟在几十毫秒级别。如果数据量超过百万建议换 Milvus。BM25 内存索引同样吃内存。10 万篇文档分词后的 term 数量可能很大注意监控内存占用。9.2 降低显存占用的通用策略大模型采用 4bit 或 8bit 量化。用 API 调用替代本地大模型本地只跑 embedding 和检索。控制 batch sizeembedding 编码批量不要太大。给答案生成设置max_tokens上限防止长文本生成时显存和响应时间飙升。空闲时释放显存或使用 vLLM 等推理框架做动态批处理。9.3 延迟链路分析一次完整问答的耗时大致分布环节耗时量级优化方向查询改写200ms - 1s简化改写逻辑或直接跳过向量检索10ms - 100ms换更快的向量库缩小检索范围BM25 检索10ms - 50ms换 Elasticsearch或直接简化重排序50ms - 500ms替换为轻量模型LLM 生成1s - 10s换更小模型或控制输出长度如果响应时间超过 15 秒优先检查 LLM 生成阶段其次检查检索阶段是否有重复计算。10. 常见问题与排查方法RAG 问答系统的问题排查很多坑是共通的。这张表整理了高频问题。问题现象可能原因排查方式解决方案回答明显错误检索返回了错误片段打印中间结果检查检索 Top 5 的片段是否包含正确答案增加重排序调整混合检索权重回答引用了不存在的章节模型幻觉检查模型输出中的引用是否在检索结果中存在加入引用校验逻辑必要时重试生成向量检索效果差Embedding 模型不适合语种或领域抽样查看相似度排序结果换用多语种 embedding 模型或微调启动后 8000 端口被占用端口冲突检查端口进程换端口启动经文文本切分后语义断裂按字符硬切导致句子不完整检查切分点是否打断句意按段落标记优先切分加 overlap批量任务中途失败网络超时或 token 超限查看错误日志加重试按批次落盘显存不足模型量化等级不够或批量过大查看nvidia-smi显存占用换更低比特量化减 batch size回答总是重复“无法回答”检索片段过少或提示词约束过强检查检索召回数尝试放宽 top_k增加 top_k到 8~10调整提示词API 调用报 401API 密钥无效检查请求头和密钥配置重新配置环境变量这里特别提醒一个经验当回答质量下降时不要急着换大模型先看检索结果。RAG 的下限由检索决定上限由生成模型决定。检索没有返回正确内容换多大的模型都救不回来。把中间过程可视化最直接的调试方式是把检索到的前 5 个片段打印出来人眼看一下是不是相关内容问题往往一眼就能看出来。11. 最佳实践与使用建议最后整理一套实用的工程建议适合所有 RAG 问答项目。11.1 先做检索评估再做生成评估启动项目后先不要测“回答得好不好”先测“检索得准不准”。准备 50 到 100 条带标准答案的测试集记录检索 Top 5 命中率。只有命中率达到 70% 以上生成阶段才有意义。11.2 维护最小可运行配置把环境依赖、模型名称、数据路径、端口等写入一个固定的配置文件。这样换机器时可以直接复现。# config.yaml embedding_model: BAAI/bge-m3 llm_model: gpt-4o-mini vector_db_path: ./gurbani_db data_path: ./data/gurbani_texts host: 127.0.0.1 port: 8000 top_k: 5 temperature: 0.3 max_tokens: 100011.3 目录和文件分类管理数据文件、临时文件、日志、输出结果分开存放避免混在一起。data/ 原始经文清洗后的文本 logs/ 运行日志 db/ 向量数据库目录 output/ 问答结果导出 config/ 配置文件 scripts/ 批量脚本11.4 接口访问控制如果 API 暴露到公网要做鉴权。最简单的做法是加一个Authorization: Bearer token头服务端校验 token 后再放行。生产环境建议用网关层统一管理。11.5 合规红线做知识类问答项目务必注意涉及经典注释和翻译时确认版权不要输出未经验证的“官方解读”在界面上明确提示“AI 回答仅供参考”。如果平台涉及用户上传内容还要做好内容审核和隐私保护。11.6 效果复核机制批量生成内容后不能直接发布必须有一个人工复核环节。对经文类项目尤其如此。建议为每条输出增加“来源片段 置信度评分”字段复核人员可以快速核对引用是否真实。12. 总结与下一步Sikhbani.ai 这类项目最值得关注和借鉴的不是某个神奇模型而是一套非常典型的 RAG 工程落地思路长文本切分、混合检索、重排序、引用校验、幻觉控制。这套方法完全可以用在其他知识密集型场景里比如企业问答、法律检索、医疗科普、二手研究等等。如果你准备动手复刻或者做一个类似项目第一步应该做什么我的建议是先搞定“数据准备 混合检索 检索评估”这三件事不要急着接大模型。等检索命中率稳定了再接生成和引用校验。最容易踩的坑也在这里——很多人一上来就把大模型接好结果回答看起来流畅但一问出处就对不上最后返工成本很高。后续可以继续扩展的方向包括引入更专业的 reranker 模型、用多轮对话改写提高追问准确率、增加经文双语对齐展示、把检索结果做成知识卡片、以及用更轻量的前端框架封装成可直接使用的 Web 应用。架构上这套东西是高度可复用的Sikhbani.ai 只是其中一个有代表性的案例。建议把本文收藏备用尤其是“幻觉专项测试”和“常见问题排查”两个章节落地时大概率用得上。