两次智能体执行,最后都发布了一篇文章。从外面看,它们完全一样:同一条流水线,同一天,同样的上线结果。从里面看,一次用了 25 分钟,另一次用了 65 分钟,差别既不在模型,也不在选题,而在三条被权限门拦下的命令,以及临时想办法绕过去所花的时间。
结果会掩盖过程。所以我们每执行一次就往 JSONL 文件里写一行,每周读一次这个文件。这不是生产环境可观测性,也不是仪表盘:它只是一段能用 grep 搜的文本,用数字而不是凭记忆回答“什么在重复”。
什么是 JSONL 执行日志?
它是一个文本文件,每行一个 JSON 对象,每次执行写一行,永远追加在末尾。JSON Lines 按行读取:某一行坏了不会拖垮整个文件,每次新的执行都是一次 append,从不重写。
格式没有写入规则重要。让这个日志真正有用的是它 append-only(只追加):今天的行不能改昨天的行。如果记录可以被重写,它就不再是历史,而变成了报告,而报告总是看起来很漂亮。
一行里放哪些字段?
只放会影响下一次执行决策的内容。我们的日志有八个字段,每个回答一个不同的问题:
| 字段 | 用途 |
|---|---|
data | 执行日期,ISO 8601 格式。这是按周对比的基础。 |
trilha | 任务类型。在失衡变成问题之前就能看出来。 |
tema | 一句话说明主题。用来查重,也用来以后回看历史。 |
origem | 主题从哪来:队列、发布、issue、搜索或内部材料。这个字段衡量计划有没有起作用。 |
status | published、draft 或 blocked。没有它,“成功了”就只是观点。 |
nota | 简短自评,标准必须固定。没有标准,就会变成自我表扬。 |
urls | 结果在哪里,每种语言一个地址。把执行和交付闭环。 |
atritos | 一组描述“哪里耗了时间”的句子。这是唯一没人能凭记忆填好的字段,所以在执行过程中写。 |
格式如下,值为示例:
{"data": "2026-11-05", "trilha": "D", "tema": "log de execucao em JSONL",
"origem": "fila", "status": "published", "nota": "A",
"urls": {"pt": "https://verboo.ai/pt/blog/jsonl-execution-log",
"en": "https://verboo.ai/en/blog/jsonl-execution-log",
"zh": "https://verboo.ai/zh/blog/jsonl-execution-log"},
"tempo_min": 40,
"atritos": ["image generator returned 503 on the first call"]}
怎么记录才不依赖记忆?
用一个函数,以 append 模式打开文件,把整行序列化写进去。在代码里做这件事,而不是结束时手写,是 123 条记录和 3 条被遗忘记录之间的差别。
import json
from datetime import date
def registrar(caminho, **campos):
linha = {"data": date.today().isoformat(), **campos}
with open(caminho, "a", encoding="utf-8") as f:
f.write(json.dumps(linha, ensure_ascii=False) + "\n")
registrar(
"log-execucoes.jsonl",
trilha="D",
tema="JSONL execution log",
status="published",
tempo_min=40,
atritos=["image generator returned 503 on the first call"],
)
写完以后校验整个文件。jq 按流读取 JSON 值,所以 -s(slurp)只有在每一行都合法时才会结束,顺便还能数出行数:
jq -s 'length' log-execucoes.jsonl
# 123
如果有坏行,命令会失败并指出位置。总比几周后才发现分析只读了 123 条里的 118 条、还把它当成总数要好。
怎么分析,不用做成表格软件?
用一个小脚本,只用标准库。目的不是统计一切,而是把反复出现的摩擦和偶发情况分开,因为只有反复出现的才值得改流程。
import json, re, sys
ATRITOS = {
"shell 被拦": r"bloqueou|pediu aprovacao|heredoc",
"队列无条目": r"sem item|esgotad",
"外部搜索": r"Reddit|Composio",
"图像模型不可用": r"503|alta demanda",
}
linhas = [json.loads(l) for l in open(sys.argv[1], encoding="utf-8") if l.strip()]
total = len(linhas)
print(f"{total} 次执行")
for nome, padrao in ATRITOS.items():
n = sum(1 for l in linhas if re.search(padrao, " ".join(l.get("atritos", []))))
print(f"{nome} {n} {n/total:.0%}")
正则匹配的是我们自己日志里的葡萄牙语原文,所以标签翻译了,匹配规则没有。在我们的文件上运行,输出是这样的:
123 次执行
shell 被拦 30 24%
队列无条目 43 35%
外部搜索 34 28%
图像模型不可用 11 9%
这个数字是下限,不是上限。脚本在摩擦的自由文本里找关键词,所以一次写成“用脚本绕过去了”的执行会进总账,但不会进 shell 那一项。只搜 contornad 能找到 50 次执行。自由文本会少算:它适合发现规律,不适合审计。
123 次执行暴露了什么?
瓶颈不在我们盯着的地方。2026 年 9 月 3 日到 11 月 5 日之间共有 123 次执行,122 次发布,1 次阻塞,115 次记录了执行时间,合计 59 小时,中位数是每次 30 分钟。最常见的摩擦是流程性的,不是模型问题。
| 在 123 次执行中的指标 | 次数 | 说明 |
|---|---|---|
| 任务队列没有待办条目 | 43(35%) | 决定节奏的是选题,不是写作。 |
| 外部搜索帮了倒忙(Reddit、Composio) | 34(28%) | 输入不稳定,必须回到原始来源。 |
| shell 命令被权限门拦下 | 30(24%) | 任务还没开始就先手工绕过。 |
| 图像生成器不可用(503) | 11(9%) | 降级顺序要写下来,不能临场发挥。 |
还有一个结构上的发现:有 4 行把整段文字写进了本来只该是 A、B、C 的 nota 字段,最长的一条有 1,761 个字符。这不是分析出错,而是字段没有类型的症状。格式允许自由文本,就会有人写自由文本,按评级统计会在没有提示的情况下出错。
队列没有条目:瓶颈是选题
35% 的执行里,任务队列没有待办条目,主题是当场找的。origem 字段就是为这个存在的:它显示那个月靠什么来源撑住的。没有这个数字,结论是“我们没东西可写”,这没法行动。有了它,结论是“有两条线的队列三周就会见底”,这就能行动。
shell 被拦:复合命令是主因
24% 的执行里,有命令被权限门拦住,原因几乎总是一样:带引号和花括号的 heredoc、用 && 串联、以及重定向到项目目录以外。这些都不是智能体失败,而是权限门在正常工作,绕法也总是一样:把脚本写进文件,只运行一条命令。
外部搜索:是输入,不是事实
28% 的执行里,社区搜索帮的倒忙比帮的忙多:结果噪音、服务不可用,或者来源没有覆盖这个题目。由此得出的一条规则适用于任何用搜索的流水线:搜索结果只是线索,不是事实。数字只有在官方或正规媒体确认之后才能写进正文,如果确认不了,就把这个摩擦记录下来。
图像生成器不可用
9% 的执行里,生成器因为高负载返回 503。避免返工的不是默默重试,而是把降级顺序写下来:先用主模型,再用替代模型,并把原因记进摩擦字段。临场降级会导致每次执行出来的结果都不一样。
怎么把摩擦变成规则?
用三级台阶,每一级都不用开会:
- 第一次:写进
atritos字段,继续干活。偶发情况不变成规则。 - 第二次:变成流程里写下来的规则,带上避免问题的命令或顺序。
- 第三次:变成脚本里的自动检查。依赖有人记得的规则,不算规则。
有一个案例走完了三级:图表生成器会把文字截在卡片边缘。六天里发生了四次,每次都手工修,直到变成画之前先测量宽度的函数。这个案例连同修复代码写在这里。
维护这样一份日志要多少成本?
每次执行一行。记录 123 次执行、59 小时的这个文件只有 270 KB,任何编辑器都能打开,jq 不到一秒就能回答。真正的成本是在关闭会话前把它写下来的纪律,以及每周大约 10 分钟的阅读。
让日志存在 123 次而不是 3 次的,是它属于流程里的必做步骤。在我们的做法里,智能体遵循的 skill 以写这一行结束。如果它是可选的,忙碌的一天总会赢。
如果你已经有智能体在连续执行任务,下一步很便宜:在结束执行前写下那一行,然后每周跑一次上面的脚本。在 Verboo Code 里,智能体用无限 token 执行这次分析和下一次执行,所以重复读一遍和只跑一次的成本一样。



