拓冰建站拓冰建站
首页 / 资讯中心 / 正文

RAG、KAG与CAG对比:从原理到Mac知识库搭建实战

RAG已经火了好几年了但最近圈子里又开始密集出现KAG、CAG这几个新缩写。我最初看到KAG和CAG的时候也愣了一下毕竟RAGRetrieval-Augmented Generation检索增强生成的坑还没完全填平怎么又冒出来两个变体后来花时间把三者的前世今生、技术思路、适用场景全部捋了一遍又在Mac上实际搭了一套完整的知识库问答系统才算是真正摸清了它们之间的关系。这篇文章不打算写成学术综述就按我自己的理解路径来讲RAG到底卡在哪、KAG和CAG分别想解决什么问题、最后再附上我在Mac上从零搭建RAG知识库的完整操作以及一路踩过来的坑。看完你至少能分清什么时候该用RAG、什么时候更适合上结构化知识库、什么时候干脆别折腾直接用CAG的笨办法反而更香。1. RAG到底在解决什么问题又卡在什么地方1.1 RAG的基本链路和我们最初对它的期待RAG的初衷其实特别朴素大模型的参数知识是训练时固化下来的你没法指望它记住你公司内部那些散落在几十个Wiki、几百份技术文档里的具体细节。RAG的做法是在模型回答之前先从一个外部知识库中检索出和问题最相关的片段把检索结果塞进上下文让模型看着资料回答。这一套链路拆开来看就是四个环节文档解析把PDF、Word、Markdown变成纯文本→ 文本切分chunking把长文切成带重叠的小段→ 向量化用embedding模型把每个chunk变成向量→ 检索排序把用户问题也向量化在向量库里做相似度检索。我早期在项目里搭的第一版RAG走的正是这条标准流程核心代码也就几十行from langchain_community.vectorstores import Chroma from langchain_community.embeddings import OllamaEmbeddings from langchain.text_splitter import RecursiveCharacterTextSplitter # 1. 切分文档 splitter RecursiveCharacterTextSplitter(chunk_size500, chunk_overlap50) docs splitter.split_text(original_text) # 2. 向量化并入库 embeddings OllamaEmbeddings(modelbge-m3) vectorstore Chroma.from_texts(docs, embeddings, persist_directory./kb_store)当时我对这套流程的期待是只要把文档喂进去模型就能精准回答任何相关问题。但用下来的真实感受是RAG在广撒网式的开放问答上确实能打——你问什么它都能给你捞出来一段相关文本但离靠谱还有相当距离。1.2 容易被低估的三个瓶颈第一个瓶颈是召回质量不稳定。向量检索本质上是在做语义相似度匹配它理解不了逻辑上的相关性。比如你问这个接口的超时时间默认值是多少如果你的文档里写的是连接建立后若在30秒内未收到响应则触发重试这个chunk在语义上确实相关但如果你的embedding模型不够强或者chunk切分把超时时间默认30秒截断了召回结果就可能丢掉最关键的句子。第二个瓶颈是上下文碎片化。RAG为了控制上下文长度通常会限制召回chunk数量比如top 5。问题是这些chunk可能是从文档不同位置捞出来的彼此之间的逻辑关系没有串联起来。模型看到的是几块断裂的拼图如果问题需要跨段落推理比如A模块调用B模块B又依赖C问整体链路的超时上限是多少经典RAG基本就歇菜了。第三个瓶颈是评估和调优闭环难。我见过太多项目RAG流程跑通了Pipeline搭起来了但问几个刁钻问题就露馅。原因很简单chunk_size的大小、overlap的比例、top_k的取值、embedding模型的选择每一个参数都会显著影响效果而这些参数之间又是耦合的。没有一套好的评测集你根本不知道改动是变好了还是变坏了。注意如果你做RAG只是为了让它能回答问题那它及格很容易但如果你要的是稳定、可信、可解释地回答复杂问题那传统向量RAG单独上阵是不够的KAG和CAG的出现本质上就是冲着补齐这些短板来的。2. KAG用知识图谱给RAG装上逻辑骨架2.1 KAG的核心思想和知识图谱的引入KAGKnowledge-Augmented Generation知识增强生成的出发点很直接既然传统RAG把知识切成了互不关联的碎片那我能不能把知识之间显式的关联关系也存进去答案就是知识图谱Knowledge Graph, KG。知识图谱里节点是实体比如接口A、模块B、超时时间边是关系比如A调用B、B的超时时间是30秒这样当问题涉及多跳关系时可以通过图谱路径直接推理出答案而不是靠向量相似度去猜。我在实际项目里测试过用KG替换纯向量的做法。你问纯向量RAG订单服务调用支付服务支付服务又调用风控服务如果风控超时会影响订单吗它很可能召回两三段相关文本然后靠模型的推理硬凑答案。但换成KG上面的链路是天然存在的图谱路径订单服务 →调用→ 支付服务 →调用→ 风控服务模型只需要沿着图做路径推理回答的准确率和可解释性都上了一个台阶。这套东西在圈子里有很多近亲比如GraphRAG就是把知识图谱和RAG结合而KAG更强调同时利用**结构化知识图谱和非结构化知识原始文本**做联合增强。具体落地时KAG的发展路径基本是从原始文档中用LLM抽取实体、关系、属性构建知识图谱把图谱三元组和原始文本chunk同时入库回答问题时先走图谱检索得到精确的路径证据再辅以文本检索补充上下文背景。2.2 向量知识库和结构知识库的区分以及各自的应用场景很多人会把知识库笼统地理解为一个能查的东西但在RAG语境下向量知识库和**结构知识库KG**是两种完全不同的物种选错了架构后面的调优全是白费功夫。维度向量知识库结构知识库知识图谱/本体存储方式文本chunk的向量表示实体、关系、属性的三元组网络检索原理语义相似度近似匹配图结构上的精确匹配和多跳遍历擅长任务宽泛语义搜索、开放域问答精确事实查询、多跳逻辑推理、可解释问答不擅长任务精确匹配、关系推理、聚合统计语义模糊表述、无明确实体的开放问题构建成本低几乎全自动高需要实体抽取、关系建设、质量校验典型场景文档问答、客服FAQ、知识库搜索企业级业务知识图谱、风控链路、合规审查拿一个常见场景做对比你想查某个销售订单的审批流程涉及哪些角色。向量知识库会把所有提到审批、订单、角色的文本段落捞出来然后让大模型自己总结结构知识库则直接查图谱订单 →属于→ 销售流程 →经过→ 审批节点A角色销售经理→经过→ 审批节点B角色财务总监。后者不仅答案准确还能在回答时给出完整的路径依据。所以我现在的选型经验是如果知识形态是大量非结构化文档问题偏总结、归纳、开放搜索老老实实用向量RAG如果知识形态是实体关系明确、业务流程固定、问题偏精确查询和多跳推理花力气构建KG是值得的如果两者都有那就做混合架构——这也是KAG最典型的落地方案。2.3 Ontology RAG让知识图谱更有规矩搜ontology rag这个热词的人多半已经在往KAG的深层探索了。Ontology本体可以理解为知识图谱的Schema层它定义了实体有哪些类型、每种类型有哪些属性、类型之间允许存在哪些关系。没有Ontology的KGLLM抽取出来的关系可能是混乱不可控的有了Ontology实体抽取和关系映射就有了明确的约束框架。比如你在做一个技术知识库本体里可以规定接口有属性超时时间、请求方式、限流阈值接口和模块之间存在被调用关系模块和模块之间存在依赖关系。LLM在抽取实体关系时就被约束在这些框架内抽出来的三元组质量更高图的一致性也更强。后续做多跳推理时本体还能提供推理规则比如如果A依赖BB依赖C那么A间接依赖C。这种能力纯向量RAG完全没有也是KAG相对RAG最核心的增量之一。实操提醒KG/本体RAG看起来很美但建设和维护成本真的不低。实体抽取的准确率、图谱的更新策略、关系的完整度每一项都直接影响问答质量。如果你只是做个人知识库或者小组文档问答我建议先别急着上KG把2.1和2.2的选型逻辑想清楚再决定要不要投入。3. CAG把检索替换成预加载的另一个思路3.1 CAG是什么它凭什么能避开RAG的检索瓶颈CAGCache-Augmented Generation缓存增强生成这个方向我关注得比较晚但一上手就发现它简直是暴力美学的典范。CAG的核心思路极其简单粗暴既然大模型的上下文窗口越来越大128K、200K甚至更多那我干脆不做检索了直接把相关文档全部塞进上下文里让模型一次性读完再回答。这听起来像是个笨办法但它精准地绕开了RAG最大的三个痛点检索召回不准、chunk切碎导致上下文断裂、多条文档之间的逻辑关系无法串联。CAG在上下文窗口足够大的前提下把检索这一步整个砍掉变成了全量加载一次读完。具体落地时CAG有两个关键优化点。第一个是缓存KV Cache。同一个知识集如果反复被用户问询每次都全量拼接上下文来预填充是不划算的所以CAG会选择把所有知识文本先做一次前向计算把KV Cache缓存下来后面每次新问题只需要复用这个缓存的KV状态只对新增提问部分做预填充。第二个是知识文本的有序组织。虽然不做检索但文档的排版顺序仍然影响模型的理解效果我会按照从总览到细节、从抽象到具体的顺序拼接知识文档实测这个顺序对回答质量有明显影响。3.2 CAG、RAG、KAG三者的适用边界这三者放在一起比较真正有价值的不是谁替代谁而是各自的适用边界。RAG适合的场景是知识库非常庞大几千份文档以上切分可以容忍部分召回噪音要求的回答不需要严格多跳推理CAG适合的场景是知识集固定且规模可控比如几份核心产品文档、几十页操作手册上下文窗口能装下且你有相对确定的问答场景KAG适合的场景是知识的实体关系结构非常明确回答需要精确、可解释、多跳推理且你愿意投入图谱构建和维护的成本。我用一张表来总结一下决策逻辑知识规模问题类型推荐方案理由大海量文档开放搜索、归纳总结RAG检索可以快速缩小范围摊薄成本中可完整装载针对固定文档的精确问答CAG免去检索环节无召回损失中/大多跳推理、关系查询KAG/GraphRAG图谱路径提供明确推理证据混合型复杂业务问答RAG KG混合同时兼顾宽泛检索和精确推理我自己在Mac上做过一个对比实验同一套产品FAQ文档分别用RAGChroma bge-m3和CAG直接把整本FAQ拼进prompt跑了一组问题。结论是在知识规模不大约40页文档的前提下CAG的回答准确率和一致性明显优于RAG而且实现代码简单得惊人——没有向量库、没有检索逻辑、没有chunk调参就是一段把文档读进来拼进Prompt的代码。当然一旦文档量涨到几百份CAG就力不从心了上下文塞不下成本也扛不住。关于CAG的实用提示如果你决定用CAG建议抓主要矛盾——先确认你的上下文窗口充足率是否达到了知识的2倍以上留出模型回答的空间再用KV Cache做预加载优化否则每问一个问题都全量计算一遍延迟和成本都会很尴尬。4. Mac上自己搭一套RAG知识库的完整过程4.1 环境准备与工具选型Mac友好版听说很多人是冲着怎么在mac上搭建rag知识库这个关键词来的。我自己的开发环境是Apple SiliconM2芯片整体跑RAG的体验相当顺畅核心工具链如下LLM主模型Ollama qwen2.5:7b也能直接跑llama3.1:8b本地推理免费且隐私安全Embedding模型Ollama bge-m3中文效果好兼容好也可以换成nomic-embed-text向量数据库Chroma轻量级嵌入式运行适合个人项目和原型验证编排框架LangChain或者LlamaIndex新手建议先用LangChain的LCEL表达式代码直观文档加载LangChain社区的PyPDFLoader、MarkdownTextSplitter等。安装环节其实没什么神秘的先装Ollama再拉模型# 安装Ollama也可以用官网下载器 brew install ollama # 启动服务并拉取所需模型 ollama serve ollama pull qwen2.5:7b ollama pull bge-m3注意如果你第一次跑Ollama拉取模型尽量确保网络稳定模型文件比较大中断后可以用ollama pull重新执行续传。4.2 文档加载、切分和入库的详细操作这一步是整个RAG知识库的地基。我在实际项目中踩过一个非常深的坑不预先清洗文档解析出来的文本全是页眉页脚、代码残留、表格错位。所以我现在都会先做一轮文本清洗再入库效果提升非常显著。下面是一段可以直接复用的完整入库代码核心步骤是加载文档→按结构切分→清洗过滤→向量化→写入Chromafrom langchain_community.document_loaders import PyPDFLoader from langchain_community.embeddings import OllamaEmbeddings from langchain_community.vectorstores import Chroma from langchain.text_splitter import RecursiveCharacterTextSplitter import re # 1. 加载PDF文档 loader PyPDFLoader(./product_manual.pdf) raw_docs loader.load() # 2. 合并全文并清洗噪音文本 full_text \n.join([doc.page_content for doc in raw_docs]) full_text re.sub(r\n{3,}, \n\n, full_text) # 合并多余空行 full_text re.sub(r[ \t]{2,}, , full_text) # 合并多余空格 # 3. 按语义结构切分chunk_size600, overlap100 splitter RecursiveCharacterTextSplitter( chunk_size600, chunk_overlap100, separators[\n\n, \n, 。, , , , ], ) chunks splitter.split_text(full_text) # 4. 向量化并持久化到本地 embeddings OllamaEmbeddings(modelbge-m3) vectorstore Chroma.from_texts( chunks, embeddings, persist_directory./kb_product, collection_nameproduct_manual, ) vectorstore.persist() print(f入库完成共 {len(chunks)} 个chunk)这段代码里有几个细节参数值得展开说说。chunk_size为什么设600这个值不是随手写的。chunk太小比如200单段信息量不足检索时经常只命中答案的半个片段chunk太大比如1200向量表示的语义容易被稀释而且会占掉大量上下文空间。600对于大多数技术文档是一个比较稳妥的中间值。chunk_overlap设100又是为什么因为切分点容易把关键句从中间截断overlap的作用是把前后文的衔接信息多保留一点给检索兜底。separators的排列顺序也很关键。RecursiveCharacterTextSplitter会按照我给的优先级依次尝试切分先按段落双换行再按单换行然后按句号、分号、逗号最后实在不行才按空格或硬切。这样切出来的chunk基本能保持语义完整性而不是硬生生按字数剁碎。入库完成后我强烈建议先做一次检索验证不要急着写问答链路。用下面这几行代码确认向量库能正确返回相关内容query 登录超时时间默认值是多少 results vectorstore.similarity_search_with_score(query, k3) for doc, score in results: print(fscore: {score:.4f} | content: {doc.page_content[:120]})如果返回的前3条都跟问题强相关说明知识库的索引质量是达标的如果全是无关内容不用怀疑多半是切分参数或embedding模型出了问题。4.3 问答链路的实现知识库准备好了剩下的就是搭问答链路。我用的LangChain LCEL方式代码非常直观检索器把相关chunk拿回来组装成上下文交给LLM生成回答。from langchain_community.llms import Ollama from langchain.prompts import PromptTemplate from langchain_core.runnables import RunnablePassthrough # 1. 加载已有向量库 from langchain_community.vectorstores import Chroma embeddings OllamaEmbeddings(modelbge-m3) vectorstore Chroma( persist_directory./kb_product, embedding_functionembeddings, ) # 2. 构造检索器取top 4 retriever vectorstore.as_retriever(search_kwargs{k: 4}) # 3. 定义提示词模板约束模型基于上下文回答 template 你是产品文档问答助手。请仅依据以下知识上下文回答用户问题。 如果上下文中没有相关信息请直接回答“资料中未找到相关信息”不要编造。 【知识上下文】 {context} 【用户问题】 {question} prompt PromptTemplate.from_template(template) llm Ollama(modelqwen2.5:7b, temperature0.2) # 4. 组装LCEL链路 def format_docs(docs): return \n\n---\n\n.join([d.page_content for d in docs]) rag_chain ( {context: retriever | format_docs, question: RunnablePassthrough()} | prompt | llm ) # 5. 测试问答 print(rag_chain.invoke(登录超时时间默认值是多少))这里我故意把temperature调到0.2而不是0。有些教程会让你设成0但实测下来LLM完全确定性的输出反而容易让表述变得生硬0.2的随机性可以让回答更连贯同时不会明显牺牲准确性。如果是对事实准确性要求极高的场景可以再往下压到0.05。整个链路跑通后,我一般会用一个10到20条问题的测试集去验证效果。注意测试集要覆盖不同难度简单问题直接可从单chunk找到、中等难度需跨chunk拼接、困难问题需多跳推理。这样才能真实评估知识库到底能不能打。5. 我在实际项目里踩过的坑问题排查实录5.1 chunk参数和检索结果的坑问题1检索结果经常命中无关内容。后来排查发现两个主因一是文档清洗不够干净很多无效文本版权声明、页眉信息被当成有效知识入库二是embedding模型对中文长文本的区分度不足。解决办法是把清洗逻辑前置同时切换chunk策略不按固定字数切而是尽量按段落切保证每个chunk内部主题一致。问题2top_k到底取多少合适我是经历了大起大落的。最开始取top 2回答经常信息量不足调到top 8上下文一下子被无关内容塞满模型开始被带偏甚至引用不相关段落。最终稳定在top 4到top 5之间上下文质量最高。你需要理解的是top_k并不是越大越好它决定了上下文信噪比而大模型的注意力很容易被无关信息干扰。问题3不同文档之间主题相近怎么防止检索串味比如一个库里既有技术文档又有销售FAQ用户问这个产品多少钱技术文档里提到的成本优化就可能被误召回。我的处理方式是给每个chunk加metadata元数据标签来源、文档类型检索时用filter参数限定文档范围精度明显提升。5.2 硬件与embedding模型的现实问题问题4Mac内存不够怎么跑本地大模型我的M2 MacBook Pro建议把Ollama的模型量化版本控制在4bit或6bit。qwen2.5:7b在量化后大概占用5GB左右内存配合32GB内存的机器跑起来不会有明显卡顿。如果你的Mac只有16GB内存建议换qwen2.5:3b或更小的模型否则Ollama会疯狂占用swap速度和体验都会崩。问题5中文语义搜得不准可能是模型没选对。我之前用all-MiniLM-L6-v2跑中文知识库结果检索质量惨不忍睹。换bge-m3之后召回质量提升了一个档次。embedding模型和主流LLM是两套生态不能图省事随便挑特别是中文场景领域专属embedding模型的差距会直接体现在问答准确率上。问题6Chroma本地持久化偶尔数据不完整。我遇到过重启后向量库内容丢失一部分的情况原因多半是持久化目录没有正确落盘。稳妥做法是在每次写入后显式调用persist()或者干脆用Chroma(persist_directory...)时确认存储路径存在。如果知识库体量再大一点可以考虑上Docker版PostgreSQLpgvector但对Mac个人项目来说Chroma完全够用。5.3 KAG和CAG方向的扩展避坑问题7KAG类项目上手的最大阻力是什么不是LLM抽取实体的效果而是图谱的schema设计。我最初做KG抽取时没有定义清楚本体约束结果实体类型五花八门关系混乱不堪查询时根本没法用。建议是先花一天时间把本体的类别层级、关系类型、属性约束全部定好再用LLM做抽取而不是让LLM自由发挥。这一步做得越细后面的推理效果越好。问题8CAG项目最容易犯什么错把不检索理解成无脑拼接。我在测试CAG时发现如果知识文本的顺序混乱、首尾割裂模型仍然会漏掉关键信息。CAG里的文本组织本身就是一种检索——只不过由用户/开发者手动完成。另外KV Cache在商业API中并不总是可用的需要确认你的模型服务是否支持缓存复用否则全量预填充的算力开销会让你怀疑人生。结尾我最初对RAG、KAG和CAG也是一知半解但把这三条路线全部实际跑过一轮之后最大的体会是没有任何一种方案是银弹。RAG擅长广撒网但怕碎片化KAG擅长精确推理但建造成本高CAG简单直接但只适合中小规模固定知识集。不要一上来就追求最高级的架构先把你的知识形态和问题类型梳理清楚再回头看选型通常都能找到最匹配的答案。最后分享一个小技巧。无论你选哪种方案建议维护一个人工的评测问答集每次改动之后都跑一遍把回答结果逐条检查。RAG类项目翻车大多不是因为模型不够强而是因为改了参数或数据之后不知道它到底是变好了还是变坏了。一套固定的评测集就是你的安全带越早建立后面越稳。
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门