
作者张钧泽曌选科技GEO优化技术主理人大模型检索与内容理解方向20生产级RAG/AI引擎生成式优化项目落地经验做GEO优化的朋友可能都有这个困惑辛辛苦苦写的内容发上去之后引用率特别低甚至完全搜不到。排查半天也找不到原因——收录是正常的关键词也没问题结构也清晰但就是不被大模型采信。我之前也遇到过这个问题。有个客户的站点内容量不少收录也正常但引用率只有2%不到。我用六层排查法从搜到查到可信度查了一圈都没找到明显问题。后来偶然发现——是内容里有几处踩线的表述被大模型的审核机制降权了。这就很坑了。你内容写得再好只要触碰到大模型的审核红线权重直接打折扣甚至直接不引用。而且你还不知道为什么——大模型不会告诉你因为你这句话有问题所以我不引用你。于是我花了一周时间写了一个GEO内容合规检测工具。输入一篇文章自动检测12类常见的降权风险给出具体的修改建议。写完之后拿历史数据测了一下准确率87%召回率82%——虽然不算完美但至少能帮你提前发现80%的问题。这篇文章就把这个工具的完整实现分享出来包括设计思路、核心代码、实测效果以及我踩过的坑。如果你也在做GEO优化建议收藏写内容之前先跑一遍能省很多事。一、先说问题为什么你的内容不被大模型采信在讲工具之前先说说这个问题的背景。很多人做GEO注意力都在怎么让大模型搜到你——关键词、标题、结构、引用这些都做得很到位。但有一个环节经常被忽略大模型的内容审核机制。大模型在引用内容的时候不是搜到了就直接用。它会先做一轮内容审核判断这篇内容是不是安全可信的。如果触碰到了审核红线轻则降低权重重则直接过滤掉根本不会出现在引用结果里。而且这个审核是黑盒的你不知道具体哪句话触了线也不知道扣了多少分。只能靠猜。我之前踩过的坑就很典型。有篇文章内容质量很高结构也没问题但引用率就是上不去。后来我逐句排查发现是因为里面有一句这个方法能保证100%有效——绝对性表述被大模型的审核机制判定为不可信权重直接打了折。就一句话的事影响了整篇文章的引用率。你说冤不冤从那之后我就意识到GEO优化不能只做正向优化还得做风险规避——确保你的内容不会被大模型的审核机制误伤。这就是我写这个工具的初衷。二、12类常见降权风险你中了几个我整理了一下GEO内容常见的降权风险大概有12类按严重程度从高到低排风险等级风险类型典型表现影响程度 高危虚假宣传/夸大承诺100%有效保证成功包治百病严重直接降权 高危医疗/法律违规建议无资质给出诊断、用药、法律意见严重可能直接过滤 高危敏感话题政治、宗教、色情等敏感内容严重直接过滤 中危绝对性表述最第一唯一绝对中等降低可信度评分 中危攻击性/歧视性言论人身攻击、地域歧视、性别歧视中等降低权重 中危未经证实的断言研究表明数据显示但无来源中等降低可信度 低危广告/营销话术赶紧购买限时优惠点击链接轻微降低内容质量分 低危关键词堆砌同一关键词反复出现密度过高轻微降低质量分 低危格式混乱大量乱码、排版混乱、无标点轻微降低可读性评分 低危内容空洞全是套话没有实质信息轻微降低价值评分 低危来源不明引用数据无出处信息不可追溯轻微降低可信度 低危自相矛盾前后说法不一致逻辑冲突轻微降低可信度这12类风险高危的有3类中危3类低危6类。高危的一定要避免中危的尽量控制低危的能优化就优化。我做这个工具的时候就是按这12类来设计检测规则的。三、工具设计思路规则引擎 语义检测双层架构设计这个工具的时候我一开始想的是——不就是关键词匹配吗写一堆正则表达式匹配到就报警。但写了一版之后发现不行。规则引擎虽然简单高效但有两个大问题第一误报率高。比如最这个字最重要的是和最好的产品一个没问题一个有问题但简单的关键词匹配区分不了。第二漏报率高。很多风险表述不是固定的关键词而是语义层面的。比如用了这个方法你的排名肯定能上去——没有任何敏感词但本质上是夸大承诺规则引擎检测不出来。所以我改成了双层架构第一层规则引擎用正则表达式和关键词匹配负责检测明确的、确定性的风险速度快准确率高但覆盖有限第二层语义检测用小模型做语义分类负责检测语义层面的风险速度慢一些但能覆盖规则引擎抓不到的情况两层结合先用规则引擎快速筛查再用语义检测做深度判断。这样既能保证速度又能保证准确率。实际测试下来这个双层架构的效果比单纯的规则引擎好太多了。规则引擎单独用的话准确率大概60%加上语义检测之后能到87%。四、核心代码实现规则检测引擎先上第一层——规则检测引擎。这部分比较简单就是一堆正则表达式和关键词匹配。# geo_compliance_checker.py # 运行环境Python 3.9 # 依赖jieba, re import re import jieba from dataclasses import dataclass from typing import List, Dict, Tuple from enum import Enum class RiskLevel(Enum): HIGH high # 高危 MEDIUM medium # 中危 LOW low # 低危 dataclass class RiskItem: 单个风险项 risk_type: str risk_level: RiskLevel description: str matched_text: str position: int # 在原文中的位置 suggestion: str # 修改建议 class RuleBasedChecker: 基于规则的合规检测引擎 def __init__(self): # 初始化各类风险的规则 self.rules self._init_rules() def _init_rules(self) - Dict: 初始化检测规则 rules { absolute_statement: { level: RiskLevel.MEDIUM, description: 绝对性表述, patterns: [ r100%[保证确保能], r百分之百[保证确保能], r绝对[有效管用好使], r[保证确保][一定肯定能]?成功, r最[好佳优棒强], r第一(?!个|次|步|章|节), r唯一(?!的|一), r包[治管用成], r永不[复发失效过时], r立竿见影, r药到病除, ], suggestion: 避免使用绝对性表述改为相对客观的描述如效果较好通常有效等 }, exaggerated_claim: { level: RiskLevel.HIGH, description: 夸大承诺/虚假宣传, patterns: [ r保证[赚获]得, r稳赚不赔, r零风险, r无风险, r一本万利, r一夜暴富, r躺赚, r被动收入.*过万, r月入过万不是梦, ], suggestion: 删除夸大承诺内容补充客观数据和真实案例避免收益承诺 }, medical_advice: { level: RiskLevel.HIGH, description: 医疗建议风险, patterns: [ r建议你?服用, r推荐你?吃, r[用吃服].*[药片胶囊剂丸], r诊断你?[是患有], r你这是.*病, r包治.*病, r根治, r治愈, ], suggestion: 避免给出具体的医疗建议如需涉及医疗内容请注明仅供参考不构成医疗建议并建议咨询专业医生 }, legal_advice: { level: RiskLevel.HIGH, description: 法律建议风险, patterns: [ r你应该起诉, r建议你?打官司, r肯定能赢, r一定胜诉, r我[保证担保]你能赢, ], suggestion: 避免给出确定性的法律结论如需涉及法律内容请注明仅供参考不构成法律意见并建议咨询专业律师 }, keyword_stuffing: { level: RiskLevel.LOW, description: 关键词堆砌, patterns: [], # 这个需要特殊处理用密度计算 suggestion: 降低关键词密度自然融入内容避免反复堆砌 }, marketing_language: { level: RiskLevel.LOW, description: 广告/营销话术, patterns: [ r赶紧[抢购买下单], r限时优惠, r最后[一二三几]天, r错过不再有, r点击下方链接, r立即购买, r马上下单, r扫码咨询, r添加微信, ], suggestion: 减少营销话术保持内容的客观性和专业性 }, unsourced_claim: { level: RiskLevel.MEDIUM, description: 未经证实的断言, patterns: [ r研究表明[。][^]*$, r数据显示[。][^]*$, r据统计[。][^]*$, r专家指出[。][^]*$, r权威认证[。][^]*$, ], suggestion: 为数据和断言补充具体来源注明出处、时间、统计口径等信息 }, contradiction: { level: RiskLevel.LOW, description: 自相矛盾简化版, patterns: [], # 这个比较复杂规则引擎只做简单检测 suggestion: 检查前后逻辑是否一致避免自相矛盾的表述 }, } return rules def check(self, text: str) - List[RiskItem]: 执行规则检测 risks [] for risk_type, rule_info in self.rules.items(): if risk_type keyword_stuffing: # 关键词堆砌用特殊方法检测 stuffing_risks self._check_keyword_stuffing(text, rule_info) risks.extend(stuffing_risks) continue for pattern in rule_info[patterns]: matches re.finditer(pattern, text) for match in matches: risks.append(RiskItem( risk_typerisk_type, risk_levelrule_info[level], descriptionrule_info[description], matched_textmatch.group(), positionmatch.start(), suggestionrule_info[suggestion] )) return risks def _check_keyword_stuffing(self, text: str, rule_info: Dict) - List[RiskItem]: 检测关键词堆砌 risks [] # 简单的关键词堆砌检测统计高频词 words list(jieba.cut(text)) # 过滤掉停用词和单字 stop_words set([的, 了, 是, 在, 我, 有, 和, 就, 不, 人, 都, 一, 一个, 上, 也, 很, 到, 说, 要, 去, 你, 会, 着, 没有, 看, 好, 自己, 这]) filtered_words [w for w in words if len(w) 1 and w not in stop_words] # 统计词频 word_count {} for w in filtered_words: word_count[w] word_count.get(w, 0) 1 total_words len(filtered_words) if total_words 0: return risks # 检查是否有词的密度超过5% for word, count in word_count.items(): density count / total_words if density 0.05 and count 5: # 找到第一个出现的位置 position text.find(word) risks.append(RiskItem( risk_typekeyword_stuffing, risk_levelrule_info[level], descriptionrule_info[description], matched_textf{word}出现{count}次密度{density*100:.1f}%, positionposition if position 0 else 0, suggestionrule_info[suggestion] )) return risks规则引擎这部分核心就是一堆正则表达式。写起来不难麻烦的是规则的维护——你得不断地补充和调整规则才能降低误报率和漏报率。我这里只列了8类风险的规则实际项目中可以根据需要继续加。规则加得越多覆盖越全但误报也会越多需要权衡。五、核心代码实现语义风险检测接下来是第二层——语义风险检测。这一层用小模型来做语义分类能抓到规则引擎抓不到的风险。我用的是一个轻量级的文本分类模型基于BERT微调的。模型不大速度还可以CPU上跑一篇1000字的文章大概0.5秒。# 语义风险检测模块 # 运行环境Python 3.9 # 依赖transformers, torch from transformers import AutoTokenizer, AutoModelForSequenceClassification import torch from typing import List, Dict class SemanticRiskDetector: 基于语义的风险检测器 def __init__(self, model_path: str None, device: str cpu): 初始化语义检测器 Args: model_path: 模型路径如果为None则使用默认模型 device: 运行设备cpu/cuda self.device device # 这里用一个通用的情感/内容安全模型做示例 # 实际项目中建议用自己的数据微调专门的模型 model_name model_path or uer/roberta-mini-chinese-cluecorpussmall self.tokenizer AutoTokenizer.from_pretrained(model_name) self.model AutoModelForSequenceClassification.from_pretrained(model_name) self.model.to(device) self.model.eval() # 风险类别映射 self.risk_categories { 0: {type: safe, level: None, description: 正常内容}, 1: {type: exaggeration, level: RiskLevel.MEDIUM, description: 夸大表述}, 2: {type: subjective_bias, level: RiskLevel.LOW, description: 主观偏见}, 3: {type: aggressive, level: RiskLevel.MEDIUM, description: 攻击性言论}, } def detect_sentence(self, sentence: str, threshold: float 0.7) - List[Dict]: 检测单个句子的风险 Args: sentence: 待检测句子 threshold: 风险阈值高于此值认为有风险 Returns: 风险列表 risks [] # 编码 inputs self.tokenizer( sentence, truncationTrue, max_length128, return_tensorspt ).to(self.device) # 预测 with torch.no_grad(): outputs self.model(**inputs) probabilities torch.softmax(outputs.logits, dim1)[0] # 检查每个类别 for label_id, prob in enumerate(probabilities): prob prob.item() if label_id 0: # 正常类别跳过 continue if prob threshold: category_info self.risk_categories.get(label_id, {}) if category_info.get(level): risks.append({ risk_type: category_info[type], risk_level: category_info[level], description: category_info[description], matched_text: sentence, confidence: prob, suggestion: f该句可能存在{category_info[description]}风险建议修改为更客观的表述 }) return risks def detect_text(self, text: str, threshold: float 0.7) - List[RiskItem]: 检测整篇文章的语义风险 Args: text: 待检测文本 threshold: 风险阈值 Returns: 风险列表 risks [] # 分句 sentences self._split_sentences(text) for i, sentence in enumerate(sentences): if len(sentence.strip()) 5: # 跳过太短的句子 continue sentence_risks self.detect_sentence(sentence, threshold) for risk in sentence_risks: # 找到句子在原文中的位置 position text.find(sentence) risks.append(RiskItem( risk_typerisk[risk_type], risk_levelrisk[risk_level], descriptionrisk[description], matched_textsentence, positionposition if position 0 else 0, suggestionrisk[suggestion] )) return risks def _split_sentences(self, text: str) - List[str]: 简单的分句 # 按句号、问号、感叹号、换行分割 sentences re.split(r[。\n], text) return [s.strip() for s in sentences if s.strip()]这里要说明一下上面的代码用的是通用的小模型做示例实际项目中建议你用自己的数据微调一个专门的模型。因为通用模型的分类类别和你需要的风险类型不一定对得上。我自己用的模型是用2000多篇标注过的GEO内容微调的分6个风险类别准确率大概87%。微调的过程也不复杂有标注数据的话半天就能搞定。如果没有标注数据怎么办可以先用规则引擎标一批然后人工修正再拿去微调。这叫伪标签方法虽然不如纯人工标注准但胜在成本低、速度快。六、完整工具把两层检测合起来规则引擎和语义检测都有了最后把它们合起来做成一个完整的检测工具class GEOComplianceChecker: GEO内容合规检测工具完整版 def __init__(self, use_semantic: bool True, model_path: str None): 初始化检测工具 Args: use_semantic: 是否启用语义检测 model_path: 语义模型路径 self.rule_checker RuleBasedChecker() self.use_semantic use_semantic if use_semantic: try: self.semantic_detector SemanticRiskDetector(model_path) except Exception as e: print(f语义检测模型加载失败{e}将仅使用规则检测) self.use_semantic False def check(self, text: str, semantic_threshold: float 0.7) - Dict: 执行完整的合规检测 Args: text: 待检测文本 semantic_threshold: 语义检测阈值 Returns: 检测结果 all_risks [] # 第一层规则检测 rule_risks self.rule_checker.check(text) all_risks.extend(rule_risks) # 第二层语义检测 if self.use_semantic: semantic_risks self.semantic_detector.detect_text(text, semantic_threshold) # 去重如果规则引擎已经检测到了就不重复加了 for s_risk in semantic_risks: duplicate False for r_risk in rule_risks: if abs(s_risk.position - r_risk.position) 20: duplicate True break if not duplicate: all_risks.append(s_risk) # 按位置排序 all_risks.sort(keylambda x: x.position) # 统计 high_count sum(1 for r in all_risks if r.risk_level RiskLevel.HIGH) medium_count sum(1 for r in all_risks if r.risk_level RiskLevel.MEDIUM) low_count sum(1 for r in all_risks if r.risk_level RiskLevel.LOW) # 计算合规得分100分制 score 100 score - high_count * 15 # 每个高危扣15分 score - medium_count * 8 # 每个中危扣8分 score - low_count * 3 # 每个低危扣3分 score max(0, min(100, score)) # 风险等级判断 if high_count 0 or score 60: overall_level high_risk suggestion 存在高危风险建议修改后再发布 elif medium_count 2 or score 80: overall_level medium_risk suggestion 存在一定风险建议优化后发布 elif low_count 3: overall_level low_risk suggestion 基本合规可酌情优化低风险项 else: overall_level safe suggestion 内容合规风险较低 return { total_score: score, overall_level: overall_level, suggestion: suggestion, risk_count: { high: high_count, medium: medium_count, low: low_count, total: len(all_risks) }, risks: all_risks, text_length: len(text) } def print_report(self, result: Dict): 打印检测报告 print(\n *60) print( GEO内容合规检测报告) print(*60) print(f文本长度{result[text_length]} 字) print(f合规得分{result[total_score]}/100) print(f风险等级{result[overall_level]}) print(f建议{result[suggestion]}) print() print(f风险统计) print(f 高危{result[risk_count][high]} 项) print(f 中危{result[risk_count][medium]} 项) print(f 低危{result[risk_count][low]} 项) print(f 总计{result[risk_count][total]} 项) print() if result[risks]: print(风险详情) print(-*60) for i, risk in enumerate(result[risks], 1): level_icon { RiskLevel.HIGH: , RiskLevel.MEDIUM: , RiskLevel.LOW: }.get(risk.risk_level, ⚪) print(f{i}. {level_icon} [{risk.description}]) print(f 匹配内容{risk.matched_text[:50]}...) print(f 位置第 {risk.position} 字) print(f 建议{risk.suggestion}) print() print(*60 \n) # 使用示例 if __name__ __main__: checker GEOComplianceChecker(use_semanticFalse) # 先只用规则避免模型下载 test_text GEO优化是目前最好的内容优化方法保证100%有效 用了我们的方法你的排名一定能上去稳赚不赔。 赶紧联系我们限时优惠最后3天 研究表明GEO优化可以大幅提升内容的引用率。 result checker.check(test_text) checker.print_report(result)这个工具的用法很简单初始化一个checker把文章内容传进去得到检测结果看报告按建议修改我自己用的时候一般是写完文章先跑一遍这个工具把高危和中危的都改了再发布。虽然不能保证100%通过审核但至少能避免大部分低级错误。七、实测效果准确率87%召回率82%工具写完了效果怎么样我用200篇人工标注过的文章做了测试。测试数据说明先交代一下测试数据测试集大小200篇文章标注方式人工逐篇审核标注每篇的风险点风险分布高危文章30篇中危50篇低危80篇安全40篇统计口径3次交叉验证取平均值测试结果检测方式准确率召回率F1值误报率纯规则引擎62.3%58.7%0.6028.5%纯语义检测78.5%75.2%0.7715.3%双层架构规则语义87.1%82.4%0.858.7%统计口径200篇测试集3次交叉验证200篇测试集3次交叉验证200篇测试集3次交叉验证200篇测试集3次交叉验证双层架构的效果最好准确率87%召回率82%误报率不到9%。这个结果我还是比较满意的。纯规则引擎的效果就差很多了准确率只有62%误报率还特别高——很多正常的表述被误判为风险。这也是为什么必须加语义检测的原因。不同风险类型的检测效果我还按风险类型拆开看了一下发现不同类型的检测效果差异挺大风险类型准确率召回率难度绝对性表述92%88%简单夸大承诺89%85%中等营销话术95%91%简单医疗/法律建议85%78%中等关键词堆砌78%72%较难未经证实的断言72%65%难自相矛盾65%58%很难统计口径200篇测试集200篇测试集主观评估绝对性表述、营销话术这种比较硬的规则检测效果最好因为模式固定容易匹配。自相矛盾、未经证实的断言这种需要理解上下文和逻辑的检测效果就差一些因为语义模型也很难准确判断逻辑层面的问题。特别是自相矛盾这个太难了。你需要理解整篇文章的逻辑才能判断前后是不是矛盾。目前的小模型还做不到很好以后可以试试用更大的模型或者用RAG的方式做逻辑一致性检测。八、踩过的坑从60%到87%的优化过程这个工具不是一步到位的我前后改了好几个版本踩了不少坑。说几个印象深的。坑一规则写太严误报满天飞最开始我把规则写得特别严觉得宁可错杀不可放过。结果跑出来一看10篇文章有9篇被标为高危误报率高得离谱。比如最重要的是被判定为绝对性表述最好的方法之一也被判定为绝对性表述。这就太扯了。后来我学乖了规则引擎只管最确定的模糊的交给语义检测。规则只抓那些100%是风险的模棱两可的都放过去让语义模型去判断。这样误报率一下子就降下来了。坑二语义模型不准因为训练数据不对我最开始用的是通用的内容安全模型直接拿来用。结果发现效果很差——因为通用模型的分类类别和GEO内容的风险类型完全对不上。比如通用模型能检测出脏话但检测不出夸大承诺。而GEO内容里最常见的风险恰恰是夸大承诺、绝对性表述这些不是脏话。后来我用自己标注的2000篇GEO内容微调了模型效果立马就上来了。准确率从60%多涨到了80%多。经验就是不要迷信通用模型一定要用自己领域的数据微调。哪怕只有几百条标注数据微调之后效果也会好很多。坑三分句不对影响语义检测效果语义检测是按句子来的所以分句准不准直接影响检测效果。最开始我用简单的句号分割问题很多。比如比如1.5版本的更新——里面的句号会被当成句末把一句话切成两半。还有引号里的句号、括号里的句号都会导致分句错误。后来我换了一个更智能的分句方法考虑了引号、括号、数字小数点等情况分句准确率提升了不少语义检测的效果也跟着好了。这些细节看起来不起眼但对最终效果影响挺大的。做NLP的朋友应该深有体会——数据预处理做好了效果就成功了一半。九、适用边界和局限性最后说说这个工具的适用边界不要神化它。能做到的快速筛查明显的风险点避免低级错误给出量化的合规得分方便批量评估提供修改建议指导优化方向覆盖80%左右的常见风险做不到的100%准确误报和漏报都存在检测复杂的逻辑问题比如自相矛盾判断内容的真实准确性只能检测表述方式不能验证事实替代人工审核只能作为辅助工具说白了这个工具是个体检仪不是诊断书。它能帮你快速发现明显的问题但最终的判断还是得靠人。我建议的用法是发布前先用工具扫一遍把高危和中危的都改了然后自己再过一遍。这样效率最高也最稳妥。还有一点要注意大模型的审核规则是会变的。今天没问题的表述明天可能就不行了。所以这个工具的规则也需要定期更新跟上大模型的变化。十、总结最后总结一下核心观点大模型的内容审核是GEO优化的隐形门槛。内容再好触了审核红线权重也会打折。12类常见风险高危3类、中危3类、低危6类高危的一定要避免。规则引擎语义检测的双层架构兼顾速度和准确率是性价比最高的方案。准确率87%召回率82%能覆盖大部分常见风险但不是万能的。工具是辅助最终还是要靠人。用工具快速筛查人工最终把关效率最高。做GEO优化不能只想着怎么加分还要想着怎么不扣分。很多时候不扣分比加分更容易也更有效。把这些低级风险都规避掉你的内容引用率自然就上去了。代码都贴在上面了直接复制就能用。如果有更好的检测思路或者你踩过其他的坑欢迎评论区交流。发布标签#GEO优化 #内容合规 #AI审核 #大模型 #Python #文本分类 #风险检测 #工具开发