规则 DSL
规则是 KBFold 的正文。工具本身只做三件事:读规则、判文件、记账。所以「它能不能用在我的资料上」几乎等价于「我这份 kbfold.yml 写得对不对」。
kbfold 命令=克隆仓库后的 python hosts/cli/kbfold_cli.py。改完规则先用 kbfold validate(或宿主里的 kb.validate 工具)校验结构,不认识的版本会直接报错退出。01一份规则文件长什么样
version: 1
root: ~/Projects
basis: kbfold:generic/v1 # L0 通用基底,永远兜底,不可删除
kbs:
- name: 星海项目
id: XH-2024
template: kbfold:trade/v1 # 行业模板:贸易
detect:
markers: [".xhproject"] # 有这个文件即认定,置信 0.95
keywords: ["星海", "XH-2024"] # 置信 0.80
paths:
- match: "**/合同/**"
to: "02-合同"
- match: "**/会议记录/**"
to: "03-会议"
- match: "**/过程稿/**"
to: "@exclude" # 永不入投影,但仍登记指纹一段话读完:version 是格式版本;root 是扫描根;basis 是兜底模板;kbs 下面每个知识库有名字、编号、模板,以及两组「怎么认出我」的探测器(detect)和一组「认出来之后往哪放」的路径规则(paths)。
02字段说明
| 字段 | 必填 | 说明 |
|---|---|---|
version | 是 | 目前只有 1。不认识的版本直接报错退出,不做猜测性兼容 |
root | 是 | 扫描根目录。相对路径按规则文件所在目录解析 |
basis | 是 | L0 通用基底模板,任何项目都没认出来时用它兜底。不可删除、不可改名 |
kbs[].name | 是 | 人类可读名字,出现在报告的标签里 |
kbs[].id | 是 | 短编号(如 XH-2024),用于投影目录前缀与检索时的库过滤 |
kbs[].template | 否 | 行业/场景模板。不写则继承 basis |
kbs[].detect.markers | 否 | 标记文件名列表,命中即认定,置信 0.95(最高级) |
kbs[].detect.keywords | 否 | 名称/路径中的关键词,置信 0.80 |
kbs[].paths[].match | 是 | glob 写法,如 /合同/ |
kbs[].paths[].to | 是 | 目标目录(相对投影根)、@exclude 或 @inbox |
03三个特殊去向
- 普通目录名:如
02-合同,投影里就放这儿。 @exclude:这个匹配永远不进投影。常用于过程稿、缓存、临时导出。注意它仍然进指纹台账——这样将来你把它从@exclude里拿掉,不必重新扫全盘。@inbox:显式承认「我不知道该放哪」。判断链自己走不通时会自动落到这里,你也可以手动指定。
@inbox 不是垃圾桶,它是一个待办队列:每份带候选与理由,你处理完一批再跑一次 plan 就会看到它变短。
04冲突怎么裁
同一份文件被多条规则命中,按下面四条依次裁决:
| 情形 | 裁决 | 理由 |
|---|---|---|
| 多条路径规则命中 | 最具体的胜:路径段更多者胜;同样多则书写顺序后者覆盖前者 | 越具体越像人为表态 |
| 具体程度打平 | 不猜,进 _inbox/ 并附两条候选 | 平手时任何选择都是抛硬币,抛硬币的错误率 50% |
| S1 显式声明与路径规则冲突 | S1 胜 | 用户明确写过 .xhproject,比路径暗示更可信 |
| 一条都没命中 | 落 basis 通用基底;仍无解 ⇒ _inbox/ | 有兜底但不硬塞 |
05什么名字不许写进规则
data、config、cache、app、docs 这类全行业同名的通用目录名,写进 to: 会在启动时直接报错:
✗ 规则错误:目标目录 "data" 属于通用名黑名单(GENERIC_NAMES,共 40+ 项) 这类目录名不携带任何场景信息,放进规则等于把判定权交给猜测。 换成场景名,例如 "01-原始资料/data"。
黑名单沿用主产品 dir_classify.py 里的同一份 GENERIC_NAMES,不在 KBFold 里另抄一份。
06模板是怎么选的
优先级从高到低,上一级命中就不往下走:
| 级 | 依据 | 说明 |
|---|---|---|
| A | 显式声明 | .kbfold.yaml,或目录已成体系(用户当年就按某套结构建的) |
| B | 行业模板 | 软件 / 贸易 / 制造 / 咨询 / 工程项目 / 医疗,共六套 |
| C | 通用-项目阶段 | 八段子项(立项 → 交付 → 复盘) |
| D | 通用-文件类型 | 十二类子项(合同、报告、图纸、影音 …) |
D 是最后一道,也是最通用的:它保证任何一坨文件都能得到「至少按类型分开」的结果,而不是原封不动。
只有 A–D 里 A 和 D 的通用部分随开源版一起发布;B 的六套行业模板属于我们自己的场景积累,不在开源仓库里,需要的话可以自己写——格式完全一样。
07目录规模要跟着文件量伸缩
模板不是固定深度。同一个模板,文件少时只长出几段:
| 文件量 | 建议段数 | 建议目录数 |
|---|---|---|
| < 50 | ≤ 3 段 | ≤ 12 个 |
| 50 – 500 | 4 – 6 段 | 20 – 40 个 |
| > 500 | 8 段全集 | 40 – 60 个 |
少于 50 个文件却建出 60 个空目录,是最典型的「工具比资料还重」。这三个阈值是工程判断,没有用真实样本校准过——如果你觉得不对,改模板就行,不用改代码。
08adopt 优先于 mkdir
如果目录里已经有一个 合同/,它不会另建一个 02-合同 然后让你挑。它会把既有的 合同/ 认作同类容器纳入体系(adopt),不改名、不移动,只在真正的缺口处 mkdir。
所以一次 plan 的产物是两张清单:adopt 了什么、要新建什么,而不是一句「重建为 X」。
09为什么必须记一份 rules.lock.yaml
因为规则是会变的。半年后你想知道「这份合同当初是按什么规则归到 02-合同 的」,只能靠当时的规则快照。rules.lock.yaml 就是这个快照,内容 = 那一批实际生效的规则 + 它的 sha256,与 manifest 里的 rules_locked 字段互相印证。
删了它不影响运行,但会让历史判定变成「无法复现」。