OX
OPEN-OX

// docs / modify-agent

修改 Agent

受 Claude Code query() 设计启发的 Agent 循环。 Agent 搜索、阅读、编辑、验证 — 配备质量门控,防止提前停止。

概览

与生成流水线(确定性步骤)不同,修改流是一个开放循环。 Agent 自主决定读哪些文件、改什么、何时结束。 Stop Hook 强制执行最低质量标准。

runModifyProject(projectId, instruction, imageBase64?)
│
├── resolve_project   加载项目元数据
├── read_context      文件树 + design-system.md + globals.css
└── agent_loop        while(true),最多 100 次迭代
      │
      ├── 第 1 次迭代:tool_choice="required"(必须行动,不允许空想)
      ├── 工具执行 → 更新循环状态
      ├── stop hook 检查 → 注入错误或允许停止
      └── 停止时:计算 diff → 持久化到 modificationHistory

Agent 循环

tool_choice 状态机

循环根据上一步的结果控制 tool_choice

首次迭代required强制先行动,再思考
stop hook 重试后required已要求使用工具 — 强制执行
工具执行后autoLLM 已有上下文,让它自主决策

自然语言 → 代码定位

用户描述的是 UI/产品语言,不是文件路径。由 Agent 结合 file tree、预读文件和工具自行定位——编排层不做关键词切分或固定目录猜测。

入口 intent router(LLM)先分 conversation / read_only / plan_only / code_change,并可选输出 preloadPaths

工具集

read_file

读取文件完整内容(支持行范围)

search_code

ripgrep 全项目搜索

list_dir

列出目录内容

edit_file

精确字符串替换(old_string → new_string)

write_file

创建或覆盖文件

generate_image

AI 生图写入 public/images(与生成流水线相同;仅代码修改 intent 暴露)

run_build

执行 next build 验证变更

exec_shell

受限 shell(诊断与脚手架;受策略与白名单约束)

think

无副作用推理草稿,压缩进 reasoning 轨迹

revert_file

按快照回滚单个文件

edit_file 的精确匹配要求

old_string 必须在文件中精确匹配唯一一处。 Agent 被要求包含上下文行以确保唯一性,防止意外的多处替换。

Stop Hook

当 LLM 停止调用工具时,stop hook 在允许循环退出前运行。 它检查循环状态(是否探索过仓库、是否完成编辑/回答、类型错误、scoped tsc),并注入通用 Agent 指令——不从用户句子里切关键词,也不硬编码搜索路径。

function runStopHook(state, instruction, modifyMode, { profile }): string | null {
  if (!state.hasSearched && !state.hasEdited) {
    return modifyMode === "read_only"
      ? "Use read/search/list; answer in natural language; do not edit."
      : "Explore with tools from the file tree, then edit_file.";
  }
  if (modifyMode === "read_only" && state.hasSearched) return null;
  if (modifyMode === "code_change" && !state.hasEdited) return "Make edits now…";
  if (profile.verificationMode === "tsc_only") return null;
  // optional: remind run_scoped_tsc before finish
  return null;
}
参数用途
MAX_ITERATIONS100循环迭代硬上限
MAX_STOP_HOOK_RETRIES5防止 stop hook 自身无限循环
stopHookRetries reset工具调用后成功执行工具后计数器归零

对话记忆

每次修改运行都可以访问之前的修改记录作为上下文。记忆从两个来源合并并去重:

// DB 历史(跨 session 持久化)
const dbHistory = project.modificationHistory.map(r => ({
  instruction: r.instruction,
  summary: `${r.plan.analysis} Files: ${r.touchedFiles.join(", ")}`,
}));

// Session 历史(从客户端传入)
const merged = [
  ...dbHistory,
  ...sessionHistory.filter(h => !seenInstructions.has(h.instruction)),
].slice(-10); // 最多 10 轮

用户可以在修改输入框中输入 /clear 重置 session 记忆。/memory 命令打开调试面板,显示将注入下次 prompt 的完整合并历史。

图片输入

用户可以粘贴截图配合文字指令。图片以 vision content array 格式发送在第一条用户消息中:

content: imageBase64 ? [
  {
    type: "image_url",
    image_url: {
      url: imageBase64.startsWith("data:") ? imageBase64
         : `data:image/png;base64,${imageBase64}`,
      detail: "high",
    },
  },
  { type: "text", text: userMessage },
] : userMessage
图片保存在 modifyHistory 中,并在对话 UI 中显示为缩略图。 Agent 可以参考截图中的视觉细节来决定修改内容。