Skip to content

About

archive classifier skill

Resources

Stars

1 star

Watchers

0 watching

Forks

Latest commit

 

History

6 Commits

Folders and files

Repository files navigation

Archive Classifier

Archive Classifier 工作流程:杂乱文件、学习规则、自动分类

简体中文 · English

为充分发挥顶级模型的理解、推理与归纳能力,本 Skill 采用“模型设计规则,代码批量执行”的架构:模型先作为一次性的“分类规则设计师”,观察目标分类树与待分类数据,为当前项目生成定制化的分组与分类规则;规则确定后,再由普通 Python 代码高速、稳定、可重复地完成批量分类。

如果您想进一步了解它的详细工作原理,请将以下 Prompt 发给您的 Agent:

请阅读当前 archive-classifier 仓库,用通俗易懂的语言分步讲解其底层工作原理,重点说明 Agent 如何分析目标分类树与待分类数据、生成定制化的分组与分类规则,以及 Python 代码如何批量执行规则并形成人工审阅闭环。

如需安装,请将以下 Prompt 发给您的 Agent:

请前往 https://github.com/misdolicc/archive-classifier,为你自己安装并配置该 Skill。

这是一个 Agent Skill。SKILL.md 是供 Agent(如 Codex、Claude Code、OpenCode、TRAE 等)阅读的精简操作规程。

本 README.md 是给人看的,把同一套流程展开讲清楚每一步在做什么、为什么这么设计、遇到问题怎么排查。


目录


核心思路

  1. 文件只能落在"叶子节点"——目标分类树中没有子目录的目录。中间节点(还有子分类的目录) 不允许直接塞文件,否则以后没法再往下细分。

  2. 整体保持在一起——一份数据手册和它的 3D/2D 模型、一份多语言/多分卷的手册、一个厂商的产品资料夹、 一个软件安装包,都作为一个单元整体处理,绝不会被拆散到不同目标目录。

  3. 软件/驱动/库/整机转储按"包"为粒度——安装包、SDK、驱动库、运行时数据转储 (.ors/.res/.dll/.bin、安装程序、日志)、整个设备资料夹,作为一个单元移动, 绝不逐文件拆分处理(那样既没有意义,数量也太大)。

  4. 结构/名字看不出来就先去看一眼,而不是硬猜——这条用在两个判断上:

    • 单元深度(eff_depth)——不该凭顶层文件夹的名字就给整个分支拍板一个深度, 写之前先用 units_lib.preview_depth(src, top) 看一眼该分支下子目录的真实形状 (各深度目录是不是已经没有子目录的完整包),再决定这一层该设几;同一顶层分支下 不同子树形状不一样时,eff_depth(parts) 可以按 parts[1](甚至更深)分开处理, 不是只能靠一张对全分支一视同仁的固定表;
    • 分类目标(classify())——路径/文件名是编号、代码这类没有语义信息、仅凭名字 会被判 uncertain 兜底的单元,值不值得打开来看由 Agent 按单元/簇自行判断: 父目录/厂商上下文已经给够信息就不用开;属于整包处理的软件/驱动/库转储(第 3 条) 也不用开——反正整包不拆,开了也不影响归类结果。

    这两步都是 Agent 在当次会话里编写/修改 eff_depth/classify() 时当场做的判断, 不是逐文件/逐目录调用 AI 的自动化流水线(几千个文件、大量二进制文件的场景下, 那样做既没意义又极慢极贵)。

  5. 不确定就标记,交给人——落到通用的"其他/Other"叶子、或者压根没有合适类别的东西, 都会带上 status=uncertain 和一句简短原因,方便人工在审阅页面里重点复核。

  6. 默认只出计划,不动文件——只有明确要求执行时,才会用 move_plan.py,而且默认 DRY_RUN=True。


项目文件一览

archive-classifier/
├── SKILL.md                     # 给Agent看的操作规程
├── README.md                    # 给人看的全流程介绍
├── README_EN.md                 # 英文版完整说明
├── head-cn.png                  # 中文版头图
├── head-en.png                  # 英文版头图
├── scripts/
│   ├── units_lib.py             # 通用工具库:遍历/叶子发现(含可信清单 stable_leaf_dirs)/单元枚举/深度形状预览/计数/写计划/验收
│   ├── classify_example.py      # 可运行的分类驱动骨架(复制它改造成 run.py)
│   ├── verify_coverage.py       # 独立的覆盖率/合法性/重复性验收脚本
│   ├── build_review_html.py     # 用 units.json + 目标树叶子列表生成审阅 HTML(并把 review_server.py 复制到工作空间)
│   ├── review_server.py         # 本地后端:打开源文件预览 + 接收重分类队列(无外部依赖,可单独复制运行)
│   ├── reclassify.py            # 对"标记为需要重新分类"的单元重新跑一遍分类规则
│   └── move_plan.py             # 执行已审阅的计划:整单元复制/移动进目标叶子
└── templates/
    └── review.html              # 审阅界面工具模板骨架

其中 units_lib.py 是唯一的"通用引擎",其余脚本都是围绕它的驱动/工具;每个具体分类项目 只需要新写一个 run.py(复制 classify_example.py 改造),提供两个函数:

  • eff_depth(parts) -> int:某个路径分支应该在第几层被当作一个"单元"。
  • classify(rel) -> (leaf, status, note):某个源单元应该归入目标树的哪个叶子目录。

完整流程(分步详解)

下面按实际执行顺序讲解,每一步给出对应脚本/函数和示例。假设:

  • 源目录:E:/dump/2025-资料整理(杂乱、待分类)
  • 目标分类树:E:/知识库(已经存在的分类骨架,只需要把文件放进合适的叶子)

1. 侦察源目录

不要对成千上万个文件逐一列出,先摸底:

import sys; sys.path.insert(0, "scripts")
import units_lib as U
print(U.summarize("E:/dump/2025-资料整理"))
# {"total": 8342, "per_top": {"设备厂商资料": 5210, "软件安装包": 2100, "杂项": 1032}}

summarize()(units_lib.py)统计每个顶层目录下递归的文件总数, 以及总文件数,按数量从多到少排列,能一眼看出"体量集中在哪个顶层目录", 也能提前发现里面藏着的软件安装包/驱动转储(体积大但文件数可能很集中在少数几个包里)。

2. 发现目标叶子目录

目标分类树里"没有子目录的目录"才是合法的归档目标。但要用 stable_leaf_dirs(),不要直接用 leaf_dirs():

leaves = U.stable_leaf_dirs("E:/知识库")
# ["01_物料手册/华为/Datasheet", "01_物料手册/华为/3D模型", "05_学习资料/其他", ...]

原因见下面的"⚠️ 一个容易踩的坑"——leaf_dirs() 是每次都实时扫描目标树、纯靠"有没有子目录" 判断叶子,一旦目标树里已经有分类挪进去的内容,这个判断就会失真。stable_leaf_dirs() 首次调用时 会自动引导(bootstrap)一份持久化清单(写在 目标树根目录/.archive_classifier_leaves.json), 之后每次都读这份清单,不再受目标树实际内容变化的影响。

后续所有分类规则产出的目标路径,最终都要能在这份列表里精确匹配到(verify() 会检查这一点)。

⚠️ 一个容易踩的坑:目标树里已经有内容了,再扫描会把内容误判成叶子

假如上一轮分类已经把一个文件夹 A(比如某个产品资料包)挪进了叶子 01_物料手册/华为/Datasheet 里。这一轮再对目标树做实时扫描(leaf_dirs())会发现:

  • Datasheet 目录现在有子目录(A)了 → 不再被判定为叶子(明明它还是那个该用的目标);
  • A 自己如果没有子目录 → 被误判成一个新叶子(其实它只是被挪进去的内容,不是分类类目)。

两个方向都是错的,而且这不是理论假设——这是这个 skill 被实际使用几轮之后必然会遇到的情况。 解决方式就是上面说的:任何时候要拿"目标树有哪些合法叶子",都调用 stable_leaf_dirs(dst), 不要直接调用 leaf_dirs(dst)。 后者只在两种场合还有用:(a)stable_leaf_dirs 内部第一次 引导清单时;(b)你想要看一眼目标树"实时"的真实结构做诊断时(比如怀疑清单本身有问题)。 所有的驱动脚本(verify、build_review_html.py、move_plan.py、reclassify.py)现在都已经 走 stable_leaf_dirs,正常使用不需要自己操心这个区别,只有你自己写 Python 直接调用 units_lib 时才需要留意选对函数。

3. 决定分类粒度

规模大/混杂时,应该先跟用户确认粒度,而不是自己假设:

  • 文件级:适合数量不大(几百个)、已经比较规整的文档集合。
  • 包/文件夹级(推荐用于转储):数千+文件、软件安装包、设备资料夹——整包移动。

粒度通过 eff_depth(parts) 函数表达——它告诉 enumerate_units() "某个顶层目录下,第几层目录才算一个完整单元"。

这个深度不该凭顶层文件夹的名字拍脑袋定——写之前先用 units_lib.py:preview_depth 看一眼该分支下子目录的真实形状:

U.preview_depth("E:/dump/2025-资料整理", "华为服务器资料")
# {2: {"count": 3, "sample": [
#        {"path": "华为服务器资料/FusionServer2288", "has_subdir": False},
#        {"path": "华为服务器资料/交换机CE6857",     "has_subdir": False},
#        {"path": "华为服务器资料/路由器AR2200",     "has_subdir": True}]},
#  3: {"count": 1, "sample": [{"path": "华为服务器资料/路由器AR2200/资料", "has_subdir": False}]}}

深度 2 的目录里,前两个 has_subdir 都是 False(已经是完整产品包,无需再拆),但 "路由器AR2200"是 True(下面还分了"资料"子目录)——这种情况下,depth=2 对这个分支整体 是合适的,"路由器AR2200"内部的子目录也不会被拆开,因为一旦某目录被判定为单元根, 其所有子孙目录就不再单独成为单元(见下方算法说明)。如果反过来,某分支下深度2的目录 几乎全部 has_subdir=True(说明真正的产品/资料在深度3),就该把这个分支的深度设成3。 看完形状再落笔:

BASE = {"01_物料手册": 3, "02_行业装备": 3, "05_学习资料": 2}
def eff_depth(parts):
    top = parts[0]
    return BASE.get(top, 2)   # 默认深度 2:顶层 -> 子目录 即为一个单元

01_物料手册 深度设为 3,意味着 01_物料手册/华为/交换机 这样"厂商/产品"两层都存在时, 产品目录整体是一个单元;05_学习资料 深度 2,则 05_学习资料/Python教程 这一层就是单元, 不再往下拆。如果同一个顶层分支下不同子树的形状不一样(比如某个厂商产品都在深度2, 另一个厂商内部还分了子系列在深度3),eff_depth(parts) 可以按 parts[1](甚至更深的段) 分开处理,不是只能靠 BASE 对整个顶层分支一视同仁:

def eff_depth(parts):
    top = parts[0]
    if top == "华为服务器资料" and len(parts) > 1 and parts[1] == "路由器AR2200":
        return 3   # 这一个产品系列内部还分了子系列,其余产品仍按默认/BASE 的深度处理
    return BASE.get(top, 2)

枚举单元的核心算法在 units_lib.py:enumerate_units:

  • 一个目录如果"深度正好等于该分支应有的单元深度",或者"更浅但已经没有子目录了", 就被判定为一个"单元根";
  • 单元根一旦确定,其所有子孙目录不会再被单独当作单元(避免嵌套重复计数);
  • 不属于任何单元根的散落文件,各自单独成为一个"单文件单元";
  • 保证每个文件恰好属于一个单元——这是后面覆盖率验收的基础。

4. 处理目标树里没有的类别

如果源目录里有目标树完全没有对应位置的内容(比如混进了竞品设备资料,而目标树只为"我方设备" 建过分类),不要擅自发明新目录——应该向用户提出建议的新顶层类别,确认后,在目标树里 先手动建好新的叶子目录,这样后续分类规则产出的目标路径才是"真实存在的叶子",能通过验收。

建完新叶子目录后,别忘了紧接着登记进可信清单:

U.add_leaves("E:/知识库", "02_行业装备/竞品设备资料")

原因见上一步的"⚠️"说明——stable_leaf_dirs() 不会自动重新扫描目标树发现这个刚建好的新叶子, 必须显式调用 add_leaves() 登记,它才会出现在后续分类可用的叶子列表里。这也是刻意的: "这是我刚新建的分类目录"和"这是被挪进去的内容",只能靠人工/Agent 明确知道自己做了什么来 区分,没法靠重新扫描目标树自动分辨。

同时,遇到语义模糊的现有类别(例如"产品信息资料"到底是指"我们要测试的对象"还是"我们自己的设备"), 也应该跟用户确认清楚并记下来,避免分类规则一开始就理解错方向。

5. 编写分类驱动脚本 run.py

复制 scripts/classify_example.py 为项目专属的 run.py, 只需要修改文件里明确标出的 3 处:

① 路径与兜底叶子(脚本顶部常量,务必用绝对路径 + 正斜杠,且不要通过命令行传中文路径, 原因见下方"Windows + 中文路径注意事项"):

SRC   = "E:/dump/2025-资料整理"
DST   = "E:/知识库"
PLAN  = "E:/dump/2025-资料整理_文件分类移动计划.txt"
JSONF = "E:/dump/2025-资料整理_units.json"
FALLBACK_LEAF = "05_学习资料/其他"   # 必须是目标树里真实存在的叶子

② 单元深度 eff_depth(parts)——见上一节。

③ 分类规则 classify(rel)——按"先限定顶层大类范围,再用关键字细化"的方式写有序规则, 第一条命中的规则生效;所有规则都没命中时,兜底落到 FALLBACK_LEAF 并标记 uncertain:

def classify(rel):
    parts = rel.split("/"); top = parts[0]; low = rel.lower()
    OK  = lambda leaf: (leaf, "normal", "")
    UNC = lambda leaf, why="待人工复核": (leaf, "uncertain", why)

    if top == "设备厂商资料":
        if "半导体" in rel:
            return OK("同行业装备资料/半导体检测装备")
        return OK("同行业装备资料/汽车电子检测装备")

    if top == "软件安装包":
        if "驱动" in low or "driver" in low:
            return OK("软件驱动库/设备驱动")
        return UNC("软件驱动库/其他软件", "未识别具体软件类型")

    return UNC(FALLBACK_LEAF, "未归类 — 待人工复核")

规则编不出来的情况——遇到编号/无语义文件名,该不该打开看内容,由 Agent 自己判断: 路径和文件名给不出任何有用线索时(一堆 GB50054-2011.pdf、7F3D2A1.pdf 这种编号/代码型文件), 继续硬凑关键字规则只会瞎猜。这时候按情况处理:

  • 父目录/厂商上下文已经够用 → 不用开文件,比如 国家标准资料/GB50054-2011.pdf 光看父目录 国家标准资料 就能归类,文件名本身的编号无所谓;
  • 编号本身有规律 → 用正则匹配编号前缀(如 re.match(r'GB\s?/?T?\s?\d+', name)), 不用逐个打开,看几个样本认出规律就行;
  • 规律也看不出来、数量不多、值得看一眼 → Agent 直接用 Read 工具打开该文件(或提取 PDF/Word 文本)确认内容,据此写规则或者干脆建一张"文件名 → 目标叶子"的精确对照表;
  • 属于软件安装包/驱动库/整机资料这类整包处理的单元(核心思路第 3 条) → 不用开, 反正整包就是一个单元,内部文件类型再杂也不拆分处理,打开了也不影响归类结果。

这一步是 Agent 在编写/修改 classify() 时当场做的判断和操作,不是一套自动化的"每个文件 都调用一次 AI 生成摘要"的流水线——几千个文件、大量二进制文件的场景下,那样做时间和成本都不划算, 也没必要。真正认不出来的,照旧老实标 uncertain,交给审阅页面里的人工复核。

6. 运行分类,生成计划

PYTHONIOENCODING=utf-8 python run.py

run.py 的主流程(骨架里已经写好):

  1. U.enumerate_units(SRC, eff_depth) 枚举出所有单元及各自的文件数;

  2. U.build_rows(units, classify) 对每个单元跑一遍 classify(),生成 [{src, leaf, count, status, note}, ...];

  3. U.dump_json(rows, JSONF) 写出 units.json(后续 HTML 审阅工具 / 重分类脚本都依赖它);

  4. U.write_plan(rows, PLAN, SRC, DST) 写出人类可读的计划文本:

    源目录根:E:/dump/2025-资料整理    目标目录根:E:/知识库
    粒度:整包/文件夹为单位(软件/驱动/库/整包不拆)。格式:源单元  =>  目标叶子   (文件数)[待确认]
    ==========================================================================================
    设备厂商资料/华为/交换机X1  =>  同行业装备资料/半导体检测装备   (214 文件)
    软件安装包/未知安装器v2  =>  05_学习资料/其他   (1032 文件)   [待确认:未识别具体软件类型]
    
  5. U.verify(rows, SRC, DST) 自检一遍并打印结果(下一步细说)。

7. 验收覆盖率

run.py 运行结束会自动打印验收结果,也可以单独用 verify_coverage.py 对着已生成的 units.json 复查:

PYTHONIOENCODING=utf-8 python scripts/verify_coverage.py

三项必须全部通过(units_lib.py:verify):

检查项 含义 不通过说明什么
coverage_ok 各单元文件数之和 == 源目录实际文件总数 有文件被漏掉或被重复计数了
invalid_leaves 计划里的目标路径必须是目标树里真实存在的叶子 classify() 写错了目标路径,或目标树缺这个叶子
duplicate_units 同一个源单元不应该出现两次 单元枚举逻辑有问题(理论上不会发生)

三项都为空/为真才算 PASS,否则要回去修 eff_depth/classify/目标树,直到通过。

invalid_leaves 的校验用的是 stable_leaf_dirs()(可信清单),不是实时扫描目标树——所以 即便目标树里已经有之前分类挪进去的内容,这一步也不会被污染而误报。

8. 生成 HTML 审阅工具

PYTHONIOENCODING=utf-8 python scripts/build_review_html.py

编辑 build_review_html.py 顶部的 UNITS/SRC/DST/OUT/NAME/BIG/COPY_BACKEND 常量后运行,它会把 units.json 的内容和 stable_leaf_dirs(DST) 的叶子列表(可信清单,不受目标树里已有内容干扰),一起塞进 templates/review.html 模板的三个 占位符(__LEAVES__/__UNITS__/__META__),生成一个单文件、零依赖的 HTML,双击即可在浏览器里打开使用。

同时(COPY_BACKEND 默认为 True)会把 scripts/review_server.py 复制一份到 OUT 所在的 工作空间目录里(和 review.html/units.json/计划文件放在一起)——这样工作空间自成一体, 在那个目录里直接 python review_server.py review.html 就能启动后端,不用再回到 skill 自身的 scripts/ 目录去找这个文件。review_server.py 本身只用标准库、不依赖 units_lib,所以能单独 拷贝、独立运行。每次生成/重建 HTML(包括 reclassify.py 触发的重建)都会重新复制一份, 保证工作空间里的副本始终是当前版本;如果 OUT 恰好就在 scripts/ 目录下(源和目标是同一个 文件),会自动跳过复制。

审阅页面能做什么:

  • 搜索 / 按大类筛选 / 按源目录或目标目录分组 / 按路径或文件数排序;
  • 筛选药丸:待确认 / 已改目标 / 未审阅 / 已审阅 / 大包(超过 BIG 阈值)/ 待重分类;
  • 直接编辑目标路径(自动补全 + 非法目标红框提示,只能选真实存在的叶子);
  • 逐条标记"已审阅",顶部有整体审阅进度条;
  • 勾选"需要重新分类",标记那些分类结果不满意、需要改规则重新跑一遍的单元;
  • 导出已审阅计划(txt,供 move_plan.py 使用)/ 导出 CSV;
  • 进度保存在浏览器 localStorage(按数据集名称隔离,多个审阅页面互不干扰),刷新不丢失。

纯 file:// 方式打开也能正常审阅、编辑、导出;只有"点击源文件打开"和"提交待重分类" 这两个功能需要下一步的本地后端配合。

9. (可选)启动本地审阅后端 + 重新分类闭环

PYTHONIOENCODING=utf-8 python scripts/review_server.py ./review.html

review_server.py 是一个只绑定 127.0.0.1 的极简 HTTP 服务, 自动读取 HTML 里内嵌的 __META__(源目录根、数据集名),启动后自动打开浏览器。有它之后:

  • 点击某一行的源路径,会用系统默认程序只读打开对应的文件/文件夹(GET /open, 服务端会校验解析后的真实路径必须仍在源目录根之内,防止路径穿越访问到源目录之外的内容);
  • 点击"提交待重分类",把勾选的单元 POST 到 /reclassify-queue,写成 <name>_reclassify_queue.json,供下一步消费。

审阅中发现某批单元分类得不对时的闭环流程:

  1. 页面上勾选这些行的"需要重新分类",点击"提交待重分类";

  2. 去 run.py 里改进 classify() 规则(方法和第一次分类完全一样);

  3. 运行 reclassify.py:

    PYTHONIOENCODING=utf-8 python scripts/reclassify.py

    它只对队列里的单元重新跑一遍 classify()(OVERRIDES 字典还支持对个别单元手动 指定目标,不走规则),更新 units.json 里这些行的 leaf/status/note,并把 rev 版本号加 1,然后重建 HTML;处理完的队列文件会被重命名为 .done,防止被重复应用。

  4. 浏览器刷新页面——rev 提升会让页面发现这些单元的本地审阅状态已过期, 自动丢弃旧的"已审阅/已改"标记,展示最新分类结果,重新进入待审阅状态。

10. 执行移动计划

只有用户明确要求执行时才做这一步。在审阅页面点击"导出计划(txt)",得到 <name>_文件分类移动计划_已审阅.txt,然后:

PYTHONIOENCODING=utf-8 python scripts/move_plan.py

编辑 move_plan.py 顶部常量:

PLAN    = "E:/dump/2025-资料整理_文件分类移动计划_已审阅.txt"
MODE    = "copy"     # "copy"(保留源文件,推荐) 或 "move"
DRY_RUN = True        # 先 True 看预览+日志,确认无误后改成 False 才会真的执行

务必先 DRY_RUN=True 跑一遍,它会把每一条要执行的操作(COPY/MOVE 哪个源到哪个目标, 是否因为改名冲突而重命名)打印出来并写一份日志,但不改动任何文件;确认无误后再改成 DRY_RUN=False 正式执行。执行时的几个安全保证:

  • 整单元操作——一个软件包/资料夹要么整体复制/移动,绝不会被拆散逐文件搬运;
  • 冲突安全——目标位置如果已存在同名文件/文件夹,自动加 (2)、 (3) 后缀, 绝不覆盖或合并已有内容;
  • 目标校验——计划里的目标路径会重新对照 stable_leaf_dirs()(可信清单)做匹配(忽略大小写/ 分隔符差异),匹配不上的目标默认跳过(CREATE_MISSING_LEAVES=True 时会新建该叶子目录, 并自动调用 add_leaves() 把它登记进可信清单——这样下一轮分类才认得它是"真实类目", 而不是要靠重新扫描目标树去发现);
  • 执行结束会打印完成/跳过/出错的数量,并把完整日志写到 <plan>_move_log.txt。

关键概念详解

概念 说明
单元 (unit) 分类的最小整体处理粒度:一个软件包、一个产品资料夹、或一个散落文件。整体归类,不拆散。
叶子目录 (leaf) 目标分类树中没有子目录的目录,是唯一合法的文件归档位置。判断时应该用 stable_leaf_dirs()(可信清单),不是每次实时重新扫描。
可信叶子清单 (stable_leaf_dirs/.archive_classifier_leaves.json) 持久化保存在目标树根目录下的隐藏文件,记录"目标树本来的分类骨架长什么样",不受后续移动进去的内容干扰;新增真实类目要用 add_leaves() 显式登记。
单元深度 (eff_depth) 某个顶层目录分支下,第几层目录被当作一个完整单元(由项目自己决定,体现分类粒度)。写它之前应该用 preview_depth 先看看真实形状,而不是凭名字猜。
深度形状预览 (preview_depth) 按深度分层展示某分支下的目录样本 + 是否还有子目录,帮助决定 eff_depth 该设几层。
覆盖率验收 (verify) 源目录里"每个文件都被计入且只被计入一次"、"每个目标都是真实叶子"、"没有重复单元"三项检查。
rev 版本号 每个单元的分类版本;重新分类后 +1,驱动审阅页面自动重置该单元的本地审阅状态。
status: uncertain 规则没能确定分类、落到兜底叶子的单元,会带一句原因,提示人工重点复核。

Windows + 中文路径注意事项

本项目主要在 Windows + 中文路径 场景下使用(纯 Linux/macOS 或纯 ASCII 路径可忽略本节):

  • 运行任何脚本都建议加上 PYTHONIOENCODING=utf-8,避免中文在控制台输出乱码/报错。
  • Git Bash 会破坏命令行参数和 here-doc 里的中文字符——所以所有中文路径都应该写在 每个脚本顶部标好的 EDIT-ME 常量里(用文本编辑器/Write 工具写一个 UTF-8 的文件), 然后直接 python run.py 运行整个文件,而不是把中文路径当命令行参数传入, 也不要放进 bash <<'PY' ... PY 这样的 here-doc。
  • 路径统一使用 posix 风格的正斜杠 / 作为唯一"规范分隔符"(展示和内部匹配都用它), 它在 Windows 上同样合法;审阅页面在匹配目标时会自动兼容用户粘贴内容里的 \ 和大小写差异。

常见问题排查

  • verify() 报 invalid_leaves 不为空:classify() 里写的目标路径和可信清单 (stable_leaf_dirs(DST))里的叶子对不上(打错字、目标树里其实是中间节点没有叶子、 或者目标树本来就缺这个类目,又或者是刚建的新叶子忘了调用 add_leaves() 登记)。 打印 units_lib.stable_leaf_dirs(DST) 核对拼写和登记情况,而不是 leaf_dirs(DST) (后者是实时扫描,目标树里如果已经混了内容,打印出来的东西本身就可能是错的)。
  • coverage_ok 为 False:一般是 eff_depth 写得不对,导致某些目录被判定为单元后, 它的子文件又被算进了别的单元(或者反过来漏算)。先看 enumerate_units 返回的单元列表, 对照实际目录结构确认深度设置是否符合预期。
  • 怀疑某个分支的单元被分得太粗或太细(比如好几个不相关的产品被捆进了同一个单元, 或者一个完整的包被拆成了好几个单元):用 units_lib.preview_depth(SRC, 该顶层分支) 看一眼真实形状,确认该深度上的目录是不是已经是没有子目录的完整包;同一分支内形状不一致时, 在 eff_depth(parts) 里按 parts[1](甚至更深)针对性处理,而不是整个分支套用同一个深度。
  • 目标树里明明已经建好了新叶子目录,但分类/审阅页面里还是找不到它:新建的叶子目录必须 显式调用 units_lib.add_leaves(DST, "新叶子路径") 登记进可信清单才会生效——stable_leaf_dirs() 不会自动重新扫描目标树去发现它,这是刻意设计(见"发现目标叶子目录"一节的⚠️说明)。
  • 怀疑可信清单本身有问题(比如第一次生成清单时目标树就已经不干净了):清单就是 目标树根目录/.archive_classifier_leaves.json,一份普通的 JSON 数组,可以直接打开 编辑核对;确认目标树恢复干净状态后,删掉这个文件,下次调用 stable_leaf_dirs()/verify() 会用当前实时结构重新引导一份新的。
  • 审阅页面里目标输入框一直是红框:说明当前填的目标不在 stable_leaf_dirs(DST) 列表里, 从自动补全下拉里选一个真实存在的叶子;如果这是一个刚建好但还没登记的新叶子, 按上面说的用 add_leaves() 登记后重新生成审阅页面。
  • "点击打开源文件"/"提交待重分类"没反应:这两个功能需要通过 scripts/review_server.py 提供服务打开页面(http://127.0.0.1:端口/), 纯 file:// 双击打开的页面会自动降级(提示需要后端 / 改为下载 JSON)。
  • move_plan.py 大量 SKIP invalid leaf:说明审阅导出的计划文件里,有目标路径 在可信清单里找不到——通常是目标树在生成计划之后新增过类目但没调用 add_leaves() 登记; 打印 stable_leaf_dirs() 核对,或者按需打开 CREATE_MISSING_LEAVES=True(谨慎使用, 会自动登记新建的叶子)。

About

archive classifier skill

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages