feat: initial commit

This commit is contained in:
2026-06-23 20:29:02 +08:00
commit ea8f6066c4
217 changed files with 60754 additions and 0 deletions
+207
View File
@@ -0,0 +1,207 @@
"""
Reader-Pull Checker (追读力检查器)
检查钩子强度、期待管理、追读力:
- 章节开头的钩子
- 章节结尾的悬念
- 追读力债务
"""
from dataclasses import dataclass, field
from typing import List, Dict, Any, Optional
@dataclass
class ReaderPullIssue:
"""追读力问题"""
chapter: int
issue_type: str # weak_hook / no_ending_hook / debt_high
description: str
severity: str
@dataclass
class ReaderPullReport:
"""追读力检查报告"""
chapter: int
hook_strength: float # 0-1
ending_hook: bool
debt_level: float
issues: List[ReaderPullIssue] = field(default_factory=list)
passed: bool = True
summary: str = ""
class ReaderPullChecker:
"""
追读力检查器
检查:
1. 章节开头的钩子强度
2. 章节结尾的悬念
3. 追读力债务水平
"""
# 强钩子关键词
STRONG_HOOK_KEYWORDS = [
"危机", "危险", "困境", "生死", "抉择", "真相",
"震惊", "意外", "转折", "揭秘", "冲突", "大战"
]
# 结尾悬念关键词
ENDING_HOOK_KEYWORDS = [
"然而", "但是", "就在这时", "突然", "意外",
"未完待续", "敬请期待", "欲知后事", "悬念",
"却不知道", "更大的"
]
# 悬念关键词
SUSPENSE_KEYWORDS = [
"悬念", "伏笔", "暗示", "预示", "危机", "威胁"
]
def __init__(self):
self.chapter_debts: List[float] = []
def check_chapter(
self,
chapter_num: int,
chapter_text: str,
previous_debt: float = 0.0
) -> ReaderPullReport:
"""检查单章追读力"""
# 分析开头钩子
opening = chapter_text[:500] if len(chapter_text) > 500 else chapter_text
hook_strength = self._analyze_hook_strength(opening)
# 分析结尾悬念
ending = chapter_text[-500:] if len(chapter_text) > 500 else chapter_text
ending_hook = self._has_ending_hook(ending)
# 计算本章追读力债务
debt_level = self._calculate_debt_level(chapter_text, hook_strength, ending_hook)
report = ReaderPullReport(
chapter=chapter_num,
hook_strength=hook_strength,
ending_hook=ending_hook,
debt_level=debt_level
)
# 检测问题
if hook_strength < 0.3:
report.issues.append(ReaderPullIssue(
chapter=chapter_num,
issue_type="weak_hook",
description="章节开头钩子较弱,可能无法吸引读者",
severity="medium"
))
if not ending_hook:
report.issues.append(ReaderPullIssue(
chapter=chapter_num,
issue_type="no_ending_hook",
description="章节结尾缺少悬念,读者缺乏继续阅读的动力",
severity="high"
))
# 检查追读力债务
total_debt = previous_debt + debt_level
if total_debt > 80:
report.issues.append(ReaderPullIssue(
chapter=chapter_num,
issue_type="debt_high",
description=f"追读力债务过高({total_debt:.0f}),建议释放",
severity="critical"
))
self.chapter_debts.append(debt_level)
report.passed = len([i for i in report.issues if i.severity == "critical"]) == 0
if report.passed:
report.summary = f"钩子强度 {hook_strength:.0%},追读力债务 {total_debt:.0f}"
else:
report.summary = f"发现问题: {', '.join(i.issue_type for i in report.issues)}"
return report
def _analyze_hook_strength(self, opening: str) -> float:
"""分析钩子强度"""
if not opening:
return 0.0
strength = 0.3 # 基础分
# 检查强钩子关键词
strong_count = sum(1 for kw in self.STRONG_HOOK_KEYWORDS if kw in opening)
strength += min(0.4, strong_count * 0.1)
# 检查疑问句
question_count = opening.count('?') + opening.count('')
strength += min(0.2, question_count * 0.1)
# 检查省略号(制造悬念)
ellipsis_count = opening.count('...')
strength += min(0.1, ellipsis_count * 0.05)
return min(1.0, strength)
def _has_ending_hook(self, ending: str) -> bool:
"""检查结尾是否有悬念"""
if not ending:
return False
# 检查结尾悬念关键词
for kw in self.ENDING_HOOK_KEYWORDS:
if kw in ending:
return True
# 检查是否以冲突/悬念结尾
suspense_ending = ["", "", "……"]
if any(ending.strip().endswith(s) for s in suspense_ending):
return True
return False
def _calculate_debt_level(
self,
text: str,
hook_strength: float,
ending_hook: bool
) -> float:
"""计算追读力债务"""
debt = 0.0
# 无开头钩子
if hook_strength < 0.3:
debt += 20
# 无结尾悬念
if not ending_hook:
debt += 30
# 积累的悬念/伏笔
unresolved_count = 0
for kw in self.SUSPENSE_KEYWORDS:
unresolved_count += text.count(kw)
debt += min(30, unresolved_count * 5)
# 钩子强度可以抵消部分债务
debt -= hook_strength * 20
return max(0, min(100, debt))
def get_arc_reader_pull(self, start_chapter: int, end_chapter: int) -> Dict[str, Any]:
"""分析情节弧的追读力"""
if not self.chapter_debts:
return {"status": "no_data"}
relevant = self.chapter_debts[start_chapter - 1:end_chapter]
return {
"chapters": f"{start_chapter}-{end_chapter}",
"avg_hook_strength": sum(h.hook_strength for h in self.chapter_debts[start_chapter-1:end_chapter]) / len(relevant) if relevant else 0,
"avg_debt": sum(relevant) / len(relevant) if relevant else 0,
"debt_trend": relevant[-1] - relevant[0] if len(relevant) > 1 else 0
}