Read the Loop · 第一章(共八章)

智能体就是一个循环

语言模型只会做一件事:读入文本,再写出更多文本。它打不开文件,跑不了测试,也玩不了游戏。下面,一个模型要修好一个小游戏里的 bug,而它自始至终看不到这个游戏。先自己玩一下,几秒钟之内就能看到这个 bug。

修复前的游戏

修复前 用球拍接住球,别让它掉下去。球拍跟着你的手指或鼠标走;用键盘的话,先点一下游戏或者用 Tab 键进入,再按方向键或 A、D 键。注意看第一个球:它落在球拍的右半边,直接掉了下去。

背景

这个游戏你多半见过:底下一块球拍,上面一排排砖块,一个球在中间来回弹。可在这里,球落到球拍的右半边会直接穿过去,好像那里什么都没有。落在左半边,它就照常弹起来。

我们把这个游戏的文件夹交给了 AI 编程智能体(agent)Claude Code,只输入了一句话:

智能体看不到这个游戏,更别说玩了。它能请求的操作只有 种:。所以它只能从代码和游戏的测试入手。测试文件里有 个小检查,用 Node(一个运行 JavaScript 的程序)就能跑,什么都不用安装。

整个会话用了 秒,其间模型被调用了 次。录制于 ,模型是 。这个任务我们录了 次,这里展示的是第 次。选哪一次,用的是录制前就定好的规则:第一个在修复前后都跑了测试的运行。全部 次都放在代码仓库里。在一个玩具游戏上成功一次,证明不了智能体会修 bug;第七章讲的正是它们修不好的时候。

用下面的按钮、滑块或键盘方向键,一次一次地查看这些调用。也可以点“按真实速度观看”,按它们当时发生的速度重放。

逐步查看录制的会话

循环:模型把请求发给外壳程序,外壳程序在你的电脑上执行它,再把结果发回给模型。 模型 文本进 文本出 外壳程序 Claude Code 你的 电脑 目前的全部内容 一个请求 执行它
整个思路,十行代码
messages = [instructions, your_message]
while True:
    reply = model(messages)  # 每次都读入全部内容
    messages.append(reply)
    if not reply.requests:   # 没有请求:结束
        break
    for request in reply.requests:
        result = run(request)  # 外壳程序来执行
        messages.append(result)
show(reply.text)

修好的游戏

这是修好的游戏,其中的 和智能体改完时一模一样,别的什么都没动。再看一次第一个球。

修复后 操作和前面一样:用手指、鼠标,或者点进游戏后按方向键。

值得注意的三件事

修改前后它都检查了

模型从来没看到过这个游戏。改动任何东西之前,它先跑了测试,传回来一个失败的测试:。修改出现在下一次调用里,同时还有第二次测试运行,这一次全部 个都通过了。这两步各是循环的一轮,每次的结果都是外壳程序贴进来的文本。规则选中这一次运行,也是这个原因:

每一次都把全部内容读一遍

模型在两次调用之间什么都不保留。对话就是它的记忆,所以外壳程序每次都把整段对话发过去。为了改 行代码, 次调用一共读了 个 token,其中 个从缓存读取。缓存在第三章讲。

“完成”是模型自己说了算

没有谁叫智能体停下。它最后一条回复里没有请求,外壳程序就停了。同一条回复说,智能体检查过快速运动的球不会越过球拍,而这项检查只以几句话的形式存在于回复里。在这里,测试和修好的游戏都支持它的主要结论。什么都无法印证的情况,第七章再讲。

次模型调用
秒
按标价计算
改前 → 改后的行数

下一章

第二章:上下文窗口

在一个只有 个文件的玩具项目上, 次调用下来,对话从 个 token 涨到了 个,是开始时的 倍。涨出来的大部分, 个 token,是它读的代码。真实的项目有成千上万个文件,会话一跑就是几个小时。项目是真的、会话又很长的时候,模型读到的是什么?