聊完了RAG系统的幻觉治理——怎么让AI答得准接下来聊另一个同样重要的问题怎么让AI守规矩。企业知识库里有客户信息、合同条款、内部数据RAG系统上线后这些数据会不会被AI说漏嘴注本文讨论的RAG智能体指基于检索增强生成RAG构建的企业知识问答系统不涉及自主规划、工具调用等 Agent 机制。易科势腾是一家专注于AI智能营销的国家级高新技术企业北京市专精特新中小企业、中关村高新技术企业拥有21年行业深耕经验。在给企业做 RAG 落地的过程中数据安全往往是比幻觉治理更靠前的一道坎。一、RAG系统的数据安全风险模型先搞清楚RAG系统在数据安全上和传统Web应用有什么不同。传统Web应用的安全模型是边界防护——防火墙拦住外部访问数据库在内网API有认证。但RAG系统的数据流是原始文档 → 解析 → 切块 → 向量化 → 存入向量库 ↓ 用户提问 → 检索向量库 → 拼装Prompt → LLM生成 → 返回用户这条链路上有三个传统安全模型覆盖不到的风险点风险一数据摄入阶段的信息泄露原始文档在入库前会经过解析和切块。如果文档中包含PII个人身份信息这些信息会被原样写入向量库和索引中。后续用户提问时AI可能直接把PII背出来。举个例子一份客户合同被切分后某个Chunk里包含了甲方XX公司联系人张三电话138xxxx。用户问我们跟XX公司的合同条款是什么AI检索到这个Chunk在回答中可能把联系人姓名和电话都输出。风险二检索阶段的越权访问企业知识库通常对不同角色开放不同内容——销售能看到客户信息技术看不到技术能看到架构文档销售不需要看。但如果向量库没有做权限隔离任何能访问RAG系统的用户理论上都能检索到所有Chunk。传统数据库的权限控制是行级的——WHERE department ‘sales’。向量库的检索是语义匹配——按相似度排序返回Top-K。你没法在向量检索时加WHERE条件来过滤权限至少没那么简单。风险三生成阶段的信息外泄即使检索阶段做对了LLM在生成回答时也可能多说。比如用户问A产品的参数AI检索到了包含A产品和B产品参数的Chunk在回答中把两个产品都输出。如果B产品是未公开的内部产品这就是信息泄露。这三个风险点对应三道防线摄入阶段的PII脱敏、检索阶段的权限管控、生成后的审计追溯。二、PII脱敏从源头切断信息泄露PIIPersonally Identifiable Information个人身份信息脱敏是RAG数据安全的第一道防线。在数据进入向量库之前就把敏感信息处理掉。2.1 PII识别先找到敏感数据在哪里脱敏的前提是识别。在企业文档中常见的PII类型包括PII类型正则匹配模式示例手机号1[3-9]\d{9}13812345678身份证号[1-9]\d{5}(?:1920)\d{2}(?:0[1-9]邮箱[a-zA-Z0-9._%-][a-zA-Z0-9.-]\.[a-zA-Z]{2,}zhangsancompany.com银行卡号\b\d{16,19}\b需配合Luhn校验否则容易误匹配订单号等长数字6222021234567890123地址NLP实体识别北京市朝阳区XX路XX号人名NLP实体识别张三、李四正则匹配能覆盖格式固定的PII手机号、身份证、邮箱但人名和地址需要NERNamed Entity Recognition命名实体识别模型来识别。2.2 脱敏策略设计不是所有PII都要一刀切地抹掉。根据业务场景脱敏有不同的强度级别完全脱敏用占位符替换不保留任何原始信息。适用于L3/L4级别的数据。原文联系人张三电话13812345678 脱敏后联系人[人员A]电话[手机号A]部分脱敏保留部分信息可用于统计和关联但不可逆推出个人。原文张三13812345678 脱敏后张**138****5678可逆脱敏用加密存储有权限的用户可以解密查看原始信息。适用于需要审计追溯的场景。原文张三13812345678 脱敏后ENC(a2V5MTIz)ENC(bGNkNDU2)在RAG系统中推荐使用完全脱敏——因为RAG的核心用途是知识检索和问答不是客户信息管理。如果用户需要查看客户联系方式应该走CRM系统的权限体系而不是通过RAG系统。2.3 脱敏工程实现在数据摄入管线中脱敏应该作为一个独立的处理环节位于文档解析之后、切块之前classPIIDetector:PII检测器正则 NER模型双重检测def__init__(self):# 正则规则集self.regex_patterns{phone:re.compile(r1[3-9]\d{9}),id_card:re.compile(r[1-9]\d{5}(?:19|20)\d{2}(?:0[1-9]|1[0-2])(?:0[1-9]|[12]\d|3[01])\d{3}[\dXx]),email:re.compile(r[a-zA-Z0-9._%-][a-zA-Z0-9.-]\.[a-zA-Z]{2,}),# 注意16-19位纯数字也可能匹配到订单号、流水号等# 生产环境应配合Luhn算法校验降低误匹配率bank_card:re.compile(r\b\d{16,19}\b),}# NER模型用于人名和地址识别# 可选HanLP / LAC / 自训练模型self.ner_modelload_ner_model()defdetect(self,text):检测文本中的所有PIIfindings[]# 正则检测forpii_type,patterninself.regex_patterns.items():formatchinpattern.finditer(text):findings.append({type:pii_type,value:match.group(),start:match.start(),end:match.end()})# NER检测人名和地址ner_resultsself.ner_model.recognize(text)forentityinner_results:ifentity[label]in[PER,LOC]:findings.append({type:personifentity[label]PERelseaddress,value:entity[text],start:entity[start],end:entity[end]})returnfindingsclassPIIMasker:PII脱敏器# 接受原始字符串返回脱敏后的字符串MASK_MAP{phone:lambdas:s[:3]****s[-4:],id_card:lambdas:s[:6]********s[-4:],email:lambdas:s.split()[0][0]***s.split()[1],# 仅保留本地部分首字符bank_card:lambdas:s[:4]**(len(s)-8)s[-4:],# 16-19位变长动态星号person:lambdas:s[0]**iflen(s)1else*,address:lambdas:s[:2]****,}defmask(self,text,findings):对文本中的PII进行脱敏# 从后往前替换避免位置偏移forfindinginsorted(findings,keylambdax:x[start],reverseTrue):originalfinding[value]mask_funcself.MASK_MAP.get(finding[type])ifmask_func:maskedmask_func(original)texttext[:finding[start]]maskedtext[finding[end]:]returntext在摄入管线中集成的位置defingest_document(doc_path):文档摄入管线# 1. 解析文档raw_texttika_parser.extract(doc_path)# 2. PII检测和脱敏在切块之前findingspii_detector.detect(raw_text)iffindings:logger.info(f检测到{len(findings)}个PII开始脱敏)clean_textpii_masker.mask(raw_text,findings)# 记录脱敏日志audit_log.record({action:pii_masked,doc_path:doc_path,pii_count:len(findings),pii_types:list(set(f[type]forfinfindings)),})else:clean_textraw_text# 3. 切块chunksstructure_based_chunker.split(clean_text)# 4. 向量化embeddingsembedding_model.encode(chunks)# 5. 写入向量库附带metadatavector_store.add(textschunks,embeddingsembeddings,metadata[{doc_id:doc_id,doc_path:doc_path,pii_masked:len(findings)0,masked_count:len(findings),}for_inchunks])2.4 脱敏的注意事项注意一脱敏要在切块之前做如果先切块再脱敏切分可能把一个手机号切成两半正则就匹配不到了。必须在完整文档解析后、切块之前做PII检测和脱敏。注意二脱敏后的文本质量影响检索脱敏后的文本语义会有些变化。比如张三负责的项目变成张**负责的项目Embedding的向量会有偏移。对于高频出现在文档中的核心人名建议在脱敏的同时保留一个角色标识如项目经理A而不是简单的星号掩码这样语义信息保留更好。注意三保留脱敏映射表如果后续需要追溯脱敏前的原始信息如审计需要需要保留脱敏映射表。映射表应该加密存储访问需要审批权限。映射表本身也是敏感数据需要纳入数据分级管理。三、权限管控让用户只能检索到他能看的内容RAG系统的权限管控比传统应用复杂因为向量检索不是精确匹配你没法简单地用WHERE条件过滤。3.1 权限模型设计推荐使用RBAC基于角色的访问控制 ABAC基于属性的访问控制混合模型RBAC层定义角色和知识库的映射关系。角色 可访问知识库 ───────────────────────────── 销售 产品文档、公开案例、FAQ 技术 产品文档、技术架构、FAQ 管理层 全部知识库 外部合作伙伴 产品文档指定范围ABAC层在角色基础上根据用户属性做细粒度过滤。属性 过滤规则 ──────────────────────────────── 部门销售一部 只能看本部门的客户案例 职级经理 可以看脱敏后的财务数据 渠道合作伙伴 只能看公开产品文档不含内部参数3.2 向量库的权限隔离方案在技术实现上向量库的权限隔离有三种方案方案一分库隔离为每个角色/部门建独立的向量索引。用户检索时只在自己有权限的索引中搜索。索引列表 - kb_public → 所有人可访问 - kb_sales → 销售部可访问 - kb_tech → 技术部可访问 - kb_finance → 财务部可访问优点权限隔离干净实现简单。缺点知识库数量多时管理成本高跨库检索需要合并结果增加复杂度。适合部门界限明确、知识库数量适中的场景。方案二Metadata过滤所有文档存在同一个向量库中每个Chunk附带权限metadata。检索时在向量查询中加入过滤条件。# Elasticsearch 8.x KNN 查询示例 # query_vector 是由 Embedding 模型生成的查询向量浮点数数组 { query: { knn: { field: embedding, query_vector: [0.0123, -0.456, 0.789], k: 20, num_candidates: 100, filter: { bool: { must: [ {terms: {allowed_roles: [sales, public]}} ] } } } } }优点单库管理维护简单。缺点过滤条件太复杂时影响检索性能Chunk的权限metadata需要维护。适合中小规模知识库。方案三检索后过滤先做全量检索返回Top-K结果后在应用层按权限过滤。# 先检索Top-20raw_resultsvector_store.search(query,top_k20)# 应用层过滤filtered_results[rforrinraw_resultsifuser.has_access(r.metadata[doc_id])]# 过滤后如果不足Top-5需要补检索按 chunk_id 去重iflen(filtered_results)5:seen_ids{r.metadata[chunk_id]forrinfiltered_results}additionalvector_store.search(query,top_k50)forrinadditional:ifuser.has_access(r.metadata[doc_id])andr.metadata[chunk_id]notinseen_ids:filtered_results.append(r)seen_ids.add(r.metadata[chunk_id])iflen(filtered_results)5:break优点不需要修改向量库查询逻辑对接现有系统改动最小。缺点效率低可能需要多次检索才能凑够结果检索质量受影响——过滤掉的结果可能是最相关的。适合权限层级简单、大部分文档对大部分用户开放的场景。选型建议知识库数量少5个、权限层级简单2-3级方案二Metadata过滤知识库数量多5个、部门界限明确方案一分库隔离历史系统改造、不想动向量库层方案三检索后过滤作为过渡方案在易科势腾的实际项目中方案二用得最多。Elasticsearch 8.x的KNN查询原生支持filter参数在10万级Chunk的测试环境下加了filter后的查询延迟增加通常在数十毫秒量级对用户体验影响有限。实际影响取决于filter的选择性和数据分布建议根据自己的数据规模做基准测试。3.3 会话级别的权限透传权限不能只在检索层做控制。从用户发起提问到AI生成回答的整条链路权限信息需要全程透传。用户请求携带用户Token ↓ API网关验证Token → 提取用户角色和属性 ↓ RAG服务层根据角色和属性构建检索过滤条件 ↓ 检索层带过滤条件的混合检索 ↓ Prompt拼装在拼装时标注每个Chunk的来源和权限等级 ↓ LLM生成Prompt中包含安全约束如不得输出标注为内部的参数 ↓ 输出后处理检查生成内容是否包含越权信息 ↓ 返回用户附带来源标注关键设计点Prompt中要包含安全约束。不要指望LLM自己判断哪些信息该说哪些不该说——在系统Prompt中明确写出约束规则安全约束 1. 只能基于以下检索到的内容回答问题 2. 如果内容中标注了[内部]不得在回答中引用该部分信息 3. 不得输出任何个人身份信息姓名、电话、邮箱等 4. 如果用户请求的信息超出其权限范围回答该信息不在可公开范围内四、审计日志让每一次问答都可追溯审计日志的价值不只是合规检查。当出现AI回答了不该说的内容时审计日志是你定位问题的唯一手段。4.1 日志架构RAG系统的审计日志需要覆盖三个维度数据摄入审计{timestamp:2026-08-04T10:30:00Z,action:document_ingested,doc_id:doc_20260804_001,doc_path:/data/contracts/customer_a.pdf,doc_size:245678,chunk_count:12,pii_detected:true,pii_masked_count:3,pii_types:[phone,email,person],classification:L3,allowed_roles:[sales,management],operator:admincompany.com}检索审计{timestamp:2026-08-04T10:35:00Z,user_id:user_12345,user_role:sales,query:客户A的合同条款,query_hash:sha256:abc123...,retrieved_chunks:[{chunk_id:doc_001_c3,score:0.89,doc_classification:L3},{chunk_id:doc_001_c5,score:0.85,doc_classification:L3},{chunk_id:doc_002_c1,score:0.71,doc_classification:L1}],filtered_out:2,final_context_chunks:3,response_time_ms:856}注意如果 query 字段可能包含 PII存储时应做同等脱敏处理。query_hash 用于去重分析避免原始 query 中的敏感信息长期保留。filtered_out 记录因权限被过滤的 Chunk 数量。生成审计{timestamp:2026-08-04T10:35:01Z,user_id:user_12345,model:glm-4-flash,prompt_tokens:1245,completion_tokens:380,response:根据合同条款...,response_hash:sha256:def456...,contains_pii:false,safety_check_passed:true}注意response 字段可能包含 PII建议脱敏后存储contains_pii 由输出后处理模块检测后标记。4.2 日志存储和查询日志存储推荐使用Elasticsearch如果你的RAG系统已经在用ES可以复用。建立专门的审计日志索引{mappings:{properties:{timestamp:{type:date},action:{type:keyword},user_id:{type:keyword},user_role:{type:keyword},doc_id:{type:keyword},query_hash:{type:keyword},pii_detected:{type:boolean},safety_check_passed:{type:boolean},response_time_ms:{type:integer}}}}日志保留期限参考日志类型保留期限依据检索和生成日志至少180天《网络安全法》及等保2.0要求数据摄入日志至少1年内部审计需要权限变更日志至少1年合规审计需要安全事件日志至少2年事后追溯需要4.3 异常检测审计日志不只用来存着等检查还应该配置实时告警。以下情况需要触发告警同一用户短时间大量检索不同分类的文档可能在试探权限边界检索结果频繁被权限过滤说明用户权限设置可能不合理输出后处理检测到PII说明脱敏流程有遗漏非工作时间的异常访问API调用频率突增可能有爬虫行为告警可以通过Webhook推送到企业微信或钉钉由运维和安全团队跟进。五、私有化部署的安全考量对于数据安全要求高的企业金融、医疗、政务RAG系统通常需要私有化部署。私有化部署不是简单地把系统搬到内网还需要考虑以下安全架构5.1 组件安全清单组件公有云方案私有化替代安全要求Embedding模型OpenAI/ZhipuAI APIBGE/M3E本地部署模型权重不出网向量库Pinecone/Weaviate云Milvus/ES本地数据不出内网LLMGPT-4/Claude APIGLM/Qwen/DeepSeek本地推理不外调文档解析云端APIApache Tika本地文件不外传日志分析云日志服务ELK本地集群日志不外传5.2 网络隔离私有化部署的网络架构建议分为三个区域┌─────────────────────────────────────────────┐ │ DMZ区 │ │ 负载均衡 API网关 WAF │ │ ↓ 只开放必要端口 │ ├─────────────────────────────────────────────┤ │ 应用区 │ │ RAG服务 检索服务 向量库 │ │ ↓ 内网通信无外网出口 │ ├─────────────────────────────────────────────┤ │ 数据区 │ │ 原始文档 向量库存储 审计日志 │ │ ↓ 加密存储最小权限访问 │ └─────────────────────────────────────────────┘关键安全规则应用区的服务不允许直接访问外网防止数据外泄和LLM API外调数据区的存储只对应用区的特定服务开放访问DMZ区只转发HTTPS请求拦截所有非标准端口的访问如果使用国产大模型的API如ZhipuAI需要通过网络策略限制只允许访问特定的API域名5.3 模型安全私有化部署中使用开源模型时需要注意模型权重文件从官方渠道下载下载后做MD5校验不使用来路不明的微调模型可能被植入后门模型部署在受控环境中不对外暴露推理接口定期更新模型版本但更新前需要做回归测试六、一套最小可用的安全配置如果团队资源有限没法一次性把所有安全措施都做到位以下是一套最小可用的配置必须有上线前必须完成PII正则检测脱敏至少覆盖手机号、身份证、邮箱基于Metadata的权限过滤至少区分内部/外部两个层级检索和生成日志至少记录用户ID、问题摘要、检索到的文档ID系统Prompt中的安全约束建议有上线后3个月内完成NER模型补充人名和地址的PII检测输出后处理检测检查生成内容是否包含PII日志告警机制脱敏映射表的加密存储最好有资源允许时推进全链路权限透传Token → 角色解析 → 检索过滤 → Prompt约束 → 输出检查网络隔离的三区架构定期安全审计和渗透测试这套配置不是一步到位的。在易科势腾的项目经验中大多数企业RAG系统的安全建设是一个渐进过程——先把必须有的做扎实再根据业务发展需要逐步推进后面的。七、总结三道防线一个闭环RAG系统的数据安全本质上是在解决一个问题让AI用该用的信息回答该回答的问题不多说也不乱说。PII脱敏解决的是不该入库的别入库权限管控解决的是不该看的别看到审计日志解决的是出了问题能查到。三道防线从数据摄入到最终输出形成一个完整的安全闭环。幻觉治理是答得准不准的质量问题数据安全是答得该不该的合规问题。前者影响用户体验后者影响企业安全。两件事都重要但在合规要求高的行业数据安全的优先级应该更高——因为答错了可以改数据泄露了没法收。如果你在RAG系统的数据安全落地中踩过坑或者有更好的实践方式欢迎分享你的经验。