RAG中的Schema设计:从元数据到知识图谱的完整解读 RAG做到后面很多人会发现一个奇怪的现象Embedding模型换了好几个Chunk策略调了一轮又一轮Prompt模板改得都快认不出来了可检索质量始终卡在80分上不去。问题不在模型也不在提示词而是在索引构建之前那个最容易被忽略的环节——Schema。Schema这个词听起来像是数据库工程师的专属名词但在RAG链路里它的含义要宽得多。它不光是关系表里的字段定义还包括元数据字段设计、向量库中的数据组织形式、知识图谱中的本体定义。可以说RAG项目的上限由Embedding决定下限却由Schema兜着。这一节的内容就是把“1.4 RAG中的Schema”这件事彻底讲透它到底在RAG链路里起什么作用怎么设计才能避免后面反复返工以及实际项目中那些文档里不会告诉你的坑。1. 为什么要单独谈RAG中的Schema整体定位与设计思路1.1 RAG链路里Schema到底卡在哪一环先把RAG的标准链路铺开文档解析、清洗、分块、元数据抽取、向量化、入库然后是召回、重排、生成。很多人下意识地把注意力放在“向量化”和“召回”这两步因为这两步最直观效果好不好一眼就能看到。但实际操作下来你会发现一个规律索引阶段没想清楚的东西到了检索阶段一定会以诡异的方式暴露出来。举个例子。你辛辛苦苦把几万份文档切好块、灌进向量库上线之后运营同学说“我要按部门过滤”你打开代码一看发现当初压根没存部门这个字段。这时候能怎么办要么全量重新解析一遍文档重新灌索引要么在前端硬筛——可你连字段都没有拿什么筛这就是典型的Schema设计缺位。Schema在RAG链路里的位置本质上是连接非结构化数据和结构化检索规则的桥梁。文档解析完了要抽哪些元数据字段字段类型是什么允许的取值有哪些哪些字段参与检索过滤哪些字段只是展示用这些问题在索引构建之前就应该全部定下来。RAG流程里Schema决定了两个关键动作一是写库时数据以什么形态落盘二是读库时系统按什么规则取数。写库和读库的规则如果不一致轻则过滤失效重则整条检索链路都跑偏。1.2 三种Schema的分类与选型RAG场景中的Schema按数据形态可以分成三类这三类也是这一节后续内容的骨架。第一类是元数据Schema描述文档本身的属性。比如标题、作者、发布日期、所属部门、文档类型、权限级别、标签。它挂在每个Chunk上面召回后用来做过滤和路由。这是最常见的形态几乎所有RAG项目都要做。第二类是关系表Schema也就是传统数据库里的表结构定义。适用于“用户提问系统查库返回结构化结果”的场景通常配合Text-to-SQL一起用。用户问“上季度华东区销售额是多少”系统把问题转成SQL去数据库里查再把结果交给模型回答。这个场景里表Schema的质量直接决定SQL生成得准不准。第三类是知识图谱Schema也叫本体定义。适用于GraphRAG或基于知识图谱的问答场景需要定义实体类型、属性、关系类型和约束。比如“某公司内部的智能问答系统把人员、项目、文档做成一张图回答跨实体的复杂问题”。三类Schema的定位差异很大选型时主要看业务问题和数据形态。为了直观我整理了一个对比表格Schema类型核心对象落地位置主要价值典型问题元数据Schema文档属性字段向量库的标量字段或独立元数据库检索前过滤、多路路由字段缺失、命名混乱、类型不匹配关系表Schema表、列、类型、注释、外键关系型数据库精确查询、聚合计算列名歧义、枚举缺失、注释不完整知识图谱Schema实体、属性、关系、约束图数据库或GraphRAG索引多跳推理、全局问答节点爆炸、关系冗余、实体合并困难很多RAG项目并不是只用一种Schema。比如一个企业知识库文档问答走元数据过滤统计报表走关系表查询跨部门人员关系走图谱问答三条线并存。这个时候三类Schema要有统一的设计规范否则各自为政后期整合成本非常高。1.3 Schema先行原则先定结构再灌数据做过几次RAG落地之后我有个很深的体会RAG项目里Schema设计应该先于Chunk策略和Embedding选型。不少人习惯先跑通DemoEmbedding模型随便选一个Chunk大小拍脑袋定个500向量库建个Collection就往里灌数据。Demo阶段没事一上真实数据就崩——各种元数据对不上、过滤字段不存在、文档版本混乱。最后只能全部删库重建。我把这个原则叫“Schema先行”。启动RAG项目时第一周不急着写代码先把下面几个问题确认完业务上需要按哪些维度过滤检索结果哪些维度是高频的有没有多租户或权限隔离的需求谁来定义数据可见范围文档需要区分版本吗新旧版本冲突时保留哪个检索结果里需要展示哪些辅助信息这些信息从哪里来这几个问题全部落到纸面上形成一份Schema设计文档再开始做解析和索引构建。后面所有环节都围绕这份Schema展开。别嫌这一步慢后面省下来的返工时间起码是十倍。2. 元数据Schema检索前最有效的围栏2.1 从业务问题反推字段而不是堆字段元数据Schema最常见的误区是字段越多越好。文档里的信息全抽一遍标题、作者、创建时间、修改时间、文件大小、页数、审批人、关键词……全塞进去。结果检索的时候真正用到的就那么三五个字段剩下的既占存储又增加解析的出错概率。正确的做法是从业务问题反推字段。你把用户可能会问的问题列一遍逐个拆解这些问题落到检索上需要什么约束条件。比如“查2024年发布的安全规范”就需要发布时间字段“找A部门最近的培训材料”就需要部门字段和发布日期字段“这份文档能不能给我的合作伙伴看”就需要权限字段。我在项目里一般会列一个字段设计Checklist稳定性标识字段文档的唯一ID必须幂等。推荐用UUID或内容哈希不要用自增ID。业务维度字段部门、项目、业务线、标签按实际业务来定。生命周期字段发布日期、生效日期、失效日期过滤时非常常用。权限控制字段可见范围、密级、访问控制列表多租户场景必备。审计追踪字段上传人、最后修改人、版本号排查问题的时候靠它们还原现场。字段类型的选择也要动脑子。日期字段坚决不用字符串不然“2024-1-5”和“2024/01/05”会同时存在排序和范围过滤全部错乱。枚举字段要定死取值范围不能今天写“部门A”明天写“A部门”。布尔字段命名要能看懂is_confidential就比confidential_flag清晰得多。2.2 一个可落地的元数据Schema示例说个我们实际用过的简化版元数据Schema字段不多但覆盖了过滤、路由、权限三类核心场景。用字段表展示字段名类型必填说明doc_idstring是文档唯一标识使用内容哈希重建时不会产生重复数据org_idstring是组织/租户标识多租户硬隔离字段categorystring否文档类别如“制度”“规范”“FAQ”tagarray否标签列表用于多维度交叉过滤ownerstring否文档负责人用于“查某人负责的材料”这类需求access_levelint是密级字段0公开、1内部、2机密过滤时按阈值控制versionstring是版本号如“v1.2”决定多版本文档的取舍逻辑published_atdate否发布日期时间范围过滤的核心字段created_attimestamp是入库时间排查问题时定位数据批次这个Schema里有两个字段是我特别想说明的。一是doc_id用哈希而不是自增ID这样同一份文档更新后重新解析只要内容没变生成的doc_id就不变去重逻辑会简单很多。二是access_level用数字而不用字符串“机密”“秘密”“内部”这种中文枚举看起来直观但实际过滤时“机密”和“秘密”谁级别高很难比较。数字可以直接做范围过滤大于阈值的一方直接不返回省掉一层逻辑判断。2.3 过滤下推与多租户隔离的实现元数据字段设计好之后怎么在检索时用起来是另一个关键问题。这里有一个概念必须搞清楚前置过滤还是后置过滤。前置过滤是在向量检索之前先用元数据条件缩小候选集再去里面做向量相似度匹配。比如用户是A部门的那就先在org_iddept_A的范围内做向量检索。后置过滤则是先向量召回一批结果再在应用层把不符合元数据条件的Chunk删掉。后置过滤写起来简单但埋着一个大坑向量召回只找了全局最近的Top K压根没考虑过滤之后还剩多少。如果过滤条件很苛刻比如真机上百万份文档里只有几十篇属于目标部门全局Top 20可能全都不在目标部门里过滤完后返回空结果。这就是典型的召回率崩塌。正确做法是尽量把元数据过滤下推到向量数据库里。主流的向量数据库都支持在相似度搜索时附带标量过滤条件在底层就完成候选集裁剪。实在做不到那就把元数据过滤和向量检索合并到一个混合查询里用BM25之类的稀疏检索补一刀别一条路走到黑。元数据Schema不光是字段定义还决定了过滤能不能在底层执行。设计字段时就要考虑这个字段在后端怎么建索引、怎么参与Filter别设计出来一堆没法检索的字段。多租户隔离是另一个元数据Schema的典型应用。最简单的方案是每个租户独立的Collection互不干扰但租户数量一多管理成本和存储成本都会膨胀。更常见的做法是共享Collection通过org_id字段做强制过滤再把org_id纳入主键的一部分防止跨租户的数据串味。3. 结构化查询中的表Schema让模型自己写SQL3.1 Text-to-SQL在RAG中的位置文档类RAG解决的是“知识在哪、答案是什么”的问题但对于“上季度各区域销售额排名”“最近一个月故障工单数量变化趋势”这类需要精确计算的问题文档检索就使不上劲了。这就要用到结构化的RAG用户输入自然语言大模型把它转成SQL去数据库里执行查询返回结果后组装答案。这条链路里表Schema就是大模型的“眼睛”。模型压根不知道你的数据库里有哪些表、每个表里有什么列、列里存的是什么枚举值它只能看到你喂给它的定义。你喂什么它就基于什么写SQL。喂一份含糊其辞的表结构写出来的SQL大概率也是含糊的。我见过最典型的翻车现场业务表里存的是数字状态码0和1模型不知道这回事用户问“有多少订单已支付”模型写了WHERE status 已支付数据库里根本没有这个值直接报错或者返回空。问题不在模型而在Schema没有把枚举值映射说清楚。表Schema设计里的每个细节最后都会反映在SQL质量上。3.2 自解释的DDL设计要点为了让大模型准确生成SQL表Schema设计首先要遵循一条核心原则自解释。也就是不依赖额外文档只看表结构就能理解每个字段的业务含义。列名是关键。表里的列名能写清楚绝对不缩写。product_name就是product_name别整个prd_nm。虽然有些数据库表设计规范要求列名精简但在RAGText-to-SQL场景里语义明确的列名能显著降低模型误解。按区域聚合的表列就写region、sales_amount别写r、amt。字段类型同样重要。金额字段用DECIMAL还是FLOAT日期字段是DATE还是DATETIME这些差别会直接影响SQL生成的写法。模型看到DECIMAL类型的金额通常不会乱写字符串比较看到FLOAT类型的字段可能会写出不严谨的等值判断。尽量用精确类型给模型明确的边界。COMMENT注释是自解释设计里最核心的部分。大模型读取表信息主要依赖列注释来理解字段业务含义。一份好的列注释应该包含完整的枚举值列表和单位信息。比如状态字段的注释可以写成订单状态枚举值为pending(待支付)、paid(已支付)、shipped(已发货)、cancelled(已取消)。单位信息直接写在注释里金额单位为人民币元日期均指订单创建日期。下面给一个示例DDL这个表结构我们测下来是比较稳的CREATE TABLE sales_order ( order_id BIGINT PRIMARY KEY COMMENT 订单号, customer_name VARCHAR(128) COMMENT 客户名称如北京某公司, region VARCHAR(64) COMMENT 销售区域枚举值华东、华南、华北、西南, product_category VARCHAR(64) COMMENT 产品大类枚举值家电、数码、家具、服饰, product_name VARCHAR(256) COMMENT 产品全称, order_amount DECIMAL(12,2) COMMENT 订单金额单位人民币元, status VARCHAR(16) COMMENT 订单状态枚举值pending(待支付)、paid(已支付)、shipped(已发货)、cancelled(已取消)、refunded(已退款), order_date DATE COMMENT 下单日期格式YYYY-MM-DD );注意每个字段的注释里我都刻意加了“单位”“格式”“枚举值”这类信息这比干巴巴一句“订单日期”管用得多。模型在写SQL的时候会把这些信息当作语料来推理。3.3 把Schema喂给LLM的正确姿势表Schema设计好之后接下来是提示词里组织Schema的问题。直接把整个库的几十张表结构全塞给模型既占Token又容易让模型迷路。正确做法是每次查询只提供相关的表结构或者用“表结构少量示例”的方式组织。一个标准的系统提示词至少要包含下面几块表结构信息符合实际业务的DDL或简化字段清单枚举值映射字段注释里出现过还好没有就得单独列一份查询限制规则只允许SELECT禁止UPDATE/DELETE必须带LIMIT少量示例同一个业务场景下2到3组问题转SQL的例子示例的作用最容易被人低估。给模型一个参考范式它倾向于模仿参考范式来写答案。用户问“上季度华东区销量前10的产品”你给的示例里写过类似的问题怎么转化模型就会照葫芦画瓢而不是凭空发挥。还有一个很重要的技巧把公共的业务固定写法写到Schema旁边。比如“所有时间过滤条件统一用DATE(col)...”这种规范直接写进提示词里能大幅减少时间条件写错的概率。用户问“最近三天”不同模型对“最近三天”的理解不一样有的写INTERVAL 3 DAY有的写DATE_SUB(NOW(), INTERVAL 3 DAY)还有的直接把当前日期写死。你把这些规则在提示词里提前声明就不会出现这种五花八门的写法。3.4 外键、枚举与同义词的补全表Schema里还有一种常见问题业务用户说的话和表里的术语对不上。用户说“客户”表里叫customer_name用户说“商品”表里叫product_name用户说“买了多少”表里叫order_amount。模型不一定能把这些同义词串起来。解决这个问题我用的办法是在Schema后面补一张同义词映射表。类似这样用户常用说法标准字段/表名使用方式客户、顾客、买方customer_name用LIKE模糊匹配或等值过滤销售额、卖了多少钱SUM(order_amount)聚合函数订单状态、是否完成status枚举值过滤这几周、最近一段时间order_date配合DATE条件过滤这张映射表本身也要进提示词让模型在写SQL之前先做一步“翻译对齐”。实测下来加了这一步之后SQL生成准确率提升非常明显。特别是涉及多个业务术语的场景模型不再自己脑补字段名了。外键关系也要在Schema里体现。两张表之间有关联却没在提示词里说明模型就不知道该用什么JOIN条件。最简单的方式是在字段注释里写明外键指向比如customer_id字段注释后面加一句“关联customer表的id字段”。这一句话能省掉排查JOIN条件的半天时间。4. 向量库中的Schema字段类型、度量方式与索引参数4.1 字段类型与主键设计向量数据库是RAG项目最核心的存储组件它的Schema设计直接影响检索效果和运维成本。不同向量数据库的结构不太一样但核心概念类似Collection一个逻辑上的数据集合里面定义字段类型和索引参数。设计向量库Schema第一件事是确定主键。强烈建议用业务幂等ID也就是从业务上能唯一确定数据的ID比如文档的doc_id加上Chunk序号doc_7f3a_chunk_002。不要用自增数字当主键。你的数据如果经历了重新解析自增ID会变同一个Chunk会变成完全不同的ID后面做增量更新、去重、关联引用记录时全部对不上号。第二件事是定义向量字段和标量字段的边界。向量字段存储Embedding标量字段存储元数据。有一个小原则参与过滤的字段、要在引用中展示的字段必须拆成独立的标量字段只是随数据带来、不太会用到的信息可以合并成一个JSON字段存起来别一个信息一个字段搞出一大堆冗余列。第三件事是向量维度。这个值取决于Embedding模型比如有些模型输出768维有些输出1024维有些更狠直接上3072维。维度在Collection创建后就固定下来了原则上不能改。要换Embedding模型就要重建整个Collection或者做双向量字段别名切换。所以选Embedding模型的时候就要想清楚别今天用A模型下周换B模型每次换模型都等于一次全量重建。4.2 距离度量方式的选择逻辑向量库Schema里另一个容易忽略的点是距离度量。常见的有欧氏距离L2、内积IP、余弦相似度Cosine。选哪个不是随缘的要看你用的是什么Embedding模型以及向量是否归一化。如果你的Embedding模型输出向量做了归一化那Cosine和IP在数学上等价选哪个都行但主流向量库一般建议用IP因为IP计算更快。如果你的Embedding向量没做归一化Cosine相似度更稳妥因为它不受向量长度影响只关注方向。文本Embedding模型大多会做归一化但多模态模型和部分开源模型不一定需要确认一下再定。索引参数也和度量方式有关。以常见的HNSW图索引为例有两个关键参数M控制每个节点的最大连接数M越大图越密集召回率越高但内存开销和构建耗时都会涨efConstruction控制构建索引时的搜索宽度越大索引质量越好但构建时间越长。实际项目中M给16到32efConstruction给100到200属于一个比较折中的区间。线上检索时的efSearch参数可以在查询时动态调先给个100跑通流程再根据时延和召回率调。这些参数听起来很枯燥但它们确实会直接影响最终效果。我见过一个项目全员默认参数一把梭索引参数完全没调结果召回率忽高忽低。后面把M从16调到32efSearch从100调到200效果立刻稳定下来。4.3 分区、别名与多租户的实践向量库Schema设计还牵涉到数据组织方式。多租户场景下是在Collection里加一个租户字段做过滤还是按租户拆Collection或者用分区来做隔离需要根据数据规模权衡。租户数量少几十个以内、数据量悬殊不大用租户字段加过滤是最便捷的。代码层面保证每次查询都带上租户过滤条件就行。但要注意漏加过滤条件的代码只要发生一次就可能造成跨租户数据泄露。这个风险要在查询封装层就堵死写一个统一检索入口强制注入租户条件。按租户拆Collection隔离效果最好运维成本也随之上升。分区则是折中方案数据物理隔离但共享同一个集群。还有一点值得重视别名切换。向量库的Collection不能直接改名或改Schema需要重建。一批新数据重新构建完索引之后怎么切流量而不中断线上服务用别名机制。Collection构建完成后将别名从旧Collection指向新Collection查询端只认识别名就实现了无缝切换。这套操作在元数据Schema调整、Embedding模型升级、向量维度变更时都会用到属于RAG运维的基本功。5. 知识图谱RAG的Schema本体设计决定图质量5.1 GraphRAG为什么要先定义本体GraphRAG这类做法最近讨论很多思路是把文档实体抽取出来建成图然后用社区检测、图遍历等方式做检索增强。但很多人第一次跑通GraphRAG流程后会发现抽出来一堆乱七八糟的东西有的节点是人有的是组织有的是地点还有的是文档标题关系也各式各样。图查询的结果让人完全没法用。问题几乎都出在没定义本体。本体就是知识图谱的Schema它规定了图里有哪些类型的实体、实体有哪些属性、实体之间能建立哪些类型的关系、每个关系连的实体类型是什么。没有这些约束大模型在抽取时的自由度太大它会把任何有语义关联的短语都抽成关系。加一层本体约束等于给抽取过程上了紧箍咒模型只能按照预定义的范畴来提取信息。GraphRAG的效果很大程度上不是由模型抽取能力决定的而是由本体设计的质量决定的。本体设计得清晰社区发现、实体消歧、多跳推理都会轻松很多。5.2 一个本体Schema的示例在项目里我通常用“实体类型-属性-关系类型-约束”四个维度来描述本体Schema。下面是一个内部知识库的简化示例实体类型Person人员属性包括id、name、title、departmentOrganization组织属性包括id、name、industry、levelProject项目属性包括id、name、status、start_date、end_dateDocument文档属性包括id、title、category、published_at关系类型Person - works_at - Organization人员就职于组织Person - collaborates_with - Person人员之间协作关系Person - participates_in - Project人员参与项目Document - references - Project文档引用项目Organization - cooperates_with - Organization组织间合作关系约束声明每个实体必须有id和name没有name的实体直接丢弃抽取时的实体重要度低于0.6的节点不建阈值在抽取阶段控制一次抽取进程中同名实体优先复用已有节点而不是新建节点关系只在本体定义的类型里选不抽取本体之外的自由关系这个约束列表特别重要尤其是“同名实体复用”这一点。如果不加这条同一家公司在不同文档里出现三次就会被建成三个孤立节点等要做跨文档推理时图是零散的社区发现完全失去意义。5.3 本体抽取任务的落地方式本体定义好之后怎么让它生效做法是在抽取环节给大模型一个结构化抽取指令告诉模型有哪些实体类型和关系类型让模型从文本片段中抽取符合预定义类型的实体和关系输出为JSON格式。一次抽取会话中模型被要求严格按照给定的本体来找信息不在本体范围内的信息就不抽。实际操作中还有一个提升抽取质量的小技巧在抽取提示词里加“否定示例”。比如明确告诉模型“不要创建Organization类型的自我引用关系”、“不要将文档标题抽取为实体标题只用来生成Document节点”。这些负面约束能让模型少走弯路。做过几轮测试后抽取结果的准确率提升非常明显。图谱维护的问题同样绕不开。文档持续更新意味着需要增量抽取而不是每次全部重建。增量抽取时新抽取的实体和旧图里的实体如何合并最简单的方案是先用统一的实体名称做哈希匹配匹配不上再借助模型的语义判断再不行才创建新节点。规则比模型好调试能用规则解决的就不动用模型。6. 常见问题与排查技巧实录6.1 元数据过滤失效“过滤条件没生效”现象是查询时明明在代码里加了过滤条件结果却还是返回了不符合条件的内容。或者反过来原本应该有几条结果的过滤完变成0条。这一类问题90%出在字段名不一致。元数据写入时用的字段名和查询端Filter里写的字段名对不上比如写入端存量数据用的是access_level查询端Filter写的是confidential_level自然永远过滤不到。另一个常见原因是数据本身缺字段刚上线时的历史文档没有后来新增的字段过滤条件一加这些旧文档全部命中不了。排查思路先在向量库管理后台看一眼实际数据的字段分布确认字段是否真的存在、类型是否匹配再写一个空过滤条件的查询打日志看原始返回结果里字段的值最后看过滤条数和无过滤条数的差值判断是过滤逻辑的问题还是索引数据的问题。这类问题最好在索引构建阶段就堵住解析流程里加一步字段完整性校验缺必填字段的文档直接进死信队列。6.2 Text-to-SQL的SQL写错“列名不存在”或“过滤值不对”现象模型生成的SQL执行时报列名不存在或者能执行但结果明显不对。列名不存在的常见原因是提示词里给的表结构和实际数据库不一致。有些情况下数据库表做过字段调整但提示词里拷贝的还是旧DDL有时候是表名对上号了但列名缩写太严重模型没法理解。过滤值不对的常见原因则是枚举值映射缺失也就是前面反复提过的status字段用中文而库里面存的是英文的问题。我的排查顺序先看提示词里实际生效的Schema是哪个版本是不是线上最新的把模型生成的SQL原样在数据库工具里跑一遍看具体报错信息对过滤值执行一条SELECT DISTINCT看枚举值的真实分布和提示词里列的枚举项比对。最后为了防止这类问题反复出现我给Schema做了一套版本号机制DDL变更时同步更新提示词里的Schema片段并且把版本号也写进去模型端收到的Schema永远是当前版本。6.3 向量库重建后查询无结果现象索引重建完成、别名切到新Collection之后线上查询返回空结果或报错。备选Collection没建好就切了流量是常见原因或者重建过程中写入的字段类型和旧的不一致新Collection允许一个字段为空查询时过滤条件匹配不到任何数据。另一个坑是Embedding模型导致的数据跨代旧向量用A模型新索引用B模型虽然都存进一个Collection候选空间里的向量分布完全变样查询质量剧烈波动。排查时先把查询端临时改成不经过别名、直接指定Collection名确认数据是否正常再看字段类型和命名是否和查询代码完全一致最后看重建批次的日志确认Embedding模型是不是在重建过程中被换掉了。实际项目中我吃过一次亏重建索引时顺手升级了Embedding模型结果新旧向量混存查出来的数据排名质量几乎不可用。后面就把Embedding模型和向量库Schema一起纳入版本管理动用别名切换新模型先建独立Collection跑A/B对比验证没问题再切流量。6.4 图谱节点爆炸与关系冗余现象GraphRAG上线后发现图谱里的节点和关系数量涨得离谱几份文档抽出来的节点比文本句子还多查询结果也两极分化。节点爆炸通常是因为实体名称没有归一化。同一个机构在不同文档中可能有“某公司”、“某科技公司”、“某某科技股份有限公司”三种写法模型不加约束时就会各建一个节点。关系冗余则是指抽取出了大量没有业务意义的关系比如“Document - located_in - 某个地址”这种对问答毫无帮助的边。根治办法只有一个本体约束加实体合并策略双管齐下。抽取指令里明确“同名归一同一实体在不同上下文中出现时合并为一个节点”关系类型白名单外的全部丢弃阈值控制重要度太低的实体不建图。有这两道防线图谱规模一般能控制在初版预期的三分之一左右质量也稳定很多。关于Schema几个实操经验做过的RAG项目越多越发现一个问题凡是前期花时间把Schema理清楚的项目后期基本不折腾凡是急急忙忙建索引的项目后面全在还债。元数据字段缺失让人返工表Schema注释不全让SQL质量忽高忽低图谱没定义本体让GraphRAG跑成笑话——这些坑我都踩过。最后分享一个一直沿用的习惯索引构建前先跑一个字段诊断脚本统计每个元数据字段的覆盖率和取值分布。字段覆盖率低于95%的说明解析链路有漏数据的情况先去修解析逻辑再继续建索引字段取值分布异常集中的也要警惕可能是解析时字段映射串了。这一步只要十分钟却能把索引阶段的隐性缺陷提前暴露出来省下的排查时间远不止十分钟。Schema这件事放在RAG整个技术栈里并不显眼它不会直接提升模型效果但它决定了系统的上限和下限。Embedding强、Prompt好救不了一个Schema混乱的工程化RAG系统。