loop-context 实战:为 AI Agent 循环加上上下文管理与熔断器(loop-engineering 状态记忆原语)

发布时间:2026/9/23 14:35:21
loop-context 实战:为 AI Agent 循环加上上下文管理与熔断器(loop-engineering 状态记忆原语) loop-context 实战为 AI Agent 循环加上上下文管理与熔断器loop-engineering 状态记忆原语【免费下载链接】loop-engineeringPractical patterns, starters CLI tools for loop engineering with AI coding agents. Design systems that prompt and orchestrate agents (inspired by Addy Osmani and Boris Cherny). Includes loop-audit, loop-init, loop-cost.项目地址: https://gitcode.com/gh_mirrors/lo/loop-engineering导读loop-context是 loop-engineering 项目中的「有状态内存管理器」Stateful Memory Manager专门解决长跑 AI 编码循环的两大经典故障——上下文膨胀/腐化Context rot与停滞空转Stagnant loop。本文围绕 tools/loop-context/README.md 展开结合其 Quickstart 接入方式与源码实现带你掌握如何用--check熔断器让无人值守的 L2 循环在烧光 Token 之前升级给人类如何用--prune/--inject/--summary保持上下文窗口干净以及如何从loop-cost自动解析 Token 预算而不是手写猜测。读完你可以在自己的循环控制脚本中直接落地这套机制。一、为什么长跑循环会失败两类经典故障长期运行的 Agent 循环通常在两个方面失守这正是 loop-engineering 文档反复警告的上下文溢出与腐化——对话历史和错误日志不断累积直到模型丢失最初的目标或淹没在过期的堆栈跟踪里。停滞 / 无进展循环——Agent 一遍又一遍重试同一个失败动作悄悄烧掉整个 Token 预算。loop-context就坐在 Agent 与其持久内存STATE.md、运行日志之间在每次迭代之前做三件事Summarize总结——汇总已经尝试过什么、失败了什么Prune修剪——裁剪冗长的堆栈跟踪、折叠重复错误、丢弃最近窗口之外的陈旧尝试Inject注入——只把必要信息注入下一次 Prompt。而**熔断器circuit breaker**同时盯着迭代次数、Token 消耗、停滞同一错误连续出现 N 次和无进展连续失败过多四种信号——一旦命中就升级给人类处理而不是在无望的循环里空转。关键设计是整个过程完全确定、零依赖——总结和修剪不需要调用任何 LLM因此便宜到可以在每次迭代都跑。这一点可以从 context-manager.ts 的模块注释直接印证「All logic here is deterministic and dependency-free — no LLM call is required to summarize or prune」。二、安装与运行cobusgreyling/loop-context已发布到 npmpackage.json 中版本为 1.5.0Node 18MIT 协议见 tools/loop-context/package.json无需克隆仓库即可使用# 查看完整帮助含全部操作与选项说明 npx cobusgreyling/loop-context --help # 从本仓库源码构建并测试 cd tools/loop-context npm install npm test仓库内的npm test会先执行 TypeScript 构建npm run build再运行node --test test/*.test.mjs覆盖熔断器判定、修剪折叠、摘要分组、CLI 退出码与预算解析等全部行为见 tools/loop-context/package.json。三、运行账本Ledger循环的内存模型工具读取一个运行账本——记录循环目标与每次尝试的 JSON 文件{ goal: Get the failing migration test to pass, attempts: [ { iteration: 1, action: run migration, outcome: failure, error: Error: connect ECONNREFUSED 127.0.0.1:5432, tokensUsed: 1500 }, { iteration: 2, action: run migration again, outcome: failure, error: Error: connect ECONNREFUSED 127.0.0.1:5432, tokensUsed: 1400 } ] }账本结构定义在 context-manager.tsgoal是循环的原始目标Agent 绝不能丢失的锚点attempts是按时间先后排序的尝试列表。每条尝试Attempt的字段字段类型说明iterationnumber循环内从 1 开始计数的迭代号timestampstring可选尝试的 ISO 时间戳actionstring本轮 Agent 尝试的动作简短描述outcomesuccess \| failure \| noop尝试结果errorstring可选失败时的原始错误信息或堆栈跟踪tokensUsednumber可选本轮消耗的 Token 数repeatednumber由修剪器在折叠连续相同失败时写入的重复次数outcome是success | failure | nooperror与tokensUsed均为可选。启动循环时初始化一次即可{ goal: Get CI green on main, attempts: [] }。CLI 对账本有严格校验缺少goal字符串或attempts数组会直接报Invalid ledger: expected { goal: string, attempts: Attempt[] }.见 cli.ts。四、五个操作check / prune / inject / summary / status# 熔断器——退出码 0 继续2 升级接入循环的控制流 loop-context --check --ledger run.json # 为下一次 Prompt 生成紧凑的上下文块 loop-context --inject --ledger run.json # 对整轮运行做事实性汇总 loop-context --summary --ledger run.json --json # 输出修剪后的账本保留最近窗口、截断跟踪、折叠重复 loop-context --prune --ledger run.json # 从 stdin 管道读取 cat run.json | loop-context --check各操作含义与 cli.ts 中帮助文本一致操作作用输出--check运行熔断器判定退出码0继续 /2升级可加--json输出结构化决策--prune生成修剪后的账本JSON只保留window条最近尝试堆栈截断、重复折叠--inject生成注入下一次 Prompt 的上下文块Markdown 文本--summary事实性汇总文本或--json结构化汇总--status人类可读总览summary 熔断判定文本默认操作是--status账本来源缺省为 stdin也可用-f, --ledger file指定文件。五、熔断器何时熔断、如何判定5.1 六类触发条件--check是接入循环控制流的核心。它按「最具体、最易修复」优先的顺序判定见 context-manager.ts 的注释先查停滞、再查无进展之后才是绝对上限——这样当多个条件同时成立时报告的原因最有可操作性触发条件默认阈值含义stagnation停滞同一错误连续出现3次相同错误签名在尾部重复frustration挫败循环语义相近动作连续3次Agent 反复尝试高度相似的动作但始终失败no-progress无进展连续失败5次中间没有任何成功的连续失败token-budgetToken 预算无不启用累计 Token 达到上限daily-budget日预算无不启用跨多次运行的当日累计花费达到loop-cost建议的每日上限max-iterations迭代上限10次硬性迭代次数上限判定顺序说明停滞与挫败循环检查的是「尾部失败段」最后一次非失败之后的所有失败见trailingFailureRun一次成功会重置尾部失败段——测试breaker stagnation resets after a success验证了这一点失败-失败-成功-失败 的序列尾部只剩 1 次失败不会误触熔断见 context-manager.test.mjs。5.2 「同一个错误」是怎么识别的错误签名 Jaccard 相似度这是整个熔断器的技术核心。两个细节值得一提错误签名归一化errorSignature把原始错误/堆栈折叠成稳定签名即使行号、地址、时间戳、端口、临时路径等易变细节不同也能识别为「同一个错误」。它依次做取首个非空行、把 ISO 时间戳替换为ts、十六进制地址替换为addr、路径折叠为 basename、去掉行:列后缀、剩余数字替换为#见 context-manager.ts。测试验证了/home/u/app/foo.js:12:5与/tmp/build/foo.js:88:9会得到相同签名而127.0.0.1:5432与127.0.0.1:5433也会被折叠为同一类见 context-manager.test.mjs。字符三元组 Jaccard 相似度calculateSimilarity用小写化的字符三元组集合计算 0.0~1.0 的相似度对措辞细微变化非常鲁棒。--similarity-threshold默认0.85同时作用于熔断判定与修剪折叠测试用0.5阈值验证了「connection timeout on port 8080 / timeout on port 8080 / socket timeout on port 8080」这样措辞略有差异的错误同样会被判定为停滞见 context-manager.test.mjs。5.3 熔断决策的结构化输出--check --json会输出完整的BreakerDecisionshouldContinue、escalate、trigger六类触发之一或ok、reason人类可读说明、iterations、tokensUsed结构定义见 context-manager.ts。退出码约定0继续 ·2升级 ·1错误。例如相同的ECONNREFUSED连续出现 3 次时--check输出ESCALATE [stagnation] — Same error repeated 3× in a row ...并以退出码 2 结束。六、选项速查表Flag默认值含义--max-iterations n10硬性迭代次数上限--stagnation n3同一错误连续出现 N 次则升级--no-progress n5连续失败 N 次则升级--token-budget n无累计 Token 达到上限则升级--window n5修剪时保留的最近尝试条数--max-trace-lines n8修剪时每个堆栈跟踪保留的行数--similarity-threshold f0.850.0~1.0 浮点用于聚类相似错误同时作用于熔断与修剪值得注意的实现细节所有数值 Flag 都经过parsePositiveIntFlag/parsePositiveFloatFlag严格校验拒绝 NaN、0、浮点数——注释明确说明这样做的目的是「坏参数不能静默关闭熔断器」见 cli.ts。对应的 CLI 测试覆盖了--stagnation nope、--no-progress 0、--max-iterations 1.5等非法输入均以退出码 1 拒绝见 cli.test.mjs。七、上下文管理三件套的源码行为7.1 修剪--prunepruneLedger只保留最近window条尝试做两件事堆栈截断超过max-trace-lines行默认 8 行的跟踪保留头部末尾标注… (N more lines pruned)——pruneStackTrace的实现见 context-manager.ts。重复折叠连续相同相似度 ≥ 阈值的失败合并为一条repeated计数递增iteration前进到最新一轮——测试验证 3 条相同失败会折叠为repeated: 3, iteration: 3的 1 条见 context-manager.test.mjs。另一个保证修剪绝不修改输入账本——pruneLedger返回新对象测试pruneLedger does not mutate the input ledger明确断言了这一点见 context-manager.test.mjs。7.2 汇总--summarysummarizeAttempts是不需要 LLM 的确定性事实汇总总尝试数、成功/失败/noop 计数、累计 Token、按频次排序的去重错误分组按签名相似度聚类、已尝试过的动作列表。结构见 context-manager.ts。CLI 的人类可读输出形如Attempts: 4 (1 ok · 3 failed · 0 no-op) Tokens used: 1200 Distinct errors (most frequent first): (2×) Error: connect ECONNREFUSED 127.0.0.1:5432 Actions tried: - run migration - patch code7.3 注入--injectbuildContextInjection生成追加到下一次 Prompt 的紧凑 Markdown 块只包含 Agent 推进所需的信息目标、进度迭代数/成败/token、已尝试动作标注 do NOT repeat、失败模式分组、最近的已修剪错误、熔断器状态OK 或 STOP — circuit breaker tripped (trigger)。生成逻辑见 context-manager.ts测试断言了注入块包含 goal、动作列表、do NOT repeat与停滞时的STOP指令见 context-manager.test.mjs。八、从 loop-cost 自动解析 Token 预算手写--token-budget n等于靠猜。loop-cost已经为每个 pattern 和就绪级别L1–L3计算了贴近实际的单次运行估算scenarios.realistic.tokensPerRun所以正确的做法是从那里解析上限loop-context --check --ledger run.json --budget-from-pattern ci-sweeper --budget-level L2相关选项Flag默认值含义--budget-from-pattern id无在loop-cost注册表中查询的 pattern id--budget-level L1\|L2\|L3L1传给loop-cost的就绪级别--budget-scenario realistic\|action\|report\|cachingrealistic用作上限的loop-cost场景caching要求 pattern 配置了stable_fraction并会向loop-cost传--with-caching--budget-cadence specpattern 默认透传给loop-cost的节奏覆盖--budget-conservative关闭使用区间内较慢的节奏loop-cost自身的 Flag优先级规则显式--token-budget n永远优先于--budget-from-pattern——派生值只在你没写数字时兜底。测试cli --token-budget wins over --budget-from-pattern验证了两者同时给出时以token-budget触发熔断见 cli.test.mjs。实现层面loop-context通过 spawn 子进程调用loop-cost的 CLI先找 monorepo 兄弟包../loop-cost/dist/cli.js再找已安装的cobusgreyling/loop-cost依赖解析其--json输出——这与loop-init调用loop-audit所用的解析方式相同两个包在源码层面保持独立见 budget-resolver.ts。若 pattern id 未知会直接暴露loop-cost自身的错误如Unknown pattern: not-a-pattern见 cli.test.mjs而不是静默失败。九、跨多次运行的每日预算跟踪--budget-from-pattern只守护单次运行。但 pattern 还有跨越一天多次运行的每日上限loop-cost的suggestedDailyCap由--daily-budget-from-pattern负责跟踪loop-context --check --ledger run.json --daily-budget-from-pattern ci-sweeper \ --budget-level L2 --on-exceed ./scripts/on-budget-exceed.shFlag默认值含义--daily-budget-from-pattern id无跟踪并限制该 pattern 的当日累计花费--daily-state-dir dir.loop-contextdaily-spend.pattern.json的存放目录--on-exceed script无任何升级发生时把BreakerDecision以 JSON 形式通过 stdin 管道给该脚本工作原理结合 daily-spend.ts 与 cli.ts每次--check调用把账本最新一条尝试的tokensUsed累加到该 pattern 的状态文件{ date: ..., tokensUsedToday: N }以UTC 日期为滚动边界存储日期不是今天则归零重计todayUTC()见 daily-spend.ts当日累计达到 pattern 的建议每日上限后以daily-budget触发升级——但仅当没有更早触发的单次运行信号停滞、无进展、token-budget、max-iterations 优先测试does not override an existing per-run trigger验证了这一点见 cli.test.mjs状态文件读写使用锁文件 轮询串行化30 秒陈旧超时防止两个重叠的循环进程读到同一份过期总数、后写覆盖前写、静默丢失每日熔断增量——与loop-worktree的 manifest 互斥锁同型见 daily-spend.ts。--on-exceed script在任何升级时都会触发不限于daily-budget——它是自动化loop-budget.md中「On budget exceed」清单暂停调度器、记录事件、通知人类的通用钩子。脚本的退出码不会被检查也不影响--check自身的退出码即便脚本不读 stdin 就退出进程也不会崩溃EPIPE/EOF 写失败被吞掉见 cli.ts 及对应回归测试 cli.test.mjs。十、填充账本接入你的循环控制脚本循环控制脚本每轮迭代向run.json追加一个对象或把同构 JSON 从 stdin 管道传入# 每轮 Agent 迭代之后——先追加本次尝试再为下一轮把关 node -e const fs require(fs); const ledger JSON.parse(fs.readFileSync(run.json, utf8)); ledger.attempts.push({ iteration: ledger.attempts.length 1, action: run migration tests, outcome: failure, error: process.argv[1], tokensUsed: Number(process.argv[2] || 0), }); fs.writeFileSync(run.json, JSON.stringify(ledger, null, 2)); \$ERROR_MSG \$TOKENS loop-context --check --ledger run.json || { loop-context --inject --ledger run.json; exit 2; }循环内每轮迭代之前的标准写法# 在循环控制脚本内部、每次迭代之前 if ! loop-context --check --ledger run.json; then loop-context --inject --ledger run.json escalation.md # 把干净的摘要交给人类 exit 0 # 停止而不是重试 fi--inject的输出可以直接作为升级材料包含目标、已尝试动作、失败模式与最新已修剪错误人类接手时有完整上下文无需翻原始日志。十一、库级 API直接嵌入你的程序loop-context同时导出库 APIexports入口为dist/context-manager.js见 tools/loop-context/package.jsonimport { checkCircuitBreaker, pruneLedger, summarizeAttempts, buildContextInjection, } from cobusgreyling/loop-context; const decision checkCircuitBreaker(ledger); if (decision.escalate) escalateToHuman(decision.reason); else runNextIteration(buildContextInjection(ledger));四个核心导出与 CLI 一一对应全部是纯函数、无副作用、零依赖便于单元测试与嵌入。十二、在 Quickstart 中接线L2 熔断器关联文档的落地场景在 docs/QUICKSTART.md 的「3. Check cost before you schedule」之后Quickstart 已经为 L2 循环预留了熔断器小节——这正是本次关联文档scripts/issue-bodies/quickstart-loop-context.md要落实的内容。核心链路如下何时需要当循环开始无人值守地修代码时L2 及以上例如 CI Sweeper、PR Babysitter就应接入熔断器让它升级而非无限重试同一失败。脚手架预置loop-init会为可修复fix-capable的 pattern 生成loop-ledger.json和一个loop-guardskill见 tools/loop-init/README.md。每次重试前检查npx cobusgreyling/loop context --check --ledger loop-ledger.json # 等价于独立包形式 npx cobusgreyling/loop-context --check --ledger loop-ledger.json解读退出码0继续 ·2升级给人类。熔断在以下任一条件触发达到最大迭代数、同一错误连续重复 N 次、连续失败过多、或 Token 预算触顶。完整 API 与全部选项见 tools/loop-context/README.md。与其它工具配合时的两条建议Quickstart 中已给出当loop-context --check退出码为2时把对应的 worktree 标记为escalated再移交给人类loop-worktree mark --run-id id --status escalated两个工具保持独立——见 tools/loop-worktree/README.md。预算触顶的替代路径L3 自主循环在达到每日 Token 上限的 90% 且仍有高优先级事项时可以通过budget-negotiatorskill 请求一次扩展每日最多一次20%或至多50ktokens而不是被硬性截停人类必须在loop-budget.md中显式批准Agent 严禁自提上限。十三、它在 loop-engineering 原语体系中的位置从架构看loop-context对应的是 docs/primitives.md 中「五大原语 内存」里的Memory / State 原语的动态化实现STATE.md静态存储状态loop-context则在多次迭代之间动态管理它。成本侧由 tools/loop-cost 提供估算与注册表数据来自 patterns/registry.yamltools/loop-cost/README.md 中也有与本文对应的「Feed the circuit breaker」示例。运行与安全语义可进一步参考 docs/operating-loops.md。一句话总结这套模式的落地路径静态状态写在STATE.md动态状态放进run.json账本每次迭代前用loop-context --check把关、用--inject喂上下文触顶就退出码 2 升级给人类——这样无人值守的循环才能既不被上下文腐化拖垮也不在失败里烧光预算。【免费下载链接】loop-engineeringPractical patterns, starters CLI tools for loop engineering with AI coding agents. Design systems that prompt and orchestrate agents (inspired by Addy Osmani and Boris Cherny). Includes loop-audit, loop-init, loop-cost.项目地址: https://gitcode.com/gh_mirrors/lo/loop-engineering创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考