凌晨2点14分,摘要任务跑到第27条时挂了。40条里挂在第27条,报错是超时——免费接口在慢夜里最普通的那种超时。我重启脚本,然后看着它把第1到第26条重新摘要了一遍。
那些摘要已经在磁盘上,已经是对的,已经用token付过钱了。同一批26段文字,我付了两次,因为脚本不记得自己做过。
真正值得问的是:能不能让一个批处理任务在崩溃后接着跑,而不是从头再来,让不稳定的免费额度变成一件无聊的事?能。整套机制就是一张表、一个字段,用来判断某个请求是新的,还是已经答过了。下面是把它搭出来的过程,也包括我哈希写错、照样重复付费的那一版。
先说说我要什么
我维护一份论文摘要阅读清单,目前40条,存成JSON Lines。我想给每条摘要配一段短总结,方便研讨会前扫一眼。单条很便宜,攒成一批就烦人——这正是那种适合丢到夜里跑、跑完再也不用管的活。
目标很明确:跑起来,随时杀掉,再跑一次,每条不重复的摘要只付一次钱。不是“通常一次”,是恰好一次,而且能从计数器上验证。
环境是Linux上的Python 3.11.9,用到sqlite3、hashlib、urllib、http.server,全部来自标准库。不需要虚拟环境,不需要第三方包,前半部分也不需要API密钥。3.10以上都能跑,我实际跑的是3.11.9。
第一步:先把网络挡在门外
拿真实接口测幂等性是个坏主意,因为你分不清是多调了一次还是漏调了一次。所以我写了个很短的桩程序,它统计请求数,并在回复里把计数报出来。
当输出写着summary #3,意思是“这是历史上到达的第三个请求”。这个数字就是我用来核对账本的账本。
工作进程的逻辑是:对每段摘要做哈希,拿哈希去SQLite里查,只有查不到才调接口。如果某条因为进程死掉而缺失,下一次运行只补那一条,别的都不碰。
跑一遍、弄崩、再跑一遍,第二次运行时桩的计数器停在26,直到循环走到缺失的那几条。这就是我要的性质:崩溃的代价是那次在途请求,不是已经做完的那些。
把我第一版搞崩的输入
我最初的哈希是对原始字符串直接做sha256,没有归一化。后来我用PDF提取器重新生成了readings.jsonl,其中一条摘要末尾多了个空格,中间多了个软换行。同一篇论文,字节不同,哈希就不同。工作进程把它当成陌生人,又付了一次钱。
我那句“恰好一次”于是悄悄变成了“两次,只要你的源文件格式动过”。
修法是加一个normalize(),在哈希之前把所有空白折叠掉。它不是通用解——它也会把两段只差空格的、真正不同的字符串当成同一份文档。对摘要来说这没问题。对法律条款,或者任何空白本身带含义的东西,应该哈希一个规范ID,把正文完全排除在键之外。
想复现我那个坏输入,复制一行,在它的text里加上空格和换行再跑。去掉normalize(),你会看到那篇论文多出一行fetched;加上它,就是cached。
免费额度在这里的位置
披露一下:本文是作为MonkeyCode产品推广的一部分准备的。MonkeyCode是一个开源项目,它的推广材料描述了两件对我这种学生级工作进程有意义的事:免费模型访问,量级在千万token额度;以及一个免费服务器选项。我无法从外部核实配额或硬件,也没有审计过仓库,所以这些说法都应当先在你自己账号上测过,再拿它养成习惯。
但有两点观察是诚实的。第一,免费模型访问是把一个40条的摘要任务从预算决策变成周六下午小项目的东西——但也正因为免费,账本更重要而不是更不重要。当请求感觉不要钱,人就容易跳过幂等性、直接全部重跑,而这就是一个便宜习惯在额度某天变了之后变成昂贵习惯的方式。第二,免费服务器选项解决的是合盖问题:我早先那版只能在我笔记本开着的时候跑。一个带持久账本、合上盖子也能活的工作进程,和一个必须你坐那儿盯着才活的工作进程,是两种动物。
想端到端试这个循环,把桩留在仓库里当测试夹具,把ENDPOINT指向你自己的接口,先跑一个小批次。这是我能给的最温和的建议,也确实是对的做事顺序。
它不解决什么
这个账本不是队列。它不做失败重试和退避,不限制并发,遇到一个不断增长的文件会乐呵呵地一直磨下去。它还把每条摘要不压缩地存在SQLite里——40行没问题,4万行就不太行了。
如果你要发送的材料根本不该到达第三方接口,如果你需要响应时间保证,或者有别人指望这个任务今晚必须跑完,就别用这个办法。一个单进程脚本加一个本地数据库,是学生工具,在需求说别的之前,它就该是学生工具。
先预测,再测试
跑任何东西之前,看这两条,告诉我未归一化的版本会对哪一条重复收费:
- {"id": "a", "text": "Attention is all you need."}
- {"id": "b", "text": "Attention is all you need.\n"}
如果你说b,你对了,理由很无聊,而你现在知道为什么normalize()放在哈希前面而不是后面。如果你找到一个夹具,能让我的归一化把两份本该分开的文档合并——比如一段缩进本身就是全部的代码片段——把最小反例发给我。那才是我更想读到的那版文章。
特别声明:以上内容(如有图片或视频亦包括在内)为自媒体平台“网易号”用户上传并发布,本平台仅提供信息存储服务。
Notice: The content above (including the pictures and videos if any) is uploaded and posted by a user of NetEase Hao, which is a social media platform and only provides information storage services.