跳到主要内容

宣传文案审校与润色

收藏0

逐轮检查宣传文案的清晰度、语气、利益点与证据,给出保留原意的修改稿和修改理由。

让 AI 安装

github最新版本 0.1.1更新于 2026-10-21下载 2 次2 个版本

scripts/ai_content_detector.py

10.8 KB
1#!/usr/bin/env python3
2"""
3ai_content_detector.py — Detect AI-generated content patterns.
4
5Three detection methods:
6 1. Burstiness analysis — human writing has high variance in sentence length;
7 AI writing has consistently medium-length sentences
8 2. Vocabulary diversity (Type-Token Ratio) — AI reuses words more than humans
9 3. Known AI phrases — specific phrases that appear disproportionately in
10 AI-generated text
11
12This is a heuristic tool, NOT a proof engine. False positives are expected.
13The goal is to flag passages that FEEL AI-generated so a human editor can
14inject voice, variance, and specificity.
15
16Usage:
17 python ai_content_detector.py article.md
18 python ai_content_detector.py article.md --json
19 python ai_content_detector.py --demo
20
21Scoring:
22 0-20 = likely human (high burstiness, diverse vocab, no AI phrases)
23 21-50 = mixed signals (review flagged passages)
24 51-100 = likely AI (flat burstiness, repetitive vocab, AI phrase density)
25"""
26from __future__ import annotations
27import argparse
28import json
29import math
30import re
31import sys
32from collections import Counter
33from pathlib import Path
34
35# --- Known AI phrases (commonly overrepresented in LLM output) ---
36
37AI_PHRASES = [
38 "in today's digital landscape",
39 "in today's fast-paced",
40 "it's worth noting that",
41 "it is important to note",
42 "delve into",
43 "dive deep into",
44 "leverage",
45 "game-changer",
46 "game changer",
47 "unlock the potential",
48 "unlock the power",
49 "harness the power",
50 "at the end of the day",
51 "in conclusion",
52 "in summary",
53 "navigating the complexities",
54 "a comprehensive guide",
55 "seamlessly integrate",
56 "robust solution",
57 "cutting-edge",
58 "state-of-the-art",
59 "empower you to",
60 "take your .* to the next level",
61 "in the realm of",
62 "tapestry of",
63 "multifaceted",
64 "it's crucial to",
65 "paramount",
66 "foster a .* environment",
67 "elevate your",
68]
69
70AI_PHRASE_RES = [re.compile(p, re.IGNORECASE) for p in AI_PHRASES]
71
72# --- Sentence splitting ---
73
74SENTENCE_RE = re.compile(r"[^.!?]+[.!?]+", re.DOTALL)
75WORD_RE = re.compile(r"[a-zA-Z]+")
76
77DEMO_CONTENT = """In today's digital landscape, leveraging AI tools has become a game-changer for content creators. It's worth noting that the ability to harness the power of large language models can unlock the potential of your marketing efforts. This comprehensive guide will delve into the multifaceted world of AI-assisted content creation.
78
79The integration of AI into content workflows is a robust solution that seamlessly integrates with existing processes. By navigating the complexities of modern content production, you can elevate your brand's voice and foster a creative environment that empowers your team to take their content to the next level.
80
81At the end of the day, it's crucial to understand that AI is a tool, not a replacement. The cutting-edge capabilities of state-of-the-art models are paramount for staying competitive in the realm of digital marketing. In conclusion, the tapestry of modern content creation requires both human creativity and artificial intelligence working in harmony."""
82
83
84def extract_sentences(text):
85 body = re.sub(r"^---.*?---\s*", "", text, count=1, flags=re.DOTALL)
86 body = re.sub(r"^#+\s+.*$", "", body, flags=re.MULTILINE)
87 body = re.sub(r"```.*?```", "", body, flags=re.DOTALL)
88 body = re.sub(r"`[^`]+`", "", body)
89 body = re.sub(r"\[([^\]]+)\]\([^)]+\)", r"\1", body)
90 sentences = SENTENCE_RE.findall(body)
91 return [s.strip() for s in sentences if len(s.strip().split()) >= 3]
92
93
94def burstiness_score(sentences):
95 """Compute burstiness (sentence length variance). High = human, low = AI."""
96 if len(sentences) < 5:
97 return {"score": 50, "mean": 0, "std": 0, "cv": 0, "note": "too few sentences"}
98 lengths = [len(s.split()) for s in sentences]
99 mean = sum(lengths) / len(lengths)
100 variance = sum((l - mean) ** 2 for l in lengths) / len(lengths)
101 std = math.sqrt(variance)
102 cv = std / mean if mean > 0 else 0 # coefficient of variation
103
104 # Human writing: CV typically 0.4-0.8+ (high variance)
105 # AI writing: CV typically 0.15-0.35 (consistently medium)
106 if cv >= 0.5:
107 ai_prob = max(0, 30 - (cv - 0.5) * 60)
108 elif cv >= 0.35:
109 ai_prob = 30 + (0.5 - cv) * 130
110 else:
111 ai_prob = 50 + (0.35 - cv) * 250
112
113 ai_prob = max(0, min(100, ai_prob))
114 return {
115 "score": round(ai_prob, 1),
116 "mean_sentence_length": round(mean, 1),
117 "std_sentence_length": round(std, 1),
118 "coefficient_of_variation": round(cv, 3),
119 "note": "low CV = flat sentence lengths (AI-like)" if cv < 0.35 else "healthy variance",
120 }
121
122
123def vocabulary_diversity(text):
124 """Type-Token Ratio. Low TTR = repetitive vocabulary (AI-like)."""
125 body = re.sub(r"^---.*?---\s*", "", text, count=1, flags=re.DOTALL)
126 words = [w.lower() for w in WORD_RE.findall(body) if len(w) > 2]
127 if len(words) < 50:
128 return {"score": 50, "ttr": 0, "unique": 0, "total": len(words), "note": "too few words"}
129 # Use a sliding window TTR for length-independence
130 window = min(200, len(words))
131 ttrs = []
132 for i in range(0, len(words) - window + 1, window // 2):
133 chunk = words[i : i + window]
134 ttrs.append(len(set(chunk)) / len(chunk))
135 avg_ttr = sum(ttrs) / len(ttrs)
136
137 # Human: TTR 0.55-0.75+ (varied vocabulary)
138 # AI: TTR 0.35-0.50 (repetitive patterns)
139 if avg_ttr >= 0.60:
140 ai_prob = max(0, 25 - (avg_ttr - 0.60) * 150)
141 elif avg_ttr >= 0.45:
142 ai_prob = 25 + (0.60 - avg_ttr) * 330
143 else:
144 ai_prob = 75 + (0.45 - avg_ttr) * 250
145
146 ai_prob = max(0, min(100, ai_prob))
147
148 # Top repeated words (excluding stop words)
149 stop = {"the", "and", "for", "that", "this", "with", "from", "are", "was", "were", "been",
150 "have", "has", "had", "not", "but", "can", "will", "your", "you", "they", "their",
151 "more", "than", "also", "into", "when", "how", "what", "which", "about", "each"}
152 content_words = [w for w in words if w not in stop]
153 top_repeated = Counter(content_words).most_common(5)
154
155 return {
156 "score": round(ai_prob, 1),
157 "avg_ttr": round(avg_ttr, 3),
158 "unique_words": len(set(words)),
159 "total_words": len(words),
160 "top_repeated": [{"word": w, "count": c} for w, c in top_repeated],
161 "note": "low TTR = repetitive vocabulary (AI-like)" if avg_ttr < 0.45 else "healthy diversity",
162 }
163
164
165def phrase_detection(text):
166 """Count known AI phrases."""
167 found = []
168 text_lower = text.lower()
169 for i, pattern in enumerate(AI_PHRASE_RES):
170 matches = pattern.findall(text_lower)
171 if matches:
172 found.append({"phrase": AI_PHRASES[i], "count": len(matches)})
173
174 total_matches = sum(f["count"] for f in found)
175 word_count = len(text.split())
176 density = (total_matches / (word_count / 1000)) if word_count > 0 else 0
177
178 # 0-2 per 1K words = normal; 3-5 = suspect; 6+ = likely AI
179 if density <= 2:
180 ai_prob = density * 15
181 elif density <= 5:
182 ai_prob = 30 + (density - 2) * 20
183 else:
184 ai_prob = min(100, 90 + (density - 5) * 5)
185
186 return {
187 "score": round(ai_prob, 1),
188 "phrases_found": len(found),
189 "total_matches": total_matches,
190 "density_per_1k_words": round(density, 1),
191 "matches": found[:10],
192 "note": f"{density:.1f} AI phrases per 1K words" if found else "no known AI phrases",
193 }
194
195
196def analyze(text):
197 sentences = extract_sentences(text)
198 burst = burstiness_score(sentences)
199 vocab = vocabulary_diversity(text)
200 phrases = phrase_detection(text)
201
202 # Weighted composite: burstiness 35%, vocab 30%, phrases 35%
203 composite = burst["score"] * 0.35 + vocab["score"] * 0.30 + phrases["score"] * 0.35
204 composite = round(min(100, max(0, composite)), 1)
205
206 if composite <= 20:
207 verdict = "LIKELY_HUMAN"
208 elif composite <= 50:
209 verdict = "MIXED"
210 else:
211 verdict = "LIKELY_AI"
212
213 return {
214 "status": "ok",
215 "composite_score": composite,
216 "verdict": verdict,
217 "burstiness": burst,
218 "vocabulary": vocab,
219 "phrases": phrases,
220 "sentences_analyzed": len(sentences),
221 "recommendations": _recommendations(burst, vocab, phrases),
222 }
223
224
225def _recommendations(burst, vocab, phrases):
226 recs = []
227 if burst["score"] > 40:
228 recs.append("Vary sentence lengths: mix short punchy sentences (5-8 words) with longer explanatory ones (20-30 words).")
229 if vocab["score"] > 40:
230 recs.append("Diversify vocabulary: replace repeated words with synonyms. Use domain-specific jargon where appropriate.")
231 if phrases["total_matches"] > 0:
232 top = phrases["matches"][:3]
233 recs.append(f"Remove/replace AI phrases: {', '.join(p['phrase'] for p in top)}")
234 if not recs:
235 recs.append("Content reads naturally. No humanization needed.")
236 return recs
237
238
239def main():
240 p = argparse.ArgumentParser(
241 description="Detect AI-generated content via burstiness, vocabulary diversity, and phrase analysis.",
242 epilog="Score 0-20 = likely human, 21-50 = mixed, 51-100 = likely AI. Run with --demo.",
243 )
244 p.add_argument("file", nargs="?", help="Markdown/text file to analyze")
245 p.add_argument("--json", action="store_true", help="JSON output")
246 p.add_argument("--demo", action="store_true", help="Run with AI-heavy demo text")
247 args = p.parse_args()
248
249 if args.demo:
250 text = DEMO_CONTENT
251 elif args.file:
252 path = Path(args.file)
253 if not path.exists():
254 print(f"[error] {path} not found", file=sys.stderr)
255 sys.exit(1)
256 text = path.read_text(encoding="utf-8", errors="replace")
257 else:
258 p.print_help()
259 sys.exit(0)
260
261 result = analyze(text)
262
263 if args.json:
264 print(json.dumps(result, indent=2))
265 return
266
267 print(f"AI Content Detection — Composite: {result['composite_score']}/100 ({result['verdict']})")
268 print()
269 b = result["burstiness"]
270 print(f" Burstiness: {b['score']}/100 — CV={b['coefficient_of_variation']} ({b['note']})")
271 v = result["vocabulary"]
272 print(f" Vocabulary: {v['score']}/100 — TTR={v['avg_ttr']} ({v['note']})")
273 ph = result["phrases"]
274 print(f" AI Phrases: {ph['score']}/100 — {ph['total_matches']} matches, {ph['density_per_1k_words']}/1K words")
275 if ph["matches"]:
276 for m in ph["matches"][:5]:
277 print(f" → \"{m['phrase']}\" (×{m['count']})")
278 print()
279 print("Recommendations:")
280 for r in result["recommendations"]:
281 print(f" → {r}")
282
283
284if __name__ == "__main__":
285 main()

文件内容是平台检查时读出的文本,只供查看。平台不会运行包里的任何脚本或程序。