我们的图表生成器开头的注释说明了它存在的原因:"AI 生成的图表会写错文字,而且每次调用画出来的都不一样,没办法保证 pt、en、zh 三个版本是同一张图。"我们把画图的工作从 AI 换成了代码,就是为了解决这个问题。可代码也会有 bug,而我们的这个 bug 至少 6 天里都没有被真正修好:本周发布的四张图表都出现了备注文字冲出卡片边框的问题,每次都是手动改。
为什么博客的图表不是用 AI 生成的?
因为生成式 AI 每次调用画出来的都不一样。一篇文章要发布 pt、en、zh 三个语言版本,每个版本都需要完全相同的几何结构,只有文字不同。AI 图像生成器没办法保证这一点:有时候颜色变了,有时候单词拼错了,有时候某一边多画了一个框。解决办法是把画图这件事从 AI 手里拿出来,交给代码:一个 Python 脚本(render.py,用 Pillow 库)读取一个 JSON 格式的规格文件,在三个语言里画出同样的结构,只替换文字。
这个生成器具体是怎么运作的?
每张图表对应一个 specs/<名字>.json 文件,里面有一个布局字段(fluxo、comparativo 或 capa_vs),以及一个 locales 字段,里面放着 pt、en、zh 三份内容。不需要翻译的文字(命令、参数、变量名)放在 spec 的根节点;需要翻译的放进 locales。运行方式是这样的:
python3 render.py specs/diagram-name.json pt en zh
out/ 目录下会生成每个语言各一张 PNG,几何结构和颜色完全相同,只有文字不一样。
过去 6 天里,至少出现了 4 次的是什么 bug?
图表里的每一段文字,都是直接调用 d.text() 画在固定位置上,完全没有做宽度检查。每个步骤卡片的宽度,是用可用空间除以步骤数量算出来的;如果那个步骤的备注文字比这个宽度还长,它就会一直画到卡片边框外面去。直到文字长到一定程度才会被发现,而且不止发生了一次:
| 日期 | 文章 | 溢出的位置 |
|---|---|---|
| 2026-09-12 | Codex 在等你确认时偷偷烧 token | 第 1、2 步的备注 |
| 2026-09-16 | Verboo Code 的免费 token 用完了 | 第 1、2 步的备注 |
| 2026-09-16 | 为什么 Claude Code 的用量会自己重置 | 分支标签,右边被截断 |
| 2026-09-17 | Verboo Code 的回复语言设置 | 步骤备注 |
其中两次的记录里已经写着"和之前的文章一样的 bug":真实发生的次数比这 4 行还多,这里列出的只是执行日志里恰好留下了日期和文章对应关系的那几次。每次的修法都一样:打开备注文字,凭肉眼数字符数,改短,重新生成,然后祈祷英文版和中文版也刚好能放得下,因为不同语言表达同一个意思需要的文字长度完全不同。
我们是怎么修的,才能不再有第五次?
生成器现在不再靠肉眼判断,而是先测量再绘制。一个新函数 ajusta(),用 d.textlength()(也就是之前在对比类封面里用来把"vs"居中的那个 Pillow 函数),以像素为单位判断文字是否能放进可用宽度。如果放不下,就用二分查找找到仍然能放下的最长文字片段,末尾补上省略号:
def ajusta(s_, fo, maxw):
if maxw <= 0 or d.textlength(s_, font=fo) <= maxw * S:
return s_
lo, hi = 0, len(s_)
while lo < hi:
mid = (lo + hi + 1) // 2
if d.textlength(s_[:mid].rstrip() + "…", font=fo) <= maxw * S:
lo = mid
else:
hi = mid - 1
return (s_[:lo].rstrip() + "…") if lo > 0 else "…"
这个函数被用在了四个曾经出问题的地方:每个步骤的标题和备注、错误分支的标签,以及分支里的每一行备注。现在不用再猜每种语言能放下多少个字符:代码会分别测量 pt、en、zh,各自用各自的字体,各自决定要不要截断。
这个修复没有解决的问题:文字还是只占一行,被截断,从来不会换到第二行。一条太长的备注会丢失信息,而不是让卡片占用更多的垂直空间。它解决的是真正的问题(文字冲出卡片边框),不是文字排版系统的最终版本。
同样的优先级也是 Verboo Code 的原则:测量,而不是赌一把。开源模型跑在专属 GPU 上,token 不设上限,你可以按需要的次数一直重试,直到结果对为止,而不是测到一半才发现额度用完了。



