Harness套Harness:让Coding Agent连续干70轮不崩的架构设计

发布时间:2026/11/4 3:48:47

Harness套Harness:让Coding Agent连续干70轮不崩的架构设计 1. 这套“Harness 套 Harness”到底在解决什么问题第一次看到“Harness 套 Harness让 Coding Agent 连续干 70 轮”这个说法我脑子里冒出来的第一个念头不是“哇好厉害”而是“这不就是把 Agent 的上下文管理和任务编排做成了两层结构吗”。后来仔细拆了一下上海 AI Lab 这套思路发现它确实抓住了一个非常现实的痛点单个 Coding Agent 在长任务上跑不了几轮就会开始“失忆”“跑偏”“自我重复”。做过 AI 辅助开发的人应该都有体会。你让一个编码 Agent 帮你改一个模块前两三轮它表现很好能读代码、能改文件、能跑测试。但到了第五轮、第八轮它开始忘记之前改过什么开始重复修同一个 bug开始把已经通过的测试又改坏。这不是模型能力不够而是单层 Agent 架构在长程任务上的结构性缺陷——上下文窗口有限、状态管理缺失、任务分解粒度不够、缺少外部校验机制。上海 AI Lab 这篇工作的核心思路用一句话概括就是用一个外层 Harness 来管理任务分解和轮次调度用内层 Harness 来执行具体的编码操作两层之间通过结构化的状态传递来维持长程一致性。这样做的直接效果就是Coding Agent 可以连续执行 70 轮以上的复杂开发任务而不崩溃。这里需要先厘清一个概念。Harness 在 AI Agent 语境下指的是一套包裹在模型外面的工程框架它负责提示词组装、工具调用、结果解析、状态维护、错误重试这些“模型之外”的事情。你可以把它理解成 Agent 的“操作系统”——模型是 CPUHarness 是操作系统工具是外设。很多人把 Harness 和 Agent 混着用其实两者有明确区别Agent 是“谁在做决策”Harness 是“决策怎么被执行和记录”。一个 Agent 可以跑在不同的 Harness 上同一个 Harness 也可以驱动不同的 Agent。那“Harness 套 Harness”又是什么简单说就是外层 Harness 负责“做什么”内层 Harness 负责“怎么做”。外层管任务规划、轮次控制、进度追踪、失败回滚内层管代码读写、命令执行、测试运行、结果收集。两层之间通过一个结构化的“任务状态包”来通信而不是靠把全部历史对话塞进上下文。这套架构适合谁参考我认为三类人最应该看一是正在做 AI 辅助开发工具的产品和技术团队二是想把 Coding Agent 用到实际项目里的独立开发者三是研究多智能体编排和长程任务执行的研究人员。哪怕你只是用现成的编码 Agent 工具理解这套分层思路也能帮你更好地设计自己的提示词和工作流。2. 为什么单层 Harness 跑不长核心瓶颈拆解2.1 上下文窗口不是唯一的问题很多人把 Agent 跑不长归因于“上下文窗口不够大”。这个判断对了一半。上下文窗口确实是硬约束但即使你把窗口扩到很大单层 Agent 依然会在长任务上出问题。原因在于上下文里塞的东西越多信噪比越低。前面 30 轮的工具调用记录、文件内容、错误日志混在一起模型很难从中准确提取“当前应该做什么”。我实测过一个场景让单层 Agent 连续修改一个包含 15 个文件的模块。到第 12 轮左右它开始把之前已经改好的文件又改回去理由是“根据最初的代码结构”。这就是典型的上下文污染——早期的代码快照和当前的代码状态混在一起模型分不清哪个是最新的。2.2 任务分解粒度失控单层 Harness 的另一个问题是任务分解粒度不好控制。你给一个“实现用户认证模块”的任务Agent 可能第一轮就试图把所有文件都改了结果每个文件都改得不完整也可能过于保守一轮只改一行70 轮都做不完。外层 Harness 的价值就在这里。它可以把“实现用户认证模块”拆成“设计接口 → 实现数据层 → 实现业务逻辑 → 写测试 → 跑测试 → 修复失败用例”这样的结构化步骤每一步再拆成具体的文件操作。分解粒度由外层控制内层只负责执行当前步骤这样就不会出现“一口吃成胖子”或者“磨洋工”的情况。2.3 缺少外部状态锚点单层 Agent 的状态全在上下文里一旦上下文被截断或者模型开始“幻觉”状态就丢了。两层 Harness 的做法是把关键状态写到外部存储——比如一个 JSON 文件或者 SQLite 数据库记录当前完成了哪些步骤、哪些文件被改过、测试通过率是多少、下一步该做什么。这个外部状态锚点有几个好处。第一即使内层 Agent 的上下文被重置外层也能根据状态文件恢复任务。第二多个内层 Agent 可以共享同一个状态文件实现并行协作。第三人类可以在任何时候查看状态文件了解任务进展必要时手动干预。2.4 错误恢复机制缺失单层 Agent 遇到错误时通常的做法是把错误信息塞回上下文让模型自己想办法。但模型往往会陷入“修一个错引入两个新错”的循环。两层 Harness 可以在外层设置错误恢复策略比如连续三次测试失败就回滚到上一个稳定状态或者切换到不同的修复策略或者把问题标记出来交给人类处理。这套机制的核心思想是不要让模型自己决定什么时候放弃而是由外层 Harness 根据预设规则来决定。模型负责执行Harness 负责判断。3. 两层 Harness 的架构设计与核心组件3.1 外层 Harness任务规划与轮次调度外层 Harness 的职责可以概括为四个字规划、调度。它不直接操作代码而是维护一个任务队列和状态机。具体来说外层 Harness 包含这几个核心模块任务分解器把用户的高层需求拆成结构化的步骤列表。每个步骤包含目标描述、预期产出、验收标准。轮次调度器决定当前该执行哪个步骤以及这个步骤需要几轮内层调用来完成。状态管理器维护任务状态文件记录每个步骤的执行状态、产出物、测试结果。回滚控制器当某个步骤连续失败时决定是重试、跳过还是回滚。进度报告器定期输出任务进展方便人类监控。这里的关键设计是任务分解器不依赖模型自由发挥。常见做法是先用一个规划模型生成步骤列表然后由人类或者规则引擎校验这个列表的合理性最后才交给内层执行。这样避免了模型把任务拆得乱七八糟。3.2 内层 Harness编码执行与工具调用内层 Harness 的职责是执行。它接收外层传来的单个步骤调用编码 Agent 完成具体的文件操作、命令执行、测试运行。内层 Harness 的核心模块包括提示词组装器根据当前步骤和状态文件组装精简的提示词。注意这里不是把全部历史塞进去而是只放当前步骤相关的信息。工具调用层提供文件读写、命令执行、代码搜索、测试运行等工具。结果解析器解析 Agent 的输出提取文件修改、命令结果、错误信息。状态回写器把执行结果写回状态文件供外层读取。内层 Harness 的一个关键设计是上下文隔离。每个步骤执行时内层只加载当前步骤需要的文件内容和状态信息不加载全部历史。这样即使任务跑了 70 轮内层的上下文依然保持精简。3.3 两层之间的通信协议两层 Harness 之间的通信靠一个结构化的状态包。这个状态包通常是一个 JSON 对象包含{ task_id: auth-module-001, current_step: 3, total_steps: 8, step_description: 实现用户登录接口的密码校验逻辑, files_in_scope: [src/auth/login.py, src/auth/utils.py], acceptance_criteria: 登录接口对错误密码返回 401对正确密码返回 token, previous_results: { step_1: completed, step_2: completed }, test_status: 3 passed, 1 failed, retry_count: 0 }外层把这个状态包传给内层内层执行完后更新状态包返回外层。这个状态包就是两层之间的唯一接口它保证了状态的一致性和可追溯性。3.4 为什么是两层而不是三层或更多有人可能会问为什么不做三层 Harness我的理解是两层已经能覆盖“规划”和“执行”这两个本质不同的职责。再加一层往往会引入不必要的复杂度而且层与层之间的通信开销会显著增加。当然在更复杂的场景下可以在外层之上再加一个“元规划层”负责多个任务之间的协调。但对于单个 Coding Agent 的长程任务来说两层结构是最优的平衡点。4. 连续 70 轮不崩的关键实现细节4.1 状态文件的原子化更新状态文件是两层 Harness 的“记忆中枢”它的更新必须是原子化的。什么意思就是每次更新要么全部成功要么全部失败不能出现“改了一半”的情况。实操中我建议用写临时文件再重命名的方式来实现原子更新。具体做法是先把新状态写到state.json.tmp确认写入成功后再用os.replace()把临时文件重命名为state.json。这样即使写入过程中程序崩溃原状态文件也不会被破坏。注意不要直接打开原文件写入那样一旦中途出错状态文件就损坏了整个任务无法恢复。4.2 上下文裁剪策略内层 Harness 每次执行时上下文里应该放什么我的经验是遵循三放三不放原则放当前步骤的描述和验收标准当前步骤涉及的文件内容最近一次失败的错误信息如果有不放全部历史对话记录已完成步骤的详细过程与当前步骤无关的文件内容这样做的理由是模型在执行具体步骤时只需要知道“现在做什么”和“相关代码长什么样”不需要知道“之前是怎么做的”。历史信息由外层 Harness 以摘要形式维护需要时再按需加载。4.3 轮次预算与超时控制连续 70 轮不崩不代表每一轮都可以无限跑。外层 Harness 需要给每个步骤设置轮次预算和超时时间。比如一个“实现登录接口”的步骤预算 5 轮内完成每轮最多 3 分钟。超过预算就触发回滚或者人工介入。这个机制的价值在于防止 Agent 在某个步骤上无限循环。我见过太多案例Agent 在一个小 bug 上反复修了 20 轮把其他正常的代码也改坏了。有了轮次预算外层可以在第 5 轮就强制中断回滚到稳定状态换一种策略重试。4.4 测试驱动的验收机制每个步骤执行完后内层 Harness 应该自动运行相关测试把测试结果写回状态包。外层根据测试结果决定下一步全通过就进入下一步部分失败就重试当前步骤全部失败就回滚。这里的关键是测试用例要提前准备好。我通常的做法是在任务分解阶段就让规划模型生成每个步骤的验收测试人类审核后固化下来。这样执行阶段就有明确的“对错标准”不依赖模型自己判断“做完了没有”。4.5 失败模式分类与差异化处理不是所有失败都一样。外层 Harness 应该对失败模式进行分类失败类型典型表现处理策略语法错误代码无法解析重试当前步骤提示模型检查语法测试失败测试用例不通过重试附带失败用例详情超时步骤执行超预算回滚拆分步骤后重试状态冲突文件被意外修改回滚到稳定状态重新执行循环重复连续多轮无进展中断切换策略或人工介入这张表是我在实际项目中总结的不同失败类型用不同策略比一刀切地“重试三次”有效得多。5. 实操落地从零搭建一套两层 Harness5.1 环境准备与依赖选择搭建这套系统不需要特别重的依赖。我的推荐配置是Python 3.10异步支持好一个支持工具调用的模型 APISQLite 或 JSON 文件做状态存储Git 做代码版本管理回滚靠它pytest 或项目自带的测试框架为什么用 SQLite 而不是直接上 Redis 或者 PostgreSQL因为单机任务用 SQLite 足够了而且零配置、零运维。状态数据量不大读写频率也不高SQLite 完全扛得住。等你要跑多机并行任务时再考虑换。5.2 外层 Harness 的最小实现外层 Harness 的核心是一个循环读状态 → 决定下一步 → 调用内层 → 更新状态 → 判断是否继续。import json import os class OuterHarness: def __init__(self, state_path, inner_harness): self.state_path state_path self.inner inner_harness self.state self.load_state() def load_state(self): if os.path.exists(self.state_path): with open(self.state_path, r) as f: return json.load(f) return {current_step: 0, steps: [], history: []} def save_state(self): tmp_path self.state_path .tmp with open(tmp_path, w) as f: json.dump(self.state, f, ensure_asciiFalse, indent2) os.replace(tmp_path, self.state_path) def run(self, max_rounds70): for round_num in range(max_rounds): step self.get_current_step() if step is None: print(所有步骤已完成) break result self.inner.execute(step, self.state) self.update_state(step, result) if result[status] failed: if self.should_rollback(step): self.rollback() elif self.should_retry(step): continue else: self.mark_for_human(step) break self.save_state()这段代码的关键点是状态保存用原子操作以及失败处理有明确的分支逻辑。不要小看这两点很多 Agent 跑着跑着就崩了就是因为状态保存不原子或者失败处理没写好。5.3 内层 Harness 的执行流程内层 Harness 接收一个步骤组装提示词调用模型解析结果执行工具调用。class InnerHarness: def __init__(self, model_client, tools): self.model model_client self.tools tools def execute(self, step, state): prompt self.build_prompt(step, state) response self.model.chat(prompt, toolsself.tools.schemas()) actions self.parse_actions(response) results [] for action in actions: result self.tools.execute(action) results.append(result) test_result self.run_tests(step) return { status: success if test_result[passed] else failed, actions: actions, test_result: test_result } def build_prompt(self, step, state): # 只放当前步骤相关的信息 return f 当前任务{step[description]} 验收标准{step[acceptance_criteria]} 涉及文件{step[files]} 最近错误{state.get(last_error, 无)} 注意build_prompt里没有放全部历史只放了当前步骤需要的信息。这是上下文裁剪策略的具体落地。5.4 状态文件的结构设计状态文件的结构直接决定了系统的可维护性。我建议至少包含这几个字段{ task_id: unique-id, created_at: 2024-01-01T00:00:00, current_step: 3, total_steps: 8, steps: [ { id: 1, description: 设计用户认证接口, status: completed, attempts: 1, artifacts: [docs/auth_api.md] } ], last_error: null, rollback_points: [ {step: 2, git_commit: abc123} ] }rollback_points是关键。每次完成一个步骤就记录一个 Git commit hash。需要回滚时直接git reset --hard到对应的 commit。用 Git 做回滚比自己做文件快照可靠得多而且天然支持查看历史。5.5 跑通第一个 70 轮任务第一次跑的时候不要直接上真实项目。我的建议是先在一个玩具项目上验证比如一个包含 5 个文件的小型 Flask 应用任务是“添加用户注册和登录功能”。具体步骤把玩具项目初始化成 Git 仓库提交初始状态。写好任务分解拆成 8 到 10 个步骤。为每个步骤写好验收测试。启动外层 Harness设置 max_rounds70。观察日志记录每次失败的原因。我第一次跑的时候在第 23 轮遇到了“测试通过但代码风格检查失败”的情况。原因是内层 Agent 只关注了功能测试没关注 lint。后来我在验收标准里加了 lint 检查问题就解决了。这种细节只有实际跑过才会发现。6. 常见问题与排查技巧实录6.1 Agent 在第 N 轮开始重复之前的操作这是最常见的问题。表现是 Agent 反复修改同一个文件每次改的内容差不多测试结果也差不多。排查思路先看状态文件里current_step有没有正常推进。如果current_step卡住了说明外层调度有问题如果current_step在推进但内层还在改旧文件说明内层的上下文裁剪没做好把旧文件内容带进来了。解决方法检查build_prompt里的files_in_scope是否只包含当前步骤的文件。另外可以在状态包里加一个recently_modified_files列表提示模型“这些文件刚改过不要重复改”。6.2 状态文件损坏导致任务无法恢复这个问题通常是因为状态保存不是原子操作。如果程序在写状态文件时崩溃文件可能只写了一半JSON 解析失败。解决方法一是用前面说的临时文件加重命名的方式二是每次保存前先备份一份state.json.bak三是加载状态时加 try-catch解析失败就尝试从备份恢复。提示状态文件建议用 Git 一起管理这样即使本地文件损坏也能从 Git 历史里找回。6.3 内层 Agent 调用工具时参数格式错误模型调用工具时参数格式经常出错。比如该传字符串的传了数字该传数组的传了对象。解决方法在工具定义里把参数 schema 写清楚并且在提示词里给一两个正确示例。另外内层 Harness 的parse_actions要做参数校验格式不对就返回错误信息让模型重试而不是直接崩溃。6.4 测试运行时间过长拖慢整体进度如果每个步骤都跑全量测试70 轮下来时间会非常长。我的做法是分层测试每个步骤只跑相关模块的单元测试每完成 3 到 5 个步骤跑一次集成测试全部完成后跑一次全量测试。这样既保证了质量又控制了时间。实测下来分层测试比每轮全量测试快 3 到 5 倍。6.5 回滚后状态不一致回滚代码容易回滚状态难。如果代码回滚了但状态文件没回滚就会出现“代码是旧的状态是新的”这种不一致。解决方法把状态文件和代码 commit 绑定。每次记录 rollback point 时同时记录状态文件的快照。回滚时代码和状态一起回滚。这样保证两者始终一致。6.6 常见问题速查表问题可能原因快速排查方法Agent 重复操作上下文污染检查 files_in_scope状态文件损坏非原子写入检查是否有 .tmp 残留工具调用失败参数格式错误查看工具 schema 和实际参数进度太慢测试粒度过细改为分层测试回滚后不一致状态未同步回滚检查 rollback point 是否含状态快照轮次超预算步骤拆分不合理重新分解任务减小步骤粒度7. 这套架构还能怎么扩展跑通基础版本后我试过几个扩展方向效果不错。第一个是并行内层执行。当两个步骤之间没有依赖关系时可以同时启动两个内层 Harness 并行执行。比如“实现登录接口”和“实现注册接口”可以并行只要它们不修改同一个文件。并行执行能把 70 轮的总时间压缩不少。第二个是多模型混合。外层规划用推理能力强的模型内层执行用代码能力强的模型。这样各取所长整体效果比单模型好。我实测过外层用一个大模型做规划、内层用一个小模型做执行成本降了一半效果基本持平。第三个是人类介入点。在关键步骤比如数据库 schema 变更设置人工确认点外层 Harness 执行到这些步骤时暂停等人类确认后再继续。这样既保留了自动化效率又在关键决策上保留了人类控制。第四个是经验回放。把每次任务的状态文件和执行日志保存下来下次遇到类似任务时外层 Harness 可以参考历史经验来优化任务分解。这个方向我还在摸索目前的做法是维护一个“任务模板库”把成功的任务分解方案存下来复用。这套两层 Harness 的思路本质上是用工程手段弥补模型在长程任务上的不足。模型能力会越来越强但工程架构的价值不会消失——因为再强的模型也需要有人告诉它“现在做什么”“做到什么程度算完”“出错了怎么办”。把这三个问题解决好Coding Agent 才能真正从“玩具”变成“工具”。
延伸阅读

更多相关文章

2026/11/4 3:48:47

施耐德 Vijeo Citect 6.1 SCADA 工程配置与现场排障

简介:Vijeo Citect 6.1 压缩包面向工业自动化工程师、施耐德上位机软件学习者以及需要搭建SCADA监控项目的技术人员,可用于图形化操作站画面设计、实时数据采集、报警事件管理、数据库集成与脚本扩展等典型场景,也适合作为旧版本工程维护和功…

2026/11/4 3:48:47

阿里云ECS运维学习指南:从入门到实践

1. 为什么选择阿里云ECS作为运维学习服务器?作为一名从业多年的运维工程师,我强烈建议初学者从阿里云ECS经济型e实例开始学习。2核2G3M带宽的配置对于学习Linux运维、Docker和Jenkins部署来说完全够用,而且价格非常亲民(通常每月不…

2026/11/4 3:43:46

智能家居AI认知层技术:从品牌基因到场景理解

1. 项目背景与核心价值去年给某智能家居品牌做技术咨询时,遇到个有意思的现象:他们的AI系统能准确识别"打开客厅灯"这样的明确指令,但对"来点氛围光"这类抽象需求就束手无策。这暴露了当前智能家居领域的通病——AI缺乏对…

2026/11/4 9:04:19

解决IDEA中Spring Boot配置自动补全失效问题

1. 问题现象与背景分析 最近在IntelliJ IDEA中开发Spring Boot项目时,突然发现application.properties和application.yml文件失去了代码自动补全功能。这个问题看似不大,却严重影响开发效率——每次输入配置属性时都得手动查阅文档,稍不注意就…

2026/11/4 9:04:19

8G显存跑本地AI数字人:Voice Companion选型与避坑指南

不用换显卡,不用买云服务器,8G 显存的机器一样能跑出一套像模像样的本地 AI 数字人,能听能说,反应速度还凑合。这篇文章就从我最近搞的 Voice Companion 出发,把选型、时间、成本、占显存的账,以及踩过的那…

2026/11/4 9:04:19

HTML零基础入门:从创建第一个网页开始

1. 项目概述记得我第一次接触HTML时,那种既兴奋又忐忑的心情至今难忘。那是一个周末的下午,我对着电脑屏幕,试图用几行简单的代码创造出属于自己的第一个网页。对于完全零基础的我来说,这就像第一次拿起画笔面对空白画布的感觉。H…

2026/11/4 9:04:19

ResearchDownload固件烧写:多模块并行与自动检测避坑

简介:面向手机维修、产线测试与嵌入式烧录场景的ResearchDownload R24.0.0001烧写工具,主要用于通过USB数据线或串口线连接电脑与手机,将电脑中的程序写入手机硬件模块,适合需要批量刷机、多模块下载与自动检测的维修及测试人员。…

2026/11/4 9:04:19

企业形象工程的技术实践与标准化管理

1. 企业形象工程的技术实践概述 在商业竞争日益激烈的今天,企业形象工程已成为品牌建设不可或缺的一环。作为从业十余年的品牌技术顾问,我见证过太多企业在这个环节踩坑——有的花大价钱做了全套VI手册却束之高阁,有的反复修改设计规范导致执…

2026/11/4 8:59:18

括号匹配为什么用栈:有效括号判断的原理与工程实践

简介:这是一份围绕括号匹配(平衡括号)判定编写的 JavaScript 代码示例包,面向正在学习字符串处理与栈结构的初学者,以及准备算法面试的开发者,用于解决“左括号须由同类型右括号按正确顺序闭合”的有效性判…

2026/11/3 7:47:06

面向复杂光照与角度的宠物面部识别鲁棒性增强策略

在智慧养宠时代,基于视觉的宠物身份识别技术正逐步从实验室走向家庭应用。然而,与配合度高的人类不同,宠物是典型的“非合作生物识别对象”——它们不会主动配合摄像头调整姿态,更无法控制光照环境。实际应用中,我们常…

2026/11/3 14:24:53

SSM电子书店源码全解析:结构、原理、部署与排错实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/11/4 0:03:20

AI辅助技术栈评估:提升研发决策效率60%

1. 项目背景与核心价值技术决策一直是研发团队面临的重大挑战。随着技术生态的快速演进,技术栈的选择、维护和迭代变得愈发复杂。我们团队在过去两年里,先后经历了三次重大技术栈迁移,每次都需要投入大量人力进行技术评估和风险分析。直到去年…

2026/11/4 0:03:20

从生成Token到直接决策:Jev如何重塑AI Agent工程实践

1. 从"生成文字"到"拍板决策":Jev 到底在解决什么问题大多数人第一次接触大模型,脑子里建立的模型是"输入问题、输出文字"。你问它今天天气怎么样,它给你一段描述;你让它写个周报,它给你…

2026/11/4 0:03:20

32G统一内存跑大模型:内存账、TPS实测与端云分流

想在一台 32G 统一内存的小主机上把大模型跑成"日常可用",这件事我前后折腾了小半年。标题里说的这台 Mac mini M6,是我们这次讨论的目标平台,具体到手规格以实物为准;但下面这套推算方法和实测思路,对任何统…

2026/11/3 7:47:06

面向复杂光照与角度的宠物面部识别鲁棒性增强策略

在智慧养宠时代,基于视觉的宠物身份识别技术正逐步从实验室走向家庭应用。然而,与配合度高的人类不同,宠物是典型的“非合作生物识别对象”——它们不会主动配合摄像头调整姿态,更无法控制光照环境。实际应用中,我们常…

2026/11/3 14:24:53

SSM电子书店源码全解析:结构、原理、部署与排错实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
☎咨询二维码 ☎ ↑