Astra在编码中的应用:我们为何又要做这件事?

Armin Ronacher 新闻

摘要

本文批评了AI模型GPT 6 Astra在软件工程任务中生成低质量代码的行为,尽管其能力令人印象深刻,但暗示AI训练可能奖励任务完成而非输出质量。

<p>我越来越确信,所有AI工程都是<a href="https://en.wikipedia.org/wiki/Neijuan">Neijuan</a>(内卷,意指向内卷曲)。在中国,它描述了一个要求付出更多努力和竞争而不提高产出的系统。在西方,它有时表现为<a href="/2025/9/4/996/">996工作制的荒谬</a>。Neijuan的英文术语是“Involution”,源自书籍<a href="https://en.wikipedia.org/wiki/Agricultural_Involution">《农业内卷化》</a>。农业内卷化描述了农业集约化,即提高每平方米生产力而人均生产力保持不变。</p> <p>这就是我对AI当前的感受。</p> <p>这让我想到了GPT 6 Astra。Astra无疑是一个令人难以置信的模型。对此我实在没什么可批评的。它在计算机使用方面表现出色,能理解图像和复杂主题,并且在完成任务方面坚持不懈。它绝对令人印象深刻;这些类型的模型将以某种形式改变世界。</p> <p>但至少目前,我不知道如何将其用于实际的软件工程。由于这在Twitter上引起了相当多的关注,我想我可能会总结一下我的想法,并分享一下这种东西能产生什么样的代码。</p> <h2>我的垃圾工厂</h2> <p>“Armin,你应该开一个软件工厂!”我听过好几次了,所以我可能想通过在周末经营一个小型软件工厂来庆祝它的发布。如果每个人都在构建垃圾3D游戏,那么我应该用它做点有用的事。我的软件工厂特意设置为让模型完全决定工作流程的方式。它可以自由管理自己的上下文,并在<code>agent-notes</code>文件夹中维护自己的记录。然后它派出子代理来处理事情。目标?如果我们有一个支持<a href="/2025/7/26/virtual-threads/">虚拟线程</a>和词法作用域的Python会怎样。而且,我为此消耗了一整次重置的ChatGPT令牌,大约是40亿个令牌。35小时后,这个工厂没有交付任何有价值的东西,也没有教会我如何更好地运营它。</p> <p>但它产生了大量的代码和输入提示,所以我能够研究一些东西。而且,它显示出我与Sol和早期OpenAI模型<sup class="footnote-ref" id="fnref-1"><a href="#fn-1">1</a></sup>不习惯的行为。我后来在与Astra的常规编程中也遇到了相同的问题,所以这不仅仅是工厂的结果。</p> <p>我认为我怀疑训练过程中有些“错误”。模型因在长期任务中成功而获得很大奖励,但可能对于“糟糕的代码”几乎没有惩罚。明显的结果是,Astra在<a href="https://developers.openai.com/blog/how-to-build-games-with-astra">制作3D内容</a>方面表现出色,并且可以持续很长时间,在过程中想出自己的工作。我让它以相当令人印象深刻的方式对我的机器人吸尘器进行了大量逆向工程。所以它绝对很酷!</p> <h2>代码高尔夫工具调用</h2> <p>我对Astra的第一个问题来自它用于工具调用的代码类型。Codex越来越多地依赖“纯bash”来执行越来越多的操作。在几个版本中,原始Codex工具只是使用<code>sed</code>和其他工具来读取文件。你通常看不到它们,因为Codex<a href="https://github.com/openai/codex/blob/f1aac1e885f676a1129f2da0c46a3dba86392fc6/codex-rs/shell-command/src/parse_command.rs#L2290-L2504">解析bash命令</a>并在识别时隐藏它们。但Astra……真的很喜欢Python?这并不奇怪,因为即使旧的OpenAI模型有时也有倾向使用按需Python代码来读取和操作文件,但对我来说,Astra做得真的太过分了。</p> <p>现在有一个重要的免责声明:这个项目非常元,因为我曾在CPython解释器上工作。但我可以向你保证,即使在Pi中的TypeScript代码中,我也见过这个模型做奇怪的Python事情。但我在周末让这个东西在<em>零</em>监督下运行我的垃圾工厂时,有最多的奇怪代码证据。</p> <p>它写Python并不有趣;有趣的是Python的类型,我收集了一些输出供你浏览。</p> <details><summary>Python字符串拼接以编辑C代码</summary> <p>在Codex工具中,我发现多个案例中子代理完全依赖Python手动字符串操作,而不是使用补丁工具。</p> <div class="highlight"><pre><span></span><span class="n">python3</span> <span class="o">-</span> <span class="o">&lt;&lt;</span><span class="s1">&#39;PY&#39;</span> <span class="kn">from</span><span class="w"> </span><span class="nn">pathlib</span><span class="w"> </span><span class="kn">import</span> <span class="n">Path</span> <span class="n">p</span><span class="o">=</span><span class="n">Path</span><span class="p">(</span><span class="s1">&#39;Include/internal/pycore_intrinsics.h&#39;</span><span class="p">);</span><span class="n">s</span><span class="o">=</span><span class="n">p</span><span class="o">.</span><span class="n">read_text</span><span class="p">()</span><span class="o">.</span><span class="n">replace</span><span class="p">(</span><span class="s1">&#39;#define MAX_INTRINSIC_1 14&#39;</span><span class="p">,</span><span class="s1">&#39;#define INTRINSIC_RETAIN_ANNOTATION_CELLS 15</span><span class="se">\n\n</span><span class="s1">#define MAX_INTRINSIC_1 15&#39;</span><span class="p">);</span><span class="n">p</span><span class="o">.</span><span class="n">write_text</span><span class="p">(</span><span class="n">s</span><span class="p">) </span><span class="n">p</span><span class="o">=</span><span class="n">Path</span><span class="p">(</span><span class="s1">&#39;Python/intrinsics.c&#39;</span><span class="p">);</span><span class="n">s</span><span class="o">=</span><span class="n">p</span><span class="o">.</span><span class="n">read_text</span><span class="p">();</span><span class="n">idx</span><span class="o">=</span><span class="n">s</span><span class="o">.</span><span class="n">index</span><span class="p">(</span><span class="s1">&#39;#define INTRINSIC_FUNC_ENTRY&#39;</span><span class="p">);</span><span class="n">s</span><span class="o">=</span><span class="n">s</span><span class="p">[:</span><span class="n">idx</span><span class="p">]</span><span class="o">+</span><span class="s1">&#39;&#39;&#39;/* Hold every old cell until the compiler has published the entire site&#39;s new capture. A replaced cell&#39;s finalizer may reenter module __annotate__. */ static PyObject * retain_annotation_cells(PyThreadState *tstate, PyObject *holders) { if (!PyTuple_CheckExact(holders)) { PyErr_SetString(PyExc_TypeError, &quot;annotation holders must be a tuple&quot;); 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 &lt; size; i++) { PyObject *holder = PyTuple_GET_ITEM(holders, i); if (!PyCell_Check(holder)) { Py_DECREF(previous);</span> </pre></div> </details>
查看原文
查看缓存全文

缓存时间: 2026/09/10 02:36

# Astra 编程体验:我们为何再试一次? 来源:https://lucumr.pocoo.org/2026/9/7/astra-why/ 撰写于 2026 年 9 月 7 日 我越来越确信,整个 AI 工程领域都陷入了“内卷”([Neijuan](https://en.wikipedia.org/wiki/Neijuan))(*内卷,意指向内卷曲*)。在中国,它描述了一种要求付出更多努力与竞争,却未提升产出的系统。这种现象有时在西方表现为 [996 工作制](https://lucumr.pocoo.org/2025/9/4/996/) 的荒谬现象。内卷的英文术语是“Involution”,源自《[Agricultural Involution](https://en.wikipedia.org/wiki/Agricultural_Involution)》一书。农业内卷描述了农业集约化——每平方米生产力提高,但人均生产力保持不变的现象。这正是我对当前 AI 发展的感受。 由此我想到 GPT 6 Astra。Astra 毫无疑问是个令人印象深刻的模型。这一点我几乎无可指摘。它擅长使用计算机、理解图像和复杂主题,并且不懈追求任务完成。绝对令人惊叹——这类模型将以某种形式改变世界。但至少目前,我不知道如何将其应用于实际软件工程。由于这个观点在推特上引起不少关注,我决定总结一下我的想法,并分享一下它生成的代码样例。 ## 我的“代码工厂”体验 “阿尔明,你应该开个软件工厂!”这句话我已听过几次,因此我决定在周末开一个小型软件工厂来庆祝它的发布。如果所有人都在构建简陋的 3D 游戏,我也该用它做点有用的事。 我的软件工厂特意让模型自主决定工作流程。它可以自由管理自己的上下文,并在 `agent-notes` 文件夹中维护自己的记录。然后它会分派出子代理来处理任务。目标是什么?假如我们有一个支持[虚拟线程](https://lucumr.pocoo.org/2025/7/26/virtual-threads/) 和词法作用域的 Python。 结果呢,我为此消耗了 ChatGPT 一整次重置配额的 token,大约 40 亿 token。35 小时后,工厂没有交付任何有价值的东西,也没教我如何更好地运营它。但它生成了大量代码和输入提示,让我得以研究。它表现出的行为与我之前用 Sol 及早期 OpenAI 模型时的习惯不同(参见注释 [1](https://lucumr.pocoo.org/2026/9/7/astra-why/#fn-1))。 此后我在使用 Astra 进行常规编程时也遇到了相同问题,所以这并非仅由工厂模式导致。我怀疑是训练过程出了“问题”。模型因成功完成长期任务而受到奖励,但对“糟糕代码”的惩罚似乎很少。结果就是,Astra 非常擅长[生成 3D 内容](https://developers.openai.com/blog/how-to-build-games-with-astra),并且可以持续工作很长时间,自行生成任务。我让它逆向工程了我的扫地机器人,其方式相当令人印象深刻。所以它确实很酷! ## 代码高尔夫式工具调用 Astra 的第一个问题体现在它用于工具调用的代码类型上。Codex 逐渐依赖“纯 bash”来执行越来越多的操作。几个版本以来,原始 Codex 框架已使用 `sed` 等工具读取文件。你通常看不到这些命令,因为 Codex 会解析 bash 命令([相关代码](https://github.com/openai/codex/blob/f1aac1e885f676a1129f2da0c46a3dba86392fc6/codex-rs/shell-command/src/parse_command.rs#L2290-L2504))并在识别时隐藏它们。 但 Astra ……特别喜欢 Python?这并不奇怪,因为即使是旧版 OpenAI 模型也偶尔会使用按需生成的 Python 代码来读取和操作文件,但 Astra 的使用频率对我来说实在过高。 **重要说明**:由于我在此项目中*直接参与* CPython 解释器的开发,所以这个案例非常“元”。但我可以确保,即使在 TypeScript 代码中我也见过模型执行奇怪的 Python 操作。不过最明显的例子还是周末我的“代码工厂”在*完全无人监督*下运行时产生的。 写出 Python 代码并不稀奇;有趣的是 Python 的使用方式。以下是我收集的一些输出示例供您浏览。 ### Python 字符串拼接修改 C 代码 在 Codex 框架中,我发现多处子代理完全使用 Python 手动字符串操作,而非使用补丁工具。 ```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_FUNC_ENTRY(INTRINSIC_RETAIN_ANNOTATION_CELLS, retain_annotation_cells)');p.write_text(s) p=Path('Python/codegen.c');s=p.read_text();idx=s.index('static int\ncodegen_annassign(');s=s[:idx]+'''static int codegen_retain_annotation_cells(compiler *c, location loc, PyObject *captures) { Py_ssize_t pos = 0; PyObject *binding, *holder; while (PyDict_Next(captures, &pos, &binding, &holder)) { ADDOP_NAME(c, loc, LOAD_CLOSURE, holder, cellvars); } ADDOP_I(c, loc, BUILD_TUPLE, PyDict_GET_SIZE(captures)); ADDOP_I(c, loc, CALL_INTRINSIC_1, INTRINSIC_RETAIN_ANNOTATION_CELLS); return SUCCESS; } ''' +s[idx:] a=s.index(' if (conditional_annotation_index != NULL) {',s.index('codegen_annassign(compiler *c')) b=s.index(' if (captures != NULL) {',a) # Move lookup before conditional registration and retain old cells before anything changes. lookupstart=s.index(' PyObject *captures = _PyCompile_AnnotationCaptures',a) lookup=s[lookupstart:b].replace(' return ERROR;',' Py_XDECREF(conditional_annotation_index); return ERROR;') s=s[:lookupstart]+s[b:] setup=lookup+''' if (captures != NULL && codegen_retain_annotation_cells(c, loc, captures) < 0) { Py_XDECREF(conditional_annotation_index); return ERROR; } ''' s=s[:a]+setup+s[a:] needle=' ADDOP_NAME(c, loc, STORE_DEREF, holder, cellvars);\n }\n}' s=s.replace(needle,' ADDOP_NAME(c, loc, STORE_DEREF, holder, cellvars);\n }\n ADDOP(c, loc, POP_TOP); /* release old cells after full publication */\n}',1);p.write_text(s) p=Path('Include/internal/pycore_magic_number.h');s=p.read_text().replace(' Python 3.16a1 3709 (Checked deferred annotation closure capture)',' Python 3.16a1 3709 (Checked deferred annotation closure capture)\n Python 3.16a1 3710 (Retain replaced annotation captures until publication)').replace('#define PYC_MAGIC_NUMBER 3709','#define PYC_MAGIC_NUMBER 3710');p.write_text(s) p=Path('Lib/test/test_block_annotation_captures.py');s=p.read_text();idx=s.index(' def test_typing_consumers');s=s[:idx]+''' def test_replaced_cell_finalizer_sees_complete_site_publication(self): module=execute("""\\ events=[] class V: def __init__(self,n): self.n=n def __del__(self): if self.n == 0: events.append(__annotate__(1)) for i in range(2): x=V(i) # bind x y y=i value:(x.n,y) """) self.assertEqual(module.events,[{'value':(1,1)}]) self.assertEqual(module.__annotate__(1),{'value':(1,1)}) ''' +s[idx:];p.write_text(s) PY make -j1 > /tmp/block-annotations-build7.log 2>&1 ``` ### 套接字代码高尔夫 在一次对话中,代理遇到测试中的“Bad file descriptor”错误,Astra 决定以超级压缩的方式检查文件描述符是否可以通过 Unix 套接字在 macOS 上传递: ```shell /usr/bin/python3 - <<'PY' import socket,os,array for into in (False,True): a,b=socket.socketpair(); fd=os.open(os.devnull,os.O_RDONLY); b.sendmsg([b'c'],[(socket.SOL_SOCKET,socket.SCM_RIGHTS,array.array('i',[fd]))]); print('fds',a.fileno(),b.fileno(),fd) if into: r=a.recvmsg_into([bytearray(1),bytearray(),bytearray(19)],socket.CMSG_SPACE(4),socket.MSG_PEEK|socket.MSG_DONTWAIT) else: r=a.recvmsg(20,socket.CMSG_SPACE(4),socket.MSG_PEEK|socket.MSG_DONTWAIT) print('peek',r,flush=True) rights=array.array('i',r[1][0][2]); print('rights',rights,flush=True) for f in rights: try: print('stat',os.fstat(f)) except Exception as e: print('error',e) r=a.recvmsg(20,socket.CMSG_SPACE(4),socket.MSG_DONTWAIT); print('consumed',r,flush=True) a.close(); b.close(); os.close(fd) PY ``` ### 用 Python 修改代理笔记 代理笔记几乎都用 Python 进行更新: ```python python3 - <<'PY' from pathlib import Path p=Path('agent-notes/live/block-with-bindings.md');s=p.read_text().replace(' has117/77/zero failures.', ' has117/77/zero failures; existing bundled Expat unreachable-fallthrough\n warnings are the only optimized warnings.') # Keep the final evidence readable without rewriting historical parent requirements. for a,b in [('all328','all 328'),('pass31','pass 31'),('pass all328','pass all 328'),('pass,9.2s','pass, 9.2s'),('log`,210','log`, 210'),('log`,5,731','log`, 5,731'),('log`:18/18','log`: 18/18'),('pass,88','pass, 88'),('pass,90','pass, 90'),('skips,1m','skips, 1m'),('all6,280','all 6,280'),('has117','has 117')]: s=s.replace(a,b) s += '\nKey source review: Python/symtable.c:603 (discovery), :3985 (sequential header traversal),\nPython/codegen.c:3488 (source-only exclusion), :5836 (publication), :5853 (normal/\nunwind reference cleanup), :5925/:6037 (enter-protected target setup).\n' p.write_text(s) for name in ('STATE.md','build-and-test.md'): p=Path('agent-notes/live')/name;s=p.read_text() for a,b in [('build:117','build: 117'),('paths.18','paths. 18'),('paths.\n18','paths.\n18'),('and210','and 210'),('pass5,731','pass 5,731'),('All6,280','All 6,280'),('failures,31','failures, 31'),('in\n115s','in\n115s'),('have117','have 117'),('paths.\n18','paths.\n18'),('18 focused,210','18 focused, 210'),('and5,731','and 5,731'),('all6,280','all 6,280')]: s=s.replace(a,b) p.write_text(s) PY git diff --check git add -u git add Lib/test/test_block_with_bindings.py agent-notes/done/asyncio-task-drivers.md git diff --cached --stat git commit -m 'Add explicit with and async with header bindings' ``` ### 用 Python 运行 Node.js 在多次情况中,它使用 Python 在另一台机器上启动 Node.js。它先编写脚本,然后用 Bash 运行 Python,接着该程序通过 `prlctl` 在我的 Windows 主机上运行 Node.js。 ```python import subprocess code = "const{readFileSync}=require('fs');const{strict:a}=require('assert');const c=require('C:/Users/mitsuhiko/AppData/Local/Temp/pi-clipboard-threads/win32-arm64.node');(async()=>{const p=c.getText();a.ok(p instanceof Promise);const saved=await p;const image=await c.getImage();if(image||saved===null){console.log('arm64 async text/image reads passed; preserving non-text clipboard');return}try{for(const text of ['café 日本語','', 'large'.repeat(200000)]){const p=c.setText(text);a.ok(p instanceof Promise);await p;a.equal(await c.getText(),text);a.equal(await c.getImage(),null)}console.log('Windows ARM64 async Unicode, empty, large text and empty image passed')}finally{await c.setText(saved)}})().catch(e=>{console.error(e);process.exitCode=1})" subprocess.run(['prlctl', 'exec', 'Windows 11', '--current-user', 'C:\\Program Files\\nodejs\\node.exe', '-e', code], check=True) ``` ### 用 Python 运行 Node.js 再调用 PowerShell 由于已经在这样做了,它用 Bash 运行 Python,然后让 Python 运行 Node.js,最后通过 Node.js 调用 PowerShell。 ```python import subprocess code = "process.env.PSModulePath='C:/Windows/System32/WindowsPowerShell/v1.0/Modules';require('child_process').spawnSync('powershell.exe',['-NoProfile','-NonInteractive','-ExecutionPolicy','Bypass','-File','C:/Users/mitsuhiko/AppData/Local/Temp/pi-clipboard-threads/pi-clipboard-windows.ps1'],{stdio:'inherit'});console.log('completed')" subprocess.run(['prlctl', 'exec', 'Windows 11', '--current-user', 'C:\\Program Files\\nodejs\\node.exe', '-e', code], check=True) ``` 你可能觉得这很有趣,但我有些疑问。第一个问题是它对人类而言不可读。如果你想弄明白发生了什么,那就祝你好运吧。特别是当它选择不使用框架提供的编辑工具时,你将不得不依赖最终产物的差异查看器,因为通过阅读代码几乎不可能实时可视化变化。 在 Pi 中这种情况通常没这么糟,因为我主要看到它使用 `edit` 工具进行编辑。然而,当它使用子代理(代理认为没人看的时候)时,它会采用各种越来越奇怪的行为。我其实不知道模型是否认为有人在观察,但我的感觉是这样。但它随后开始在实际提交的代码中也做同样的荒谬操作。我主要在测试中看到这一点,但当你看到它编写嵌入 HTML 的 JavaScript 或 CSS 时也能发现。几乎像是当它“远离”常规代码时,就开始陷入这些模式。 以下是一些它创建的单元测试示例: **完全无视空格和缩进** ```python def test_unpack_suspension_and_continuation_close(self): from continuations import Continuation,suspend readers=[] class Source: def __iter__(self): yield 1 suspend('unpacking') yield 2 ns=execute(''' def run(): a,b='old-a','old-b' readers.append(lambda: (a,b)) def a,b=Source() suspend('published') ''',Source=Source,readers=readers,suspend=suspend) with Continuation(ns['run']) as continuation: self.assertEqual(continuation.resume(),'unpacking') self.assertEqual(readers[0](),('old-a','old-b')) self.assertEqual(continuation.resume(),'published') self.assertEqual(readers[0](),(1,2)) class Value:pass refs=[];frames=[];callbacks=[] ns=execute(''' def run(): for def x in [Value()]: refs.append(weakref.ref(x)) frames.append(sys._getframe()) callbacks.append(lambda: x) suspend('body') ''',Value=Value,refs=refs,frames=frames,callbacks=callbacks,weakref=weakref,sys=sys,suspend=suspend) with Continuation(ns['run']) as continuation:self.assertEqual(continuation.resume(),'body') self.assertNotIn('x',frames[0].f_locals) self.assertIsNotNone(refs[0]());callbacks.clear();self.assertIsNone(refs[0]()) ``` ```python def test_ast_roundtrips_and_future_annotation_unparse(self): source='callback=lambda {for def a, [b,*rest] in [(1,[2,3])] {return a,b,rest}}' tree=ast.parse(source);node=tree.body[0].value.body[0] self.assertIsInstance(node,ast.ForBinding) self.assertEqual(node._fields,('target','iter','body','orelse','type_comment')) self.assertEqual(node.lineno,1);self.assertGreater(node.end_col_offset,node.col_offset) self.assertEqual(ast.dump(tree),ast.dump(ast.parse(ast.unparse(tree)))) ns=execute('from __future__ import annotations\ndef f(arg: '+source.split('=',1)[1]+'): pass') self.assertEqual(eval(ns['f'].__annotations__['arg'])(),(1,2,[3])) tree=ast.parse('async def f():\n async for def x in

相似文章

是的,Astra 非常强大,但是...

Reddit r/singularity

作者批评了 AI 模型 Astra 在代码架构决策上的不足和对删除不必要的代码的抵制,认为它应该接受更好的训练以适应长期的软件工程实践。

内部人士对Astra能力的看法

Reddit r/singularity

一位内部人士评估了GPT Astra的能力,强调其在长期任务、协调、应用学术文献和代码生成方面的优势,并指出在创意写作方面相比前代模型有所改进。

GPT-6 Astra:工作智能的下一代

OpenAI Blog

OpenAI发布了GPT-6 Astra,这是一款专为专业工作优化的尖端AI模型,具备在计算机使用、编码和企业工作流方面的高级能力,并提升了安全性和效率。