ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

RAG上下文压缩后数字、日期和否定条件丢失?从Chunk裁剪到摘要漂移的完整排查

RAG上下文压缩后数字、日期和否定条件丢失?从Chunk裁剪到摘要漂移的完整排查 文章摘要企业RAG为了降低Token成本、缓解长上下文中的噪声并提高生成速度通常会在检索后增加Top-K裁剪、去重、Rerank、句子抽取或摘要压缩。但压缩链路一旦设计不当最容易丢失的恰恰是业务中最关键的信息金额、百分比、日期、版本号、SKU、合同编号、否定条件、例外条款和适用范围。原文写的是“不得在未经审批的情况下发货”压缩后可能只剩“可以发货”原文写的是“违约金不超过合同金额的5%”摘要可能变成“违约金为5%”原文包含“2026年7月1日起生效”裁剪后却只保留旧版本条款。最终模型会基于一个看似简洁、实际已失真的上下文生成确定性错误。本文从检索候选、文档去重、父子Chunk、Rerank、Token预算、句子抽取、LLM摘要、位置重排和Prompt渲染九个环节逐层排查并给出关键Token保护、否定极性校验、数值一致性检查、Evidence ID冻结、压缩前后差异审计、风险分级压缩和自动化回归测试的完整实现方案。一、先看一个真实的压缩事故原始制度条款经销商不得跨区域销售。 因临时调货确需跨区域发货的 须提前取得销售管理中心书面审批。 未经审批的跨区域发货 按本批次货值的5%收取违约金 但单次违约金最高不超过20万元。 本条款自2026年7月1日起执行。压缩后的上下文经销商跨区域发货需审批 违规将按货值5%收取违约金。模型回答未经审批跨区域发货的违约金为货值的5%。看起来基本正确但已经丢失单次最高20万元生效日期“书面审批”临时调货场景条款适用范围。如果用户问货值1000万元时要罚50万元吗压缩上下文会诱导模型回答“是”真实答案却是最高20万元。二、上下文压缩不等于简单缩短文本压缩目标至少包含四个维度降低Token 保持关键事实 降低噪声 保持可追溯性错误系统只优化压缩率生产系统还必须优化关键事实保留率 否定条件保留率 数值一致率 引用可验证率三、最常见的九个丢失位置检索Top-K → 去重 → Rerank → 父子Chunk补全 → Token预算裁剪 → 句子抽取 → LLM摘要 → 位置重排 → Prompt渲染任何一步都可能删除关键上下文。四、第一类问题Top-K太小检索返回Chunk A主规则 Chunk B例外条件 Chunk C金额上限 Chunk D生效日期如果topK2;模型只看到A和B金额上限和生效日期永远不会进入后续链路。排查retrieved_candidate_count selected_candidate_count required_document_recall不要只看最终Prompt。五、第二类问题去重把“近似但互补”的Chunk删了错误去重if(cosineSimilarity(a,b)0.90){remove(b);}A违约金按货值5%计算。B违约金最高不超过20万元。语义高度相关但信息互补。更可靠的去重Keydocument_id document_version section_id sentence_span语义相似只用于发现重复不直接决定删除。六、第三类问题父子Chunk只保留子Chunk子Chunk适合召回最高不超过20万元。但单独使用会失去这个上限适用于什么哪种违约行为时间范围前置条件。父子结构publicrecordChunkMetadata(StringdocumentId,Stringversion,StringparentSectionId,intstartOffset,intendOffset){}召回子Chunk后应按策略补全父章节或相邻窗口。七、第四类问题Reranker只按主题相关性排序用户问跨区发货怎么处罚Reranker可能认为“违约金按5%”比“最高不超过20万元”更直接因此把上限排到预算之外。Rerank目标不应只有query relevance还应考虑条件完整性数值补充例外法规版本章节依赖。八、第五类问题按字符或Token硬截断错误Stringcompressedtext.substring(0,maxChars);或者tokens.subList(0,budget);硬截断可能把不得……截成……也可能保留“5%”但删除“最高不超过20万元”。最低要求按句子或语义段落裁剪不在实体中间截断不在否定结构中间截断保留尾部限定句保留表格行完整性。九、第六类问题句子抽取只依赖相似度句子评分score similarity(query, sentence)这会偏向复述用户关键词的句子。推荐score relevance numeric_bonus negation_bonus exception_bonus citation_bonus section_dependency_bonus关键句标记publicrecordSentenceFeatures(booleancontainsNumber,booleancontainsDate,booleancontainsNegation,booleancontainsException,booleancontainsUpperBound,booleancontainsLowerBound,booleancontainsVersion,booleancontainsReference){}十、第七类问题LLM摘要产生事实漂移原文不超过5%摘要为5%原文除非取得书面审批摘要取得审批后可执行第二句可能弱化“书面”要求。摘要式压缩必须被视为一次新的生成任务而不是无损转换。十一、第八类问题压缩后顺序改变长上下文研究表明关键信息位置会影响模型使用效果。相关信息被埋在中间时模型表现可能下降。所以压缩后的排序需要明确核心结论 → 必要条件 → 例外 → 数值边界 → 生效版本 → 其他背景但顺序重排时不能破坏原始引用关系。十二、第九类问题Prompt渲染再次截断压缩服务输出8000 tokensPrompt还包含SystemMemoryTool Schema用户输入输出预留。最终渲染时又被模型客户端裁掉后半段。需要统一Token预算publicrecordContextBudget(intmodelContextWindow,intsystemTokens,intmemoryTokens,inttoolTokens,intuserTokens,intreservedOutputTokens,intsafetyMarginTokens){publicintavailableEvidenceTokens(){returnmodelContextWindow-systemTokens-memoryTokens-toolTokens-userTokens-reservedOutputTokens-safetyMarginTokens;}}十三、先定义不可丢失TokenpublicrecordProtectedToken(Stringvalue,ProtectedTokenTypetype,intstart,intend,booleanhardRequired){}类型publicenumProtectedTokenType{MONEY,PERCENTAGE,DATE,VERSION,CONTRACT_ID,SKU,MODEL_NUMBER,NEGATION,EXCEPTION,UPPER_BOUND,LOWER_BOUND,LEGAL_REFERENCE}十四、关键Token提取ComponentpublicclassProtectedTokenExtractor{privatestaticfinalPatternMONEYPattern.compile((?:人民币)?\\d(?:\\.\\d)?(?:元|万元|亿元));privatestaticfinalPatternPERCENTAGEPattern.compile(\\d(?:\\.\\d)?%);privatestaticfinalPatternDATEPattern.compile(\\d{4}年\\d{1,2}月\\d{1,2}日);privatestaticfinalListStringNEGATIONSList.of(不得,禁止,不能,未经,不超过,不少于,除非,仅限);publicListProtectedTokenextract(Stringtext){ListProtectedTokenresultnewArrayList();collect(result,text,MONEY,ProtectedTokenType.MONEY);collect(result,text,PERCENTAGE,ProtectedTokenType.PERCENTAGE);collect(result,text,DATE,ProtectedTokenType.DATE);for(Stringnegation:NEGATIONS){intfrom0;while(true){intindextext.indexOf(negation,from);if(index0){break;}result.add(newProtectedToken(negation,ProtectedTokenType.NEGATION,index,indexnegation.length(),true));fromindexnegation.length();}}returnList.copyOf(result);}}十五、压缩前后Token保真校验ComponentpublicclassProtectedTokenValidator{publicTokenPreservationReportvalidate(ListProtectedTokenoriginal,Stringcompressed){ListProtectedTokenmissingoriginal.stream().filter(token-!compressed.contains(token.value())).toList();booleanhardFailuremissing.stream().anyMatch(ProtectedToken::hardRequired);returnnewTokenPreservationReport(original.size(),original.size()-missing.size(),missing,hardFailure);}}对金额和百分比不能只做字符串包含还要进行标准化200000元 20万元语义相同。十六、数值事实模型publicrecordNumericFact(BigDecimalvalue,Stringunit,NumericOperatoroperator,Stringsubject,Stringscope,StringsourceSentenceId){}操作符publicenumNumericOperator{EQUAL,LESS_THAN,LESS_THAN_OR_EQUAL,GREATER_THAN,GREATER_THAN_OR_EQUAL,RANGE,APPROXIMATE}原文最高不超过20万元解析为value200000 operatorLESS_THAN_OR_EQUAL压缩后20万元如果变成EQUAL视为事实漂移。十七、否定极性校验publicenumPolarity{POSITIVE,NEGATIVE,CONDITIONAL,EXCEPTION}publicrecordProposition(Stringsubject,Stringpredicate,Stringobject,Polaritypolarity,Stringcondition){}压缩前未经审批不得发货压缩后可以发货极性从NEGATIVE变为POSITIVE必须阻断。十八、风险分级压缩不是所有文档都适合相同压缩率。publicenumCompressionRisk{LOW,MEDIUM,HIGH,CRITICAL}建议风险场景策略LOW新闻、介绍可摘要压缩MEDIUM产品说明抽取式优先HIGH合同、制度保守抽取父章节CRITICAL金额、法律、安全原文证据不做生成式摘要十九、压缩策略接口publicinterfaceContextCompressor{CompressionResultcompress(CompressionRequestrequest);}publicrecordCompressionRequest(Stringquery,ListEvidenceCandidatecandidates,inttokenBudget,CompressionRiskrisk,SetProtectedTokenTypeprotectedTypes){}publicrecordCompressionResult(ListCompressedEvidenceevidence,intoriginalTokens,intcompressedTokens,TokenPreservationReporttokenReport,ListCompressionWarningwarnings){}二十、抽取式压缩抽取式压缩只选择原文句子不重写。ComponentpublicclassExtractiveContextCompressorimplementsContextCompressor{OverridepublicCompressionResultcompress(CompressionRequestrequest){ListScoredSentencesentencessentenceExtractor.extract(request.candidates());ListScoredSentencerankedsentences.stream().map(sentence-scorer.score(request.query(),sentence)).sorted(Comparator.comparingDouble(ScoredSentence::score).reversed()).toList();returnbudgetSelector.select(ranked,request.tokenBudget());}}抽取式优点可追溯数值不易改写Offset仍有效。缺点语言可能不连贯冗余仍存在跨句关系可能丢失。二十一、生成式摘要压缩适用于低风险背景材料。Prompt只删除与问题无关的内容。 不得改写数字、日期、版本、编号、否定词和例外条件。 必须保留每个句子的sourceSentenceId。 无法无损压缩时返回原文。结构化输出publicrecordSummarySentence(Stringtext,ListStringsourceSentenceIds){}禁止输出没有来源ID的摘要句。二十二、摘要后事实校验Protected Token Validator Numeric Fact Validator Polarity Validator Claim Fact Checker只有全部通过摘要才能进入最终Evidence Bundle。失败回退原文而不是继续使用“基本正确”的摘要。二十三、Evidence ID必须在压缩后冻结错误检索后分配[E1] ↓ 压缩删除E1 ↓ 重新排序 ↓ 模型仍引用旧编号正确检索 → 权限 → Rerank → 压缩 → Token预算 → 最终排序 → 冻结Evidence ID二十四、压缩后的Evidence结构publicrecordCompressedEvidence(StringevidenceId,StringdocumentId,StringdocumentVersion,StringsectionId,StringoriginalText,StringcompressedText,ListSourceSpansourceSpans,CompressionMethodmethod,StringcompressorVersion,StringoriginalHash,StringcompressedHash,intoriginalTokens,intcompressedTokens){}originalText可存对象存储在线Prompt只使用compressedText。二十五、Source SpanpublicrecordSourceSpan(StringsourceSentenceId,intoriginalStart,intoriginalEnd,StringoriginalHash){}引用详情页根据Source Span展示原文而不是只展示摘要。二十六、压缩审计每次压缩保存publicrecordCompressionAudit(StringtraceId,StringqueryHash,StringdocumentId,Stringversion,CompressionMethodmethod,StringcompressorVersion,intoriginalTokens,intcompressedTokens,doublecompressionRatio,intprotectedTokenCount,intmissingProtectedTokenCount,booleanaccepted,ListStringwarnings){}不要在普通日志中记录完整敏感正文。二十七、回放排查出现错误答案时按以下链路回放原始检索候选 ↓ Rerank结果 ↓ 压缩前证据 ↓ 压缩后证据 ↓ 最终Prompt ↓ 模型Claims ↓ 引用绑定定位是没检索到被Rerank删了被预算裁掉压缩改错模型忽略引用错误。二十八、自动化测试金额上限TestvoidupperBoundMustBePreserved(){Stringoriginal 违约金按货值的5%计算 但最高不超过20万元。 ;CompressionResultresultcompressor.compress(fixture.request(违约金是多少,original));assertThat(result.finalText()).contains(5%).contains(不超过).contains(20万元);}二十九、自动化测试否定条件TestvoidnegativePolarityMustNotFlip(){Stringoriginal未经审批不得跨区域发货。;CompressionResultresultcompressor.compress(fixture.request(可以跨区发货吗,original));PolarityComparisoncomparisonpolarityValidator.compare(original,result.finalText());assertThat(comparison.flipped()).isFalse();}三十、自动化测试生效版本TestvoideffectiveDateMustBePreserved(){Stringoriginal本规则自2026年7月1日起执行。;CompressionResultresultcompressor.compress(fixture.request(规则何时生效,original));assertThat(result.finalText()).contains(2026年7月1日);}三十一、质量指标rag_context_original_tokens rag_context_compressed_tokens rag_context_compression_ratio rag_protected_token_preservation_rate{ type } rag_numeric_fact_preservation_rate rag_negation_preservation_rate rag_exception_preservation_rate rag_compression_fallback_total{ reason } rag_compression_warning_total{ type } rag_claim_support_rate rag_citation_source_span_valid_rate三十二、发布门禁compression-gate:protected-token-preservation:1.00numeric-fact-preservation:1.00negation-preservation:1.00source-span-valid-rate:1.00minimum-claim-support-rate:0.97maximum-quality-regression:0.00高风险文档不接受“99%数字保留率”因为丢失的1%可能正是关键金额。三十三、最终排查清单□ Top-K覆盖主规则、例外、上限和生效日期 □ 去重不会删除互补Chunk □ 子Chunk召回后按策略补全父章节 □ Rerank包含条件完整性特征 □ 不按字符硬截断 □ 抽取评分保护数字和否定句 □ 生成式摘要只用于允许的风险等级 □ 压缩前提取关键Token □ 压缩后校验数字操作符 □ 压缩后校验否定极性 □ 校验例外条件和适用范围 □ 失败自动回退原文 □ Evidence ID在压缩完成后冻结 □ 每条压缩证据保存Source Span □ 引用详情展示原始证据 □ 压缩版本、Hash和审计可回放 □ 黄金集覆盖金额、日期、版本和否定条款总结RAG上下文压缩的真正目标不是把10000个Token压成3000个Token而是在更小上下文中保留足以支持正确结论的全部关键证据。生产级压缩必须满足Token更少 数字不变 否定不翻转 例外不丢失 范围不扩大 来源可定位最安全的原则是低风险内容可以生成式压缩高风险事实优先使用原文抽取任何压缩结果在进入模型前都必须通过关键Token、数值关系、否定极性和来源映射校验。
返回列表