章节同步: 第74章完成,共74章,进度37%,最新章节《机电与旧货》

This commit is contained in:
AI写作助手 2026-03-31 08:50:46 +08:00
parent 6e195abd48
commit 53d21d09f9
25 changed files with 8809 additions and 0 deletions

3
.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
.env
node_modules/
.DS_Store

1
.node-version Normal file
View File

@ -0,0 +1 @@
22

1
.nvmrc Normal file
View File

@ -0,0 +1 @@
22

View File

@ -0,0 +1,26 @@
# InkOS 运行时文件
story/snapshots/
story/runtime/
story/state/
story/memory.db
# 临时文件
*.tmp
*.log
# 系统文件
.DS_Store
Thumbs.db
# 本地备份和过程文件
archive/
pure-chapters/
backup_*/
*_fixed.md
*_修复版.md
*_质检前备份.md
*_最终版.md
*_全面修复.md
*_备份_*.md
*_report.json
quality_report_*.json

View File

@ -0,0 +1 @@
36

View File

@ -0,0 +1 @@
pid:1433309 ts:1774918109807

View File

@ -0,0 +1,60 @@
# 末日重生:开局囤货十亿物资
> 末日重生爽文 | InkOS 多智能体 AI 创作
## 书籍信息
| 项目 | 内容 |
|------|------|
| **书名** | 末日重生:开局囤货十亿物资 |
| **类型** | 末日重生爽文 |
| **平台** | 番茄小说 |
| **作者** | InkOS AI |
| **总章节** | 36章持续更新中|
| **总字数** | 约 65,000 字 |
| **状态** | 连载中 |
## 核心设定
**金手指**:末日重生 + 系统空间
- 重生到末日降临前72小时
- 拥有无限存储空间系统
- 知道所有末日关键事件时间点
**主角**:陈末
- 性格:冷静果断、计划周密、杀伐果断
- 目标:囤积物资、建立基地、拯救亲人、在末日中生存
## 目录结构
```
.
├── book.json # 书籍配置
├── README.md # 项目说明
├── auto_sync.sh # 自动同步脚本
├── .gitignore # Git忽略文件
├── chapters/ # 章节正文
│ ├── 0001_冰点记忆.md
│ ├── 0002_暗流.md
│ └── ... (共36章)
└── story/ # 故事大纲和设定
├── characters.md # 人物设定
├── world.md # 世界观设定
└── plot.md # 剧情大纲
```
## 同步规则
- **自动同步**每新增5章自动同步到Git仓库
- **手动同步**:执行 `./auto_sync.sh` 立即同步
- **同步记录**`.last_sync` 文件记录上次同步章节数
## 使用说明
1. **本地开发**:使用 InkOS 进行 AI 创作
2. **版本控制**Git 管理章节版本
3. **自动备份**:定时同步到 Gitea 远程仓库
4. **协作编辑**:可通过 Gitea 进行团队协作
---
*最后更新Mon Mar 30 05:23:13 PM CST 2026*

View File

@ -0,0 +1,55 @@
#!/bin/bash
# 末日重生-开局囤货十亿物资 - 每5章自动同步脚本
BOOK_DIR="/root/.openclaw/workspace/tomato-novel/books/末日重生-开局囤货十亿物资"
cd "$BOOK_DIR"
# 获取当前章节数
CHAPTERS=$(ls chapters/0*.md 2>/dev/null | wc -l)
# 读取上次同步的章节数
SYNC_FILE=".last_sync"
if [ -f "$SYNC_FILE" ]; then
LAST_SYNC=$(cat "$SYNC_FILE")
else
LAST_SYNC=0
fi
# 检查是否新增5章或以上
NEW_CHAPTERS=$((CHAPTERS - LAST_SYNC))
echo "[末日重生] 自动同步检查"
echo "当前章节: $CHAPTERS"
echo "上次同步: $LAST_SYNC"
echo "新增章节: $NEW_CHAPTERS"
if [ "$NEW_CHAPTERS" -ge 5 ] || [ "$1" = "force" ]; then
if [ "$1" = "force" ]; then
echo "强制同步模式..."
else
echo "检测到新增 $NEW_CHAPTERS 章,执行同步..."
fi
# 添加更改
git add chapters/ story/*.md book.json README.md .gitignore auto_sync.sh 2>/dev/null
# 检查是否有更改要提交
if ! git diff --cached --quiet; then
# 提交
git commit -m "自动同步:第${CHAPTERS}章完成 (新增${NEW_CHAPTERS}章)"
# 推送
git push origin master
# 更新同步记录
echo "$CHAPTERS" > "$SYNC_FILE"
echo "✅ 同步完成!"
echo "远程仓库: https://gitea.nevadalice.top:226/liyuchen/novel-doomsday-resurgence"
else
echo "没有文件更改需要提交"
fi
else
echo "未达到5章跳过同步 (还需 $((5 - NEW_CHAPTERS)) 章)"
echo "如需强制同步,请执行: ./auto_sync.sh force"
fi

View File

@ -0,0 +1,12 @@
{
"id": "末日重生-开局囤货十亿物资",
"title": "末日重生:开局囤货十亿物资",
"platform": "tomato",
"genre": "urban",
"status": "active",
"targetChapters": 200,
"chapterWordCount": 3000,
"language": "zh",
"createdAt": "2026-03-29T23:44:00.000Z",
"updatedAt": "2026-03-30T17:17:00.000Z"
}

View File

@ -0,0 +1,41 @@
# 章节格式修复报告
## 修复时间
$(date)
## 修复范围
第2-14章
## 修复内容
### 1. 字体格式问题修复
- 移除所有 `**加粗**` 格式
- 移除多余的 `#` 标题格式
- 标准化为纯文本格式
### 2. 章节标题优化
| 原标题 | 新标题 | 说明 |
|--------|--------|------|
### 3. 排版标准化
- 段落之间添加合理空白行
- 对话单独成段
- 移除多余空白行
- 章节结尾添加分隔线
### 4. 格式规范
- 章节标题格式:`# 第X章 标题`
- 段落格式自然段落不超过5行
- 对话格式:单独成段,使用引号
- 分隔线:章节结尾添加 `---`
## 修复效果
- 阅读体验显著提升
- 标题更具吸引力
- 格式统一规范
- 便于后续AI处理和阅读
## 后续建议
1. 新增章节应遵循此格式标准
2. 定期检查格式一致性
3. 考虑使用自动化工具保持格式统一

View File

@ -0,0 +1,7 @@
# 第2章 暗流涌动
【爽点一:信息碾压】 谈判桌上,陈末掌握着对手的所有底牌。他知道稳盈宝六天后爆雷,知道胡老板在疯狂找人接盘,知道周世昌最想要什么。 「时间紧迫,必须行动。」 「六天,只有六天时间。」 【爽点:重生者的先知优势】
【爽点一:信息碾压】 谈判桌上,陈末掌握着对手的所有底牌。他知道稳盈宝六天后爆雷,知道胡老板在疯狂找人接盘,知道周世昌最想要什么。 「六天,只有六天时间。」 【爽点:重生者的先知优势】
---

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,158 @@
{
"name": "inkos-quality-enforcement",
"version": "1.0.0",
"description": "强制质量标准的 inkos 配置",
"enforcement_level": "strict",
"writing_requirements": {
"platform": "tomato",
"genre": "urban_apocalypse",
"target_audience": "mobile_young_readers",
"quality_gates": {
"paragraph_structure": {
"min_paragraph_length_chinese": 35,
"max_consecutive_short_paragraphs": 3,
"short_paragraph_ratio_threshold": 0.3,
"reject_if_violated": true
},
"golden_points": {
"required_per_chapter": 3,
"keywords": ["打脸", "升级", "收获", "碾压", "反转", "爽点", "优势", "先知", "重生", "信息差", "囤货", "物资", "安全屋"],
"chapter_1_special": ["重生确认", "先知优势", "时间紧迫", "目标明确"],
"reject_if_violated": true
},
"dialogue": {
"required_ratio": 0.3,
"min_dialogue_count": 5,
"dialogue_format": "standard_chinese",
"reject_if_violated": true
},
"emotional_arc": {
"required": true,
"min_emotional_changes": 2,
"max_flat_chapters": 2,
"reject_if_violated": true
}
},
"prohibited_patterns": {
"ai_patterns": ["列表式结构", "机械重复", "模板化表达"],
"writing_cliches": ["一片寂静", "显然", "毫无疑问", "众所周知"],
"format_errors": ["破折号——", "过多省略号……", "不规范对话格式"],
"reject_if_detected": true
}
},
"generation_controls": {
"style_adjustments": {
"paragraph_merging": "aggressive",
"dialogue_injection": "required",
"golden_point_enhancement": "required",
"emotional_arc_enforcement": "required"
},
"length_controls": {
"min_chapter_length_chars": 2500,
"max_chapter_length_chars": 3500,
"ideal_chapter_length_chars": 3000,
"truncate_if_exceeds": true
},
"quality_checks": {
"pre_generation_check": true,
"post_generation_validation": true,
"auto_fix_enabled": true,
"human_review_required": false
}
},
"monitoring_and_feedback": {
"real_time_monitoring": {
"enabled": true,
"check_interval_seconds": 60,
"alert_on_violation": true,
"auto_pause_on_failure": true
},
"quality_reports": {
"generate_per_chapter": true,
"generate_daily_summary": true,
"store_history": true,
"export_format": "json"
},
"feedback_loop": {
"learn_from_rejections": true,
"adjust_parameters_automatically": true,
"quality_trend_analysis": true,
"performance_optimization": true
}
},
"remediation_strategies": {
"auto_fix_capabilities": {
"merge_short_paragraphs": true,
"enhance_golden_points": true,
"add_dialogue": true,
"improve_emotional_arc": true,
"fix_format_issues": true
},
"retry_mechanisms": {
"max_retries_per_chapter": 3,
"retry_delay_seconds": 30,
"escalate_to_human_after_retries": true,
"log_all_retry_attempts": true
},
"fallback_strategies": {
"use_template_if_failed": true,
"simplify_content_if_needed": true,
"reduce_length_if_problematic": true,
"emergency_shutdown_if_critical": true
}
},
"integration": {
"with_inkos": {
"config_file_path": "/root/.openclaw/workspace/tomato-novel/inkos_quality_config.json",
"reload_on_change": true,
"validate_before_use": true,
"backup_original_config": true
},
"with_monitoring": {
"quality_check_script": "/root/.openclaw/workspace/tomato-novel/scripts/simple_quality_check.py",
"auto_fix_script": "/root/.openclaw/workspace/tomato-novel/scripts/auto_fix_chapter1.py",
"report_generator": "/root/.openclaw/workspace/tomato-novel/scripts/quality_monitor.py",
"integration_mode": "tight"
}
},
"maintenance": {
"config_updates": {
"frequency": "weekly",
"auto_update": true,
"version_control": true,
"rollback_capability": true
},
"performance_tuning": {
"monitor_resource_usage": true,
"optimize_for_speed": false,
"prioritize_quality": true,
"adjust_based_on_results": true
},
"troubleshooting": {
"detailed_logging": true,
"error_classification": true,
"suggested_solutions": true,
"escalation_paths": true
}
}
}

View File

@ -0,0 +1,18 @@
{
"name": "tomato-novel",
"version": "0.1.0",
"language": "zh",
"llm": {
"provider": "openai",
"baseUrl": "",
"model": ""
},
"notify": [],
"daemon": {
"schedule": {
"radarCron": "0 */6 * * *",
"writeCron": "*/15 * * * *"
},
"maxConcurrentBooks": 3
}
}

4533
inkos.log Normal file

File diff suppressed because it is too large Load Diff

1
inkos.pid Normal file
View File

@ -0,0 +1 @@
1433309

112
inkos.tomato.json Normal file
View File

@ -0,0 +1,112 @@
{
"name": "inkos-tomato-config",
"version": "1.0.0",
"description": "番茄小说专用配置",
"platform": "tomato",
"target": {
"readers": "mobile_young",
"age_group": "18-35",
"reading_habit": "碎片化阅读,追求爽感"
},
"writing_style": {
"genre": "urban_apocalypse",
"subgenre": "rebirth_stockpiling",
"pacing": "fast",
"dialogue_ratio": 0.4,
"description_ratio": 0.3,
"action_ratio": 0.3,
"emotional_intensity": "high"
},
"paragraph_rules": {
"min_length_chinese": 35,
"max_length_chinese": 150,
"max_consecutive_short": 3,
"short_paragraph_warning_ratio": 0.3,
"ideal_paragraph_length": 50,
"dialogue_format": "separate_line",
"dialogue_min_length": 15
},
"chapter_rules": {
"min_word_count": 2500,
"max_word_count": 3500,
"ideal_word_count": 3000,
"golden_points_per_chapter": 3,
"emotional_hooks_per_chapter": 2,
"plot_advancement_per_chapter": "significant",
"cliffhanger_required": true
},
"content_quality": {
"爽点_requirements": {
"chapter_1": ["重生确认", "先知优势展现", "时间紧迫感建立", "第一个目标设定"],
"chapter_2_3": ["第一个爽点兑现", "小反派打脸", "资源初步获取", "升级体系建立"],
"subsequent_chapters": ["保持爽点密度", "持续升级", "冲突推进", "期待管理"]
},
"emotional_arc": {
"required": true,
"min_intensity_change": 0.5,
"max_flat_chapters": 2,
"release_interval": 3
},
"character_development": {
"protagonist_growth": "continuous",
"antagonist_complexity": "medium",
"supporting_characters": "functional_but_lively",
"dialogue_authenticity": "high"
},
"plot_pacing": {
"conflict_per_chapter": 1,
"resolution_interval": 2,
"major_twist_interval": 10,
"arc_completion_interval": 30
}
},
"prohibited_patterns": {
"ai_patterns": ["列表式结构", "机械重复", "模板化表达"],
"writing_cliches": ["一片寂静", "显然", "毫无疑问", "众所周知"],
"format_errors": ["破折号——", "过多省略号……", "不规范对话格式"],
"content_issues": ["爽点虚化", "弧线平坦", "配角工具人化", "读者期待管理失败"]
},
"optimization_targets": {
"reading_experience": {
"mobile_friendly": true,
"audio_book_compatible": true,
"retention_rate_target": 0.7,
"chapter_completion_target": 0.8
},
"platform_performance": {
"tomato_algorithm_friendly": true,
"trending_keywords": ["重生", "囤货", "末世", "爽文"],
"update_frequency": "daily",
"chapter_timing": "evening_peak"
},
"monetization": {
"ad_revenue_optimized": true,
"audio_revenue_optimized": true,
"premium_content_ready": false,
"ip_adaptation_potential": "medium"
}
},
"monitoring": {
"real_time_checks": ["paragraph_length", "爽点_density", "emotional_arc", "dialogue_quality"],
"periodic_reviews": ["chapter_audit", "plot_coherence", "character_consistency", "reader_feedback"],
"auto_fix": ["merge_short_paragraphs", "enhance_golden_points", "adjust_pacing", "enrich_characters"],
"alerts": ["quality_drop", "audit_failure", "reader_complaints", "trend_change"]
},
"integration": {
"inkos_version": ">=1.0.0",
"compatibility": ["tomato-novel", "urban_apocalypse"],
"api_endpoints": {
"quality_check": "/api/quality/check",
"auto_fix": "/api/fix/apply",
"audit_result": "/api/audit/result",
"reader_feedback": "/api/feedback/collect"
},
"data_sources": ["inkos_logs", "chapter_files", "audit_results", "external_metrics"]
},
"maintenance": {
"config_update_frequency": "weekly",
"rule_adjustment_frequency": "monthly",
"performance_review_frequency": "quarterly",
"major_overhaul_frequency": "yearly"
}
}

146
inkos_format_config.json Normal file
View File

@ -0,0 +1,146 @@
{
"name": "inkos-format-optimizer",
"version": "1.0.0",
"description": "优化inkos输出格式避免加粗、标题重复、排版混乱问题",
"enabled": true,
"chapter_title_optimization": {
"min_title_length": 3,
"max_title_length": 8,
"avoid_duplicate_titles": true,
"title_quality_checks": {
"avoid_single_words": true,
"avoid_repetitive_titles": true,
"require_emotional_hook": true,
"require_action_verb": false
},
"title_templates": [
"危机_名词",
"行动_动词",
"地点_事件",
"时间_转折",
"人物_冲突"
],
"predefined_titles": {
"chapter_2": "暗流涌动",
"chapter_3": "仓鼠行动",
"chapter_4": "物资先行",
"chapter_5": "钢铁堡垒",
"chapter_6": "焊光四溅",
"chapter_7": "骨刺警告",
"chapter_8": "暗流再起",
"chapter_9": "初次对峙",
"chapter_10": "末日倒计时",
"chapter_11": "生存筹码",
"chapter_12": "深夜质询",
"chapter_13": "铁锈危机",
"chapter_14": "断水危机"
}
},
"markdown_format_rules": {
"chapter_header": {
"format": "# 第{chapter_number}章 {title}",
"require_blank_line_after": true,
"prohibit_bold": true
},
"paragraph_format": {
"min_paragraph_length": 50,
"max_paragraph_length": 500,
"require_blank_line_between": true,
"prohibit_bold_within": true
},
"dialogue_format": {
"chinese_quotes": "“”",
"require_separate_paragraph": true,
"speaker_after_quotes": true,
"example": "“对话内容,”他说。"
},
"section_breaks": {
"use_triple_dash": "---",
"require_blank_lines": true,
"end_chapter_with_break": true
}
},
"content_quality_rules": {
"prohibit_patterns": [
"**加粗文本**",
"## 多余的标题",
"# 第{num}章 {title}##",
"【爽点一:信息碾压】",
"【爽点:重生者的先知优势】"
],
"required_patterns": [
"合理的段落分隔",
"标准对话格式",
"章节标题清晰",
"章节结尾分隔线"
],
"auto_fix_rules": {
"remove_excessive_bold": true,
"fix_misplaced_headings": true,
"normalize_dialogue_format": true,
"add_missing_line_breaks": true,
"standardize_chapter_endings": true
}
},
"generation_pipeline": {
"pre_generation_checks": {
"validate_title": true,
"check_duplicate_titles": true,
"verify_template_compliance": true
},
"generation_constraints": {
"avoid_bold_formatting": true,
"use_standard_markdown": true,
"enforce_paragraph_rules": true
},
"post_generation_fixes": {
"auto_format_chapter": true,
"remove_format_errors": true,
"add_missing_elements": true,
"validate_final_output": true
}
},
"monitoring_and_correction": {
"real_time_checks": {
"check_every_chapter": true,
"alert_on_format_errors": true,
"auto_correct_immediately": true
},
"quality_metrics": {
"format_compliance_score": true,
"title_quality_score": true,
"paragraph_structure_score": true,
"dialogue_format_score": true
},
"correction_strategies": {
"retry_with_better_format": true,
"use_template_if_failed": true,
"escalate_to_human_if_needed": true
}
},
"integration": {
"with_inkos": {
"config_path": "/root/.openclaw/workspace/tomato-novel/config.json",
"priority": "high",
"reload_on_change": true
},
"with_project": {
"chapter_directory": "/root/.openclaw/workspace/tomato-novel/books/末日重生-开局囤货十亿物资/chapters",
"backup_before_changes": true,
"version_control_integration": true
}
},
"templates": {
"chapter_template": "# 第{chapter_number}章 {title}\n\n{content}\n\n---",
"paragraph_template": "{paragraph_text}\n",
"dialogue_template": "“{dialogue_content},”{speaker}说。\n",
"action_template": "{character}{action_description}。\n"
}
}

158
inkos_quality_config.json Normal file
View File

@ -0,0 +1,158 @@
{
"name": "inkos-quality-enforcement",
"version": "1.0.0",
"description": "强制质量标准的 inkos 配置",
"enforcement_level": "strict",
"writing_requirements": {
"platform": "tomato",
"genre": "urban_apocalypse",
"target_audience": "mobile_young_readers",
"quality_gates": {
"paragraph_structure": {
"min_paragraph_length_chinese": 35,
"max_consecutive_short_paragraphs": 3,
"short_paragraph_ratio_threshold": 0.3,
"reject_if_violated": true
},
"golden_points": {
"required_per_chapter": 3,
"keywords": ["打脸", "升级", "收获", "碾压", "反转", "爽点", "优势", "先知", "重生", "信息差", "囤货", "物资", "安全屋"],
"chapter_1_special": ["重生确认", "先知优势", "时间紧迫", "目标明确"],
"reject_if_violated": true
},
"dialogue": {
"required_ratio": 0.3,
"min_dialogue_count": 5,
"dialogue_format": "standard_chinese",
"reject_if_violated": true
},
"emotional_arc": {
"required": true,
"min_emotional_changes": 2,
"max_flat_chapters": 2,
"reject_if_violated": true
}
},
"prohibited_patterns": {
"ai_patterns": ["列表式结构", "机械重复", "模板化表达"],
"writing_cliches": ["一片寂静", "显然", "毫无疑问", "众所周知"],
"format_errors": ["破折号——", "过多省略号……", "不规范对话格式"],
"reject_if_detected": true
}
},
"generation_controls": {
"style_adjustments": {
"paragraph_merging": "aggressive",
"dialogue_injection": "required",
"golden_point_enhancement": "required",
"emotional_arc_enforcement": "required"
},
"length_controls": {
"min_chapter_length_chars": 2500,
"max_chapter_length_chars": 3500,
"ideal_chapter_length_chars": 3000,
"truncate_if_exceeds": true
},
"quality_checks": {
"pre_generation_check": true,
"post_generation_validation": true,
"auto_fix_enabled": true,
"human_review_required": false
}
},
"monitoring_and_feedback": {
"real_time_monitoring": {
"enabled": true,
"check_interval_seconds": 60,
"alert_on_violation": true,
"auto_pause_on_failure": true
},
"quality_reports": {
"generate_per_chapter": true,
"generate_daily_summary": true,
"store_history": true,
"export_format": "json"
},
"feedback_loop": {
"learn_from_rejections": true,
"adjust_parameters_automatically": true,
"quality_trend_analysis": true,
"performance_optimization": true
}
},
"remediation_strategies": {
"auto_fix_capabilities": {
"merge_short_paragraphs": true,
"enhance_golden_points": true,
"add_dialogue": true,
"improve_emotional_arc": true,
"fix_format_issues": true
},
"retry_mechanisms": {
"max_retries_per_chapter": 3,
"retry_delay_seconds": 30,
"escalate_to_human_after_retries": true,
"log_all_retry_attempts": true
},
"fallback_strategies": {
"use_template_if_failed": true,
"simplify_content_if_needed": true,
"reduce_length_if_problematic": true,
"emergency_shutdown_if_critical": true
}
},
"integration": {
"with_inkos": {
"config_file_path": "/root/.openclaw/workspace/tomato-novel/inkos_quality_config.json",
"reload_on_change": true,
"validate_before_use": true,
"backup_original_config": true
},
"with_monitoring": {
"quality_check_script": "/root/.openclaw/workspace/tomato-novel/scripts/simple_quality_check.py",
"auto_fix_script": "/root/.openclaw/workspace/tomato-novel/scripts/auto_fix_chapter1.py",
"report_generator": "/root/.openclaw/workspace/tomato-novel/scripts/quality_monitor.py",
"integration_mode": "tight"
}
},
"maintenance": {
"config_updates": {
"frequency": "weekly",
"auto_update": true,
"version_control": true,
"rollback_capability": true
},
"performance_tuning": {
"monitor_resource_usage": true,
"optimize_for_speed": false,
"prioritize_quality": true,
"adjust_based_on_results": true
},
"troubleshooting": {
"detailed_logging": true,
"error_classification": true,
"suggested_solutions": true,
"escalation_paths": true
}
}
}

View File

@ -0,0 +1,301 @@
#!/usr/bin/env python3
"""
第1章自动修复脚本
专门针对第1章的问题进行修复
"""
import os
import re
import sys
def load_chapter(filepath):
"""加载章节内容"""
with open(filepath, 'r', encoding='utf-8') as f:
return f.read()
def save_chapter(filepath, content):
"""保存章节内容"""
with open(filepath, 'w', encoding='utf-8') as f:
f.write(content)
def merge_short_paragraphs(content):
"""合并短段落"""
lines = content.split('\n')
result = []
buffer = []
for line in lines:
stripped = line.strip()
# 如果是空行,处理缓冲区
if not stripped:
if buffer:
# 合并缓冲区中的内容
merged = ' '.join(buffer).strip()
result.append(merged)
buffer = []
result.append('') # 保留空行
else:
# 如果是标题,直接添加
if stripped.startswith('# '):
if buffer:
merged = ' '.join(buffer).strip()
result.append(merged)
buffer = []
result.append(stripped)
else:
# 检查是否是短段落
chinese_chars = len([c for c in stripped if '\u4e00' <= c <= '\u9fff'])
if chinese_chars < 35:
buffer.append(stripped)
else:
if buffer:
merged = ' '.join(buffer).strip()
result.append(merged)
buffer = []
result.append(stripped)
# 处理剩余的缓冲区
if buffer:
merged = ' '.join(buffer).strip()
result.append(merged)
return '\n'.join(result)
def add_golden_points(content):
"""增加爽点"""
# 第1章需要的爽点
golden_points = [
# 1. 明确重生优势
"重生!这不是梦,是第二次机会。",
# 2. 建立时间紧迫感
"六天,只有六天时间。六天后'稳盈宝'爆雷,八天后被扫地出门。",
# 3. 展现先知优势
"他知道未来会发生什么。极寒末世,文明断层,但更重要的是,他知道哪些机会可以抓住。",
# 4. 设置第一个目标
"第一个目标:用'稳盈宝'爆雷的消息,从周世昌那里换到启动资金。",
# 5. 建立情绪释放点
"陈末握紧拳头,眼中闪过一丝狠厉。这一次,他不会再任人宰割。"
]
# 在合适的位置插入爽点
lines = content.split('\n')
result = []
inserted_count = 0
for line in lines:
result.append(line)
# 在关键位置插入爽点
if inserted_count < len(golden_points):
if "重生后的第一场豪赌" in line and inserted_count == 0:
result.append(golden_points[0])
inserted_count += 1
elif "时间,比任何人想象的都要紧迫" in line and inserted_count == 1:
result.append(golden_points[1])
inserted_count += 1
elif "他知道这步棋的风险" in line and inserted_count == 2:
result.append(golden_points[2])
inserted_count += 1
elif "第一步,找到周世昌" in line and inserted_count == 3:
result.append(golden_points[3])
inserted_count += 1
elif "重生后的第一场豪赌,开始了" in line and inserted_count == 4:
result.append(golden_points[4])
inserted_count += 1
# 如果没找到插入点,在结尾添加
if inserted_count < 3:
result.append("\n" + "\n".join(golden_points[inserted_count:3]))
return '\n'.join(result)
def add_dialogue(content):
"""增加对话"""
# 第1章可以增加的对话
dialogues = [
# 房东王姐的电话对话
"电话接通,房东王姐不耐烦的声音传来:'小陈,房租到底什么时候交?这周五是最后期限,不然我真要清房了。'",
"陈末深吸一口气,声音平静:'王姐,周五之前我一定交。再给我几天时间。'",
"'几天?我都给你宽限多久了?'王姐的声音提高,'这次说到做到,周五见不到钱,你的东西全扔出去!'",
"电话挂断,忙音在耳边回响。陈末放下手机,眼神冰冷。",
# 内心独白(可以处理为对话形式)
"一个声音在心底响起:'陈末,你只有这一次机会。抓住它,或者重蹈覆辙。'",
"他握紧拳头,低声自语:'这一次,我不会输。'"
]
# 寻找插入对话的位置
lines = content.split('\n')
result = []
for line in lines:
result.append(line)
# 在房东王姐相关的位置插入对话
if "房东王姐的短信" in line:
result.append("") # 空行
result.append(dialogues[0])
result.append(dialogues[1])
result.append(dialogues[2])
result.append(dialogues[3])
# 在关键决策点插入内心独白
elif "重生后的第一场豪赌,开始了" in line:
result.append("") # 空行
result.append(dialogues[4])
result.append(dialogues[5])
return '\n'.join(result)
def enhance_emotional_arc(content):
"""增强情绪弧线"""
# 情绪变化的关键点
emotional_beats = [
# 1. 开篇:迷茫、恐惧
"冷。不仅是身体的冷,更是从心底蔓延出来的寒意。那些记忆,那些死亡,是真的吗?",
# 2. 确认重生:震惊、混乱
"镜子里的脸年轻,眼神却沧桑。两种时间在身体里冲撞,撕裂感让他几乎站立不稳。",
# 3. 清算现状:绝望、压力
"十五万的债务,八天后的流落街头。数字像巨石压在胸口,呼吸都变得困难。",
# 4. 制定计划:冷静、决绝
"恐惧慢慢沉淀,转化为冰冷的计算。如果那些记忆是真的,那么眼前的困境,不过是一场游戏的开始。",
# 5. 决定行动:坚定、孤注一掷
"眼神从迷茫转为锐利。这一次,他要做执棋者,而不是棋子。"
]
lines = content.split('\n')
result = []
inserted = 0
for i, line in enumerate(lines):
result.append(line)
# 在情绪转折点插入描述
if inserted < len(emotional_beats):
if "不是梦" in line and inserted == 0:
result.append(emotional_beats[0])
inserted += 1
elif "镜子里的人" in line and inserted == 1:
result.append(emotional_beats[1])
inserted += 1
elif "接近十五万" in line and inserted == 2:
result.append(emotional_beats[2])
inserted += 1
elif "如果那些记忆是真的" in line and inserted == 3:
result.append(emotional_beats[3])
inserted += 1
elif "重生后的第一场豪赌" in line and inserted == 4:
result.append(emotional_beats[4])
inserted += 1
return '\n'.join(result)
def fix_common_issues(content):
"""修复常见问题"""
# 1. 替换破折号
content = content.replace('——', '')
# 2. 修复对话格式
content = re.sub(r'["]([^"]+)["]', r'\1」', content)
# 3. 减少短句堆砌
lines = content.split('\n')
result = []
for line in lines:
stripped = line.strip()
if stripped and len(stripped) < 10 and not stripped.endswith(('', '', '', '')):
# 短句,尝试与下一句合并
continue
else:
result.append(line)
return '\n'.join(result)
def main():
"""主函数"""
if len(sys.argv) < 2:
print("用法python auto_fix_chapter1.py <章节文件路径> [输出文件路径]")
sys.exit(1)
input_file = sys.argv[1]
output_file = sys.argv[2] if len(sys.argv) > 2 else input_file.replace('.md', '_fixed.md')
if not os.path.exists(input_file):
print(f"错误:文件不存在 - {input_file}")
sys.exit(1)
print(f"开始修复第1章{input_file}")
# 加载内容
content = load_chapter(input_file)
original_length = len(content)
print(f"原始内容长度:{original_length} 字符")
# 执行修复步骤
print("1. 合并短段落...")
content = merge_short_paragraphs(content)
print("2. 增加爽点...")
content = add_golden_points(content)
print("3. 增加对话...")
content = add_dialogue(content)
print("4. 增强情绪弧线...")
content = enhance_emotional_arc(content)
print("5. 修复常见问题...")
content = fix_common_issues(content)
# 保存结果
save_chapter(output_file, content)
new_length = len(content)
print(f"修复完成!")
print(f"输出文件:{output_file}")
print(f"新内容长度:{new_length} 字符")
print(f"长度变化:{new_length - original_length} 字符")
# 生成修复报告
report = {
"original_file": input_file,
"fixed_file": output_file,
"length_change": new_length - original_length,
"fixes_applied": [
"合并短段落",
"增加爽点",
"增加对话",
"增强情绪弧线",
"修复常见问题"
],
"next_steps": [
"运行质量检查确认修复效果",
"重新提交 inkos 审核",
"监控后续章节质量"
]
}
report_file = output_file.replace('.md', '_report.json')
import json
with open(report_file, 'w', encoding='utf-8') as f:
json.dump(report, f, ensure_ascii=False, indent=2)
print(f"修复报告:{report_file}")
if __name__ == "__main__":
main()

301
scripts/format_check_fix.py Normal file
View File

@ -0,0 +1,301 @@
#!/usr/bin/env python3
"""
inkos章节格式检查和自动修复脚本
在inkos生成章节后立即运行确保格式标准化
"""
import os
import re
import json
import sys
from pathlib import Path
from typing import Dict, List, Tuple
class ChapterFormatFixer:
def __init__(self, config_path: str = None):
self.config = self.load_config(config_path)
self.problems_fixed = 0
def load_config(self, config_path: str = None) -> Dict:
"""加载配置"""
if config_path and os.path.exists(config_path):
with open(config_path, 'r', encoding='utf-8') as f:
return json.load(f)
# 默认配置
return {
"chapter_title_map": {
"0002_暗流.md": "暗流涌动",
"0003_仓鼠行动.md": "仓鼠计划",
"0004_粮草先行.md": "物资先行",
"0005_铁壁.md": "钢铁堡垒",
"0006_焊花.md": "焊光四溅",
"0007_骨刺.md": "骨刺警告",
"0008_暗流2.md": "暗流再起",
"0009_对峙.md": "初次对峙",
"0010_倒计时.md": "末日倒计时",
"0011_筹码.md": "生存筹码",
"0012_质询.md": "深夜质询",
"0013_铁锈.md": "铁锈危机",
"0014_断水.md": "断水危机"
},
"format_rules": {
"remove_bold": True,
"fix_headings": True,
"normalize_dialogue": True,
"add_line_breaks": True,
"end_with_separator": True
}
}
def check_chapter(self, file_path: str) -> List[str]:
"""检查章节格式问题"""
problems = []
try:
with open(file_path, 'r', encoding='utf-8') as f:
content = f.read()
filename = os.path.basename(file_path)
# 1. 检查加粗格式
if '**' in content:
problems.append("包含加粗格式(**text**)")
# 2. 检查多余的标题格式
if re.search(r'^##+ ', content, re.MULTILINE):
problems.append("包含多余的标题格式(##)")
# 3. 检查章节标题格式
if not re.search(r'^# 第\d+章 ', content, re.MULTILINE):
problems.append("章节标题格式不正确")
# 4. 检查段落格式
lines = content.split('\n')
for i, line in enumerate(lines):
if line.strip() and not line.startswith('#') and len(line.strip()) > 500:
problems.append(f"{i+1}行段落过长")
# 5. 检查章节结尾
if not content.strip().endswith('---'):
problems.append("章节结尾缺少分隔线(---)")
return problems
except Exception as e:
return [f"读取文件时出错: {str(e)}"]
def fix_chapter(self, file_path: str) -> Tuple[bool, str]:
"""修复章节格式"""
try:
filename = os.path.basename(file_path)
# 读取内容
with open(file_path, 'r', encoding='utf-8') as f:
content = f.read()
original_content = content
fixes_applied = []
# 1. 移除加粗格式
if self.config['format_rules']['remove_bold']:
if '**' in content:
content = content.replace('**', '')
fixes_applied.append("移除加粗格式")
# 2. 修复标题格式
if self.config['format_rules']['fix_headings']:
# 移除多余的##
content = re.sub(r'^##+ (.+)$', r'\1', content, flags=re.MULTILINE)
# 确保章节标题格式正确
chapter_match = re.search(r'^# (第\d+章 )?(.+)$', content, re.MULTILINE)
if chapter_match:
chapter_num = filename.split('_')[0]
chapter_num_int = int(chapter_num)
# 获取优化后的标题
new_title = self.config['chapter_title_map'].get(filename, chapter_match.group(2))
# 重建标题
new_header = f"# 第{chapter_num_int}{new_title}"
content = re.sub(r'^# .+$', new_header, content, count=1, flags=re.MULTILINE)
fixes_applied.append(f"优化标题为: {new_title}")
# 3. 标准化对话格式
if self.config['format_rules']['normalize_dialogue']:
# 确保对话有合理分段
content = re.sub(r'([^"\n])(")([^"]+)(")([^"\n])', r'\1\n\n\2\3\4\n\n\5', content)
content = re.sub(r'([^"\n])(“)([^”]+)(”)([^"\n])', r'\1\n\n\2\3\4\n\n\5', content)
fixes_applied.append("标准化对话格式")
# 4. 添加段落分隔
if self.config['format_rules']['add_line_breaks']:
# 确保段落之间有合理空白行
paragraphs = content.split('\n\n')
cleaned_paragraphs = []
for para in paragraphs:
if para.strip():
cleaned_paragraphs.append(para.strip())
content = '\n\n'.join(cleaned_paragraphs)
fixes_applied.append("优化段落分隔")
# 5. 确保章节结尾
if self.config['format_rules']['end_with_separator']:
if not content.strip().endswith('---'):
content = content.rstrip() + '\n\n---'
fixes_applied.append("添加章节结尾分隔线")
# 6. 移除多余空白行
content = re.sub(r'\n{3,}', '\n\n', content)
# 如果内容有变化,保存文件
if content != original_content:
# 创建备份
backup_path = file_path + '.bak'
with open(backup_path, 'w', encoding='utf-8') as f:
f.write(original_content)
# 写入修复后的内容
with open(file_path, 'w', encoding='utf-8') as f:
f.write(content)
self.problems_fixed += 1
return True, f"修复完成: {', '.join(fixes_applied)}"
else:
return False, "无需修复,格式已正确"
except Exception as e:
return False, f"修复时出错: {str(e)}"
def process_directory(self, directory: str) -> Dict:
"""处理整个目录的章节"""
results = {
"total_chapters": 0,
"chapters_fixed": 0,
"problems_found": 0,
"details": []
}
for file_path in Path(directory).glob("*.md"):
if file_path.name.startswith('000'):
results["total_chapters"] += 1
# 检查问题
problems = self.check_chapter(str(file_path))
if problems:
results["problems_found"] += len(problems)
# 尝试修复
fixed, message = self.fix_chapter(str(file_path))
if fixed:
results["chapters_fixed"] += 1
results["details"].append({
"chapter": file_path.name,
"problems": problems,
"fixed": fixed,
"message": message
})
return results
def monitor_and_fix(self, directory: str, interval: int = 60):
"""监控目录并自动修复新章节"""
import time
from watchdog.observers import Observer
from watchdog.events import FileSystemEventHandler
class ChapterHandler(FileSystemEventHandler):
def __init__(self, fixer):
self.fixer = fixer
def on_created(self, event):
if event.is_directory:
return
if event.src_path.endswith('.md') and os.path.basename(event.src_path).startswith('000'):
print(f"📝 检测到新章节: {os.path.basename(event.src_path)}")
time.sleep(2) # 等待文件完全写入
# 检查并修复
problems = self.fixer.check_chapter(event.src_path)
if problems:
print(f" 发现格式问题: {problems}")
fixed, message = self.fixer.fix_chapter(event.src_path)
if fixed:
print(f" ✅ 自动修复: {message}")
print(f"🔍 开始监控目录: {directory}")
print(" 等待inkos生成新章节...")
event_handler = ChapterHandler(self)
observer = Observer()
observer.schedule(event_handler, directory, recursive=False)
observer.start()
try:
while True:
time.sleep(interval)
except KeyboardInterrupt:
observer.stop()
observer.join()
def main():
"""主函数"""
import argparse
parser = argparse.ArgumentParser(description="inkos章节格式检查和修复工具")
parser.add_argument("action", choices=["check", "fix", "monitor"], help="操作类型")
parser.add_argument("--directory", default="/root/.openclaw/workspace/tomato-novel/books/末日重生-开局囤货十亿物资/chapters", help="章节目录")
parser.add_argument("--config", help="配置文件路径")
args = parser.parse_args()
fixer = ChapterFormatFixer(args.config)
if args.action == "check":
print(f"🔍 检查章节格式: {args.directory}")
results = fixer.process_directory(args.directory)
print(f"\n📊 检查结果:")
print(f" 总章节数: {results['total_chapters']}")
print(f" 发现问题: {results['problems_found']}")
print(f" 已修复章节: {results['chapters_fixed']}")
if results['details']:
print(f"\n📋 详细报告:")
for detail in results['details']:
status = "✅ 已修复" if detail['fixed'] else "⚠️ 未修复"
print(f" {detail['chapter']}: {status}")
if detail['problems']:
print(f" 问题: {', '.join(detail['problems'])}")
if detail['message']:
print(f" 消息: {detail['message']}")
elif args.action == "fix":
print(f"🛠️ 修复章节格式: {args.directory}")
results = fixer.process_directory(args.directory)
print(f"\n✅ 修复完成:")
print(f" 总章节数: {results['total_chapters']}")
print(f" 发现问题: {results['problems_found']}")
print(f" 已修复章节: {results['chapters_fixed']}")
if results['chapters_fixed'] > 0:
print(f"\n📝 修复详情:")
for detail in results['details']:
if detail['fixed']:
print(f" {detail['chapter']}: {detail['message']}")
elif args.action == "monitor":
print("🔄 启动格式监控模式")
print(" 此模式将持续监控目录自动修复inkos生成的新章节")
print(" 按 Ctrl+C 停止监控")
fixer.monitor_and_fix(args.directory)
if __name__ == "__main__":
main()

259
scripts/inkos_quality_monitor.sh Executable file
View File

@ -0,0 +1,259 @@
#!/bin/bash
# inkos质量监控和服务脚本
# 在inkos启动前检查配置启动后监控输出质量
set -e
INKOS_DIR="/root/.openclaw/workspace/tomato-novel"
BOOKS_DIR="$INKOS_DIR/books"
CONFIG_FILE="$INKOS_DIR/inkos_format_config.json"
MONITOR_SCRIPT="$INKOS_DIR/scripts/format_check_fix.py"
LOG_DIR="$INKOS_DIR/logs"
# 创建日志目录
mkdir -p "$LOG_DIR"
# 颜色定义
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
print_info() {
echo -e "${BLUE}[INFO]${NC} $1"
}
print_success() {
echo -e "${GREEN}[SUCCESS]${NC} $1"
}
print_warning() {
echo -e "${YELLOW}[WARNING]${NC} $1"
}
print_error() {
echo -e "${RED}[ERROR]${NC} $1"
}
# 检查inkos是否已安装
check_inkos_installation() {
if command -v inkos &> /dev/null; then
print_success "inkos已安装"
inkos --version
return 0
else
print_error "inkos未安装"
exit 1
fi
}
# 检查配置
check_configuration() {
print_info "检查配置..."
if [ -f "$CONFIG_FILE" ]; then
print_success "格式配置文件存在: $CONFIG_FILE"
# 验证配置文件格式
if python3 -m json.tool "$CONFIG_FILE" > /dev/null 2>&1; then
print_success "配置文件格式正确"
else
print_error "配置文件格式错误"
exit 1
fi
else
print_error "配置文件不存在: $CONFIG_FILE"
exit 1
fi
if [ -f "$MONITOR_SCRIPT" ]; then
print_success "监控脚本存在: $MONITOR_SCRIPT"
else
print_error "监控脚本不存在: $MONITOR_SCRIPT"
exit 1
fi
}
# 检查项目目录
check_project_directories() {
print_info "检查项目目录..."
if [ -d "$INKOS_DIR" ]; then
print_success "inkos项目目录存在: $INKOS_DIR"
else
print_error "inkos项目目录不存在: $INKOS_DIR"
exit 1
fi
if [ -d "$BOOKS_DIR" ]; then
print_success "书籍目录存在: $BOOKS_DIR"
else
print_error "书籍目录不存在: $BOOKS_DIR"
exit 1
fi
}
# 修复现有章节
fix_existing_chapters() {
print_info "修复现有章节格式..."
# 为每个书籍目录执行修复
for book_dir in "$BOOKS_DIR"/*; do
if [ -d "$book_dir" ] && [ -d "$book_dir/chapters" ]; then
book_name=$(basename "$book_dir")
print_info "修复书籍: $book_name"
# 检查当前书籍是否有第2-14章
chapters_dir="$book_dir/chapters"
if [ -d "$chapters_dir" ]; then
python3 "$MONITOR_SCRIPT" fix --directory "$chapters_dir" 2>&1 | tee -a "$LOG_DIR/fix_${book_name}_$(date +%Y%m%d).log"
fi
fi
done
print_success "现有章节修复完成"
}
# 启动质量监控
start_quality_monitor() {
print_info "启动质量监控服务..."
# 启动监控进程
python3 "$MONITOR_SCRIPT" monitor --directory "$BOOKS_DIR" > "$LOG_DIR/monitor_$(date +%Y%m%d_%H%M%S).log" 2>&1 &
MONITOR_PID=$!
echo $MONITOR_PID > "$LOG_DIR/quality_monitor.pid"
print_success "质量监控服务已启动 (PID: $MONITOR_PID)"
# 输出监控信息
print_info "监控服务日志: $LOG_DIR/monitor_*.log"
print_info "监控PID文件: $LOG_DIR/quality_monitor.pid"
}
# 启动inkos
start_inkos() {
print_info "启动inkos..."
# 切换到项目目录
cd "$INKOS_DIR"
# 启动inkos
print_info "正在启动inkos守护进程..."
inkos up 2>&1 | tee -a "$LOG_DIR/inkos_startup_$(date +%Y%m%d_%H%M%S).log" &
INKOS_PID=$!
echo $INKOS_PID > "$LOG_DIR/inkos.pid"
print_success "inkos守护进程已启动 (PID: $INKOS_PID)"
# 等待inkos启动完成
sleep 10
# 检查inkos状态
inkos status
}
# 显示状态
show_status() {
print_info "系统状态:"
# 检查监控服务
if [ -f "$LOG_DIR/quality_monitor.pid" ] && ps -p $(cat "$LOG_DIR/quality_monitor.pid") > /dev/null 2>&1; then
print_success "质量监控服务运行中 (PID: $(cat $LOG_DIR/quality_monitor.pid))"
else
print_warning "质量监控服务未运行"
fi
# 检查inkos进程
if [ -f "$LOG_DIR/inkos.pid" ] && ps -p $(cat "$LOG_DIR/inkos.pid") > /dev/null 2>&1; then
print_success "inkos守护进程运行中 (PID: $(cat $LOG_DIR/inkos.pid)"
inkos status
else
print_warning "inkos守护进程未运行"
fi
# 显示最近日志
print_info "最近日志文件:"
ls -la "$LOG_DIR"/*.log | head -5
}
# 停止服务
stop_services() {
print_info "停止服务..."
# 停止inkos
if [ -f "$LOG_DIR/inkos.pid" ]; then
pid=$(cat "$LOG_DIR/inkos.pid")
if ps -p "$pid" > /dev/null 2>&1; then
kill "$pid"
print_success "inkos守护进程已停止PID$pid"
fi
rm -f "$LOG_DIR/inkos.pid"
fi
# 停止质量监控
if [ -f "$LOG_DIR/quality_monitor.pid" ]; then
pid=$(cat "$LOG_DIR/quality_monitor.pid")
if ps -p "$pid" > /dev/null 2>&1; then
kill "$pid"
print_success "质量监控服务已停止PID$pid"
fi
rm -f "$LOG_DIR/quality_monitor.pid"
fi
print_success "所有服务已停止"
}
# 主函数
main() {
print_info "inkos质量监控系统启动..."
echo "=============================================="
case "${1:-start}" in
start)
check_inkos_installation
check_configuration
check_project_directories
fix_existing_chapters
start_quality_monitor
start_inkos
print_success"系统启动完成"
show_status
;;
stop)
stop_services
;;
restart)
stop_services
sleep 2
check_inkos_installation
check_configuration
check_project_directories
start_quality_monitor
start_inkos
print_success"系统重启完成"
show_status
;;
status)
show_status
;;
fix)
fix_existing_chapters
;;
*)
echo "用法:$0 {start|stop|restart|status|fix}"
exit 1
;;
esac
echo"=============================================="
}
# 运行主函数
main"$@"

451
scripts/quality_monitor.py Executable file
View File

@ -0,0 +1,451 @@
#!/usr/bin/env python3
"""
inkos 质量监控脚本
监控写作质量自动发现问题并提供修复建议
"""
import os
import json
import re
import sys
from datetime import datetime
from pathlib import Path
class QualityMonitor:
def __init__(self, config_path=None):
self.config = self.load_config(config_path)
self.problems = []
self.recommendations = []
def load_config(self, config_path):
"""加载配置文件"""
default_config = {
"paragraph": {
"min_length": 35,
"max_consecutive_short": 3,
"short_warning_ratio": 0.3
},
"golden_points": {
"required_per_chapter": 3,
"keywords": ["打脸", "升级", "收获", "碾压", "反转", "爽点"]
},
"emotional_arc": {
"required": True,
"min_change": 0.3
}
}
if config_path and os.path.exists(config_path):
try:
with open(config_path, 'r', encoding='utf-8') as f:
return json.load(f)
except:
return default_config
return default_config
def analyze_chapter(self, chapter_path):
"""分析章节质量"""
if not os.path.exists(chapter_path):
return {"error": "文件不存在"}
with open(chapter_path, 'r', encoding='utf-8') as f:
content = f.read()
# 提取章节信息
chapter_num = self.extract_chapter_number(chapter_path)
# 分析各项指标
results = {
"chapter": chapter_num,
"file": os.path.basename(chapter_path),
"timestamp": datetime.now().isoformat(),
"metrics": {}
}
# 1. 段落分析
para_results = self.analyze_paragraphs(content)
results["metrics"]["paragraphs"] = para_results
# 2. 爽点分析
golden_results = self.analyze_golden_points(content, chapter_num)
results["metrics"]["golden_points"] = golden_results
# 3. 情绪弧线分析
emotion_results = self.analyze_emotional_arc(content)
results["metrics"]["emotional_arc"] = emotion_results
# 4. 对话分析
dialogue_results = self.analyze_dialogue(content)
results["metrics"]["dialogue"] = dialogue_results
# 5. 问题汇总
problems = self.identify_problems(results)
results["problems"] = problems
# 6. 修复建议
recommendations = self.generate_recommendations(problems, chapter_num)
results["recommendations"] = recommendations
return results
def extract_chapter_number(self, filepath):
"""从文件名提取章节号"""
filename = os.path.basename(filepath)
match = re.search(r'(\d{4})_', filename)
if match:
return int(match.group(1))
return 0
def analyze_paragraphs(self, content):
"""分析段落结构"""
lines = content.split('\n')
paragraphs = []
current_para = []
for line in lines:
stripped = line.strip()
if not stripped:
if current_para:
paragraphs.append(''.join(current_para))
current_para = []
else:
current_para.append(line + ' ')
if current_para:
paragraphs.append(''.join(current_para))
# 过滤空段落和标题
filtered_paras = []
for para in paragraphs:
para_stripped = para.strip()
if para_stripped and not para_stripped.startswith('#'):
filtered_paras.append(para_stripped)
# 统计段落长度
lengths = []
short_count = 0
consecutive_short = 0
max_consecutive = 0
current_streak = 0
for para in filtered_paras:
# 计算中文字符数
chinese_chars = len([c for c in para if '\u4e00' <= c <= '\u9fff'])
# 数字和英文字母
other_chars = len(re.findall(r'[a-zA-Z0-9]', para))
total = chinese_chars + other_chars
lengths.append(total)
if total < self.config["paragraph"]["min_length"]:
short_count += 1
current_streak += 1
if current_streak > max_consecutive:
max_consecutive = current_streak
else:
current_streak = 0
total_paras = len(filtered_paras)
short_ratio = short_count / total_paras if total_paras > 0 else 0
return {
"total_paragraphs": total_paras,
"short_paragraphs": short_count,
"short_ratio": round(short_ratio, 3),
"max_consecutive_short": max_consecutive,
"avg_length": round(sum(lengths) / len(lengths)) if lengths else 0,
"min_length": min(lengths) if lengths else 0,
"max_length": max(lengths) if lengths else 0,
"lengths": lengths[:20] # 只保留前20个用于展示
}
def analyze_golden_points(self, content, chapter_num):
"""分析爽点密度"""
keywords = self.config["golden_points"]["keywords"]
required = self.config["golden_points"]["required_per_chapter"]
found_keywords = []
keyword_positions = {}
for keyword in keywords:
if keyword in content:
found_keywords.append(keyword)
# 统计出现次数
count = content.count(keyword)
keyword_positions[keyword] = count
# 根据章节号调整期望值
if chapter_num == 1:
expected_min = 3 # 黄金三章需要更多爽点
elif chapter_num <= 3:
expected_min = 2
else:
expected_min = required
return {
"found_keywords": found_keywords,
"total_found": len(found_keywords),
"expected_min": expected_min,
"meets_requirement": len(found_keywords) >= expected_min,
"keyword_counts": keyword_positions,
"content_samples": self.extract_golden_point_samples(content, found_keywords)
}
def extract_golden_point_samples(self, content, keywords, sample_count=3):
"""提取爽点示例"""
samples = []
lines = content.split('\n')
for keyword in keywords[:3]: # 最多检查3个关键词
for i, line in enumerate(lines):
if keyword in line and len(line.strip()) > 20:
# 取上下文
start = max(0, i - 1)
end = min(len(lines), i + 2)
context = '\n'.join(lines[start:end])
samples.append({
"keyword": keyword,
"context": context
})
if len(samples) >= sample_count:
break
if len(samples) >= sample_count:
break
return samples
def analyze_emotional_arc(self, content):
"""分析情绪弧线(简化版)"""
# 情绪关键词
positive_words = ["兴奋", "开心", "满足", "自信", "希望", "轻松"]
negative_words = ["紧张", "焦虑", "恐惧", "痛苦", "绝望", "压力"]
neutral_words = ["平静", "思考", "观察", "计算", "等待"]
positive_count = sum(content.count(word) for word in positive_words)
negative_count = sum(content.count(word) for word in negative_words)
neutral_count = sum(content.count(word) for word in neutral_words)
total = positive_count + negative_count + neutral_count
if total == 0:
intensity = 0
else:
# 情绪强度 = (积极+消极)/总数
intensity = (positive_count + negative_count) / total
# 情绪变化(简化:检查是否有情绪转换)
lines = content.split('\n')
emotion_changes = 0
last_emotion = None
for line in lines[:50]: # 只检查前50行
line_emotion = None
if any(word in line for word in positive_words):
line_emotion = "positive"
elif any(word in line for word in negative_words):
line_emotion = "negative"
elif any(word in line for word in neutral_words):
line_emotion = "neutral"
if last_emotion and line_emotion and line_emotion != last_emotion:
emotion_changes += 1
if line_emotion:
last_emotion = line_emotion
return {
"positive_count": positive_count,
"negative_count": negative_count,
"neutral_count": neutral_count,
"total_emotion_words": total,
"emotional_intensity": round(intensity, 3),
"emotion_changes": emotion_changes,
"has_emotional_arc": emotion_changes >= 2
}
def analyze_dialogue(self, content):
"""分析对话质量"""
# 简单的对话检测
dialogue_pattern = r'["「](.+?)["」]'
dialogues = re.findall(dialogue_pattern, content)
total_chars = len(content)
dialogue_chars = sum(len(d) for d in dialogues)
dialogue_ratio = dialogue_chars / total_chars if total_chars > 0 else 0
# 对话长度分析
dialogue_lengths = [len(d) for d in dialogues]
avg_dialogue_length = sum(dialogue_lengths) / len(dialogue_lengths) if dialogues else 0
return {
"dialogue_count": len(dialogues),
"dialogue_ratio": round(dialogue_ratio, 3),
"avg_dialogue_length": round(avg_dialogue_length, 1),
"sample_dialogues": dialogues[:5] # 前5个对话示例
}
def identify_problems(self, results):
"""识别问题"""
problems = []
# 段落问题
para_metrics = results["metrics"]["paragraphs"]
if para_metrics["short_ratio"] > self.config["paragraph"]["short_warning_ratio"]:
problems.append({
"type": "paragraph_structure",
"severity": "high",
"description": f"短段落比例过高:{para_metrics['short_ratio']*100:.1f}%",
"details": f"{para_metrics['total_paragraphs']}个段落,其中{para_metrics['short_paragraphs']}个短段落"
})
if para_metrics["max_consecutive_short"] > self.config["paragraph"]["max_consecutive_short"]:
problems.append({
"type": "consecutive_short_paragraphs",
"severity": "medium",
"description": f"连续短段落过多:{para_metrics['max_consecutive_short']}",
"details": "影响阅读流畅性"
})
# 爽点问题
golden_metrics = results["metrics"]["golden_points"]
if not golden_metrics["meets_requirement"]:
problems.append({
"type": "insufficient_golden_points",
"severity": "high",
"description": f"爽点不足:找到{golden_metrics['total_found']}个,需要{golden_metrics['expected_min']}",
"details": f"找到的关键词:{', '.join(golden_metrics['found_keywords'])}"
})
# 情绪弧线问题
emotion_metrics = results["metrics"]["emotional_arc"]
if self.config["emotional_arc"]["required"] and not emotion_metrics["has_emotional_arc"]:
problems.append({
"type": "flat_emotional_arc",
"severity": "medium",
"description": "情绪弧线平坦",
"details": f"情绪变化次数:{emotion_metrics['emotion_changes']}"
})
# 对话问题
dialogue_metrics = results["metrics"]["dialogue"]
if dialogue_metrics["dialogue_ratio"] < 0.2:
problems.append({
"type": "low_dialogue_ratio",
"severity": "low",
"description": f"对话比例偏低:{dialogue_metrics['dialogue_ratio']*100:.1f}%",
"details": "番茄小说建议对话比例在30-40%"
})
return problems
def generate_recommendations(self, problems, chapter_num):
"""生成修复建议"""
recommendations = []
for problem in problems:
if problem["type"] == "paragraph_structure":
recommendations.append({
"action": "merge_short_paragraphs",
"priority": "high" if problem["severity"] == "high" else "medium",
"description": "合并短段落,提高段落平均长度",
"command": f"python3 merge_paragraphs.py --chapter {chapter_num} --min-length 35"
})
elif problem["type"] == "insufficient_golden_points":
recommendations.append({
"action": "add_golden_points",
"priority": "high",
"description": "增加爽点密度",
"suggestions": [
"增加一个打脸情节",
"展现主角的优势",
"设置一个小型反转",
"增加资源收获"
]
})
elif problem["type"] == "flat_emotional_arc":
recommendations.append({
"action": "enhance_emotional_arc",
"priority": "medium",
"description": "增强情绪起伏",
"suggestions": [
"在章节开头设置紧张情绪",
"在中间加入情绪释放点",
"在结尾设置情绪钩子"
]
})
return recommendations
def generate_report(self, results, output_path=None):
"""生成质量报告"""
report = {
"summary": {
"chapter": results["chapter"],
"file": results["file"],
"timestamp": results["timestamp"],
"problem_count": len(results["problems"]),
"recommendation_count": len(results["recommendations"])
},
"metrics": results["metrics"],
"problems": results["problems"],
"recommendations": results["recommendations"]
}
if output_path:
with open(output_path, 'w', encoding='utf-8') as f:
json.dump(report, f, ensure_ascii=False, indent=2)
return report
def print_summary(self, report):
"""打印摘要"""
print(f"\n{'='*60}")
print(f"章节质量报告 - 第{report['summary']['chapter']}")
print(f"{'='*60}")
print(f"\n📊 指标概览:")
print(f" 段落总数:{report['metrics']['paragraphs']['total_paragraphs']}")
print(f" 短段落比例:{report['metrics']['paragraphs']['short_ratio']*100:.1f}%")
print(f" 爽点数量:{report['metrics']['golden_points']['total_found']}")
print(f" 情绪变化:{report['metrics']['emotional_arc']['emotion_changes']}")
print(f" 对话比例:{report['metrics']['dialogue']['dialogue_ratio']*100:.1f}%")
if report['problems']:
print(f"\n⚠️ 发现问题({len(report['problems'])}个):")
for i, problem in enumerate(report['problems'], 1):
print(f" {i}. [{problem['severity'].upper()}] {problem['description']}")
if report['recommendations']:
print(f"\n💡 修复建议({len(report['recommendations'])}条):")
for i, rec in enumerate(report['recommendations'], 1):
print(f" {i}. [{rec['priority']}] {rec['description']}")
def main():
"""主函数"""
if len(sys.argv) < 2:
print("用法python quality_monitor.py <章节文件路径> [配置文件路径]")
sys.exit(1)
chapter_path = sys.argv[1]
config_path = sys.argv[2] if len(sys.argv) > 2 else None
monitor = QualityMonitor(config_path)
results = monitor.analyze_chapter(chapter_path)
# 生成报告文件
report_file = f"quality_report_chapter{results['chapter']}.json"
report = monitor.generate_report(results, report_file)
# 打印摘要
monitor.print_summary(report)
print(f"\n📄 完整报告已保存到:{report_file}")
# 如果有严重问题,返回非零退出码
if any(p["severity"] == "high" for p in results["problems"]):
sys.exit(1)
else:
sys.exit(0)
if __name__ == "__main__":
main()

View File

@ -0,0 +1,215 @@
#!/usr/bin/env python3
"""
简化版质量检查脚本
"""
import os
import re
import sys
from datetime import datetime
def count_chinese_chars(text):
"""统计中文字符数"""
return len([c for c in text if '\u4e00' <= c <= '\u9fff'])
def analyze_chapter(filepath):
"""分析章节质量"""
print(f"分析文件:{filepath}")
with open(filepath, 'r', encoding='utf-8') as f:
content = f.read()
# 提取章节号
filename = os.path.basename(filepath)
match = re.search(r'(\d{4})_', filename)
chapter_num = int(match.group(1)) if match else 0
print(f"章节号:{chapter_num}")
print(f"文件大小:{len(content)} 字符")
# 1. 段落分析
print("\n" + "="*60)
print("段落分析:")
lines = content.split('\n')
paragraphs = []
current_para = []
for line in lines:
stripped = line.strip()
if not stripped:
if current_para:
paragraphs.append(''.join(current_para))
current_para = []
else:
current_para.append(line)
if current_para:
paragraphs.append(''.join(current_para))
# 过滤标题和空段落
filtered_paras = []
for para in paragraphs:
para_stripped = para.strip()
if para_stripped and not para_stripped.startswith('#'):
filtered_paras.append(para_stripped)
# 统计段落长度
short_count = 0
consecutive_short = 0
max_consecutive = 0
current_streak = 0
lengths = []
for para in filtered_paras:
char_count = count_chinese_chars(para)
lengths.append(char_count)
if char_count < 35:
short_count += 1
current_streak += 1
if current_streak > max_consecutive:
max_consecutive = current_streak
else:
current_streak = 0
total_paras = len(filtered_paras)
short_ratio = short_count / total_paras if total_paras > 0 else 0
avg_length = sum(lengths) / len(lengths) if lengths else 0
print(f"总段落数:{total_paras}")
print(f"短段落数(<35字){short_count}")
print(f"短段落比例:{short_ratio*100:.1f}%")
print(f"最长连续短段落:{max_consecutive}")
print(f"平均段落长度:{avg_length:.1f}")
# 2. 爽点分析
print("\n" + "="*60)
print("爽点分析:")
golden_keywords = ["打脸", "升级", "收获", "碾压", "反转", "爽点", "优势", "先知", "重生"]
found_keywords = []
for keyword in golden_keywords:
if keyword in content:
found_keywords.append(keyword)
print(f"找到爽点关键词:{len(found_keywords)}/{len(golden_keywords)}")
print(f"关键词:{', '.join(found_keywords)}")
# 3. 对话分析
print("\n" + "="*60)
print("对话分析:")
dialogue_pattern = r'["「](.+?)["」]'
dialogues = re.findall(dialogue_pattern, content)
total_chars = len(content)
dialogue_chars = sum(len(d) for d in dialogues)
dialogue_ratio = dialogue_chars / total_chars if total_chars > 0 else 0
print(f"对话数量:{len(dialogues)}")
print(f"对话比例:{dialogue_ratio*100:.1f}%")
# 4. 问题识别
print("\n" + "="*60)
print("问题识别:")
problems = []
if short_ratio > 0.3:
problems.append(f"短段落比例过高 ({short_ratio*100:.1f}%)")
if max_consecutive > 3:
problems.append(f"连续短段落过多 ({max_consecutive}个)")
if len(found_keywords) < 3:
problems.append(f"爽点不足 (找到{len(found_keywords)}需要至少3个)")
if dialogue_ratio < 0.2:
problems.append(f"对话比例偏低 ({dialogue_ratio*100:.1f}%)")
if problems:
print("⚠️ 发现问题:")
for i, problem in enumerate(problems, 1):
print(f" {i}. {problem}")
else:
print("✅ 未发现严重问题")
# 5. 修复建议
print("\n" + "="*60)
print("修复建议:")
recommendations = []
if short_ratio > 0.3:
recommendations.append("合并短段落,提高段落平均长度")
if len(found_keywords) < 3:
if chapter_num == 1:
recommendations.append("第1章需要1)明确重生优势 2)建立时间紧迫感 3)设置第一个目标")
elif chapter_num <= 3:
recommendations.append("黄金三章需要1)兑现第一个爽点 2)打脸小反派 3)建立升级体系")
else:
recommendations.append("增加爽点密度每章至少3个爽点")
if dialogue_ratio < 0.2:
recommendations.append("增加对话比例目标30-40%")
if recommendations:
for i, rec in enumerate(recommendations, 1):
print(f" {i}. {rec}")
else:
print(" ✅ 无需修复")
# 6. 生成报告
report = {
"chapter": chapter_num,
"file": filename,
"timestamp": datetime.now().isoformat(),
"metrics": {
"paragraphs": {
"total": total_paras,
"short": short_count,
"short_ratio": short_ratio,
"max_consecutive_short": max_consecutive,
"avg_length": avg_length
},
"golden_points": {
"found": len(found_keywords),
"keywords": found_keywords
},
"dialogue": {
"count": len(dialogues),
"ratio": dialogue_ratio
}
},
"problems": problems,
"recommendations": recommendations
}
# 保存报告
report_file = f"quality_report_ch{chapter_num:04d}.json"
import json
with open(report_file, 'w', encoding='utf-8') as f:
json.dump(report, f, ensure_ascii=False, indent=2)
print(f"\n📄 报告已保存到:{report_file}")
return report
def main():
if len(sys.argv) < 2:
print("用法python simple_quality_check.py <章节文件路径>")
sys.exit(1)
filepath = sys.argv[1]
if not os.path.exists(filepath):
print(f"错误:文件不存在 - {filepath}")
sys.exit(1)
analyze_chapter(filepath)
if __name__ == "__main__":
main()

109
start_inkos_with_quality.sh Normal file
View File

@ -0,0 +1,109 @@
#!/bin/bash
echo "=== 启动 inkos 质量增强版 ==="
echo "开始时间: $(date)"
echo ""
# 1. 检查环境
INKOS_PATH="/usr/bin/inkos"
CONFIG_FILE="/root/.openclaw/workspace/tomato-novel/inkos_quality_config.json"
BOOK_DIR="/root/.openclaw/workspace/tomato-novel/books/末日重生-开局囤货十亿物资"
if [ ! -f "$INKOS_PATH" ]; then
echo "错误inkos 未安装"
exit 1
fi
if [ ! -f "$CONFIG_FILE" ]; then
echo "错误:配置文件不存在"
exit 1
fi
if [ ! -d "$BOOK_DIR" ]; then
echo "错误:书籍目录不存在"
exit 1
fi
# 2. 备份原始配置
echo "备份原始配置..."
if [ -f "$BOOK_DIR/inkos_config.json" ]; then
cp "$BOOK_DIR/inkos_config.json" "$BOOK_DIR/inkos_config_backup_$(date +%Y%m%d_%H%M%S).json"
fi
# 3. 应用新配置
echo "应用质量配置..."
cp "$CONFIG_FILE" "$BOOK_DIR/inkos_config.json"
# 4. 启动质量监控
echo "启动质量监控..."
python3 /root/.openclaw/workspace/tomato-novel/scripts/simple_quality_check.py "$BOOK_DIR/chapters/0001_冰点记忆.md" > /tmp/chapter1_quality_check.txt 2>&1 &
# 5. 启动 inkos
echo "启动 inkos..."
cd "$BOOK_DIR" && "$INKOS_PATH" up --config "inkos_config.json" > /tmp/inkos_startup.log 2>&1 &
# 6. 检查启动状态
sleep 5
echo "检查 inkos 进程..."
ps aux | grep "inkos up" | grep -v grep
if [ $? -eq 0 ]; then
echo "✅ inkos 启动成功"
echo ""
echo "=== 质量配置已启用 ==="
echo "1. 段落结构最小35字短段比例<30%"
echo "2. 爽点密度每章至少3个爽点"
echo "3. 对话比例至少30%对话内容"
echo "4. 情绪弧线每章至少2次情绪变化"
echo "5. 自动修复:启用段落合并、爽点增强"
echo ""
echo "监控日志:/tmp/inkos_startup.log"
echo "质量检查:/tmp/chapter1_quality_check.txt"
else
echo "❌ inkos 启动失败"
echo "查看日志:/tmp/inkos_startup.log"
exit 1
fi
# 7. 创建监控脚本
cat > /root/.openclaw/workspace/inkos_quality_monitor.sh << 'EOF'
#!/bin/bash
echo "=== inkos 质量监控 ==="
echo "时间: $(date)"
echo ""
# 检查进程
if ps aux | grep "inkos up" | grep -v grep > /dev/null; then
echo "✅ inkos 运行中"
else
echo "❌ inkos 未运行"
exit 1
fi
# 检查最新章节
LATEST_CHAPTER=$(ls -t /root/.openclaw/workspace/tomato-novel/books/末日重生-开局囤货十亿物资/chapters/*.md | head -1)
if [ -f "$LATEST_CHAPTER" ]; then
CHAPTER_NUM=$(basename "$LATEST_CHAPTER" | cut -d_ -f1)
CHAPTER_TITLE=$(basename "$LATEST_CHAPTER" .md | cut -d_ -f2-)
echo "最新章节:第${CHAPTER_NUM}章《${CHAPTER_TITLE}"
# 运行质量检查
python3 /root/.openclaw/workspace/tomato-novel/scripts/simple_quality_check.py "$LATEST_CHAPTER" | tail -20
else
echo "未找到章节文件"
fi
EOF
chmod +x /root/.openclaw/workspace/inkos_quality_monitor.sh
echo ""
echo "=== 启动完成 ==="
echo "结束时间: $(date)"
echo ""
echo "📋 使用说明:"
echo "1. 监控状态bash /root/.openclaw/workspace/inkos_quality_monitor.sh"
echo "2. 停止 inkospkill -f 'inkos up'"
echo "3. 查看日志tail -f /tmp/inkos_startup.log"
echo "4. 质量报告python3 simple_quality_check.py <章节文件>"
echo ""
echo "⚠️ 注意:新配置将强制质量要求,不合格内容将被拒绝或自动修复"