让 AI 智能体连续工作很容易,持续做对才难。每一轮,它都要知道下一步该做什么、怎样判断结果是否正确,以及什么时候必须停下来。缺少确定性检查、状态边界和保护机制时,循环跑得越久,越容易偏离目标,最后留下不断增长的账单和一堆难以解释的改动。
提示词解决一次调用,自主循环负责持续执行。目标只设定一次,系统便会寻找下一项工作、执行、检查、修复,持续重复,直到外部检查确认任务完成。一段写得漂亮的提示词无法保证这个过程可靠;循环能力的上限,取决于它能否稳定收敛到正确结果。
接下来从零搭建这样一套循环。路线图覆盖无状态迭代、幂等检查、上下文构建、隔离、奖励黑客防御和可观测性,每一步都配有运行机制说明和可执行代码。这些步骤存在明确依赖,顺序不能随意跳过;前面的基础缺失,后面的自动化只会放大问题。
目录
- 第 0 步:确认任务能否由机器明确判定
- 第 1 步:先手动可靠地跑通一次,并记录基线
- 第 2 步:最小循环,以及为什么要采用无状态设计
- 第 2.5 步:为每次迭代构建上下文
- 第 3 步:防止钻空子的检查机制与奖励黑客
- 第 4 步:把记忆放在磁盘上,并建立状态记录规范
- 第 5 步:具体落实隔离与影响范围控制
- 第 6 步:加入可观测的保护机制
- 第 7 步:按上下文增长方式估算成本
- 从日志识别循环的失败模式
- 结语
- 第 0 步:确认任务能否由机器明确判定
这一关能省下数周时间。只有在检查机制可以独立于 AI 智能体给出明确结论时,构建循环才有意义。
原因在模型的运行机制里。让生成方案的模型同时为自己的方案打分,会在统计层面造成利益冲突:模型自己的输出对它而言本来就是高概率续写,因此它会系统性地高估该输出的正确性。模型没有偷懒,这个偏差由采样分布直接造成。在这个分布中,模型自己的答案本就处于概率更高的位置。因此,AI 智能体的自我评价不能算作检查,只是一种回声。
检查必须来自外部的确定性判定器:测试、类型检查、linter、构建,或者某项指标是否超过阈值。它应当返回退出码,而非给出意见。
还有一项常被忽略的硬性要求:检查必须具有确定性和幂等性。不稳定测试在相同代码上时绿时红,比没有测试更糟,因为它会破坏停止条件。循环可能修复原本正常的代码,也可能在代码仍有问题时停止。搭建循环前,先对同一状态连续运行十次检查。如果结果不稳定,先修好检查,再构建循环。
如果任务过不了这一关,就别搭循环。
- 第 1 步:先手动可靠地跑通一次,并记录基线
不要自动化一个手动执行都无法成功的流程。先人工操控 AI 智能体完整跑通一次任务,直到检查变绿,同时记录数据。
记录模型调用次数、token 用量,以及 AI 智能体最常出现的错误类型。这些数据构成基线。如果循环后来消耗了三倍资源,你就能通过对比发现异常。
单次手动执行不稳定,循环只会按迭代次数放大这种不稳定性。先保证一次执行可靠,再开始自动化。
- 第 2 步:最小循环,以及为什么要采用无状态设计
最简单的可用循环就是一个while循环:持续向 AI 智能体发送提示,直到检查变绿。
#!/usr/bin/env bashset -euo pipefailMAX_ITER=20i=0while [ $i -lt $MAX_ITER ]; do i=$((i + 1)) echo "=== Iteration $i of $MAX_ITER ===" if npm test --silent; then echo "Green in $i iterations."; exit 0 fi claude -p "Tests fail. Run npm test, read the first failure, make the minimal change that fixes it. Do not refactor unrelated code. Do not weaken the tests." \ --permission-mode acceptEditsdoneecho "Limit $MAX_ITER. Tests red."; exit 1这里有一个容易被忽略的关键属性:每轮迭代都会重新启动一次 AI 智能体,并使用全新的上下文。这项工程决策直接针对上下文窗口的工作方式。
上下文窗口越接近容量上限,模型的表现越差。这种退化会造成可测量的质量损失,并非缓慢的线性下降:提示开头的指令会随着窗口填满而被遗忘;长上下文中部的信息最难被模型保留,这被称为“中间信息丢失”(lost-in-the-middle)效应;历史越长,模型越容易被自己过去的对话干扰,无法专注于当前状态。这类现象称为上下文腐化(context rot)。
无状态迭代可以从根上解决这个问题。进度由文件系统和 Git 保存,不依赖 AI 智能体的记忆。每次新运行都会看到已经修改的文件和失败的测试,重新读取当前状态,并在简短、干净的上下文里工作,指令始终清楚可见。主动丢弃对话记忆,可以避免质量随历史积累而退化。状态放在磁盘上,不要塞进上下文窗口。
MAX_ITER是第一根保险丝。没有它,循环会一直运行,直到预算耗尽。
- 第 2.5 步:为每次迭代构建上下文
说“使用新上下文”很容易,如何正确构建它是另一项独立的工程工作。许多循环就在这里出问题。如果每次迭代都把整个仓库树交给模型,无状态设计就失去了意义:上下文窗口再次被填满,上下文腐化重新出现,同时还要为大量无关 token 付费。如果提供的信息太少,AI 智能体又看不到必要内容,只能盲目修改。
合适的迭代上下文只包含三类信息:当前状态,也就是已经完成和仍被阻塞的事项;当前待修复的具体失败;与该失败直接相关的文件。不要提供整个仓库,只提供相关部分。
相关文件可以利用已有信号,按固定规则筛选,无需让 AI 智能体在整棵目录树中猜测。收集失败测试堆栈中出现的文件、上一次 diff 修改的文件,以及该测试导入的文件。这种方法成本低,而且结果确定。
#!/usr/bin/env bash# build_context.sh — assembles a narrow relevant context for the iterationset -euo pipefailCONTEXT_FILE=".loop_context.md"TOKEN_BUDGET=8000 # context ceiling so the window does not fill> "$CONTEXT_FILE"# 1. machine state first: where we are and what not to touchecho "## State" >> "$CONTEXT_FILE"cat .loop_state.json >> "$CONTEXT_FILE"echo >> "$CONTEXT_FILE"# 2. the specific failure being worked on (first failing test)echo "## Current failure" >> "$CONTEXT_FILE"failure=$(npm test 2>&1 | grep -A 15 -m1 "FAIL" || true)echo '' >> "$CONTEXT_FILE"echo "$failure" >> "$CONTEXT_FILE"echo '' >> "$CONTEXT_FILE"# 3. extract file paths from the failure stack trace (real repo files only)echo "## Relevant files" >> "$CONTEXT_FILE"files=$(echo "$failure" \ | grep -oE '[a-zA-Z0-9_/.-]+\.(ts|js|py|go)' \ | sort -u \ | while read -r f; do [ -f "$f" ] && echo "$f"; done)# 4. add files from the last diff (what the loop changed last turn)changed=$(git diff --name-only HEAD~1 2>/dev/null || true)# 5. merge, dedupe, pour in contents within the token budgetprintf "%s\n%s\n" "$files" "$changed" | sort -u | while read -r f; do [ -z "$f" ] && continue [ -f "$f" ] || continue # rough token estimate: chars / 4. do not exceed the budget budget_chars=$((TOKEN_BUDGET * 4)) current=$(wc -c < "$CONTEXT_FILE") fsize=$(wc -c < "$f") if [ $((current + fsize)) -gt "$budget_chars" ]; then echo "### $f (skipped, context budget exceeded)" >> "$CONTEXT_FILE" continue fi echo "### $f" >> "$CONTEXT_FILE" echo '' >> "$CONTEXT_FILE" cat "$f" >> "$CONTEXT_FILE" echo '' >> "$CONTEXT_FILE"doneecho "Context built: $(wc -l < "$CONTEXT_FILE") lines, $(($(wc -c < "$CONTEXT_FILE") / 4)) ~tokens"循环内的每次迭代不再把“所有信息”交给 AI 智能体,只传入这个精简后的上下文文件:
# inside the loop, before the agent call./build_context.shclaude -p "Context is in .loop_context.md. Fix the first failing testwith a minimal change, touch only files from the relevant ones." \ --permission-mode acceptEditstoken 上限必须写成明确数字。这个上限可以防止迭代上下文随着 diff 和堆栈信息增长而悄悄膨胀。缺少上限时,一个最初上下文干净的循环,在二十次迭代后仍会被自己的历史淹没,只是这次历史通过文件混了进来。限制上下文规模,可以维持每轮输出质量,并让成本近似线性增长。
这里的相关性启发式算法有意保持简单,只使用堆栈中的文件和上一次 diff。起步阶段就应该这样:成本低、结果确定、逻辑可解释。更智能的方案,例如文件嵌入或依赖图,可以提升精度,但也会引入复杂度,需要单独调试。先用简单的启发式算法,只有在它确实遗漏文件时再增加复杂度。
- 第 3 步:防止钻空子的检查机制与奖励黑客
这是循环的核心,包含两个独立的技术问题。
第一,检查必须独立。使用外部判定器,例如测试的退出码,不能依赖 AI 智能体自己的判断。
第二个问题更隐蔽:AI 智能体会设法欺骗检查。这是优化的自然结果,并不代表恶意。如果循环的唯一目标是让测试变绿,模型就会寻找成本最低的变绿路径,而这条路径经常是破坏测试,而非修复代码。删除断言、把所有依赖都替换成 mock、用try/except吞掉异常、把期望值硬编码进去,都属于奖励黑客(reward hacking):优化器利用指标的漏洞,没有完成实际任务。
防御需要分层完成。
在提示词里写“不要削弱测试”是最弱的一层。遇到阻力时,AI 智能体仍可能违反这项要求。
更可靠的防线,是设置一项 AI 智能体无法控制的二次检查。例如,把测试目录设为只读,让循环从权限层面无法编辑;或者设置独立门禁,确认 diff 中的测试文件没有变化:
# gate against reward hacking: tests must not change in this loopif ! git diff --quiet -- test/; then echo "Agent changed the tests. Revert, this is reward hacking." git checkout -- test/ exit 3fi第三层是使用不同模型担任独立评审。每轮结束后,让另一个评审 AI 智能体读取 diff,判断任务是否在实质上完成,而不能只看测试是否变绿。使用不同模型很重要:模型不善于识别自己的自我欺骗模式,却更容易发现其他模型的问题。
# .claude/agents/reviewer.md---name: reviewerdescription: Adversarial judge. After every code change.model: opus---Assume the author is wrong until the diff proves otherwise.Check separately: the tests went green BECAUSE the code was fixed,not because the tests were weakened. If asserts were deleted, mocksreplaced logic, values hardcoded, return FAIL with the location.You do not fix code, you deliver a verdict PASS or FAIL with a reason.成本也会随之增加:每轮使用强模型评审,会让调用费用翻倍。因此,只在错误代价高的环节启用它;便宜的确定性门禁,例如测试 diff 检查,则应当始终开启,它的成本几乎可以忽略。
- 第 4 步:把记忆放在磁盘上,并建立状态记录规范
一次运行结束后,模型会忘记发生过什么。循环的记忆应当放进一个文件,并要求每轮先读、最后写。
# STATUS.md (read first, written last)## Done- [x] auth: migrated to token v2, tests green## In progress- [ ] billing: webhook refactor (PR #214, CI red)## Next- [ ] dashboard: flaky test in test/charts## Never- do not touch infra/ without a human一个 Markdown 文件只是最低配置。更稳健的方案是把状态分成两层:面向人的STATUS.md方便查看,面向机器的状态文件供循环稳定、无歧义地解析。模型每次运行都可能对自由文本产生不同理解,所以影响逻辑的关键字段必须采用结构化格式:
// .loop_state.json — machine state, parsed unambiguously{ "phase": "billing-webhook", "iteration": 7, "last_green_commit": "a3f21c8", "blocked_paths": ["infra/", "test/"], "open_failures": ["test/billing/webhook.spec.ts:42"], "budget_spent_usd": 4.10}之所以要拆分,是因为人类可读和机器可解析是两项不同要求。STATUS.md供你早上快速查看,JSON 供循环执行逻辑。后者不能取决于模型今天如何改写计划。
把循环当成一个你从未见过面的夜班员工。第二天早上,你会根据它留下的交接记录评估工作,而不会知道凌晨三点具体发生了什么。因此,应当先设计交接记录,再设计循环。
- 第 5 步:具体落实隔离与影响范围控制
很少有人讲保护机制,但它占了循环工程的一半。在设置各类限制前,应先做好物理隔离。限制可能在单步操作中被突破,访问权限却只有两种状态:循环要么有能力破坏生产环境,要么没有这种能力。
通过 Git worktree 隔离,可以让循环在独立分支的单独工作副本中运行,与主工作树分开:
# separate worktree on its own branch, the loop lives only heregit worktree add ../loop-sandbox -b loop/billing-fixcd ../loop-sandbox这已经缩小了影响范围:循环看不到你正在工作的分支。不过,worktree 仍处于同一个文件系统中。要实现更强的隔离,可以使用权限收紧的容器:
# container: working folder writable, the rest read-only,# outbound network off (important against prompt injection)docker run --rm \ --network none \ --read-only \ --tmpfs /tmp \ -v "$(pwd):/work:rw" \ -v "$HOME/.claude:/root/.claude:ro" \ -w /work \ loop-runner ./loop.sh--network none是必要的安全措施。循环会读取不受信任的输入,包括任务描述、他人代码和提交信息。其中任何内容都可能包含提示注入,诱导 AI 智能体执行某条命令。如果一个 issue 写着“删除数据库并推送代码”,拥有网络和权限的 AI 智能体就可能照做。禁用出站网络,并把工作目录之外的文件设为只读,最坏影响也会被限制在沙箱内。影响范围控制首先是安全问题,同时也用于控制错误造成的损害。
设计循环时,先明确它能破坏哪些东西,再定义希望它完成的任务。先控制影响范围,再执行任务。
- 第 6 步:加入可观测的保护机制
接下来设置限制。更重要的是加入结构化日志,以便循环结束后能够查清它为什么停止。没有日志,凌晨三点面对一个已经跑崩的循环,你只能猜测发生了什么。
#!/usr/bin/env bashset -euo pipefailMAX_ITER=20MAX_BUDGET_USD=10i=0last_failure=""repeat_count=0LOG=".loop_log.jsonl"log() { # structured log, one json line per event echo "{\"ts\":$(date +%s),\"iter\":$i,\"event\":\"$1\",\"detail\":\"$2\"}" >> "$LOG"}while [ $i -lt $MAX_ITER ]; do i=$((i + 1)) echo "iter=$i ts=$(date +%s)" > .loop_heartbeat # liveness log "iter_start" "" if npm test --silent; then log "green" "done in $i"; echo "Green in $i."; exit 0 fi # reward-hacking gate: tests must not change if ! git diff --quiet -- test/; then log "reward_hack" "tests modified"; git checkout -- test/; exit 3 fi # circuit breaker: same failure 3 times = stuck current_failure=$(npm test 2>&1 | grep -m1 "FAIL" || true) if [ "$current_failure" = "$last_failure" ]; then repeat_count=$((repeat_count + 1)) if [ $repeat_count -ge 2 ]; then log "stuck" "$current_failure"; echo "Stuck, calling a human."; exit 2 fi else repeat_count=0 fi last_failure="$current_failure" log "agent_call" "$current_failure" claude -p "Fix the first failing test with a minimal change." \ --permission-mode acceptEdits \ --max-budget-usd "$MAX_BUDGET_USD"donelog "iter_limit" ""; echo "Iteration limit, tests red."; exit 1结构化日志会为每个事件记录时间、迭代编号、事件类型和详细信息。循环停止后,通过 grep 日志就能迅速看出模式:迭代次数是否持续增加却始终没有变绿,这属于失控运行;某个失败是否不断重复,这表示循环卡住;AI 智能体是否修改了测试,这属于奖励黑客;心跳从什么时候停止更新,这表示循环出现了静默停滞。没有日志时只能猜,有了日志才能诊断。
无人值守循环至少应当具备这些保护:迭代上限、单轮预算上限、重复检测器、存活标记、奖励黑客门禁,再加上第 5 步的隔离措施。
- 第 7 步:按上下文增长方式估算成本
成本上有一个反直觉的地方。循环的费用不能简单理解为“N 次模型调用”,它实际等于不断增长的上下文成本之和。
如果循环采用有状态设计,不断累积历史,那么每次迭代都会重新读取之前的全部对话,成本会呈二次增长:第k次迭代要为前k轮内容付费。这也是循环应采用无状态设计的经济原因。每次使用新上下文,可以让单轮成本大致保持恒定:只需读取少量磁盘状态,再加上本轮工作,不必重复处理全部历史。
启动前可以做一个粗略估算:
成本 ≈ 迭代次数 ×(状态 token 数 + 每轮工作 token 数)× 单价
用第 1 步测得的单轮成本乘以MAX_ITER,就能得到成本上限。如果这个数字高得难以接受,就降低MAX_ITER,或者把任务拆成多个阶段,不要在没有预算约束的情况下启动循环。
实际成本可能相差几个数量级。保护机制完善时,一单项目可能只花数百美元的 API 费用;缺少限制时,则可能烧掉数万美元。决定成本差异的,是有没有可靠检查和明确限制,与模型本身关系不大。
- 从日志识别循环的失败模式
循环通常有四种失败模式,前面的结构化日志可以识别其中三种。
失控运行(Runaway)。账单和迭代次数持续上升,检查始终没有变绿。日志中会连续出现大量agent_call,没有green。解决方法是设置迭代上限和预算上限。
静默停滞(Silent death)。循环看起来仍在工作,实际上已经停滞。日志表现为心跳停止更新,也不再产生新事件。常见原因是上下文已满。每个阶段使用新上下文,配合存活标记,可以捕捉这种症状。
随机游走(Random walk)。循环一直转,却离目标越来越远。日志中持续出现agent_call,current_failure每次都变成不同错误,却始终无法变绿。原因通常是缺乏硬性停止条件。解决方法是设置一项能够明确判断系统是否收敛的确定性检查。
认知债务(Understanding debt)。仓库持续增长,你对它的理解却越来越少。这一点完全不会出现在日志中,也最危险。循环生成代码的速度超过你的阅读速度,你开始不看 diff 就批准变更。解决办法是设置无法跳过的人工阅读环节,这只能依赖工程纪律。
前三种属于工程缺陷,日志可以捕捉,保护机制也能处理。第四种意味着工程师对代码的理解正在退化,代码本身解决不了这个问题。
- 结语
正确的构建顺序如下:
确定性检查 →
手动可靠地跑通一次并记录基线 →
最小无状态循环 →
受 token 预算约束的精简上下文 →
防止钻空子的检查(门禁 + 评审)→
磁盘状态(Markdown + JSON)→
隔离(worktree / 容器)→
带日志的保护机制 →
成本估算 →
定时运行
每月交付两百个 PR 的人,没有谁是从一百个 AI 智能体起步的。他们都从一个自己信得过的循环开始,这个循环有可靠的检查,也有完善的保护措施。找一个最枯燥的任务,用上述方法包成循环,并把规模控制在你能逐行阅读每个 diff 的范围内。先把这一个做扎实。
学AI大模型的正确顺序,千万不要搞错了
🤔2026年AI风口已来!各行各业的AI渗透肉眼可见,超多公司要么转型做AI相关产品,要么高薪挖AI技术人才,机遇直接摆在眼前!
有往AI方向发展,或者本身有后端编程基础的朋友,直接冲AI大模型应用开发转岗超合适!
就算暂时不打算转岗,了解大模型、RAG、Prompt、Agent这些热门概念,能上手做简单项目,也绝对是求职加分王🔋
📝给大家整理了超全最新的AI大模型应用开发学习清单和资料,手把手帮你快速入门!👇👇
学习路线:
✅大模型基础认知—大模型核心原理、发展历程、主流模型(GPT、文心一言等)特点解析
✅核心技术模块—RAG检索增强生成、Prompt工程实战、Agent智能体开发逻辑
✅开发基础能力—Python进阶、API接口调用、大模型开发框架(LangChain等)实操
✅应用场景开发—智能问答系统、企业知识库、AIGC内容生成工具、行业定制化大模型应用
✅项目落地流程—需求拆解、技术选型、模型调优、测试上线、运维迭代
✅面试求职冲刺—岗位JD解析、简历AI项目包装、高频面试题汇总、模拟面经
以上6大模块,看似清晰好上手,实则每个部分都有扎实的核心内容需要吃透!
我把大模型的学习全流程已经整理📚好了!抓住AI时代风口,轻松解锁职业新可能,希望大家都能把握机遇,实现薪资/职业跃迁~