WRITING
我给 AI 助手的配置画了张图 —— TL Harness 说明书

我给自己的 AI 助手攒了八百多个零件,却说不出哪些还在转。
这篇是 TL Harness 的说明书,也是我做它的过程。它是一个 Mac 上的小程序,打开就把我这套 AI 助手配置扫一遍,画成图。
一个静默断了两天的检查
先说那件让我动手的事。
我那时在清理「没人挂的钩子」——钩子就是每次对话前后自动跑的小检查。我有一份名单,上面标着哪些钩子挂在总开关里、哪些没挂。我照着名单,删掉了一个看起来没人要的。
两天后我才发现,它不是没人要。它是被另一个索引间接挂着的,不在总开关里,所以名单把它算成了孤儿。这两天里,我以为在跑的那道检查一次都没跑过,而且什么都没报。
问题不在我粗心,在于名单答不出这个问题。名单能告诉你「有 36 个钩子」,答不出「删掉这个会断谁」。后面这个问题是关于关系的,而关系不在名单里,在每个文件的正文里——谁调了谁,谁援引了哪条规矩。

所以我写了这个程序。它做的事一句话说得完:把散在几百个文件正文里的引用关系,全部提出来,连成图。
不存缓存,不留中间文件。每次打开就是当场扫一遍磁盘,看到的一定是此刻的真实状态。加了新技能、改了钩子、写了新笔记,按一下重扫就更新。
两张网,故意不合成一张
扫出来的东西自然分成两坨,我让它画成两张互不相干的图。
| 画的是什么 | 回答的问题 | 现在的规模 | |
|---|---|---|---|
| 工具网 | 规矩 / 钩子 / 命令 / 技能 / 脚本这八类零件,和它们之间的调用关系 | 我的工具怎么串起来 | 228 个点,306 条线 |
| 经验网 | 我攒的每一篇笔记,和笔记正文里互相引用的双向链接 | 我的经验怎么串起来 | 677 篇,913 条链 |

最初我确实想合成一张,看起来更气派。画出来才发现,九百多条笔记链会把三百条工具线整个盖住——你想看「改这个命令会波及谁」,眼睛却全被笔记网糊住了。
跨在两张图之间的线(比如一条规矩的正文里点名了某篇笔记)没有丢,只是径向图上找不到落点就跳过不画。点开任何一个点,详情面板里两侧都查得到。
三个视图,分别回答三种问题
程序打开就是这三个,顶栏切换。它们看的是同一份数据,只是给眼睛的方式不同。
| 视图 | 什么时候用 | 给你什么 |
|---|---|---|
| 图 | 「这一坨到底长什么样」「改这个会波及谁」 | 点和线。悬停看名字,点一下看详情,选中之后可以「只看它周围 1 跳」并重新排布 |
| 列表 | 「翻某一层」「搜个关键词」「跳到那个文件」 | 三栏:左边选入口,中间是全部条目加搜索,右边是详情和双向的「引用了 / 被谁引用」 |
| 洞察 | 「哪些是死的」「改哪个最危险」 | 五段结论:连通性、枢纽榜、断掉的链接、重名撞车、孤儿榜 |

图里有三个模式:思维导图(中心发散,看我有哪些东西、哪层多哪层少)、知识网络(同一批点加上依赖线,看跨层依赖长什么样)、经验网络(笔记按归属分扇区)。
三张图的口径不一样,所以切过去之后图上方常驻一行说明,写着这张图在画什么、点的大小和颜色各代表什么。比如前两张图里点越大表示它每次对话都要占掉的篇幅越多,第三张图里点越大表示它连的链越多。
还有一条我踩过的坑值得写进说明书:别让界面替你省字。
我原先把一个开关放在窗口顶栏,Mac 的顶栏会把开关的文字标签直接吃掉,只留一个光秃秃的方块。那个开关按下去会让图上的脚本从 77 个涨到 572 个、整张图被淹没,而它自己一个字都没写。后来我把它挪进图的控制条,带上文字和数量。挪完还不够——窗口一窄,那行控件会把文字压成零宽,又变回一个方块。得明确告诉界面这几个字不许压缩。
现在的判据很简单:新用户不看说明书能不能用。要是你得回来查文档才知道某个按钮干嘛,那是界面的毛病,不是文档没写清。
孤儿必须分四档,一锅端等于没说
这是我做下来觉得最值钱的一条,也是这个程序最有用的功能。
第一版我算出「有 1018 个东西零度」,然后盯着这个数发了半天呆——它什么都没告诉我。因为「没人连它」有四种完全不同的含义,混在一起就互相稀释。
| 档 | 定义 | 现测 | 该不该管 |
|---|---|---|---|
| 真孤儿 | 没引用别人,也没人引用它 | 171 | 要看。但图上死不等于真死,它可能还在被我手敲、被定时任务调 |
| 只出不入 | 引用了别人,没人引用它 | 210 | 正常。命令和技能天生就是链条起点 |
| 只入不出 | 被人引用,自己不引用别人 | 142 | 正常。脚本和笔记天生就是链条终点 |
| 零引用脚本 | 工具目录下没有任何配置文件提到它 | 495 | 单列。混进孤儿会把上面 171 个真问题整个淹掉 |

分完档还有个坑:清单不能截断。
我一开始把孤儿榜放在洞察面板里,只摆得下前 40 条。「有 171 个孤儿」和「能把 171 个挨个看完」是两件事——截断的榜等于把剩下 131 个藏起来。而且排序按名字字母序的话,前 40 条会被首字母最小的那一层整层占满,别的层一条都露不出来。
现在这六份名册(上面四档,加上断掉的链接、重名撞车)各自是侧栏里的独立入口,全量、可搜、可点、可跳详情。
它抓到了什么
第一件:263 个文件名有两种写法。
笔记之间互相引用是靠名字找人的。我那时看到 182 条链接指向不存在的目标,第一反应是「我欠了 182 篇没写」。真去查才发现,其中 142 条的目标是存在的,只是磁盘上的文件名用下划线、正文里的引用用连字符,两边对不上。统一命名之后,142 条自己消失了,剩下的才是真没写。
这条的教训是:报「不存在」之前先归一化。归一化之前算出来的差集,量的是我自己的口径混乱,不是发现。

第二件:677 篇笔记里有 419 篇的类型标错了。
每篇笔记开头会标一个类型,用来在图上着色。有一篇的开头写了个稍微不同的字段名,我的正则把它的尾巴也读了进去,于是四百多篇被标成了一个根本不存在的类型。
要命的是所有计数都是自洽的:加起来对得上,前后一致,程序不报任何错。我是截图看见图上一大片灰色才发现的。
这就是为什么自洽不等于正确。 一条路径算出来的数,拿它自己去验它自己,永远是绿的。必须换一条独立的路径重新算一遍再比。

同一轮里,独立复算还抓出另外两个:跨归属的链接在去重之前统计(两个数差 2,但集合完全一致,只错一个数);还有一段脚本里的方括号被当成了笔记引用,凭空造出 81 条假的断链。
所以现在程序自带一道自检,构建时跑,不过就拒绝安装。它验五件事:图上点能不能点中、经验网能不能点中、只看周围 1 跳的时候图有没有真的变小、重名的条目编号还唯一不唯一、以及界面上那六份名册的数和命令行报的数对不对得上。
最后这条是有来历的:侧栏曾经显示 698 篇,命令行说 675 篇——断链的占位被算进笔记层了,两边各记一本账。修完之后我把修复撤掉验了一次,确认它真的会亮红,才算数。
后续怎么用
日常就三条命令:
open -a "TL Harness" # 看图
cd ~/Apps/mac/tl-harness && ./build.sh # 改完代码:构建 + 装机
~/Dev/tools/dev/lib/tools/report/harness-graph --stats # 不想开窗口时
命令行版和程序用的是同一份扫描代码,所以不会各说各话:程序里看到「真孤儿 171」,命令行也是 171。一个给眼睛,一个给管道。
| 我想 | 用哪个 |
|---|---|
| 看清楚这一坨长什么样 | 程序 · 图 |
| 知道改某个命令会波及谁 | 程序 · 图,选中之后聚焦 1 跳 |
| 翻某一层、搜关键词、跳到文件 | 程序 · 列表 |
| 要孤儿或断链的清单,边看边点 | 程序 · 洞察 |
| 要同样的清单,但拿去存文件、喂给别的脚本 | 命令行 |
| 确认改完配置有没有变坏 | 命令行的统计摘要,比改动前后的计数 |
统计摘要长这样,三行:
nodes rule 11 · hook 36 · check 12 · command 18 · skill 40
· agent 2 · engine 77 · memory 32 · edges 306
笔记网 note 677 · 双链 913 · 死链 27(唯一靶子 24) · 同名撞车 6
连通性 真孤儿 171 · 只出不入 210 · 只入不出 142 · 零引用脚本 495
几条边界,用之前先知道:
「零度」不等于「没人用」。有两个已知的口径盲区:技能自带的脚本按定义不算「脚本层」,所以它调自己的文件仍然记 0 度;脚本层只覆盖一个目录,指向别处的调用看不见。看孤儿榜的时候要记得这两条。
扫不到就报错,不画一张「很干净」的空图。任何一层的扫描目录不存在,程序会挂一条红横幅、命令行会非零退出。但「目录在、里面 0 个」是合法状态——有一层我全退役了就是这样,把这两种情况混为一谈,会让任何一次正常清理都炸掉整张图。
这篇里的所有数字都会漂。 我写这段的时候笔记是 677 篇,两小时前还是 675——因为我中间写了两篇新的。所以对拍的正确用法是比改动前后,不是拿今天的数去对这篇文章里印的数。发现对不上,先想想是不是自己刚增删了什么。
最后一条,也是我认为最要紧的:这张图能让数字变好看,但守不住它。
断链从 182 降到 26、孤儿从 183 降到 149,是我照着图去清磁盘清出来的,不是代码改出来的。没有东西把关的话,它一天长回来一点,半年后又是老样子。所以真正的闸门不在这个程序里,在写笔记那一刻:每次落盘时检查文件名规范、引用指得到、至少挂一条链,四条各带修复命令。
画图的负责让你看见,闸门负责不让它长回来。 只有前者的话,你会得到一张越来越难看的图,和一个越来越不想打开它的自己。