# 流云图库 — 元数据管线架构文档 > 版本:v2.0 · 更新:2026-07-19 --- ## 总览 流云图库是一个 Rust 编写的图片管理应用,核心功能是从相册标题中自动提取**人物名称**和**分类标签**,并建立人物-相册关联关系。所有提取和关联逻辑由一套三阶段的**处理管线(Pipeline)** 驱动。 ``` 相册标题(如 "水淼Aqua cosplay Zani – Wuthering Waves 69 photos") │ ▼ ┌─ Pipeline ──────────────────────────────────────┐ │ Phase 1 全量解析 提取人名 + 提取标签 │ │ Phase 2 人物合并 按中文核心合并重复人物 │ │ Phase 3 缓存刷新 重新加载内存缓存 │ └──────────────────────────────────────────────────┘ │ ├──→ albums 表(person_name_original, tags 等) ├──→ persons 表(人物主表) ├──→ person_aliases 表(别名关联) └──→ album_tags 表(标签关联) ``` --- ## 启动流程与触发时机 应用启动后,管线任务按顺序串行执行三个阶段: ``` 启动管线(单次,后台任务 A) │ ├── ① 扫描导入新相册(import) ├── ② 全量解析 + 人物合并(run_pipeline) └── ③ 刷新 AlbumsCache ``` - **启动管线**:import → run_pipeline → refreshCache,三件事**顺序串行**,不并发 - **定时管线**:每 30 分钟触发一次,执行 run_pipeline → refreshCache(不重复 import) - **API 触发**:POST /api/pipeline/run 可手动触发 run_pipeline --- ## Phase 1:全量解析 **入口函数:** `phase_parse_all(db)` → `(person_updated, tag_updated)` 对数据库中**每一个相册**独立执行以下流程: ``` for each album: │ ├── jieba 分词(词性标注) │ ├──→ 提取推荐人名(词性 nr/nrfg/nrt/nz) │ └──→ 提取推荐标签(词性 n/nz/ns/eng/x) │ ├── 确定最佳人名 │ ├── 有人物库匹配?→ 用库匹配结果 │ └── 否则 → 用 jieba 推荐 │ ├── 写入 DB(强制覆盖,不比对旧值) │ ├── 提取标签 │ ├── 三步提取:分类词库 → jieba → 正则兜底 │ ├── unique_tags() 去重+排序+坏标签过滤 │ └── 写入 DB(强制覆盖,不比对旧值) │ └── 下一个相册 ``` ### 人名提取策略 按优先级依次尝试: 1. **人物库精确匹配** — `person_merger::match_title()`,在已有 persons 表中查询标题子串 2. **jieba 词性标注** — `extract_person_from_tags()`,优先取 nr(人名)类词 3. **标题模式匹配** — `extract_person_name_from_title()`,尝试 cosplay 格式、分隔符切分、正则扫描 ### 人名处理规则 - **禁止中英混杂**:人名主体为纯中文或纯英文(中文优先级更高) - 中英文混合(如"水淼Aqua")由 `split_person_name_with_aliases` 拆分为:主名="水淼" + 别名="Aqua" - 别名保存到 `person_aliases` 表,用于后续搜索匹配 ### 标签分类体系 | 分类 | 标识 | 示例词 | |------|------|--------| | 风格 | style | 日系、韩系、复古、甜美、sexy | | 服装 | outfit | 比基尼、旗袍、制服、丝袜 | | 场景 | scene | 室内、户外、街拍、浴室、beach | | 身体 | body | 美腿、翘臀、玉足、美背 | | 情绪 | mood | 清纯、诱惑、唯美、冷艳 | | 主题 | topic | 写真、私房、旅拍、cosplay | | 名词 | noun | jieba 提取的普通名词 | | 专名 | proper | jieba 提取的专有名词 | | 地名 | location | jieba 提取的地名 | | 其他 | other | 正则兜底提取的剩余关键词 | ### 标签过滤链(从外到内共 5 层) ``` 1. is_punctuation_only() → 检测不含任何字/字母/数字,则跳过 2. 停用词表过滤 → photos、cosplay、美女、模特 等 50+ 词 3. 人名排除 → 排除标签提取时确定的人名 4. 词性排除 → 形容词(a)、动词(v)、名动词(vn)、机构(nt) 等不产出标签 5. unique_tags() 最终过滤 → 调用 is_bad_tag() 二次确认 ``` ### 强制覆盖写入 每次管线执行都是**完全重新解析**,提取到人名就写入覆盖、提取到标签就写入覆盖。不做"新旧好坏"比对: - **人名**:jieba/fuzzy 提取到 → 直接写入 `albums.person_name_*`,创建/更新 `persons` 记录及别名关联 - **标签**:经过 5 层过滤链后仍有结果 → 直接写入 `albums.tags`,重建 `album_tags` 表 这样每次启动或定时触发时,标签和人名都会用最新的解析逻辑重新计算并强制覆盖,解析逻辑改进后老数据自动受益。 --- ## Phase 2:人物合并 **入口函数:** `phase_merge_persons(db)` → `merged_count` 按**中文核心**自动合并重复人物: ``` 1. 扫描所有 persons,提取其中文核心(开头的连续中文字符) 2. 将中文核心相同的 person 分为一组 3. 每组选相册数最多的作为目标(dst) 4. 其余合并到目标: - 移动所有相册关联 - 移动所有别名 - 源人物原名也作为目标别名 - 删除源人物 5. 更新目标的统计计数 ``` 示例:`杨晨晨Yome` 和 `杨晨晨Sugar` 的中文核心都是 "杨晨晨" → 合并为一个人物 --- ## 核心数据表结构 ### albums 表(关键字段) | 字段 | 类型 | 说明 | |------|------|------| | id | TEXT | UUID | | title | TEXT | 相册标题,来源是文件夹名或 index.json | | person_name_original | TEXT | 提取的原始人名(可能带英文后缀) | | person_name_cleaned | TEXT | 清洗后的人名(仅中文主名) | | tags | TEXT | JSON 数组,如 `["比基尼","室内","清纯"]` | | all_words | TEXT | JSON 数组,如 `[["比基尼","outfit"],["室内","scene"]]` | | processed_at | TEXT | 最后处理时间 | ### persons 表 | 字段 | 类型 | 说明 | |------|------|------| | id | INTEGER | 自增主键 | | name | TEXT | 人物名(通常是中文主名) | | display_name | TEXT | 显示名 | | total_albums | INTEGER | 关联相册数 | | total_images | INTEGER | 关联图片数 | ### person_aliases 表 | 字段 | 类型 | 说明 | |------|------|------| | id | INTEGER | 自增主键 | | person_id | INTEGER | 关联 persons.id | | alias_type | TEXT | 别名类型(english/nickname/chinese/original) | | alias_name | TEXT | 别名文本 | ### album_tags 表 | 字段 | 类型 | 说明 | |------|------|------| | id | INTEGER | 自增主键 | | album_id | TEXT | 关联 albums.id | | tag | TEXT | 标签文本 | --- ## 定时任务 每 30 分钟触发一次,顺序执行: ``` 1. run_pipeline() → Phase 1(全量解析)+ Phase 2(人物合并) 2. 刷新 AlbumsCache(内存缓存,为 API 提供快速查询) ``` --- ## API 接口 | 方法 | 路径 | 说明 | |------|------|------| | POST | /api/pipeline/run | 手动触发一次完整管线 | 返回: ```json { "success": true, "persons_updated": 12, "tags_updated": 45, "persons_merged": 3, "message": "管线执行完毕:更新人名 12 个,更新标签 45 个,合并人物 3 人" } ``` --- ## 关键设计原则 1. **幂等性**:管线可反复执行,每次都是完整重新解析 + 强制覆盖写入 2. **渐进改进**:解析逻辑改进后,老数据在下次管线执行时自动受益 3. **强制覆盖**:提取到即写入,不做新旧比对,确保数据始终使用最新逻辑 4. **中文优先**:人名提取优先中文,中英文混合被拆分为主名+别名 5. **多层过滤**:标签经过 5 层过滤链,确保质量 6. **纯中文/纯英文**:人名主体不允许中英混杂