gwe-engine 3.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (118) hide show
  1. package/README.md +265 -0
  2. package/README_EN.md +126 -0
  3. package/dist/cli.js +10444 -0
  4. package/dist/cli.js.map +1 -0
  5. package/dist/index.d.ts +847 -0
  6. package/dist/index.js +10569 -0
  7. package/dist/index.js.map +1 -0
  8. package/package.json +1 -0
  9. package/src/kb/base-fillers.json +35 -0
  10. package/src/kb/base-prompt.ts +151 -0
  11. package/src/kb/base-vocab.json +87 -0
  12. package/src/kb/nodes/node_ai_continue_length/opt_ai_long.kb.json +36 -0
  13. package/src/kb/nodes/node_ai_continue_length/opt_ai_medium.kb.json +30 -0
  14. package/src/kb/nodes/node_ai_continue_length/opt_ai_short.kb.json +34 -0
  15. package/src/kb/nodes/node_ai_continue_length/opt_ai_xtra_long.kb.json +37 -0
  16. package/src/kb/nodes/node_ai_creativity/opt_ai_adventurous.kb.json +31 -0
  17. package/src/kb/nodes/node_ai_creativity/opt_ai_balanced.kb.json +31 -0
  18. package/src/kb/nodes/node_ai_creativity/opt_ai_conservative.kb.json +31 -0
  19. package/src/kb/nodes/node_anchor_density/opt_anchor_dense.kb.json +36 -0
  20. package/src/kb/nodes/node_anchor_density/opt_anchor_extreme.kb.json +37 -0
  21. package/src/kb/nodes/node_anchor_density/opt_anchor_sparse.kb.json +34 -0
  22. package/src/kb/nodes/node_anchor_density/opt_anchor_standard.kb.json +34 -0
  23. package/src/kb/nodes/node_battle_style/opt_battle_atmosphere.kb.json +32 -0
  24. package/src/kb/nodes/node_battle_style/opt_battle_detail.kb.json +36 -0
  25. package/src/kb/nodes/node_battle_style/opt_battle_psychological.kb.json +36 -0
  26. package/src/kb/nodes/node_battle_style/opt_battle_result.kb.json +37 -0
  27. package/src/kb/nodes/node_description_style/opt_desc_clinical.kb.json +36 -0
  28. package/src/kb/nodes/node_description_style/opt_desc_minimal.kb.json +37 -0
  29. package/src/kb/nodes/node_description_style/opt_desc_poetic.kb.json +35 -0
  30. package/src/kb/nodes/node_description_style/opt_desc_sensory.kb.json +39 -0
  31. package/src/kb/nodes/node_dialogue_style/opt_dialogue_concise.kb.json +34 -0
  32. package/src/kb/nodes/node_dialogue_style/opt_dialogue_natural.kb.json +32 -0
  33. package/src/kb/nodes/node_dialogue_style/opt_dialogue_stylized.kb.json +33 -0
  34. package/src/kb/nodes/node_dialogue_style/opt_dialogue_verbose.kb.json +34 -0
  35. package/src/kb/nodes/node_emotion_style/opt_emotion_both.kb.json +40 -0
  36. package/src/kb/nodes/node_emotion_style/opt_emotion_show.kb.json +41 -0
  37. package/src/kb/nodes/node_emotion_style/opt_emotion_tell.kb.json +40 -0
  38. package/src/kb/nodes/node_ending_hook/opt_hook_break.kb.json +54 -0
  39. package/src/kb/nodes/node_ending_hook/opt_hook_soft.kb.json +29 -0
  40. package/src/kb/nodes/node_ending_hook/opt_hook_suspense.kb.json +30 -0
  41. package/src/kb/nodes/node_info_density/opt_info_balanced.kb.json +37 -0
  42. package/src/kb/nodes/node_info_density/opt_info_fast.kb.json +37 -0
  43. package/src/kb/nodes/node_info_density/opt_info_leisurely.kb.json +41 -0
  44. package/src/kb/nodes/node_opening_impact/opt_opening_medium.kb.json +30 -0
  45. package/src/kb/nodes/node_opening_impact/opt_opening_strong.kb.json +46 -0
  46. package/src/kb/nodes/node_opening_impact/opt_opening_weak.kb.json +29 -0
  47. package/src/kb/nodes/node_paragraph_density/opt_dense_high.kb.json +36 -0
  48. package/src/kb/nodes/node_paragraph_density/opt_dense_low.kb.json +37 -0
  49. package/src/kb/nodes/node_paragraph_density/opt_dense_medium.kb.json +28 -0
  50. package/src/kb/nodes/node_payoff_frequency/opt_payoff_every_3.kb.json +31 -0
  51. package/src/kb/nodes/node_payoff_frequency/opt_payoff_every_chapter.kb.json +35 -0
  52. package/src/kb/nodes/node_payoff_frequency/opt_payoff_sparse.kb.json +37 -0
  53. package/src/kb/nodes/node_platform/opt_platform_feilu.kb.json +44 -0
  54. package/src/kb/nodes/node_platform/opt_platform_generic.kb.json +31 -0
  55. package/src/kb/nodes/node_platform/opt_platform_jinjiang.kb.json +42 -0
  56. package/src/kb/nodes/node_platform/opt_platform_newmedia.kb.json +43 -0
  57. package/src/kb/nodes/node_platform/opt_platform_qidian.kb.json +39 -0
  58. package/src/kb/nodes/node_platform/opt_platform_qimao.kb.json +43 -0
  59. package/src/kb/nodes/node_platform/opt_platform_tomato.kb.json +51 -0
  60. package/src/kb/nodes/node_pov/opt_pov_first_past.kb.json +34 -0
  61. package/src/kb/nodes/node_pov/opt_pov_first_present.kb.json +37 -0
  62. package/src/kb/nodes/node_pov/opt_pov_limited_third_past.kb.json +31 -0
  63. package/src/kb/nodes/node_pov/opt_pov_limited_third_present.kb.json +35 -0
  64. package/src/kb/nodes/node_pov/opt_pov_omniscient_third_past.kb.json +37 -0
  65. package/src/kb/nodes/node_pov/opt_pov_omniscient_third_present.kb.json +36 -0
  66. package/src/kb/nodes/node_rhetoric/opt_rhetoric_metaphor.kb.json +35 -0
  67. package/src/kb/nodes/node_rhetoric/opt_rhetoric_ornate.kb.json +37 -0
  68. package/src/kb/nodes/node_rhetoric/opt_rhetoric_plain.kb.json +25 -0
  69. package/src/kb/nodes/node_rhetoric/opt_rhetoric_sketch.kb.json +37 -0
  70. package/src/kb/nodes/node_sentence_rhythm/opt_rhythm_long.kb.json +42 -0
  71. package/src/kb/nodes/node_sentence_rhythm/opt_rhythm_medium.kb.json +40 -0
  72. package/src/kb/nodes/node_sentence_rhythm/opt_rhythm_mixed.kb.json +40 -0
  73. package/src/kb/nodes/node_sentence_rhythm/opt_rhythm_short.kb.json +41 -0
  74. package/src/kb/nodes/node_strictness/opt_strict_lenient.kb.json +50 -0
  75. package/src/kb/nodes/node_strictness/opt_strict_standard.kb.json +30 -0
  76. package/src/kb/nodes/node_strictness/opt_strict_strict.kb.json +52 -0
  77. package/src/kb/nodes/node_target_length/opt_length_1500.kb.json +39 -0
  78. package/src/kb/nodes/node_target_length/opt_length_2200.kb.json +39 -0
  79. package/src/kb/nodes/node_target_length/opt_length_3000.kb.json +39 -0
  80. package/src/kb/nodes/node_target_length/opt_length_4000.kb.json +39 -0
  81. package/src/kb/nodes/node_target_length/opt_length_custom.kb.json +31 -0
  82. package/src/kb/nodes/node_tone/opt_tone_badass.kb.json +45 -0
  83. package/src/kb/nodes/node_tone/opt_tone_classical.kb.json +41 -0
  84. package/src/kb/nodes/node_tone/opt_tone_cold.kb.json +42 -0
  85. package/src/kb/nodes/node_tone/opt_tone_colloquial.kb.json +33 -0
  86. package/src/kb/nodes/node_tone/opt_tone_folksy.kb.json +40 -0
  87. package/src/kb/nodes/node_tone/opt_tone_playful.kb.json +42 -0
  88. package/src/kb/nodes/node_tone/opt_tone_serious.kb.json +42 -0
  89. package/src/kb/nodes/node_twist_frequency/opt_twist_dense.kb.json +36 -0
  90. package/src/kb/nodes/node_twist_frequency/opt_twist_sparse.kb.json +26 -0
  91. package/src/kb/nodes/node_twist_frequency/opt_twist_standard.kb.json +32 -0
  92. package/src/kb/nodes/node_vocab_filler/opt_filler_relaxed.kb.json +45 -0
  93. package/src/kb/nodes/node_vocab_filler/opt_filler_standard.kb.json +46 -0
  94. package/src/kb/nodes/node_vocab_filler/opt_filler_strict.kb.json +56 -0
  95. package/src/kb/nodes/node_vocab_filler/opt_filler_webnovel.kb.json +56 -0
  96. package/src/kb/nodes/node_vocab_sensory/opt_vocab_daily.kb.json +46 -0
  97. package/src/kb/nodes/node_vocab_sensory/opt_vocab_esports.kb.json +45 -0
  98. package/src/kb/nodes/node_vocab_sensory/opt_vocab_fantasy.kb.json +40 -0
  99. package/src/kb/nodes/node_vocab_sensory/opt_vocab_generic.kb.json +32 -0
  100. package/src/kb/nodes/node_vocab_sensory/opt_vocab_historical.kb.json +43 -0
  101. package/src/kb/nodes/node_vocab_sensory/opt_vocab_horror.kb.json +48 -0
  102. package/src/kb/nodes/node_vocab_sensory/opt_vocab_military.kb.json +46 -0
  103. package/src/kb/nodes/node_vocab_sensory/opt_vocab_mystery.kb.json +43 -0
  104. package/src/kb/nodes/node_vocab_sensory/opt_vocab_scifi.kb.json +40 -0
  105. package/src/kb/nodes/node_vocab_sensory/opt_vocab_urban.kb.json +40 -0
  106. package/src/kb/nodes/node_vocab_sensory/opt_vocab_xianxia.kb.json +40 -0
  107. package/src/kb/presets/preset_daily.json +42 -0
  108. package/src/kb/presets/preset_esports.json +42 -0
  109. package/src/kb/presets/preset_fantasy.json +40 -0
  110. package/src/kb/presets/preset_historical.json +41 -0
  111. package/src/kb/presets/preset_horror.json +67 -0
  112. package/src/kb/presets/preset_miner.json +123 -0
  113. package/src/kb/presets/preset_mystery.json +55 -0
  114. package/src/kb/presets/preset_scifi_hardcore.json +55 -0
  115. package/src/kb/presets/preset_urban_jinjiang.json +42 -0
  116. package/src/kb/presets/preset_urban_tomato.json +66 -0
  117. package/src/kb/presets/preset_xianxia_qidian.json +50 -0
  118. package/src/kb/presets/preset_xianxia_tomato.json +66 -0
package/README.md ADDED
@@ -0,0 +1,265 @@
1
+ # GWE - Generic Web-novel Engine 网文追读力引擎
2
+
3
+ > 「读者手指划到你的正文,只有3秒决定要不要看下去。不是三章,是三秒。」
4
+
5
+ GWE 是一个专为中文网文设计的**质量检测与评分引擎**。它通过7维雷达评分、26项质量规则、身体锚点检测,帮你写出不被划走的网文。
6
+
7
+ - **零依赖**:纯 TypeScript 实现,无 NLP 库、无模型依赖、可在浏览器和 Node.js 运行
8
+ - **7维评分**:身体反应/感官信号/动作推进/情绪张力/信息推进/转折密度/章末钩子
9
+ - **26条规则**:覆盖开篇3秒、假钩子、AI水文、套路反应、水词填充等常见问题
10
+ - **锚点三级制**:套路反应(×0.2)、普通反应(×1.0)、高质量生理反应(×1.8)
11
+ - **22个配置节点**:平台(起点/番茄/七猫/晋江)、题材(仙侠/都市/科幻/悬疑)、风格(节奏/视角/修辞/语调)等细粒度调优
12
+ - **12个内置预设**:从起点仙侠到番茄都市,开箱即用
13
+
14
+ ## 快速开始
15
+
16
+ ### CLI 命令行(推荐用于批量检测)
17
+
18
+ ```bash
19
+ # 安装
20
+ npm install -g gwe-engine
21
+
22
+ # 检测一个章节
23
+ gwe check chapter.txt
24
+
25
+ # 从标准输入读取
26
+ cat chapter.txt | gwe -
27
+
28
+ # JSON 输出(用于程序集成)
29
+ gwe --json chapter.txt > report.json
30
+ ```
31
+
32
+ 输出示例:
33
+
34
+ ```
35
+ GWE V3.2.0 网文追读力检测 chapter.txt
36
+
37
+ 综合得分: 84.7分 (通过) 字数: 388字 ⚠ 及格
38
+
39
+ ─── 7维雷达评分 ───
40
+ 身体反应 ██████████████░░░░░░ 73
41
+ 感官信号 ████████████████████ 100
42
+ 动作推进 ████████████████████ 100
43
+ 情绪张力 ████████████████████ 100
44
+ 信息推进 ████████████████████ 100
45
+ 转折密度 ████████████████████ 100
46
+ 章末钩子 ███████████████░░░░░ 78
47
+
48
+ ─── 违规项 (0 error, 1 warning, 2 info) ───
49
+
50
+ ⚠ [连续对话过多] 有 5 句连续对话未穿插动作/描写,上限为 4 句
51
+ → 在对话之间插入角色的动作、表情或环境描写
52
+
53
+ ℹ [视觉描写占比过高] 视觉描写占比 54%,上限为 45%
54
+ → 减少纯视觉描写,加入其他感官体验
55
+
56
+ ℹ [比喻过多] 全文比喻密度千字5.2个,超过5个/千字
57
+ ```
58
+
59
+ ### 代码调用(Node.js / 浏览器)
60
+
61
+ ```typescript
62
+ import { createEngineWithKB, MockProvider } from 'gwe-engine';
63
+
64
+ // 创建引擎实例(自动加载所有内置知识库)
65
+ const { engine } = createEngineWithKB(new MockProvider());
66
+
67
+ // 检测章节文本
68
+ const result = engine.check(chapterText);
69
+
70
+ console.log(`得分: ${result.score}`);
71
+ console.log(`通过: ${result.passed}`);
72
+ console.log(`7维评分:`, result.radarScores);
73
+ console.log(`违规项:`, result.violations);
74
+ ```
75
+
76
+ ### 配合 LLM 使用(AI写作)
77
+
78
+ ```typescript
79
+ import { createEngineWithKB, OpenAICompatibleProvider } from 'gwe-engine';
80
+
81
+ const provider = new OpenAICompatibleProvider({
82
+ apiKey: 'your-api-key',
83
+ baseURL: 'https://api.deepseek.com/v1',
84
+ model: 'deepseek-chat',
85
+ });
86
+
87
+ const { engine } = createEngineWithKB(provider);
88
+
89
+ // 选择预设(番茄都市风格)
90
+ engine.selectPreset('preset_urban_tomato');
91
+
92
+ // 生成章节
93
+ const stream = await engine.writeChapter({
94
+ outline: '主角获得系统,第一次使用就遇到危机',
95
+ previousChapter: '...',
96
+ onDelta: (text) => process.stdout.write(text),
97
+ });
98
+ ```
99
+
100
+ ## 评分说明
101
+
102
+ | 分数区间 | 等级 | 说明 |
103
+ |---------|------|------|
104
+ | ≥90分 | 🏆 优秀 | 对标一线热门网文,追读力极强 |
105
+ | ≥85分 | ✅ 良好 | 达到发布标准,读者留存率高 |
106
+ | ≥75分 | ⚠ 及格 | 有明显问题,需要修改 |
107
+ | ≥60分 | ⚠ 较差 | 较多问题,建议重写关键段落 |
108
+ | <60分 | ✗ 不达标 | 质量不合格,读者会直接划走 |
109
+
110
+ ## 对比测试
111
+
112
+ 我们用 GWE 对三类文本进行了盲测:
113
+
114
+ | 文本类型 | 字数 | 综合得分 | 身体反应 | 感官信号 | 动作推进 | 情绪张力 | 信息推进 | 转折密度 | 章末钩子 | 违规数 |
115
+ |---------|------|---------|---------|---------|---------|---------|---------|---------|---------|-------|
116
+ | 追读力风格(矿工式) | 388字 | **84.7** | 73 | 100 | 100 | 100 | 100 | 100 | 78 | 3 |
117
+ | 经典套路开头(系统流) | 372字 | **84.4** | 92 | 100 | 100 | 83 | 100 | 99 | 68 | 3 |
118
+ | AI写景水文 | 391字 | **57** | 73 | 90 | 97 | 77 | 61 | 53 | 68 | 9 |
119
+
120
+ ### 检测效果对比
121
+
122
+ **AI写景水文(57分,9项违规)被精准识别的问题:**
123
+ - ⚠ 开篇首句49字,太长,读者扫一眼就划走
124
+ - ⚠ 开篇写景/背景开头,浪费黄金3秒
125
+ - ⚠ 连续比喻过密("宛如泼墨山水画""如水的月光")
126
+ - ⚠ 空洞成语堆砌(连绵起伏、美不胜收、波光粼粼)
127
+ - ⚠ 填充词过多("仿佛""不禁""淡淡")
128
+ - ℹ 短句占比不足(目标40%,实际21%)
129
+ - ℹ 视觉描写占比过高(58%,上限45%)
130
+
131
+ **矿工式追读文(84.7分):**
132
+ - 首句"疼。"仅1字,直接切入身体感受
133
+ - 全程无空泛写景,信息密度极高
134
+ - 章末断裂钩子"是红色。"制造强悬念
135
+ - 唯一的问题是短篇片段的锚点密度稍低
136
+
137
+ ## 7维雷达评分体系
138
+
139
+ GWE 从7个维度评估网文的追读力:
140
+
141
+ | 维度 | 核心问题 | 评分依据 |
142
+ |------|---------|---------|
143
+ | **身体反应** | 读者有没有"感受到痛"? | 身体锚点密度、质量分级、千字锚点数 |
144
+ | **感官信号** | 五感有没有被调动? | 视觉/听觉/触觉/嗅觉/味觉词频 |
145
+ | **动作推进** | 故事有没有在动? | 动作动词密度、对话标签、静态描写比例 |
146
+ | **情绪张力** | 有没有让人心跳加速? | 情绪词、紧迫感、反差、冲突信号 |
147
+ | **信息推进** | 有没有在给新信息? | 新名词/新概念出现频率,信息量增量 |
148
+ | **转折密度** | 有没有意外? | 转折词、认知颠覆、反转信号频率 |
149
+ | **章末钩子** | 读者会不会点下一章? | 最后100字的新信息密度、悬念质量 |
150
+
151
+ ## 26条检测规则
152
+
153
+ GWE 内置26条检测规则,覆盖网文写作的核心陷阱:
154
+
155
+ **Error级(必须修改):**
156
+ - 开篇300字内出现写景/回忆/背景交代
157
+ - 连续3句以上空洞描写无动作
158
+ - 假钩子("一股神秘气息""不祥的预感")
159
+ - 连续对话超过上限未穿插动作
160
+
161
+ **Warning级(建议修改):**
162
+ - 首句超过20字
163
+ - 填充词密度过高
164
+ - 锚点数量不足
165
+ - AI套路反应("瞳孔一缩""叹了口气")
166
+ - 比喻密度过高
167
+ - 成语堆砌代替具体描写
168
+ - 连续段落过长
169
+ - 对话标签重复
170
+ - 视觉描写占比过高
171
+ - 连续对话未穿插动作
172
+ - 开篇用环境/背景词开头
173
+
174
+ **Info级(优化建议):**
175
+ - 短句占比不足
176
+ - 平均段长过长
177
+ - 对话占比异常
178
+ - 五感分布不均
179
+ - 比喻密度偏高
180
+
181
+ ## 22个配置节点
182
+
183
+ | 节点 | 选项数 | 说明 |
184
+ |------|-------|------|
185
+ | 平台适配 | 7 | 起点/番茄/七猫/飞卢/晋江/新媒体/通用 |
186
+ | 题材词库 | 11 | 仙侠/都市/科幻/悬疑/历史/军事/电竞/恐怖/日常/奇幻/通用 |
187
+ | 视角 | 6 | 一/三人称 × 过去/现在时 × 有限/全知 |
188
+ | 目标字数 | 5 | 1500/2200/3000/4000/自定义 |
189
+ | 段落密度 | 3 | 低/中/高 |
190
+ | 句长节奏 | 4 | 短句/中句/长短混合/长句 |
191
+ | 锚点密度 | 4 | 稀疏/标准/密集/极端 |
192
+ | 开头力度 | 3 | 弱/中/强冲击 |
193
+ | 结尾钩子 | 3 | 柔和/悬念/断裂 |
194
+ | 信息密度 | 3 | 悠闲/平衡/快速 |
195
+ | 情绪风格 | 3 | Show/Tell/混合 |
196
+ | 对话风格 | 4 | 极简/自然/风格化/话痨 |
197
+ | 描写风格 | 4 | 白描/极简/感官/诗意 |
198
+ | 战斗风格 | 4 | 氛围/细节/心理/结果 |
199
+ | 修辞风格 | 4 | 平实/比喻/华丽/写意 |
200
+ | 语调 | 7 | 严肃/冷硬/痞气/古风/口语/俏皮/冷峻 |
201
+ | 反转频率 | 3 | 稀疏/标准/密集 |
202
+ | 回报频率 | 3 | 每章/每3章/稀疏 |
203
+ | 水词过滤 | 4 | 宽松/标准/严格/网文专用 |
204
+ | 检测严格度 | 3 | 宽松/标准/严格 |
205
+ | AI续写长度 | 4 | 短/中/长/超长 |
206
+ | AI创造性 | 3 | 保守/平衡/大胆 |
207
+
208
+ ## 12个内置预设
209
+
210
+ - `preset_xianxia_qidian` - 起点仙侠
211
+ - `preset_xianxia_tomato` - 番茄仙侠
212
+ - `preset_urban_tomato` - 番茄都市
213
+ - `preset_urban_jinjiang` - 晋江言情
214
+ - `preset_fantasy` - 西方奇幻
215
+ - `preset_scifi_hardcore` - 硬核科幻
216
+ - `preset_mystery` - 悬疑推理
217
+ - `preset_horror` - 恐怖灵异
218
+ - `preset_historical` - 历史穿越
219
+ - `preset_esports` - 电竞竞技
220
+ - `preset_daily` - 日常治愈
221
+ - `preset_miner` - 矿工式强追读(最高强度)
222
+
223
+ ## 架构
224
+
225
+ ```
226
+ gwe-engine/
227
+ ├── src/
228
+ │ ├── index.ts # 统一出口
229
+ │ ├── types.ts # 类型定义 + 默认阈值/权重
230
+ │ ├── gwe-engine.ts # 引擎主类(GWEEngine)
231
+ │ ├── checker.ts # 文本检测器(26条规则)
232
+ │ ├── radar.ts # 7维雷达评分
233
+ │ ├── anchor-detector.ts # 身体锚点检测(三级质量分级)
234
+ │ ├── filler-words.ts # 填充词检测
235
+ │ ├── config-merger.ts # 配置合并器
236
+ │ ├── node-registry.ts # 22个节点注册表
237
+ │ ├── validator.ts # 冲突/依赖验证
238
+ │ ├── prompt-builder.ts # LLM提示词构建(9种任务模板)
239
+ │ ├── llm-provider.ts # LLM抽象层(OpenAI兼容/Mock)
240
+ │ ├── kb-loader.ts # 知识库加载(静态导入)
241
+ │ ├── cli.ts # 命令行工具
242
+ │ └── kb/ # 知识库数据
243
+ │ ├── nodes/ # 22个节点 × 选项KB(85个.kb.json)
244
+ │ ├── presets/ # 12个内置预设
245
+ │ ├── base-vocab.json
246
+ │ ├── base-fillers.json
247
+ │ └── base-prompt.ts
248
+ └── dist/ # 构建产物
249
+ ```
250
+
251
+ ## 为什么不是大模型?
252
+
253
+ GWE 不依赖任何大语言模型。它的检测逻辑全部基于规则和词表,这意味着:
254
+
255
+ 1. **速度极快**:检测一个3000字章节 < 10ms,大模型需要3-10秒
256
+ 2. **零成本**:不需要API key,不需要GPU,离线可用
257
+ 3. **一致性**:同样的文本永远得到同样的分数,不会因模型版本变化而波动
258
+ 4. **可解释**:每个评分维度和违规项都有明确原因和修改建议
259
+ 5. **可定制**:22个配置节点 + 自定义词表,适配任何风格
260
+
261
+ 大模型擅长生成,GWE擅长评判。两者配合使用效果最佳:大模型写初稿,GWE做质检,迭代到分数达标再发布。
262
+
263
+ ## License
264
+
265
+ MIT
package/README_EN.md ADDED
@@ -0,0 +1,126 @@
1
+ # GWE - Generic Web-novel Engine
2
+
3
+ > "When a reader's finger scrolls to your text, you have 3 seconds to hook them. Not 3 chapters. 3 seconds."
4
+
5
+ GWE is a **quality analysis and scoring engine** purpose-built for Chinese web novels (网文). It uses 7-dimensional radar scoring, 26 quality rules, and body-reaction anchor detection to help you write prose that readers won't scroll past.
6
+
7
+ - **Zero dependencies**: Pure TypeScript. No NLP libraries, no model dependencies. Runs in browser and Node.js.
8
+ - **7-dimension scoring**: Body Reaction / Sensory Signal / Action / Emotion / Info Advance / Twist Frequency / Chapter Hook
9
+ - **26 quality rules**: Covers 3-second opening, fake hooks, AI slop, cliché reactions, filler words, and more
10
+ - **Anchor quality tiers**: Cliché reaction (×0.2), normal reaction (×1.0), quality physiological reaction (×1.8)
11
+ - **22 configuration nodes**: Platform (Qidian/Tomato/Qimao/Jinjiang), genre (xianxia/urban/scifi/mystery), style (pacing/POV/rhetoric/tone)
12
+ - **12 built-in presets**: From Qidian xianxia to Tomato urban, ready to use out of the box
13
+
14
+ ## Quick Start
15
+
16
+ ### CLI
17
+
18
+ ```bash
19
+ npm install -g gwe-engine
20
+
21
+ # Check a chapter file
22
+ gwe check chapter.txt
23
+
24
+ # Read from stdin
25
+ cat chapter.txt | gwe -
26
+
27
+ # JSON output for programmatic use
28
+ gwe --json chapter.txt > report.json
29
+ ```
30
+
31
+ ### Library Usage (Node.js / Browser)
32
+
33
+ ```typescript
34
+ import { createEngineWithKB, MockProvider } from 'gwe-engine';
35
+
36
+ const { engine } = createEngineWithKB(new MockProvider());
37
+ const result = engine.check(chapterText);
38
+
39
+ console.log(`Score: ${result.score}`);
40
+ console.log(`Passed: ${result.passed}`);
41
+ console.log(`Radar scores:`, result.radarScores);
42
+ console.log(`Violations:`, result.violations);
43
+ ```
44
+
45
+ ### With LLM (AI Writing)
46
+
47
+ ```typescript
48
+ import { createEngineWithKB, OpenAICompatibleProvider } from 'gwe-engine';
49
+
50
+ const provider = new OpenAICompatibleProvider({
51
+ apiKey: 'your-api-key',
52
+ baseURL: 'https://api.deepseek.com/v1',
53
+ model: 'deepseek-chat',
54
+ });
55
+
56
+ const { engine } = createEngineWithKB(provider);
57
+ engine.selectPreset('preset_urban_tomato');
58
+
59
+ const stream = await engine.writeChapter({
60
+ outline: 'Protagonist gets a system, encounters crisis on first use',
61
+ previousChapter: '...',
62
+ onDelta: (text) => process.stdout.write(text),
63
+ });
64
+ ```
65
+
66
+ ## Scoring Guide
67
+
68
+ | Score | Grade | Description |
69
+ |-------|-------|-------------|
70
+ | ≥90 | 🏆 Excellent | Top-tier popular web novel level, extremely high retention |
71
+ | ≥85 | ✅ Good | Publishable quality, good reader retention |
72
+ | ≥75 | ⚠ Passing | Noticeable issues, revision needed |
73
+ | ≥60 | ⚠ Poor | Multiple problems, consider rewriting key sections |
74
+ | <60 | ✗ Fail | Below standard, readers will scroll away |
75
+
76
+ ## Benchmark
77
+
78
+ | Text Type | Chars | Score | Body | Sensory | Action | Emotion | Info | Twist | Hook | Violations |
79
+ |-----------|-------|-------|------|---------|--------|---------|------|-------|------|------------|
80
+ | Hook-driven style ("Miner" style) | 388 | **84.7** | 73 | 100 | 100 | 100 | 100 | 100 | 78 | 3 |
81
+ | Classic cliché opening (system apotheosis) | 372 | **84.4** | 92 | 100 | 100 | 83 | 100 | 99 | 68 | 3 |
82
+ | AI scenery slop | 391 | **57** | 73 | 90 | 97 | 77 | 61 | 53 | 68 | 9 |
83
+
84
+ ## Why Not LLMs?
85
+
86
+ GWE doesn't depend on any large language model. Its detection logic is entirely rules-based, which means:
87
+
88
+ 1. **Blazing fast**: Checking a 3000-char chapter takes <10ms. LLMs take 3-10 seconds.
89
+ 2. **Zero cost**: No API keys, no GPU needed, works offline.
90
+ 3. **Consistent**: Same text always gets the same score, no model drift.
91
+ 4. **Explainable**: Every score and violation comes with a clear reason and fix suggestion.
92
+ 5. **Customizable**: 22 config nodes + custom vocabularies for any style.
93
+
94
+ LLMs are great at generation. GWE is great at judgment. Best used together: LLM writes the draft, GWE does quality control, iterate until the score passes the threshold.
95
+
96
+ ## Architecture
97
+
98
+ ```
99
+ gwe-engine/
100
+ ├── src/
101
+ │ ├── index.ts # Public API
102
+ │ ├── types.ts # Type definitions + default thresholds/weights
103
+ │ ├── gwe-engine.ts # Main engine class (GWEEngine)
104
+ │ ├── checker.ts # Text checker (26 rules)
105
+ │ ├── radar.ts # 7-dimension radar scoring
106
+ │ ├── anchor-detector.ts # Body reaction anchor detection (3-tier quality)
107
+ │ ├── filler-words.ts # Filler word detection
108
+ │ ├── config-merger.ts # Config merger
109
+ │ ├── node-registry.ts # 22-node registry
110
+ │ ├── validator.ts # Conflict/dependency validator
111
+ │ ├── prompt-builder.ts # LLM prompt builder (9 task templates)
112
+ │ ├── llm-provider.ts # LLM abstraction (OpenAI-compatible / Mock)
113
+ │ ├── kb-loader.ts # Knowledge base loader (static imports)
114
+ │ ├── cli.ts # CLI tool
115
+ │ └── kb/ # Knowledge base data
116
+ │ ├── nodes/ # 22 nodes × option KB files (85 .kb.json files)
117
+ │ ├── presets/ # 12 built-in presets
118
+ │ ├── base-vocab.json
119
+ │ ├── base-fillers.json
120
+ │ └── base-prompt.ts
121
+ └── dist/ # Build output
122
+ ```
123
+
124
+ ## License
125
+
126
+ MIT