# Japanese Cleaner - 日语词表清洗工具 工程化的日语词表清洗流水线,自动化处理汉字-假名-拼音对齐。采用**三层架构 + 任务化**: 清晰的职责分层,每次清洗是一个跨会话、有状态、可并存的任务。 > 完整协作规范见 [`WORKFLOW.md`](WORKFLOW.md)。 ## 架构设计 **三层职责分离**(自底向上): | 层 | 模块 | 职责 | 对外接口 | | --- | --- | --- | --- | | **底层 · 单词级** | `tango_analyser.py` | 接收单个词条 `(kanji, kana)`,输出 `AnalysisResult`(格式化行 + 状态分类)。封装 Classifier / Aligner / PinyinMaker,不碰文件、不知道"桶"。 | `TangoAnalyser.analyze()` | | **中间层 · 文件级** | `task_processor.py` | 持有 Task(因此知道所有文件路径),读源文件/review 文件 → 调底层 → 按状态分流写入桶文件;合并单批到权威库。**只搬文件,不改任务状态**。 | `TaskProcessor.process_source()` / `.process_review()` / `.merge_final()` | | **工作流层 · 编排级** | `workflow.py` | 给定任务判断走到哪一步、下一步做什么,**唯一接口 `run()`** 推进任务;更新任务状态机 `created → reviewing/ready → merged`。 | `CleanerWorkflow.run()` | **任务模型**: - `Task` / `TaskConfig` / `TaskState` — 配置 + 状态机 + 独立目录 - `TaskManager` — 任务 CRUD,多任务并存 **设计原则**: - 底层不知道文件/桶,只返回状态枚举(`AnalysisStatus`) - 中间层持有路径、负责 I/O,但不决策"该做什么" - 工作流层只做决策/编排,委托中间层执行文件操作 ## 功能特性 - **三层架构**:职责清晰,底层可独立测试,工作流可编排复杂逻辑 - **配置文件管理**:TOML 格式,支持相对/绝对路径,多项目友好 - **任务化**:每次清洗是独立任务(独立配置 + 独立临时文件 + 状态机),多任务可并存 - **两层分离**:单批临时工作区 / 最终只增去重的权威库 - **数据源只读**:处理时不修改原文件,支持任意切片(起始行 + 条数) - **严格校验**:输入输出条数自动校验 - **半自动协作**:自动分流 + 人工 review + 改代码重跑 - **工作流推进**:`run()` 一键推进,能自动处理就处理,需人工就提示 - **单元测试**:73 个测试全覆盖(底层单词分析 / 文件处理 / 工作流编排 / 配置管理) ## 数据目录布局 ```text data/ ├── db/ # 权威成品库(只增不删 + 去重) │ ├── vocabulary.txt # 所有批次累积的成品 │ └── skipped.txt # 所有批次累积的跳过项 ├── sources/ # 原始数据源(只读) │ └── xinbiaori_1.txt └── backup/ # 备份 └── *.backup.txt tasks/ └── {task_id}/ # 每个任务独立目录 ├── task.json # 任务配置 + 状态 ├── auto_done.txt # 单批结果 ├── skip.txt └── review_*.txt # 待确认桶(pinyin/split/verb/special) ``` ## 入口方式 本项目的清洗流程**需要人工介入**(review 循环:改字典/规则 → 重跑),因此 **推荐用测试驱动**协作(见 [`tests/test_cleaner_workflow.py`](tests/test_cleaner_workflow.py)), 命令行只是便捷入口。 三种等价的命令行入口: ```bash jclean ... # 安装后的 console 命令(推荐) python -m pl_japanese.cleaner.cli ... # 模块调用(无需 console 入口) python scripts/clean.py ... # 兼容薄壳(未安装包时) ``` 安装(开发模式,注册 `jclean` 命令): ```bash pip install -e . ``` ## 配置文件 支持 TOML 格式配置文件,路径可以是相对路径(相对配置文件所在目录)或绝对路径。 ### 生成示例配置 ```bash jclean init-config # 生成 jclean.toml,编辑后自动生效 ``` ### 配置文件示例 ```toml # jclean.toml [paths] # 任务根目录(存放所有任务的独立目录) tasks_root = "tasks" # 权威库(所有任务最终合并的目标) vocabulary = "data/db/vocabulary.txt" skipped = "data/db/skipped.txt" # 数据源目录(可选) sources_dir = "data/sources" [defaults] # 创建任务时的默认值 start_line = 1 count = 300 # 是否在合并前自动备份权威库 backup_before_merge = true backup_dir = "data/backup" [logging] # 日志级别:DEBUG / INFO / WARNING / ERROR level = "INFO" ``` ### 配置文件查找顺序 1. 命令行参数 `--config /path/to/config.toml`(最高优先级) 2. 当前目录 `./jclean.toml` 或 `./.jclean.toml` 3. 向上查找项目根(遇到 `.git` 停止) 4. 用户主目录 `~/.jclean.toml` 5. 硬编码默认值 ### 路径解析规则 - **绝对路径**:直接使用 - **相对路径**:相对配置文件所在目录 **示例**(配置文件在 `/home/user/project/jclean.toml`): ```toml [paths] tasks_root = "tasks" # → /home/user/project/tasks vocabulary = "/data/global/vocab.txt" # → /data/global/vocab.txt(绝对) sources_dir = "../shared/sources" # → /home/user/shared/sources ``` ### 查看当前配置 ```bash jclean show-config # 显示当前生效的配置(包含解析后的绝对路径) ``` ### 优先级 命令行参数 > 配置文件 > 默认值 ```bash # 配置文件中 tasks_root = "tasks" # 命令行参数覆盖配置文件 jclean --tasks-root /custom/tasks create batch1 --source ... ``` ## 快速开始 ### CLI 方式(极简命令) ```bash # 1. 创建任务(数据源只读,可指定处理范围) jclean create batch1 --source data/sources/xinbiaori_1.txt --start 1 --count 300 # 2. 工作流推进(自动判断下一步,能做就做,需人工就提示) jclean run batch1 # 3. 如果有 review 待确认:人工修正字典/规则后,重跑指定桶 jclean run batch1 --bucket pinyin # 4. 继续推进(review 清零后自动合并) jclean run batch1 # 5. 完成! # 其他命令 jclean list # 列举所有任务 jclean status batch1 # 查看任务状态 jclean clear batch1 # 清空单批文件 ``` ### 代码调用(三层 API) #### 工作流层(推荐,高层抽象) ```python from pl_japanese.cleaner import TaskManager, CleanerWorkflow tm = TaskManager() task = tm.create_task( task_id='batch1', source='data/sources/xinbiaori_1.txt', start_line=1, count=300, ) wf = CleanerWorkflow(task) # 自动推进 result = wf.run() print(result['action']) # 'processed' / 'need_human' / 'completed' print(result['status']) # 'reviewing' / 'ready' / 'merged' print(result['message']) # 处理结果说明 # 如果需要人工介入 if result['action'] == 'need_human': print(result['review']) # 待确认的 review 桶摘要 # 人工修正字典/规则后,重跑指定桶 wf.processor.process_review('pinyin') result = wf.run() # 继续推进 # 循环推进直到完成 while result['action'] != 'completed': result = wf.run() if result['action'] == 'need_human': break ``` #### 中间层(文件操作,精细控制) ```python from pl_japanese.cleaner import TaskManager, TaskProcessor tm = TaskManager() task = tm.load_task('batch1') proc = TaskProcessor(task) # 处理源文件 result = proc.process_source() print(result['buckets']) # 各桶条数 print(result['review_total']) # 需 review 的总数 # 重跑某个 review 桶 result = proc.process_review('pinyin') # 合并(dry-run 校验) result = proc.merge_final(dry_run=True) # 查询当前状态 print(proc.snapshot_counts()) # 各单批桶当前条数 print(proc.review_total()) # review 总数 print(proc.final_counts()) # 权威库条数 ``` #### 底层(单词分析,测试/调试用) ```python from pl_japanese.cleaner import TangoAnalyser, AnalysisStatus analyser = TangoAnalyser() # 分析单个词条 result = analyser.analyze("日本人", "にほんじん") print(result.status) # AnalysisStatus.SUCCESS print(result.formatted_line) # "日|本|人:に|ほん|じん:ri|ben|ren" print(result.is_success) # True print(result.needs_review) # False # 需人工确认的词 result = analyser.analyze("女将", "おかみ") print(result.status) # AnalysisStatus.SPLIT_FAILED print(result.needs_review) # True ``` ## 核心概念 ### 状态分类(底层) `AnalysisStatus` 枚举(底层单词分析结果): | 状态 | 含义 | 是否需 review | | --- | --- | --- | | `SUCCESS` | 成功处理,格式化行可直接采用 | ❌ | | `SKIP` | 无汉字等,跳过不进成品库 | ❌ | | `POLYPHONE` | 含多音字/多解,拼音需人工确认 | ✅ | | `SPLIT_FAILED` | 假名无法与汉字对齐分割 | ✅ | | `VERB_FORM` | 动词/敬语,需确认形式 | ✅ | | `SPECIAL_CASE` | 含字母/片假名/格式异常 | ✅ | ### 状态机(工作流层) 任务状态流转: ```text created ↓ run() → process_source reviewing (有 review_* 待确认) ↓ 人工改代码/字典 + process_review → review 清零 ↓ run() → 检测清零 ready (可合并) ↓ run() → merge_final merged (完成) ↓ run() completed (终态) ``` ### 单批桶(中间层文件分类) | 桶名 | 对应状态 | 含义 | | --- | --- | --- | | `auto_done.txt` | SUCCESS | 自动处理成功,待合并进 `vocabulary.txt` | | `skip.txt` | SKIP | 跳过(无汉字等),待合并进 `skipped.txt` | | `review_pinyin.txt` | POLYPHONE | 多音字待确认 | | `review_split.txt` | SPLIT_FAILED | 分割失败待人工 | | `review_verb.txt` | VERB_FORM | 动词形式待确认 | | `review_special.txt` | SPECIAL_CASE | 特殊情况待判断 | ### 工作流 `run()` 返回 `CleanerWorkflow.run()` 返回 dict,关键字段: | 字段 | 含义 | | --- | --- | | `action` | `processed`(推进了)/ `need_human`(需人工)/ `completed`(完成)/ `empty`(无内容) | | `status` | 推进后的任务状态(`created` / `reviewing` / `ready` / `merged`) | | `message` | 人类可读说明 | | `review` | (`need_human` 时)待确认的 review 桶摘要 `{bucket: {count, sample}}` | | `process` | (`processed` 时)处理结果统计 | | `merge` | (`processed` 且合并时)合并结果统计 | ## 分类规则 核心规则详见 [`src/pl_japanese/cleaner/classifier.py`](src/pl_japanese/cleaner/classifier.py)。 **汉字分词(自动)**: - 每个汉字单独一段 - `~`(通配符)单独一段,匹配任意长假名 - 其余非汉字连续合并成一段 **拼音生成**: - 汉字:查 `WORD_OVERRIDE` 全词覆写 → 查 `KANA_HINT` 假名提示 → pypinyin - 多音字检测:pypinyin 返回多个候选 → 标记 `POLYPHONE` **动词处理**: - 假名以 `う段` 结尾 / 含 `~` 标记 / 假名末有 "ます/ません/ました" → 判定为动词 - 去掉送り仮名(活用后缀)后处理 **跳过规则**: - 无汉字 → `SKIP` - 纯片假名/字母/符号 → `SKIP` **字典路径**(多音字覆写): - `WORD_OVERRIDE`:全词精确匹配(优先级最高) - `KANA_HINT`:`(kanji, kana_prefix)` → pinyin,用于区分假名提示多音字 ## 测试 ```bash # 运行所有测试(60 个) pytest tests/ # 只测工作流(31 个) pytest tests/test_cleaner_workflow.py -v # 只测底层分析(8 个) pytest tests/test_cleaner.py -v ``` 测试覆盖: - **底层单词分析**:状态分类、格式输出、needs_review 逻辑 - **中间层文件处理**:条数铁律、追加模式、去重、格式校验、review 重跑 - **工作流编排**:状态机推进、人工介入门禁、review 清零自动转 ready、合并授权 - **任务管理**:CRUD、多任务隔离、持久化 ## 版本历史 - **v0.3.0** (2025-01):三层架构重构 + 配置文件管理 - 三层职责清晰分离(底层单词分析 / 中间层文件处理 / 工作流编排) - 新增 `CleanerWorkflow.run()` 工作流推进 - CLI 精简(5 个命令,`run` 统一推进) - TOML 配置文件支持(相对/绝对路径) - 73 个测试全覆盖 - **v0.2.0** (2025-01):任务化架构,多任务并存,状态机管理 - **v0.1.0** (2024):初版单批流水线 ## 许可 MIT