☰
Agentic Harness Workflow:AI写代码的工程化流程与落地实践
2026/10/16 5:01:26 网站建设 项目流程

1. 为什么“让AI写代码”这件事需要一套工程化流程

1.1 从“对话式写代码”到“流程化跑任务”的认知转变

大多数人第一次用AI写代码,体验都差不多:打开一个对话框,把需求描述一遍,AI吐出一段代码,复制粘贴,跑一下,报错,再贴回去让它改,来回几轮之后代码能跑了,但整个过程像在跟一个记性不太好但很聪明的实习生聊天。这种方式在写几十行脚本的时候没问题,一旦项目规模上去,问题就全暴露出来了:上下文丢失、改一处崩三处、没有测试、没有版本记录、没法复现。

Agentic Harness Workflow 框架要解决的就是这个问题。它把AI Coding从“聊天窗口里的即兴发挥”变成“有输入、有执行、有验证、有回滚的工程化流水线”。你可以把它理解成给AI搭了一个工作台(Harness),这个工作台规定了AI能做什么、按什么顺序做、做完之后谁来检查、检查不过怎么办。AI不再是那个随叫随到的聊天对象,而是流水线上的一个执行单元。

这套东西适合谁?如果你只是偶尔写个爬虫脚本、改个配置文件,说实话用不上,直接对话就够了。但如果你在维护一个有多个模块、有测试用例、有CI流程的项目,或者你想让AI帮你批量处理重复性的编码任务,那这套框架的思路就非常值得参考。它不绑定某个具体的AI工具,核心是一套组织AI工作方式的工程方法论。

1.2 核心问题拆解:AI Coding 到底卡在哪

在展开具体方案之前,先把痛点拆清楚。我观察下来,AI Coding在实际项目中卡壳的地方主要集中在四个层面。

第一个层面是上下文管理。AI的上下文窗口是有限的,一个中型项目动辄几万行代码,你不可能每次都把整个项目塞进去。但如果你只给它看一个文件,它改出来的代码很可能跟其他模块的接口对不上。怎么在有限的上下文里给它提供最有效的信息,这是一个工程问题。

第二个层面是任务分解与编排。一个复杂需求,比如“给用户模块加上权限校验”,这不是一次对话能搞定的。它涉及数据库改动、接口层改动、中间件添加、测试补充等多个子任务,而且这些子任务之间有依赖关系。人类工程师会自然地拆解,但AI需要你显式地告诉它先做什么后做什么。

第三个层面是验证与反馈闭环。AI生成的代码不一定对,甚至看起来对但边界条件处理有问题。如果没有自动化的验证机制,你就得人工逐行审查,那效率提升就大打折扣了。理想的情况是:AI写完代码,自动跑测试,测试挂了自动把错误信息喂回给AI让它修,修完再跑,直到通过或者达到重试上限。

第四个层面是可追溯与可复现。今天AI帮你改了一版代码,明天你想知道它到底改了什么、为什么这么改、当时用的什么提示词,如果这些信息没有记录下来,出了问题你连排查的方向都没有。

Agentic Harness Workflow 框架的思路就是围绕这四个层面,搭建一套结构化的流程。下面我会逐层拆解这套框架的设计思路、核心组件和实操方法。

2. 框架整体设计:Harness 到底长什么样

2.1 核心架构:四层结构拆解

我把这套框架的架构归纳为四层,从下往上分别是执行层、编排层、验证层和记录层。每一层各司其职,层与层之间通过明确定义的接口通信。

执行层是最底层,负责实际调用AI模型生成代码。这一层需要处理的事情包括:构造提示词、管理上下文窗口、调用模型API、解析返回结果。听起来简单,但细节很多。比如上下文窗口快满了怎么办?是截断旧消息还是做摘要压缩?模型返回的代码块格式不标准怎么解析?这些都是执行层要兜住的问题。

编排层在执行层之上,负责把一个大任务拆成多个子任务,并管理它们之间的依赖关系。这一层决定了整个流程的“形状”——是线性的还是带分支的,是串行还是可以并行的,遇到失败是重试还是跳过还是终止。编排层的设计直接影响到整个流程的效率和可靠性。

验证层是保证代码质量的关键。它负责在AI生成代码之后自动执行验证逻辑,包括语法检查、单元测试、集成测试、静态分析等。验证不通过时,它需要把失败信息结构化地传回编排层,由编排层决定下一步动作。

记录层贯穿整个流程,负责记录每一步的输入、输出、耗时、结果状态。这些记录不仅是排查问题的依据,也是后续优化提示词和流程的素材。

这四层不是必须严格分开的,在小规模场景下你可以把它们揉在一起,但当流程复杂度上升时,分层带来的清晰度会帮你省下大量调试时间。

2.2 为什么选择“工作台”模式而不是“全自动”模式

这里有一个关键的设计选择需要解释:为什么叫Harness(工作台/约束框架)而不是Agent(智能体)?市面上很多方案追求的是“全自动”,你给一个需求,AI自己规划、自己执行、自己验证,全程不需要人干预。听起来很美好,但实际用下来问题很多。

全自动模式最大的问题是不可控。AI在规划阶段可能选了一条你完全不认可的路线,等你发现的时候它已经沿着这条路走了很远,改起来成本很高。而且全自动模式下,AI的每一步决策都是黑盒,出了问题你很难定位是规划错了还是执行错了。

Harness模式的思路不一样。它不追求AI自己决定一切,而是由人来定义流程的骨架,AI在骨架内执行具体任务。就像工作台上有各种夹具和导轨,工件可以在上面移动,但移动的范围和方向是被约束好的。这样做的好处是:流程可控、问题可定位、结果可预期。你牺牲了一点“全自动”的便利,换来的是工程上最需要的确定性和可维护性。

我的经验是,在代码生成这个场景下,Harness模式比全自动模式实用得多。因为代码有明确的正确性标准(能跑、测试通过),你不需要AI去“创造性地规划”,你只需要它按照你定义的步骤老老实实执行。

2.3 关键组件清单与职责划分

一套完整的Agentic Harness Workflow通常包含以下组件,我按重要性排序并说明各自的职责。

组件职责关键设计点
任务定义器把需求拆解为结构化任务任务粒度、依赖关系、验收标准
上下文构建器为每个任务组装AI需要的上下文文件筛选、摘要压缩、优先级排序
提示词模板定义AI的输入格式和输出格式角色设定、约束条件、输出解析规则
执行引擎调用AI模型并处理返回结果重试策略、超时控制、并发管理
验证器自动检查生成代码的正确性测试执行、静态分析、结果解析
反馈回路把验证失败信息传回执行引擎错误摘要、重试上限、升级策略
记录与回放记录全流程数据支持回放日志格式、存储方案、查询接口

这些组件不需要一次性全部搭好。我建议的搭建顺序是:先有任务定义器和执行引擎,让流程能跑起来;然后加验证器,保证质量;最后补记录层,为后续优化提供数据。上下文构建器和提示词模板可以随着使用逐步打磨。

3. 核心细节解析:每个环节怎么做才靠谱

3.1 任务定义:把“帮我改个功能”翻译成机器能执行的指令

任务定义是整个流程的起点,也是最容易被忽视的环节。很多人直接把用户需求原封不动丢给AI,比如“给登录接口加上频率限制”。这种模糊的指令,AI执行起来全靠猜,结果自然不稳定。

正确的做法是把需求翻译成结构化的任务描述。一个合格的任务定义至少包含以下字段:

  • 任务ID:唯一标识,用于追踪和引用
  • 任务类型:是新增代码、修改代码、重构还是修bug
  • 目标文件:涉及哪些文件,是精确路径还是模糊匹配
  • 输入上下文:需要AI参考的代码片段、接口文档、数据结构
  • 验收标准:怎么判断任务完成了,是测试通过还是人工确认
  • 依赖任务:这个任务必须在哪些任务完成之后才能开始

举个例子,“给登录接口加上频率限制”这个需求,拆解后可能是这样的:

task_id: "auth-rate-limit-001" type: "modify" target_files: - "src/middleware/auth.js" - "src/config/rateLimit.js" context: - "现有登录接口的实现代码" - "项目中已有的限流中间件用法" acceptance: - "同一IP每分钟超过10次登录请求返回429" - "单元测试全部通过" depends_on: - "auth-login-refactor-000"

这样定义之后,AI拿到的不再是一句模糊的话,而是一个有边界、有标准、有依赖的明确任务。执行起来成功率会高很多。

注意:任务粒度不要太细也不要太粗。太细会导致任务数量爆炸,编排开销大于执行开销;太粗会导致单次AI调用上下文过长,容易丢失细节。我的经验是,一个任务对应一个可独立测试的代码变更单元比较合适。

3.2 上下文构建:在有限窗口里塞进最有用的信息

上下文构建是决定AI输出质量的关键因素。AI模型再强,你给它的信息不对,它也写不出对的代码。这里面的核心矛盾是:项目代码量远大于上下文窗口,你必须做取舍。

我的做法是分三步走。第一步是相关性筛选,根据任务涉及的文件和符号,从项目索引中找出直接相关的文件。比如任务是修改登录接口,那登录接口的实现文件、它调用的工具函数、它的测试文件都是高相关的,而项目里的支付模块、消息推送模块就不需要放进去。

第二步是摘要压缩。对于高相关但代码量大的文件,不是全文塞进去,而是提取关键部分:函数签名、类型定义、关键逻辑的注释。对于低相关但需要了解接口的文件,只放接口定义不放实现。

第三步是优先级排序。上下文窗口是有限的,当信息量超过窗口时,需要按优先级截断。我的排序原则是:任务直接涉及的文件 > 被调用的接口定义 > 测试文件 > 相关配置文件 > 其他参考。越靠前的越完整保留,越靠后的越倾向于摘要。

这里有一个实操技巧:在提示词里明确告诉AI“以下代码片段按重要性排序,如果信息不足请主动询问而不是猜测”。这样当上下文确实不够时,AI会告诉你它还需要什么,而不是硬编。

3.3 提示词模板:约束AI的输出格式比优化输入更重要

很多人花大量时间打磨提示词的“输入部分”,告诉AI你是谁、你要做什么、你要注意什么。这些当然重要,但我的经验是,约束输出格式带来的收益更大。

原因很简单:AI的输出是要被程序解析和执行的。如果AI返回的代码格式不标准,你的解析器就挂了;如果AI返回的内容里混了额外的解释文字,你的自动化流程就断了。所以提示词模板里必须明确规定输出的结构。

我常用的输出格式约定是这样的:

请按以下格式输出: 1. 首先输出一个 JSON 块,包含以下字段: - files_changed: 修改的文件列表 - summary: 一句话描述改动内容 - confidence: 你对这次改动的信心程度(high/medium/low) 2. 然后输出代码块,每个代码块用文件路径作为标识: ```language:filepath 代码内容 ``` 3. 最后输出注意事项,列出你认为需要人工确认的点。

这种结构化输出让后续的解析和验证变得非常顺畅。解析器只需要按固定规则提取JSON和代码块就行,不需要做复杂的自然语言理解。

另外,提示词里要明确“不要做什么”。比如“不要修改未在任务中指定的文件”、“不要引入新的第三方依赖”、“不要删除现有的测试用例”。这些负面约束能避免很多意外情况。

3.4 验证器设计:自动化检查的边界在哪里

验证器是保证代码质量的守门人,但它不是万能的。你需要清楚哪些检查可以自动化,哪些必须人工介入。

可以自动化的检查包括:语法检查(用linter)、单元测试执行、类型检查(如果是强类型语言)、代码格式检查、依赖冲突检查。这些检查有明确的通过/失败标准,适合自动化。

难以自动化的检查包括:业务逻辑正确性、性能影响、安全性、代码可维护性。这些需要人工判断,或者需要更复杂的集成测试环境。

我的做法是分层验证。第一层是快速检查,语法和格式,几秒钟出结果,不通过直接打回。第二层是单元测试,几十秒到几分钟,不通过把失败信息喂回给AI。第三层是集成测试,可能需要几分钟到十几分钟,这一层不通过的话,先让AI尝试修复,修复失败就升级到人工处理。

实操心得:验证器的失败信息要结构化。不要直接把测试输出的几百行日志丢给AI,而是提取关键信息:哪个测试挂了、期望值是什么、实际值是什么、错误堆栈的前几行。信息越精炼,AI修复的成功率越高。

3.5 反馈回路:让AI自己修自己的代码

反馈回路是Agentic Harness Workflow区别于普通代码生成的核心环节。它的逻辑是:AI生成代码 → 验证器检查 → 不通过 → 把失败信息喂回给AI → AI修复 → 再检查 → 循环直到通过或达到上限。

这个回路的设计有几个关键参数需要调优。第一个是最大重试次数。设太少,AI还没来得及修好就放弃了;设太多,AI可能在一个死胡同里反复撞墙。我的经验值是3到5次,超过5次还没修好,大概率是任务定义或上下文有问题,继续重试也是浪费时间。

第二个是失败信息的组织方式。每次重试时,是把所有历史失败信息都带上,还是只带最近一次?我的做法是带最近两次的失败信息,再加上一个“你已经尝试过以下方案但都失败了”的摘要。这样AI知道哪些路走不通,避免重复尝试。

第三个是升级策略。当重试达到上限仍然失败时,流程应该怎么处理?是跳过这个任务继续后面的,还是终止整个流程?这取决于任务之间的依赖关系。如果后续任务依赖这个任务,那就必须终止;如果不依赖,可以标记为失败并继续。

4. 实操过程:从零搭建一套可运行的流程

4.1 环境准备与基础工具选型

搭建这套流程不需要特别复杂的环境。核心需要的东西包括:一个能调用AI模型的接口、一个代码执行环境(用来跑测试)、一个流程编排工具。

AI模型接口这块,你可以用任何提供API的模型服务。关键是要支持流式输出和函数调用(Function Calling),前者影响交互体验,后者影响任务编排的灵活性。

代码执行环境建议用容器化方案,每次执行任务时启动一个干净的容器,把代码挂载进去,跑完测试就销毁。这样做的好处是环境隔离,不会因为上一个任务的残留状态影响下一个任务。容器镜像里预装好项目需要的运行时和依赖,启动速度可以控制在几秒内。

流程编排工具的选择比较多。轻量级的可以用脚本加状态机自己写,重量级的可以用现成的工作流引擎。我的建议是先用脚本跑通最小闭环,等流程稳定了再考虑引入更重的工具。过早引入复杂工具会让调试变得很痛苦。

4.2 最小闭环实现:跑通“生成-验证-修复”三步

先不要想着一步到位搭建完整框架,先把最小闭环跑通。所谓最小闭环,就是能完成“AI生成代码 → 自动验证 → 失败则修复”这个基本循环。

第一步,定义一个最简单的任务,比如“给某个函数添加参数校验”。任务定义里写清楚目标文件、验收标准(对应的单元测试)。

第二步,写一个脚本,读取任务定义,组装提示词,调用AI接口,拿到返回的代码,写入文件。

第三步,在容器里跑测试,收集结果。如果通过,标记任务完成;如果不通过,提取失败信息,追加到提示词里,重新调用AI。

这个闭环跑通之后,你就有了一个能用的基础版本。后面所有的优化都是在这个基础上叠加。

# 最小闭环的伪代码示意 def run_task(task): context = build_context(task) prompt = render_prompt(task, context) for attempt in range(MAX_RETRIES): result = call_ai(prompt) apply_changes(result) test_result = run_tests(task) if test_result.passed: return TaskResult(success=True, attempts=attempt+1) prompt = append_failure_feedback(prompt, test_result) return TaskResult(success=False, attempts=MAX_RETRIES)

4.3 任务编排:串行、并行与条件分支的处理

当任务数量多起来之后,编排就变得重要了。最简单的编排是串行:任务一个接一个执行,前一个完成了才开始下一个。这种方式实现简单,但效率低,因为很多任务之间其实没有依赖关系,完全可以并行。

并行的实现方式取决于你的执行环境。如果每个任务都在独立容器里跑,那并行就是同时启动多个容器。需要注意的是,并行任务如果修改了同一个文件,会产生冲突。所以并行之前要先做冲突检测:两个任务的目标文件有交集,就不能并行。

条件分支是另一种常见的编排需求。比如“如果测试通过就继续下一个任务,如果不通过就执行修复任务”。这种逻辑用状态机来表达比较清晰:每个任务执行完根据结果决定下一个状态是什么。

我的经验是,编排逻辑不要写得太复杂。如果发现任务之间的依赖关系错综复杂,那大概率是任务拆分得不够合理。好的任务拆分应该是依赖关系清晰的树状结构,而不是网状结构。

4.4 记录与回放:让每一次执行都可追溯

记录层的价值在流程出问题的时候体现得最明显。当某个任务反复失败,你需要知道:AI每次收到的提示词是什么、它返回了什么、测试失败的具体信息是什么、重试了几次。没有这些记录,你只能靠猜。

记录的最小字段集包括:任务ID、执行时间、提示词全文、AI返回全文、验证结果、重试次数、最终状态。这些数据建议存成结构化的格式(比如JSON Lines),方便后续查询和分析。

回放功能是在记录的基础上更进一步。它允许你用相同的输入重新执行一遍流程,看看结果是否一致。这对于调试提示词特别有用:你改了一版提示词,想对比新旧版本的效果,回放功能可以帮你快速做A/B测试。

注意:记录里可能包含代码和业务逻辑,如果项目有保密要求,记录存储需要做好访问控制。另外,记录数据会快速累积,建议设置定期清理策略,只保留最近N天的详细记录,更早的只保留摘要。

5. 常见问题与排查技巧实录

5.1 AI反复修改同一个错误怎么办

这是最常见的问题。AI改了一版,测试还是挂,再改一版,还是挂,而且每次挂的原因都一样。这种情况通常有三个原因。

第一个原因是上下文里缺少关键信息。AI不知道某个约束条件,所以它的修复方向从一开始就是错的。解决办法是检查上下文构建逻辑,看看是不是漏掉了某个重要的接口定义或配置文件。

第二个原因是失败信息不够具体。如果只告诉AI“测试失败了”,它只能瞎猜。要把具体的断言失败信息、错误堆栈、输入输出对比都给它。信息越具体,修复越精准。

第三个原因是任务本身超出了AI的能力边界。有些问题需要理解复杂的业务逻辑或者做架构层面的调整,这不是单次代码生成能解决的。遇到这种情况,应该把任务进一步拆小,或者升级到人工处理。

我的处理策略是:第一次失败,补充失败信息重试;第二次失败,检查上下文是否完整;第三次失败,换一个提示词策略(比如让AI先分析原因再改代码);第四次失败,标记为需要人工介入。

5.2 生成代码风格与项目不一致怎么处理

AI生成的代码功能上没问题,但风格跟项目现有代码差异很大,比如命名习惯不同、错误处理方式不同、注释风格不同。这种问题在代码审查时很扎眼。

解决办法是在上下文里加入风格参考。具体做法是:从项目里挑选几个风格规范的函数作为示例,放在提示词里,告诉AI“请遵循以下代码风格”。同时,在验证层加入代码格式检查工具,风格不达标直接打回。

更彻底的办法是维护一份项目级的编码规范文档,在每次调用AI时都把它作为系统提示词的一部分。这份文档不需要很长,把命名规则、错误处理模式、日志规范这几个关键点写清楚就行。

5.3 上下文超长导致截断后信息丢失

当任务涉及的代码量很大时,上下文很容易超出模型窗口限制。截断是必然的,关键是截断之后不能丢失关键信息。

我的做法是分级截断。第一级是完整保留:任务直接涉及的文件全文保留。第二级是保留签名:被调用的接口只保留函数签名和类型定义,去掉实现。第三级是保留摘要:相关但非直接的文件,用一句话描述它的作用。第四级是丢弃:完全不相关的文件直接不放入上下文。

如果这样还是超长,那就说明任务粒度太粗了,需要进一步拆分。一个任务如果需要理解半个项目的代码才能完成,那这个任务本身就不适合交给AI独立完成。

5.4 常见问题速查表

问题现象可能原因排查方向解决手段
AI反复改同一个错上下文缺信息/失败信息不具体检查上下文构建和失败信息提取补充信息、细化失败反馈
代码风格不一致缺少风格参考检查提示词是否包含风格示例加入风格示例和格式检查
上下文超长截断任务粒度过粗检查任务涉及的文件数量拆分任务、分级截断
测试环境不稳定容器状态残留检查容器是否每次重建强制每次新建容器
并行任务冲突目标文件有交集检查任务依赖关系冲突检测、改为串行
AI输出格式错误提示词约束不够检查输出格式定义强化格式约束、加解析容错
重试次数用尽仍失败任务超出能力边界检查任务复杂度拆小任务或人工介入

5.5 几个踩过坑之后总结的避坑技巧

第一个坑是不要信任AI的自我评估。我在提示词里让AI输出confidence字段,本意是让它标注信心程度,结果发现它几乎永远标high。后来我改成让它列出“不确定的点”,这个反而更有参考价值。

第二个坑是测试用例的质量决定验证的有效性。如果项目本身的测试覆盖就不全,那验证层能起的作用很有限。在搭建流程之前,先把关键路径的测试补齐,这个投入是值得的。

第三个坑是不要一次性把所有任务都丢进去跑。先跑一两个任务,确认流程没问题,再批量执行。批量执行时如果流程有bug,会产生大量无效记录,清理起来很麻烦。

第四个坑是提示词版本要管理。提示词改来改去,过一段时间你根本不记得哪版效果好。建议用Git管理提示词文件,每次改动都提交,配合记录层的回放功能做效果对比。

6. 框架的扩展方向与个人体会

6.1 从单项目到多项目的复用思路

当这套流程在单个项目上跑通之后,自然会想到能不能复用到其他项目。复用的关键是把项目相关的部分和流程通用的部分分离。

项目相关的部分包括:上下文构建规则(哪些文件相关)、验证命令(怎么跑测试)、代码风格规范。这些应该做成配置文件,每个项目一份。流程通用的部分包括:任务定义格式、提示词模板骨架、执行引擎、记录层。这些应该做成公共库,所有项目共用。

分离之后,接入一个新项目的工作量就变成了:写一份配置文件、准备测试环境镜像、跑通一个示例任务。整个过程可以控制在半天以内。

6.2 人工介入的最佳切入点

虽然这套框架追求自动化,但人工介入仍然是必要的。关键是在什么环节介入,介入得太早会变成人工写代码,介入得太晚会浪费大量自动化资源在错误方向上。

我的经验是,人工介入的最佳切入点是任务定义阶段和最终验收阶段。任务定义阶段人工确认拆解是否合理、验收标准是否明确,这个投入几分钟,能避免后面几小时的无效执行。最终验收阶段人工检查代码质量、业务逻辑正确性,这是自动化检查覆盖不到的部分。

中间的执行和修复环节尽量交给自动化,人工不需要盯着。只有当重试次数用尽仍然失败时,才需要人工介入分析原因。

6.3 我个人的一些实操体会

这套框架我用了一段时间,最大的体会是:它带来的效率提升不是线性的,而是需要跨过一个门槛之后才显现。在流程搭建初期,你会觉得还不如自己直接写代码快。但当流程稳定运行、任务库积累起来之后,批量处理重复性编码任务的效率提升非常明显。

另一个体会是:提示词的质量比模型的强弱更重要。同一个模型,好的提示词和差的提示词,输出质量差距巨大。与其花时间研究哪个模型更强,不如花时间打磨提示词模板和上下文构建逻辑。

最后一个体会是:不要追求100%自动化。80%自动化加上20%人工介入,比追求100%自动化但流程脆弱不堪要实用得多。工程化的核心是稳定可靠,不是炫技。

这套框架还在持续演进中,后面我打算尝试的方向包括:把验证层的静态分析做得更细、引入代码审查的自动化检查、以及探索多模型协作的可能性。这些等跑出结果了再另开一篇来聊。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询