AI DAILY / 2026-09-10
Astra 编码体验:我们为何又在原地打转?
Astra for Coding: Why Are We Doing This Again?
中文翻译 · AI 生成,仅供学习交流
Astra 编程,我们到底为什么又在做这件事?
我越来越确信,整个 AI 工程就是 Neijuan(内卷,字面意思是「向内卷曲」)。在中国这个词描述的是一种系统,不断要求人们投入更多、加剧竞争,产出却没有真正提高。它在西方有时会以 996 那样的荒唐形式冒出来(译注:996 指早 9 点到晚 9 点、一周 6 天的工作制)。Neijuan 对应的英文是「Involution」,出自《Agricultural Involution》(《农业内卷化》)一书。农业内卷化讲的是这样一种现象,不断加大劳动投入,单位面积产出提高了,但人均产出却没变。这正是我现在对 AI 的感受。
由此就说到了 GPT 6 Astra。无论从哪方面看,Astra 都是一个极其惊艳的模型。我对它几乎挑不出毛病。它在 computer use(计算机操控)方面能力惊人,能理解图像和复杂主题,并且对「把事情做完」有一种不知疲倦的执着。它绝对让人印象深刻,这类模型终将以某种形式改变世界。但至少眼下,我还不知道怎么拿它来做真正的软件工程。既然这件事在 Twitter 上引来不少关注,我想不如把自己的想法梳理一下,顺便让大家看看它究竟会写出什么样的代码。
我的 Slop 工厂
「Armin,你应该去开一家软件工厂!」这话我已经听过好几次了。我想干脆趁它发布,在周末跑一家小软件工厂庆祝一下。要是大家都在用 slop 做 3D 游戏,那我总该拿它做点有用的东西。
我的软件工厂是故意这样设计的,让模型完全自主决定工作流的具体走法。它可以自由管理自己的上下文,还能在 agent-notes 文件夹里维护自己的记录。之后它再分派出 subagent(子代理)去干活。
目标是什么?设想一下,如果我们有一个带 virtual threads(虚拟线程)和 lexical scoping(词法作用域)的 Python,会怎样?
结果,我把相当于一整次重置额度的 ChatGPT tokens 全烧光了,看起来大概是 40 亿 tokens。35 个小时过去,工厂交出的东西毫无价值,也没教会我任何关于如何把工厂开得更好的东西。但它产出了大量代码和输入提示,所以我还是有东西可以研究。
而且它表现出一些我在 Sol 和更早的 OpenAI 模型上从没见过的行为 [1]。后来我在用 Astra 做常规编程时也遇到了同样的问题,所以并不是仅仅因为「工厂」才出现的现象。我怀疑训练过程里有什么东西「出了岔子」。模型在长程任务上成功会获得丰厚奖励,但据推测对「shitty code」(烂代码)的惩罚几乎为零。结果就是 Astra 极其擅长产出 3D 类的东西,而且能持续跑很久,一路上还能自己冒出新的工作。我让它对我的扫地机器人做了不少逆向工程,做得还挺让人佩服。所以它确实很酷!
Codegolf 风格的工具调用
我对 Astra 的第一个不满,来自它在做工具调用(tool calls)时写出的代码。Codex 越来越多地依赖「直接上 bash」来完成越来越多的操作。最近几个版本里,原版的 Codex harness 就只用 sed 之类的工具来读文件。你平时看不到这些命令,是因为 Codex 会解析 bash 命令,认出以后就帮你藏起来。
但 Astra …… 真的超爱 Python?这倒也不奇怪,因为即便是更早的 OpenAI 模型,有时候也会倾向于用即兴生成的 Python 代码来读写和处理文件,只是 Astra 这样做对我来说实在过头了。
这里要先说一个重要免责声明,这个项目本身就非常 meta(译注:meta 在此指自指,作者本人恰好就是 CPython 解释器的贡献者),因为我参与过 CPython 解释器的工作。但我可以向你保证,即便是在 Pi 里写 TypeScript 代码时,我也看到过这个模型写出奇奇怪怪的 Python。不过最有力的「怪代码」证据,来自周末那次让它在完全无人监督的「slop 工厂」里跑出来的结果。
它写 Python 这件事本身并不有趣,有趣的是它写出来的 Python 是什么风格。我整理了一些输出,供你随便扫一眼。
用 Python 字符串拼接来改 C 代码
在 Codex harness 里,我多次发现子代理完全放弃了 patch 工具,转而靠 Python 做手工字符串操作,
python3 - << 'PY'
from pathlib import Path
p = Path ( 'Include/internal/pycore_intrinsics.h' );
s = p . read_text () . replace ( '#define MAX_INTRINSIC_1 14' , '#define INTRINSIC_RETAIN_ANNOTATION_CELLS 15 \n\n #define MAX_INTRINSIC_1 15' );
p . write_text ( s )
p = Path ( 'Python/intrinsics.c' );
s = p . read_text ();
idx = s . index ( '#define INTRINSIC_FUNC_ENTRY' );
s = s [: idx ] + '''/* Hold every old cell until the compiler has published the entire site's new capture. A replaced cell's finalizer may reenter module __annotate__. */
static PyObject *
retain_annotation_cells(PyThreadState *tstate, PyObject *holders) {
if (!PyTuple_CheckExact(holders)) {
PyErr_SetString(PyExc_TypeError, "annotation holders must be a tuple");
return NULL;
}
Py_ssize_t size = PyTuple_GET_SIZE(holders);
PyObject *previous = PyTuple_New(size);
if (previous == NULL)
return NULL;
for (Py_ssize_t i = 0; i < size; i++) {
PyObject *holder = PyTuple_GET_ITEM(holders, i);
if (!PyCell_Check(holder)) {
Py_DECREF(previous);
PyErr_SetString(PyExc_TypeError, "annotation holder must be a cell");
return NULL;
}
PyObject *cell = PyCell_Get(holder);
PyTuple_SET_ITEM(previous, i, cell == NULL ? Py_NewRef(Py_None) : cell);
}
return previous;
}
''' + s [ idx :];
s = s . replace ( ' INTRINSIC_FUNC_ENTRY(INTRINSIC_AWAIT_BLOCK, await_block)' , ' INTRINSIC_FUNC_ENTRY(INTRINSIC_AWAIT_BLOCK, await_block) \n INTRINSIC_F