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工具调用后成功执行工具后计数器归零

对话记忆与工作记忆

持久层仍是 turn 列表(DB modificationHistory + session 合并去重)。 注入主 Agent / Router 时不再塞「最近 10 轮全文」,而是:

merged = mergeModifyHistoryTurns(dbHistory, sessionHistory)

// 工作记忆 = 从 turn 列表确定性投影(不落新 DB 列)
workingMemory = projectWorkingMemory(merged)
//   focusFiles (≤3) · pendingQuestion · lastIntent …

// 主 Agent:状态卡 + 最近 2 轮原文(非最近轮 Result 截断)
// Router:状态卡 + 短历史
// /clear → 两侧输入为空 → 投影为空

用于稳住「再大一点」类短程指代,同时压低旧 Result 噪音。/clear 重置 session;/memory 打开调试面板(GET /api/projects/[id]/memory)查看将注入的投影与历史。

图片输入

用户可以粘贴截图配合文字指令。图片以 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 可以参考截图中的视觉细节来决定修改内容。