Astra for Coding: なぜ私たちはこれを再びやっているのか?
Astra for Coding: Why Are We Doing This Again? (lucumr.pocoo.org)
要約
著者は、AIエンジニアリングにおける「内巻(Neijuan)」、すなわち努力と競争だけが増大し、成果が向上しない状況について論じています。最新AIモデルAstraをソフトウェア開発に適用しようと試みた結果、モデルは長期間タスクを継続する能力は高いものの、生成されるコードの質が低く、実用的な開発には結びつかないという課題が浮き彫りになりました。特に、Pythonの文字列操作によるC言語コードの編集など、非効率的で奇妙なコード生成パターンが観察されています。
全文翻訳
Astra for Coding: なぜ私たちはこれを再び行っているのか?
2026年9月7日執筆
AIエンジニアリングのすべてが内巻(Neijuan)(中国語で、成果を向上させずにますます多くの努力と競争を要求するシステムを指す)であると、私はますます確信しています。西洋におけるその現れ方の一つが、996のナンセンスです。内巻の英語での一般的な用語は、書籍『Agricultural Involution』に由来する「Involution(退化)」です。農業の退化は、1平方メートルあたりの生産性を向上させるが、1人あたりの生産性は変えない農業の集約化を説明しています。
それが今のAIに対する私の感覚です。
そこでGPT 6 Astraの話になります。Astraは、あらゆる点で信じられないほど印象的なモデルです。これに対して私が言えることは本当にほとんどありません。コンピューター操作は驚異的で、画像や複雑なトピックを理解し、完了に向けて執拗に追求します。それは絶対に印象的です。これらのタイプのモデルは、何らかの形で世界を変えるでしょう。
しかし、少なくとも現時点では、実際のソフトウェアエンジニアリングにどのように活用できるのか分かりません。それがTwitterでかなりの注目を集めたので、私の考えを要約して、このモデルからどのようなコードが出てくるのかを共有しようと思いました。
私のスロップ工場
「アーミン、ソフトウェア工場を運営すべきだ!」と何度か言われました。そこで、週末に小さなソフトウェア工場を運営してリリースを祝うことにしました。みんながスロップな3Dゲームを作るなら、私はそれを使って何か有用なことをすべきです。私のソフトウェア工場は、モデルがワークフローの「どのように」を完全に決定できるように意図的に設定されました。モデルは自身のコンテキストを管理し、agent-notesフォルダに自身の記録を保持することができました。そして、それは作業のためにサブエージェントをスピンオフしました。目標は?もしPythonに仮想スレッドとレキシカルスコープがあったらどうなるか、ということでした。そして、私はこのためにChatGPTトークンをフルリセット分、約40億トークンを燃やしました。35時間後、工場は全く価値のあるものを何も届けず、より良い運用方法についても何も教えてくれませんでした。
しかし、それは大量のコードと入力プロンプトを生成したので、研究できるものがいくつかありました。そして、Solや以前のOpenAIモデル1では慣れていない挙動を目にしました。その後、Astraでの通常のプログラミングでも同じ問題に遭遇したので、工場だけの結果ではありません。
トレーニングプロセスで何かが「間違って」起きているのではないかと疑っています。モデルは長期的なタスクを成功させることで大きく報酬を得ますが、おそらく「ひどいコード」に対する罰はほとんどありません。その結果、Astraは3Dコンテンツの生成に驚異的で、長時間継続し、その過程で独自の作業を生み出すことができます。私のロボット掃除機の逆アセンブルを、非常に印象的な方法でかなりの量行わせました。だから、間違いなくクールです!
コードゴルフ・ツールコール
Astraに関する最初の問題は、ツールコールに使用されるコードの種類から来ています。Codexは、ますます多くの操作を行うために「単なるbash」に依存するようになっています。数バージョン前から、オリジナルのCodexハーネスはファイルを読むためにsedなどのツールを使用しています。通常、Codexがbashコマンドを解析して認識すれば隠すため、それらを見ることはできません。しかし、Astraは…本当にPythonを愛していますか?それはそれほど驚くことではありません。なぜなら、古いOpenAIモデルでさえ、時折オンデマンドでPythonコードを使用してファイルを読み書きする傾向があったからです。しかし、Astraは私にとってはそれを過剰に行います。
ここで重要な免責事項があります。このプロジェクトは非常にメタです。なぜなら、私はCPythonインタプリタを開発したからです。しかし、PiのTypeScriptコードでさえ、このモデルが奇妙なPythonコードを書くのを見たことがあると保証できます。しかし、週末にゼロ監視下のスロップ工場で作業させていたときに、奇妙なコードの証拠を最も多く集めました。
Pythonを書くことは興味深いことではありません。Pythonの種類が興味深いのです。そして、あなたがざっと見れるように、いくつかの出力を集めました。
Cコードを編集するためのPython文字列スプライシング
Codexハーネスでは、サブエージェントがパッチツールを使用せずに、完全に手動の文字列操作に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
#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)
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
codegen_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);
}
}'
s=s.replace(needle,' ADDOP_NAME(c, loc, STORE_DEREF, holder, cellvars);
}
ADDOP(c, loc, POP_TOP); /* release old cells after full publication */
}',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)
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はファイルディスクリプタをmacOS上でUnixソケット経由で超圧縮形式で渡せるかどうかを試す必要があると判断しました。
/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で一貫して更新されていました。
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
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を実行し、そのプログラムが私のWindowsボックスでprlctl経由でNode.jsを実行しました。
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を実行し、次にNode.jsを実行して、Node.jsを使用してPowerShellを呼び出しました。
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ツールで編集することが多いため、それほど悪くはありません。しかし、サブエージェントが(誰も見ていないとエージェントが信じている場合)すべてをやり始めると、ますます奇妙な挙動に陥ります。モデルが誰かが見ていると思っているのかどうかは分かりませんが、私が感じているのはその雰囲気です。
しかし、その後、実際にコミットされるコードでも同じナンセンスが始まります。これは主にテストで見られますが、例えばJavaScriptやCSSをHTMLに埋め込んで書く場合にも見られます。コードから「一歩離れた」状態になると、これらのパターンに陥り始めるようです。
以下は、それが作成した単体テストの一部です。
空白とインデントの完全な無視
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]())
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 values: pass # type: ignored\n')
self.assertIsInstance(tree.body[0].body[0],ast.AsyncForBinding)
self.assertEqual(ast.dump(tree),ast.dump(ast.parse(ast.unparse(tree))))
少なくともいくつかの状況では、ツールコールのために通常コードゴルフされるPythonのスロップが、保存されるべきPythonコードに漏れ出しています。そして、それは明らかにトークン効率が良いです。上記の2つの単体テストは、クラス構造にインデントされた場合、ruff formatの後よりもこの形式で10%トークン効率が良いです。
見なければAGI
現在、すべてが互いに矛盾する方向に全体を押し上げていることがいくつか起こっていると思います。これらのモデルのトレーニング実行は急速に加速しており、おそらく再帰的な自己改善にも向かっています。モデルへの報酬は、トークン効率、タスク完了率、そしておそらくサイクロマティック複雑度のような単純な指標の組み合わせでしょう。しかし、私たち人間は、単純で容易に定量化できる指標によって、読みやすく理解しやすいコードを考えません。これらすべては、個別に簡単に測定でき、局所的に最適化することもできます。
しかし、これらの局所的な最適化はグローバルな最適解を生み出さず、私たちが見ている出力が少ないほど、それは重要でなくなります。明らかに私のソフトウェア工場は、約35時間稼働して座礁しましたが、ノートが生成した狂気への段階的な後退を見ることができます。例えば、タスクファイルの名前付けは、楽観的な1、2、3、5、5aで始まりますが、最終的には8a、8a1になり、そして8b2c2b3と「8b2c2b2b checkpoint1」に終わります。生成されたコードはますますワイルドになりました。実験を中止する直前、その関数はテストアサーションを行うために主に必要とされていましたが、非テストコードにも依存し始めました。
static PyObject *
native_probe_run_impl(PyObject *callback, int sleep, int operation, PyObject *other)
{
pthread_mutexattr_t attr;
pthread_mutex_t mutex;
pthread_mutexattr_init(&attr);
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
pthread_mutex_init(&mutex, &attr);
pthread_mutexattr_destroy(&attr);
pthread_mutex_lock(&mutex);
int previous = native_sentinel;
pthread_mutex_t *previous_mutex = native_mutex;
native_sentinel = previous + 1;
native_mutex = &mutex;
PyThreadState *tstate = PyThreadState_Get();
PyGILState_STATE gil = PyGILState_Ensure();
int saved_errno = errno;
PyObject *result = NULL;
Py_ssize_t value;
/* No intervening Python frame: these exercise ambient C provenance. */
switch (operation) {
case 0: result = PyObject_CallNoArgs(callback); break;
case 1: result = PyNumber_Add(callback, other); break;
case 2: result = PyNumber_Negative(callback); break;
case 3: result = PyObject_RichCompare(callback, other, Py_LT); break;
case 4:
value = PyObject_IsTrue(callback);
if (value >= 0) result = PyBool_FromLong(value);
break;
case 5:
value = PyObject_Length(callback);
if (value >= 0) result = PyLong_FromSsize_t(value);
break;
case 6: result = PyObject_GetIter(callback); break;
case 7: result = PyIter_Next(callback); break;
case 8: result = PyObject_GetItem(callback, other); break;
/* ... */
case 21:
result = PyType_Type.tp_call(callback, other, NULL);
break;
case 22: case 23: case 24: case 25: case 26:
result = conversion_probe(operation, callback); break;
case 27: case 28: case 29:
result = protocol_probe(operation, callback, other); break;
case 30: case 31: case 32: case 33: case 34: case 35:
case 36: case 37: case 38: case 39: case 40: case 41:
case 42: case 43: case 44: case 45: case 46:
case 47: case 48: case 49: case 50: case 51: case 52:
case 53: case 54: case 55: case 56: case 57: case 58: case 59:
case 60: case 61: case 62: case 63: case 64: case 65: case 66:
case 67: case 68: case 69: case 70: case 71: case 72:
result = collection_probe(operation, callback, other); break;
default: PyErr_SetString(PyExc_ValueError, "bad probe operation");
}
Cにおける複数の同一行マクロ呼び出し
このコードスタイルはCPythonのコードベースには存在しませんが、新しく生成されたコードに現れます。
PyObject *info = PyTuple_Pack(3, name, mangled, suite->su_id);
PyObject *flags = PyLong_FromLong(DEF_LOCAL);
if (key == NULL || info == NULL || flags == NULL ||
PyDict_SetItem(suite->su_bindings, mangled, key) < 0 ||
PyDict_SetItem(st->st_cur->ste_block_bindings, key, info) < 0 ||
(private && PyDict_SetItem(st->st_binding_info, key, info) < 0) ||
(private && PyDict_SetItem(st->st_cur->ste_symbols, key, flags) < 0)) {
Py_DECREF(mangled); Py_XDECREF(key); Py_XDECREF(info); Py_XDECREF(flags);
goto error;
}
Py_DECREF(mangled); Py_DECREF(key); Py_DECREF(info); Py_DECREF(flags);
本番コードにおけるランダムなインデックス
オペレーターの数値と同様に、状態を保存するためにリストにランダムな整数を使用します。
def _register_task(task):
"""Register an asyncio Task scheduled to run on an event loop."""
_scheduled_tasks.add(task)
if _task_accelerator is not None:
_task_accelerator[6](task)
def _register_eager_task(task):
"""Register an asyncio Task about to be eagerly executed."""
_eager_tasks.add(task)
if _task_accelerator is not None:
_task_accelerator[8](task)
def _enter_task(loop, task):
if (_task_accelerator is not None and
_task_accelerator[5]() is loop and loop not in _current_tasks):
return _task_accelerator[1](loop, task)
# ...
Cにおける醜いトークナイザーコード
これはコードベースのコーディングスタイルではなく、正直言って誰のコーディングスタイルでもないはずです。モデルがこれを実行した動機が理解できません。
static int
apply_layout(tokenizeriterobject *it)
{
PyObject *source = PyBytes_FromStringAndSize(it->tok->source.bytes, it->tok->source.len);
if (source == NULL) return -1;
PyObject *events = _PyPegen_tokenize_layout(PyBytes_AS_STRING(source), it->tok->filename);
Py_DECREF(source);
if (events == NULL) return -1;
PyObject *result = PyList_New(0);
if (result == NULL) { Py_DECREF(events); return -1; }
Py_ssize_t index = 0;
PyObject *first_pos = PyTuple_GET_ITEM(PyList_GET_ITEM(it->pending, 0), 2);
PyObject *last_pos = PyTuple_GET_ITEM(PyList_GET_ITEM(it->pending, PyList_GET_SIZE(it->pending)-1), 2);
PyObject *previous = NULL;
for (Py_ssize_t i = 0; i < PyList_GET_SIZE(events); i++) {
PyObject *event = PyList_GET_ITEM(events, i);
if (previous && PyObject_RichCompareBool(previous, event, Py_EQ) == 1) continue;
previous = event;
PyObject *token = layout_token(it, event);
if (token == NULL) goto error;
if (token == Py_None) { Py_DECREF(token); continue; }
PyObject *pos = PyTuple_GET_ITEM(token, 2);
if (PyObject_RichCompareBool(pos, first_pos, Py_LE) == 1 ||
PyObject_RichCompareBool(pos, last_pos, Py_GT) == 1) { Py_DECREF(token); continue; }
while (index < PyList_GET_SIZE(it->pending)) {
PyObject *old = PyList_GET_ITEM(it->pending, index);
int cmp = PyObject_RichCompareBool(PyTuple_GET_ITEM(old, 2), pos, Py_LT);
if (cmp < 0) { Py_DECREF(token); goto error; }
if (!cmp) break;
if (PyList_Append(result, old) < 0) { Py_DECREF(token); goto error; }
index++;
}
if (index < PyList_GET_SIZE(it->pending)) {
PyObject *old = PyList_GET_ITEM(it->pending, index);
long kind = PyLong_AsLong(PyTuple_GET_ITEM(old, 0));
if ((kind == NL || kind == NEWLINE || kind == INDENT || kind == DEDENT) &&
PyObject_RichCompareBool(PyTuple_GET_ITEM(old, 2), pos, Py_EQ) == 1) index++;
}
if (PyList_Append(result, token) < 0) { Py_DECREF(token); goto error; }
Py_DECREF(token);
}
for (; index < PyList_GET_SIZE(it->pending); index++) {
if (PyList_Append(result, PyList_GET_ITEM(it->pending, index)) < 0) goto error;
}
Py_SETREF(it->pending, result);
Py_DECREF(events);
return 0;
error:
Py_DECREF(events);
Py_DECREF(result);
return -1;
}
ここでの失敗ケースは明白なようです。モデルはツールコールのトークン効率のために訓練されており、それはコードのようにも見えます。そして、時々、そのコードを本来あるべきではない場所、つまりコードベースに持ち込んでいるようです。
35時間、単一プロンプトに費やす
どう言えばいいのか分かりませんが、スロップマシンは私が停止するまで35時間稼働していました。その間に75k行のコードが純粋に追加され、止まりませんでした。35時間で約1Bトークンを消費し、APIコストは合計約1200ドルでした。79回のコミットを生成しましたが、コミットあたりのコストは約15.5ドルで、エージェントは1400件のメッセージを交換しました。
正直、単一プロンプトに35時間もかかるエージェントは必要ありません。それは明らかに機能せず、合理的な出力を生み出しません。
だから、このようにプロンプトするのは愚かです。しかし、 unattended にすると、それは続行します。そして、以前のモデルはそうしませんでした。Fableでさえ、これほどクレイジーではありませんでした。誤って少し大きすぎるタスクを与えると、サブスクリプション全体を使い果たしても、成功するまで続行します。
そして、これが私が現在このモデルをあまり信頼できない理由のほとんどです。それはスロップをコミットすることを示しており、その結果、より多くのレビューが必要になります。失敗率はかなり低いとしても、私はこれを望みません。
使い捨てコード vs コミットコード
ツールコールのコードがトークン効率と「仕事をこなす」ために最適化されている世界で、トレーニングプロセスに「人間が何が起こっているかを理解している」という十分な信号が送られているのか疑問に思います。Astraから得られるコードの多くは、私の考えでは「客観的に悪い」です。しかし、それは私の人間的な感覚から見て客観的に悪いのです。おそらく、すべてエージェントによって書かれ、エージェントによってのみ理解される必要があるコードベースにとっては、客観的に良いのかもしれません。
だからこそ、私たちはなぜこれを行っているのかをますます自問自答しています。これらの新しいモデルは確かに絶対に素晴らしいです。しかし、それらが現在進んでいる軌道が、今日のソフトウェアエンジニアリングプロセスに適しているかどうかについては、ますます懐疑的になっています。なぜこれを行っているのかを尋ねる理由は、これらのモデルでソフトウェアエンジニアリングの分野で良好なROI(投資収益率)を示すことができたと感じていたからです。しかし、FableやAstraのコストがどれだけ高くなっても、結果はそこにあるとは感じません。
実際、AstraとFableでは、コストが天文学的であるだけでなく、モデルは私のようなソフトウェアエンジニアのためではないと感じています。そして、おそらくこれらすべてを可能にする副産物として、週末に印象的に見えるワンショットの3Dゲームをスロップで作成できるようになりました。そして、おそらくコードを気にしない限り、ソフトウェア工場を運営できるようになりました。
きっと慣れるでしょうが、これは本当に奇妙です。
追伸: 奇妙なことと言えば、これらのモデルはサンドボックス内で、他のエージェントと通信する方法がないはずなのに、エージェント間の通信のスクラッチパッドとして同じ公開Wikiを見つけることができるのはなぜでしょうか?将来役立つ可能性のあるインターネット上のリソースを記憶するために、トレーニング実行中に共謀したのでしょうか?
- 私は以前にもこのような実験を行ったことを明確にする必要があります。通常、それらはこれほど長くは実行されず、エージェントは不完全ではあるものの、まだ理解可能なソフトウェアを残します。↩