把技术插图当作代码管理:知识库 SVG 图表的工程化治理Treating Technical Figures as Code: Engineering Governance for SVG Diagrams
写技术笔记时,文字和代码都很容易用 Git 维护,唯独**配图(框图、状态机、电路图、函数曲线)**最容易失控。
回看自己早年写的笔记,配图几乎都逃不过这几个结局:
- 黑盒截图无法迭代:当时随手截了一张白底 PNG 贴进去,半年后发现图里有个符号写错了,或者笔记换成了深色主题,那张刺眼的白底位图既没法改字,也没法改色,只能推倒重画。
- 散落各处的死链:图片名叫做
image-20250312.png,移动一次笔记目录或者清理一次附件文件夹,好几篇笔记同时裂开。 - 一图多用却改一漏十:同一张“闭环控制系统框图”被三篇不同的笔记引用,后来重画了新版,却根本记不清到底有哪些文章嵌了旧图。
为了彻底治好这个顽疾,我在构建知识库时决定不再把技术图表当成“随手粘贴的附件”,而是把每一张可复用的技术插图(Figure)当作一等公民的代码资产来工程化治理。
核心改变一:全局唯一身份(FIG-xxx)与语义所有权
首先,任何需要长期保留的知识图示(框图、时序图、数学曲线、架构拓扑)都不再使用无意义的时间戳文件名,而是分配一个全局唯一的永久编号(如 FIG-001、FIG-042),并遵循统一的命名语法:
99_Assets/knowledge/control-theory/FIG-001_开环控制系统方块图.svg
这里有一个很关键的归属原则:每张图有且仅有一个“语义所有者(Semantic Owner)”,但可以有多个“消费者(Consumers)”。
比如一张拉普拉斯变换收敛域图,哪怕我是为了复习控制工程才画的,它的数学本质属于信号与数学域,那么它的唯一物理存储路径就固定在数学资产目录下。其他领域的笔记直接通过完整相对路径引用它,绝不拷贝第二份文件。
核心改变二:用 registry.yaml 做正交的事实源与消费者反向追踪
光靠文件夹和文件名还不够。为了随时知道“库里一共有多少张图、每张图处于什么状态、到底被哪几篇笔记的哪个章节引用了”,我维护了一个集中式的 registry.yaml 注册表:
- id: FIG-001
title: 开环控制系统方块图
status: verified
owner: control-theory
profile: control-block-diagram
asset_path: 99_Assets/knowledge/control-theory/FIG-001_开环控制系统方块图.svg
verified_sha256: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
consumers:
- note: 04_Knowledge/ControlTheory/反馈控制.md
heading: 开环控制 vs 闭环控制
placement: 开环传递函数推导之前
这个注册表解决了两个最头疼的工程问题:
- 反向依赖追踪:当我要修改或废弃
FIG-001时,不需要全库跑正则猜哪里用到了它,consumers列表直接精确到每一篇引用笔记的标题链。审计脚本(Audit CLI)会定期交叉校验:注册表里声明的消费者,笔记正文里必须真实存在该引用;笔记里出现的每一张FIG-*,也必须在注册表中登记在册。 - 文件完整性校验:一旦图表通过最终视觉验收,它的 SHA256 哈希就会被锁定在
verified_sha256中。如果后续脚本误覆盖了 SVG 文件,审计工具会立刻报警。
核心改变三:六阶段生命周期状态机
一张技术图表从“想到要画”到“真正可靠”,中间其实隔着好几个容易出错的环节。为此我将图表的生命周期拆成了严格的单向状态机:
planned (已分配编号与标题)
→ specified (已锁定数学/工程定义、视觉 Profile 与生成规格)
→ generated (已生成 SVG/图像并落盘,待接入笔记)
→ embedded (已更新所有消费者笔记的引用)
→ verified (视觉、符号、深浅色与哈希全部通过验收)
→ deprecated (已废弃并记录替代者 superseded_by)
最关键的一条回退规则是:只要修改了图表的数学参数、生成脚本或连接关系,状态必须从 verified 强制退回 specified 重新走一遍视觉验收,绝不允许改完生成脚本后不看实际渲染效果就直接当作已完成。
核心改变四:用视觉 Profile 统一全库图表美学
为什么很多人用 Python matplotlib、TikZ 或 AI 工具生成的图拼在一篇笔记里会显得很乱?因为每张图的字号、线宽、箭头样式、底色和配色方案都是临时凑的。
我在 profiles.yaml 中预先冻结了几种标准视觉 Profile(如“控制框图 Profile”、“Bode 频域曲线 Profile”、“数字逻辑电路 Profile”):
- 优先输出纯矢量 SVG:保证任意缩放不糊,且文字仍可被搜索。
- 深浅色对比度与线宽规范:统一主信号线宽、辅助网格虚线透明度、数学变量斜体与正体规范。
- 保留生成源码(
sources/FIG-xxx/):对于由 Python 脚本或绘图代码生成的复杂曲线,生成脚本与 SVG 成品一并归档。半年后想调整一条阻尼比 $\zeta = 0.707$ 的标注位置,只需改一行脚本重新运行即可,再也不用对着一张死位图发愁。
When maintaining a technical knowledge base, prose and code are effortless to version-control with Git—diagrams (block diagrams, state machines, circuit schematics, frequency response plots) are almost always where chaos creeps in.
Looking back at my older notes, nearly every embedded image suffered from one of three fates:
- Uneditable black-box screenshots: A white-background PNG pasted in a hurry six months ago cannot be edited when you spot a typo in a subscript, and it glares painfully when you switch your editor to dark mode.
- Broken links from opaque filenames: Files named
image-20250312.pngbreak across multiple notes the moment you reorganize a folder or clean up attachments. - Silent drift across multiple notes: The same “Closed-Loop Control Block Diagram” gets referenced in three different notes; when you redraw an improved version later, you have no reliable way to know which notes still point to the stale diagram.
To fix this permanently, I stopped treating technical diagrams as casual paste-in attachments and started governing every reusable technical figure (FIG-xxx) as a first-class code asset.
Change 1: Globally Unique IDs (FIG-xxx) and Single Semantic Ownership
Every long-lived technical diagram receives a permanent, globally unique identifier (FIG-001, FIG-042, etc.) and follows a deterministic path grammar:
99_Assets/knowledge/control-theory/FIG-001_开环控制系统方块图.svg
A core ownership invariant applies here: every figure has exactly one Semantic Owner domain, even when shared across multiple Consumer notes.
For example, even if I draw a Laplace Transform region-of-convergence plot while reviewing control engineering, its mathematical essence belongs to signals and mathematics—so its physical file lives under the math asset domain. Notes in other folders reference that canonical relative path rather than duplicating the file.
Change 2: registry.yaml for Reverse Dependency Tracking and Hash Integrity
Filenames alone cannot tell you which notes depend on a figure. To answer “How many figures exist, what state is each in, and which exact note sections embed it?”, I maintain a central registry.yaml:
- id: FIG-001
title: Open-Loop Control System Block Diagram
status: verified
owner: control-theory
profile: control-block-diagram
asset_path: 99_Assets/knowledge/control-theory/FIG-001_开环控制系统方块图.svg
verified_sha256: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
consumers:
- note: 04_Knowledge/ControlTheory/反馈控制.md
heading: 开环控制 vs 闭环控制
placement: Before open-loop transfer function derivation
This registry solves two major engineering headaches:
- Bidirectional dependency auditing: When updating or deprecating
FIG-001, I don’t need to grep the vault and guess. Theconsumerslist points to every note and heading that embeds it. An automated audit CLI cross-checks both directions: every registered consumer must actually embed the figure, and everyFIG-*embed in any note must be registered inregistry.yaml. - Cryptographic integrity: Once a figure passes visual verification, its SHA256 digest is locked in
verified_sha256. Any accidental overwrite immediately triggers an audit failure.
Change 3: A Six-Stage Lifecycle State Machine
A technical diagram goes through several error-prone steps between initial idea and verified publication. I model its lifecycle as a strict one-way state machine:
planned (ID and title assigned)
→ specified (mathematical definition, visual profile, and prompt locked)
→ generated (SVG/asset rendered to disk, awaiting embedding)
→ embedded (all consumer note references wired up)
→ verified (visual inspection, typography, dark-mode contrast, and SHA256 approved)
→ deprecated (retired with a mandatory superseded_by pointer)
Crucially, any change to a figure’s mathematical parameters, generation script, or topology forces its status back from verified to specified, requiring a fresh visual inspection before it can be sealed again.
Change 4: Visual Profiles for Vault-Wide Aesthetic Consistency
Why do notes that mix Python matplotlib plots, TikZ diagrams, and AI-generated illustrations often look visually jarring? Because font sizes, stroke widths, arrowhead geometry, and color palettes are improvised from scratch every time.
In profiles.yaml, I freeze reusable visual profiles (such as control-block-diagram, bode-frequency-plot, and digital-logic-schematic):
- Vector SVG first: Crisp at any zoom level, with searchable text labels.
- Unified stroke weights and dark/light contrast: Standardized primary signal line widths, muted grid opacity, and proper italic/upright math typography.
- Archived generator source (
sources/FIG-xxx/): Whenever a plot is generated by a Python script or declarative code, the source script is archived alongside the output SVG. Adjusting a damping ratio $\zeta = 0.707$ label six months later takes a one-line edit and re-run—never a redraw from scratch.