章节同步: 第74章完成,共74章,进度37%,最新章节《机电与旧货》
This commit is contained in:
parent
6e195abd48
commit
53d21d09f9
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
.env
|
||||
node_modules/
|
||||
.DS_Store
|
||||
1
.node-version
Normal file
1
.node-version
Normal file
@ -0,0 +1 @@
|
||||
22
|
||||
26
books/末日重生-开局囤货十亿物资/.gitignore
vendored
Normal file
26
books/末日重生-开局囤货十亿物资/.gitignore
vendored
Normal 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
|
||||
1
books/末日重生-开局囤货十亿物资/.last_sync
Normal file
1
books/末日重生-开局囤货十亿物资/.last_sync
Normal file
@ -0,0 +1 @@
|
||||
36
|
||||
1
books/末日重生-开局囤货十亿物资/.write.lock
Normal file
1
books/末日重生-开局囤货十亿物资/.write.lock
Normal file
@ -0,0 +1 @@
|
||||
pid:1433309 ts:1774918109807
|
||||
60
books/末日重生-开局囤货十亿物资/README.md
Normal file
60
books/末日重生-开局囤货十亿物资/README.md
Normal 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*
|
||||
55
books/末日重生-开局囤货十亿物资/auto_sync.sh
Executable file
55
books/末日重生-开局囤货十亿物资/auto_sync.sh
Executable 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
|
||||
12
books/末日重生-开局囤货十亿物资/book.json
Normal file
12
books/末日重生-开局囤货十亿物资/book.json
Normal 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"
|
||||
}
|
||||
41
books/末日重生-开局囤货十亿物资/chapter_fix_report.md
Normal file
41
books/末日重生-开局囤货十亿物资/chapter_fix_report.md
Normal file
@ -0,0 +1,41 @@
|
||||
# 章节格式修复报告
|
||||
|
||||
## 修复时间
|
||||
$(date)
|
||||
|
||||
## 修复范围
|
||||
第2-14章
|
||||
|
||||
## 修复内容
|
||||
|
||||
### 1. 字体格式问题修复
|
||||
- 移除所有 `**加粗**` 格式
|
||||
- 移除多余的 `#` 标题格式
|
||||
- 标准化为纯文本格式
|
||||
|
||||
### 2. 章节标题优化
|
||||
| 原标题 | 新标题 | 说明 |
|
||||
|--------|--------|------|
|
||||
|
||||
### 3. 排版标准化
|
||||
- 段落之间添加合理空白行
|
||||
- 对话单独成段
|
||||
- 移除多余空白行
|
||||
- 章节结尾添加分隔线
|
||||
|
||||
### 4. 格式规范
|
||||
- 章节标题格式:`# 第X章 标题`
|
||||
- 段落格式:自然段落,不超过5行
|
||||
- 对话格式:单独成段,使用引号
|
||||
- 分隔线:章节结尾添加 `---`
|
||||
|
||||
## 修复效果
|
||||
- 阅读体验显著提升
|
||||
- 标题更具吸引力
|
||||
- 格式统一规范
|
||||
- 便于后续AI处理和阅读
|
||||
|
||||
## 后续建议
|
||||
1. 新增章节应遵循此格式标准
|
||||
2. 定期检查格式一致性
|
||||
3. 考虑使用自动化工具保持格式统一
|
||||
7
books/末日重生-开局囤货十亿物资/chapters/0002_暗流.md.bak
Normal file
7
books/末日重生-开局囤货十亿物资/chapters/0002_暗流.md.bak
Normal file
@ -0,0 +1,7 @@
|
||||
# 第2章 暗流涌动
|
||||
|
||||
【爽点一:信息碾压】 谈判桌上,陈末掌握着对手的所有底牌。他知道稳盈宝六天后爆雷,知道胡老板在疯狂找人接盘,知道周世昌最想要什么。 「时间紧迫,必须行动。」 「六天,只有六天时间。」 【爽点:重生者的先知优势】
|
||||
|
||||
【爽点一:信息碾压】 谈判桌上,陈末掌握着对手的所有底牌。他知道稳盈宝六天后爆雷,知道胡老板在疯狂找人接盘,知道周世昌最想要什么。 「六天,只有六天时间。」 【爽点:重生者的先知优势】
|
||||
|
||||
---
|
||||
1839
books/末日重生-开局囤货十亿物资/chapters/index.json
Normal file
1839
books/末日重生-开局囤货十亿物资/chapters/index.json
Normal file
File diff suppressed because it is too large
Load Diff
158
books/末日重生-开局囤货十亿物资/inkos_config.json
Normal file
158
books/末日重生-开局囤货十亿物资/inkos_config.json
Normal 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
|
||||
}
|
||||
}
|
||||
}
|
||||
18
inkos.json.backup.20260330_082512
Normal file
18
inkos.json.backup.20260330_082512
Normal 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
|
||||
}
|
||||
}
|
||||
112
inkos.tomato.json
Normal file
112
inkos.tomato.json
Normal 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
146
inkos_format_config.json
Normal 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
158
inkos_quality_config.json
Normal 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
|
||||
}
|
||||
}
|
||||
}
|
||||
301
scripts/auto_fix_chapter1.py
Normal file
301
scripts/auto_fix_chapter1.py
Normal 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
301
scripts/format_check_fix.py
Normal 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
259
scripts/inkos_quality_monitor.sh
Executable 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
451
scripts/quality_monitor.py
Executable 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()
|
||||
215
scripts/simple_quality_check.py
Normal file
215
scripts/simple_quality_check.py
Normal 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
109
start_inkos_with_quality.sh
Normal 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. 停止 inkos:pkill -f 'inkos up'"
|
||||
echo "3. 查看日志:tail -f /tmp/inkos_startup.log"
|
||||
echo "4. 质量报告:python3 simple_quality_check.py <章节文件>"
|
||||
echo ""
|
||||
echo "⚠️ 注意:新配置将强制质量要求,不合格内容将被拒绝或自动修复"
|
||||
Loading…
Reference in New Issue
Block a user