从写代码,到创作下一幕

探索 字节跳动 - 火山方舟 的 AI 编程与视频创作活动。

火山方舟 Agent Plan 与 Coding Plan 活动,价格 ¥9.9 起

Agent Plan & Coding Plan

一站体验多款热门模型,为 AI 编程与智能体开发提供更多选择。新用户可联系(微信: goo_lvyouyou)免费体验 9.9 agent plan。

查看套餐优惠
Seedance 2.5 视频创作活动:30 秒视频、多模态参考、局部编辑

Seedance 2.5

让创意,跃然成片。探索 30 秒视频、多模态参考与局部编辑,把脑海中的画面变成下一支作品。

探索视频创作

从缺陷报告到修复

查看 Markdown

本应用通过 Grok API 构建 AI 编程 Agent,将缺陷报告转化为通过测试的修复,可作为让模型安全修改实际代码的起点。

描述小型示例仓库 tiny-checkout 中的一个缺陷,Grok 会像开发者一样修复它:先阅读仓库说明,找到产生问题的代码,编写能复现失败的测试,再修改代码并重新测试,直到通过为止。Grok 编写的命令都在本次运行新建的仓库副本中执行。白名单中的命令立即执行,其余命令需等你选择 Approve 或 Deny。网页应用会将命令和输出流式显示在终端中,旁边同步显示代码差异。

你将学到什么

  • 实现工具调用循环:流式收到完整调用后立即执行,回传结果,在某一轮没有发起调用时停止,并限制总轮数

  • 处理 shell 工具:它以 shell_call 条目发出命令,要求返回一个 shell_call_output,其中每条命令对应一项结果

  • 在仓库的临时副本中运行模型编写的命令,绕过 shell 校验白名单,限制执行时间和输出长度,其余命令需人工批准

  • 通过 shell 工具的 environment.skills 向 Grok 提供技能,使其了解如何运行仓库测试以及应遵循的约定

  • 让 Grok 通过只能写入临时副本的函数工具编辑文件

  • 使用 prompt_cache_key 提高长会话的速度并降低成本;使用 responses.compact() 在输入超过阈值时压缩上下文

  • 通过服务器发送事件(SSE)将运行过程从 Node 服务器推送到网页,并把网页中的 Approve 或 Deny 决定回传给运行中的任务

运行示例

需要 Node.js 22.13 或更高版本。将 API 密钥写入 .env(位于仓库根目录),或导出环境变量 XAI_API_KEY。

Bash

cd examples/bug-report-to-fix/typescript
npm install
npm run web

打开 http://localhost:3000,点击三份示例缺陷报告中的一份,或自行描述问题,然后点击 Fix it。页面类似编程工具。左侧列出缺陷报告和运行轮次,并标出每轮的输入 token 数及缓存命中数。中间的终端显示 Grok 的推理、每条命令及其输出,以及工作过程中的说明。右侧的仓库视图标记它读取的文件,展示被修改文件的差异。命令不在白名单上时,终端会显示该命令,并提供 Approve 和 Deny。结束时,页面会显示测试是否通过、本次费用,以及下载修复补丁的链接。点击 Stop 或关闭网页即可取消运行。

也可以在终端传入示例报告的编号,或直接描述问题。不传参数时使用第一份示例报告。

Bash

npm start -- 2
npm start -- "A sticker priced at \$0.29 shows up in the cart as \$0.28"

终端在执行白名单之外的命令前会先询问。两个版本都将每次运行保存在 output/<time>-<report>/,修复后的仓库副本位于 repo/,更改保存在 fix.patch,可以通过 git apply 将补丁应用到 sample-repo/。sample-bugs.json 中的每份示例报告都对应一个实际存在的缺陷,所在仓库为 sample-repo/。实测每次需 7 至 8 轮,耗时 40 至 50 秒,费用为 0.03 至 0.05 美元。

工作原理

共享代码位于 src/agent.ts(负责运行循环)和 src/workspace.ts(负责执行命令)。fixBug() 接收缺陷报告,并通过回调报告每一步,因此网页和终端能以不同方式展示同一次运行:

  1. createWorkspace() 将 sample-repo/ 复制到一个新建文件夹中,位置在 output/ 下。所有命令都在这里运行,write_file 也只能在这里写入文件。

  2. 每一轮都会向 grok-4.7 发起流式请求,提供内置的 shell 工具和 write_file 函数工具。流中的 client_tool_call 事件在参数接收完整时交出调用,fixBug() 随即按顺序逐一执行,因为编辑后立即运行的测试必须看到刚才的更改。轮次结束后,模型输出和执行结果都会作为下一次请求的输入。如果本轮未发起工具调用,或已经达到第 25 轮,就会结束运行。

  3. 每个 shell_call 都在以下字段中包含一组命令:action.commands。checkCommand() 按照 shell 的分词方式解析每条命令。包含管道、重定向、通配符或变量等只能由 shell 解读的语法时,命令会被拒绝。其余命令必须在白名单中:ls、cat、head、tail、wc、grep、find,pwd 这些命令只能使用已知参数,且所有路径即使解析符号链接后也必须位于副本内。另外允许 node --test 和 npm test。runCommand() 无需 shell 就能执行获准命令,超时时间为 20 秒,每个输出流最多保留 10,000 个字符,也不继承当前进程中含 API 密钥的环境变量。由于测试会执行 Grok 的代码,它们在 Node 权限模型下运行,文件读写仅限副本内部,子进程也被阻止。白名单之外的命令会交给 approve 回调。若你批准,就在副本中通过 /bin/sh 执行。若你拒绝,Grok 会收到对应结果和该命令需要批准的原因。每次调用的结果通过一个 shell_call_output 返回,其中每条命令对应一项。

  4. Grok 只能使用 write_file 修改文件。Shell 命令也能通过 sed -i 或重定向写文件,但这样应用就必须分析每条命令是否写文件、写到哪里。write_file 接收路径和完整文件内容,解析路径,并拒绝写入副本外或经过符号链接的位置。对于这样的小文件,传入完整内容简单而安全;对于大型代码库,逐段替换可节省输出 token。每次调用后,repoChanges() 会对照副本与原仓库,后者的路径为 sample-repo/,src/diff.ts 随后会为每个变动文件生成统一差异(unified diff)。获批命令造成的修改也会被捕获。

  5. 示例仓库的技能位于 skills/fix-a-bug/SKILL.md,其中说明了测试运行方式、代码和测试的位置、每次修复要遵循的步骤及仓库约定。fixBug() 从文件头的元数据读取名称和描述,并将该技能列在 shell 工具的 environment.skills 中,同时提供路径。在通过 shell 工具读取文件之前,Grok 对该技能的了解仅限这些元数据。因此白名单允许在副本内运行 cat 来读取文件。

  6. 同一次运行中的各个请求都发送相同的 prompt_cache_key,从而被路由到已缓存对话开头的服务器。输入始终只在末尾追加,因此开头保持不变。从大约第四轮起,多数轮次的输入缓存命中率达到 80% 至 99%,缓存 token 的价格是通常价格的四分之一。缓存条目仍可能被丢弃,因此有时某轮几乎没有缓存命中。请求输入超过 20,000 个 token 时,compact() 会将最新一轮之前的全部内容发送给 responses.compact(),然后以返回的单个加密条目继续,并在其后追加最新一轮。示例运行的输入不足 6,000 个 token,因此不会触发压缩。压缩以细节换取更短的上下文:测试中将阈值设为 3,000 个 token 时,摘要遗漏了仓库里的文件名,Grok 随后计划修改一个不存在的文件。请等到会话较长时才进行压缩。

  7. Grok 停止后,fixBug() 会亲自再运行一次测试来验证结果,然后将更改保存为 fix.patch。

Grok 使用中等推理强度。低强度时,它跳过了技能指定的步骤,在同一轮中同时编写修复和测试,测试从未先出现失败。中等强度下,每轮需几秒,Grok 会按步骤操作。若 Grok 尚未开始流式输出,请求便失败,客户端会重试,否则一轮失败就会中断整次运行。每轮费用根据 token 数和以下方法返回的价格计算:client.models.language.get(),因为 responses.compact() 的响应不包含费用。

src/server.ts 是一个小型 node:http 服务器。EventSource 只能发送 GET 请求,因此网页先把缺陷报告发到 /api/runs,再打开 /api/events 并携带返回的 ID。该 ID 只能使用一次,因此重新连接的 EventSource 无法重复启动同一个任务。服务器运行 fixBug(),将每个步骤以服务器发送事件推送给网页。需要审批命令时,服务器向网页发送随机 token,网页会将该 token 连同 Approve 或 Deny 决定 POST 到 /api/approvals。服务器还将 AbortSignal 传入每次请求和每条命令,关闭页面时触发中止,也会拒绝所有待批准的命令。服务器仅提供本服务创建的运行所生成的 fix.patch 文件。public/index.html 使用原生 HTML 和 JavaScript 展示这些事件。它加载 Prism 的 CDN 版本来高亮差异中的代码,无法加载时显示纯文本代码。src/index.ts 在终端中完成相同的工作。

本应用除 SDK 外没有其他依赖。Node 不提供内置差异算法,所以 src/diff.ts 中包含一段简短实现,足够处理示例中的小文件。

防护范围与局限

白名单、权限模型和精简的环境变量可降低误操作影响副本之外内容的概率,但这并不构成沙箱。Node 24 的权限模型不限制网络,测试代码仍可发送请求。经你批准的命令会通过 shell 以你当前用户的权限执行。要将此类 Agent 用于重要代码,请在容器或虚拟机中执行命令。

后续步骤

  • Responses API 还提供 WebSocket 模式,各轮在同一条连接中只发送新增条目,可在多轮执行中节省时间。SDK 暂不支持这种模式,因此本应用每一轮都发送 HTTP 请求。

  • 可以将 sample-repo/ 替换为自己的项目并重写其技能。大型代码库宜用逐段编辑的工具取代 write_file。