
我接手第一版 AI Coding 流程改造时最大的感受不是“效率翻倍”而是“失控”。AI 能写代码但它写出来的代码质量时高时低它改文件但经常顺手把不该动的配置也改了它跑测试但失败了之后会自己给自己找理由然后换一种方式重试直到把问题搞得更复杂。那段时间团队的共识是AI 很强但如果没有一套流程去约束它它更像一个能力很强但纪律很差的新同学。后来我们把 AI Coding 从“对话框里的一问一答”改造成了类似 CI/CD 流水线的一套工程化流程任务进来之后要经过解析、规划、执行、审查、批准这套固定节点每个节点都有明确的输入输出、工具边界和终止条件。这套模式就是标题里说的Agentic Harness Workflow 框架。它解决的并不是“让 AI 更聪明”而是“让 AI 的行为变得可预期、可追踪、可回滚”。这篇文章我会从设计思路讲到最小可用版本再讲我们踩过的坑适合正在把 AI Coding 引入团队、但又不想让代码库变成无人看管的荒野的开发者参考。1. 为什么需要 Harness WorkflowAI Coding 失控的根源1.1 直接对话模式更适合“结对编程”而不是“流水线生产”很多人刚开始用 AI 写代码时用的是最原始的方式把一个需求丢给大模型然后等它输出一段代码再手动应用、手动测试、手动 review。这种模式本质上是在做“结对编程”AI 是副驾驶人是主驾。问题在于当任务变多、参与者变多之后这种模式暴露出三个很难忍受的缺陷。第一个缺陷是不可控。AI 在生成代码时并不清楚它被允许修改哪些文件、禁止碰哪些目录。它可能为了完成一个小需求重写了大半个模块然后告诉你“我顺手重构了”这个重构里可能带着它自己风格的命名、格式化方式甚至在不知情的情况下引入了新的依赖。第二个缺陷是不可观测。你只看到最终结果看不到它中途经历了什么它尝试过哪个方案、失败了几次、浪费了多少 token、修改了哪些文件。第三个缺陷是不可复现。同样的需求描述换一天、换一次模型版本结果可能完全不一样。你没法在代码评审里说“上次生成的不是这样”因为没有可回放的过程记录。这三个缺陷叠加起来导致 AI Coding 很难从“个体工具”升级为“团队基础设施”。因为工程化最核心的四个词——可观察、可控制、可复现、可度量——它一个都不满足。1.2 工程化的本质不是限制而是让能力可以被安全释放我见过一些团队反过来走向另一个极端既然 AI 不可控那就干脆禁用它全部人肉写代码。这其实是把问题简单化了。工程化的核心逻辑不是限制人的能力而是通过流程、工具和护栏让一个能力强但可能犯错的执行者稳定地输出结果。就像生产线上的机械臂它本身很有力量但真正让它能连续工作 16 小时的是轨道、传感器、限位开关和急停按钮。这些约束不是削弱它而是让它能在复杂环境里安全地做功。Harness这个词本身就很有意思它的本意是“挽具、背带”在测试领域里Test Harness 指的是“测试脚手架”一个能装载被测对象、提供输入、校验输出、收集结果的外壳框架。放到 AI Coding 的情境里Harness 的职责就是给每个 Agent 划定活动范围你只能读哪些文件、只能写哪些目录、只能执行哪些命令、最多迭代几次、输出必须是什么格式。Agent 是在这个范围内自由发挥的但范围本身是工程化设计和管理的。Workflow 解决的是另一层问题单个 Agent 被约束得再好它也只是孤军奋战。一个完整的代码变更可能需要一个 Agent 负责理解需求、一个 Agent 负责生成实现、一个 Agent 负责审查、一个 Gate 负责质量检测最后还要一个人工节点去做最终批准。这些节点之间的依赖关系、数据传递、失败重试、超时降级就是 Workflow 层的工作。所以 Agentic Harness Workflow 不是某个单一工具而是一整套把“AI 干活”这件事编排成标准化流水线的方法论。1.3 这套框架适合谁不适合谁先说不适合的情况。如果你的团队只有一两个人日常任务都是几百行的小脚本那直接开个对话框让 AI 写反而最快引入完整的工作流框架属于过度设计。再比如你只是偶尔用 AI 查个 API 用法、写个正则表达式也不需要上 Harness。它真正适合的情况是AI 生成代码已经成为日常开发路径的一部分而且团队对代码质量有硬性要求。比如某个模块的代码主要靠 AI 生成或者你把 AI 接入了需求到提测的完整链路再或者你有大量机械性代码变更升级依赖、生成单元测试、重构老接口需要自动化。这类场景下你需要的是可审计的流程、可回滚的变更、可量化的效率。Harness Workflow 就是为这些场景准备的。下面是直接 Prompt 模式与 Harness Workflow 模式的关键差异对照。对比维度直接 Prompt 模式Agentic Harness Workflow输出结果一段代码文本一次完整的、可回滚的代码变更过程记录无只有对话结构化日志含每一步的输入输出与动作工具权限由使用者自行控制白名单机制Agent 只能触碰授权资源失败处理人工发现、手动修正自动重试、超时终止、降级转人工质量校验依赖人工 review内置质量门禁不达标不进入下一节点可复现性几乎不可复现同一任务描述 同一输入快照 同一流程再往深一层看这套框架真正改变的是人机协作的分工模式人不再逐行盯着 AI 生成代码而是去定义流程、设置约束、处理异常、做最终决策。这也是为什么我把标题定为“把 AI Coding 变成工程化流程”——它把一个“玄学”问题变成了一个“管理学”问题。2. 核心概念拆解Harness、Agent、Workflow 各管哪一段2.1 Agent干活的执行者不是“万能助手”在框架语境里Agent 是具备一定自主执行能力的 AI 程序它通常由模型、提示词、工具集三个部分组成。提示词定义了它的角色和行为准则工具集定义了它能调用哪些外部能力读文件、写文件、跑命令、调接口。这里的 Agent 不是对话框里的聊天机器人而是一个有明确任务目标、按特定协议工作的执行单元。设计 Agent 时最忌讳的一点是“一个 Agent 什么都干”。我们早期犯过这个错把一个 Agent 配了读写文件、执行命令、搜索代码、修改配置等一大堆工具结果它在拿到任务后容易跑偏经常在一个任务里又是改代码又是跑测试又是调整配置最后出错时根本分不清是哪一步引起的。后来我们把 Agent 拆成了专职角色需求分析 Agent 只负责产出结构化任务描述实现 Agent 只负责在授权目录内修改代码审查 Agent 只负责读代码并产出审查意见。每个 Agent 只做一件事工具越少越好这是整个框架里最重要的设计原则之一。一个合格 Agent 的执行单元至少要有三样东西清晰的输入格式任务描述、上下文、约束条件、清晰的输出格式结构化 JSON 或 Markdown 报告、以及明确的终止条件最大迭代次数、超时时间、成本上限。没有终止条件的 Agent 是最危险的它可能在一个问题上反复横跳消耗大量 token 后仍然没有结果。2.2 HarnessAgent 身上的安全绳和约束带Harness 层负责的是执行环境与边界控制。如果把 Agent 比作一辆跑车Harness 就是它的赛道护栏、限速器和安全气囊。它具体包含以下机制。第一是工具白名单。Agent 能调用什么、不能调用什么必须用白名单而非黑名单来控制。黑名单永远有漏网之鱼而白名单能保证 Agent 的活动空间是预先设计好的。比如实现 Agent 的工具白名单可以设置成只能读指定目录下的文件、只能在临时分支上写入、只能运行测试命令其余全部拒绝。第二是操作边界。包括文件系统的边界只能改某个模块或某个目录、网络请求的边界只能访问内网指定服务、执行命令的边界只能跑预设的脚本。我们框架里有一个必选项Agent 修改代码前必须先 checkout 一个独立分支任何写操作都发生在隔离环境中这样即使它闯了祸主分支也不会受影响。第三是规则护栏。比工具更进一步的是一组自然语言或硬编码的规则。例如“不允许删除公共工具函数”“修改公共 API 时必须同步更新调用方”“不允许跳过测试直接提交”。这些规则有些可以写成程序化检查有些则需要在审查节点由另一个 Agent 或人来把关。很多人在第一次搭 Harness 时会觉得“限制这么多AI 还能干活吗”。其实是反的——边界清晰反而让 AI 更敢干活、更能专注。因为规则明确之后它不需要猜“我这么干会不会违规”只需要在授权范围内大胆输出。人也是一样的如果一份工作职责范围写得清清楚楚你反而更愿意承担责任。2.3 Workflow把 Agent 们串成一条流水线Workflow 层是这套框架的骨架它定义了节点之间的依赖关系、数据传递方式和状态流转逻辑。一个标准工作流通常包含下面几类节点。任务解析节点把自然语言需求转换成结构化任务规格改哪个文件、达到什么效果、验收条件是什么。规划节点让规划 Agent 根据任务规格生成实施计划改动哪些模块、涉及哪些测试、有哪些风险点。执行节点调用实现 Agent 按照计划产出代码 diff。质量节点跑编译、测试、静态检查判断 diff 是否满足门槛。审查节点由审查 Agent 或人工 reviewer 对代码变更做最终把关。提交节点将审查通过的变更合并到目标分支或生成 MR。这些节点之间的依赖关系可以用 DAG 来描述下游节点等待上游节点成功完成后启动上游节点失败时下游不执行。每个节点都有自己的超时时间、重试策略和降级路径。整体来看Workflow 解决的核心问题是把单次对话式的 AI 交互变成多角色协作的工程过程。2.4 三个概念缺一不可单独有 Agent 没有 Workflow就是一堆能力很强但各行其是的“自由人”你无法预判它们组合起来会产出什么单独有 Harness 没有 Workflow约束是有了但任务路径还是随机的Agent 不知道下一步该做什么、产出该交给谁单独有 Workflow 没有 Harness流程是串起来了但执行者拥有无限权限流程越顺畅闯的祸越大。这三个概念合在一起才构成一个完整闭环Agent 负责出力Harness 负责兜底Workflow 负责编排。这也是为什么我坚持用“Agentic Harness Workflow”这个名字——它强调的既不是某个模型有多强也不是某种提示词技巧而是一整套让 AI Coding 像流水线一样稳定运转的工程机制。3. 实操从零搭一个最小可用版本3.1 圈定任务范围从小而痛的点切入第一次搭建这个框架我一贯的建议是不要贪心先挑一个垂直任务跑通全流程。我们第一个跑通的任务是“自动生成未覆盖模块的单元测试”。选它有三个原因目标清晰生成测试不修改业务代码、验收标准客观行覆盖率是否达标、失败影响面小最坏情况就是测试写得烂重跑一遍即可。任务范围确定之后下一步是把任务的输入输出定义清楚。我们的输入是一个模块路径输出是一个包含测试文件 diff 和覆盖率报告的结构化结果。定义好输入输出实际上就定义了 Workflow 的边界从哪开始到哪结束。3.2 定义工具集与权限宁可窄不可宽这个最小版本里实现 Agent 的工具集我建议控制在三个以内读文件、创建新测试文件、运行测试命令。不给写业务代码的权限不给执行任意 shell 命令的权限不加网络工具。原理很简单工具越少Agent 跑偏的空间越小出问题时排查范围也越小。权限的配置可以通过环境变量或配置文件实现。比如设置ALLOWED_PATHS/repo/testsAgent 的写操作只能落在该目录下ALLOWED_COMMANDSpytest启动的进程只能执行指定命令。这些配置本身要纳入版本管理后续每一次放宽权限都应当走 review 流程而不是在跑任务时临时改。3.3 搭建 Workflow 节点与状态流转下面是一个最小工作流的 YAML 定义示例用于演示节点编排方式。这个示例是我们在实践中使用的模式的一个简化版具体字段可以按团队习惯调整但结构值得参考。version: 1 name: test-generation-flow entry: task_parser nodes: task_parser: type: agent role: requirements_analyst prompt_ref: prompts/task_parser.md output_ref: task_spec plan_builder: type: agent role: architect prompt_ref: prompts/plan_builder.md depends_on: [task_parser] output_ref: implementation_plan exec_agent: type: agent role: implementer depends_on: [plan_builder] tools: [repo_read, test_file_write, test_runner] max_iterations: 5 output_ref: generated_diff quality_gate: type: gate depends_on: [exec_agent] checks: - coverage_rate 80 - test_exceptions 0 - diff_size 300 human_review: type: approval depends_on: [quality_gate] assignee: reviewer commit_node: type: git_commit depends_on: [human_review]逐节点解释一下。task_parser拿到原始需求后输出一份结构化的任务规格书包括目标模块、测试策略、验收标准。plan_builder根据任务规格书生成实施计划明确要写哪几个测试文件、mock 哪些依赖、预期覆盖率是多少。exec_agent按照计划去生成代码最多迭代 5 次每次都基于上一次的测试反馈尝试修正。quality_gate是硬性门槛不达标直接结束流程不会流到下一步。human_review是人工审批节点审批通过才真正提交代码。commit_node执行最后的合并或提交动作。3.4 关键逻辑的伪代码实现如果你的团队更习惯用代码来定义流程可以用类似下面的伪代码来承载节点状态机和重试逻辑。这个实现方式在需要复杂分支判断时比纯声明式配置更好维护。class WorkflowNode: def __init__(self, node_id, depends_on, action, max_retries1): self.node_id node_id self.depends_on depends_on self.action action self.max_retries max_retries self.state pending def run(self, ctx): for attempt in range(self.max_retries 1): try: result self.action(ctx) self.state success return result except RetryableError as e: ctx.log(fattempt {attempt 1} failed: {e}) continue self.state blocked raise BlockedError(self.node_id) def run_workflow(nodes, ctx): ready [n for n in nodes if n.state pending] while ready: for node in ready: if not all(dep.state success for dep in node.depends_on): continue node.run(ctx) ready [n for n in nodes if n.state pending]这套逻辑非常简单只有上游节点全部成功后下游节点才会执行某个节点超过重试次数后整个流程进入阻塞状态等待人工介入。实际生产环境中还要加上超时中断、事件通知、日志收集等细节但核心骨架就是这样一个朴素的状态机。3.5 接入可观测性每一步都要留痕框架跑起来之后第一件要做的事就是让每一步都留下结构化日志。我们的做法是每个节点执行时都生成一条事件记录包含以下字段trace_id一次完整任务的唯一标识node_id当前节点agent_role执行该节点的 Agent 角色input_digest输入内容的摘要或 hashoutput_digest输出内容的摘要或 hashtoken_cost本次调用的 token 消耗latency_ms耗时status成功 / 失败 / 重试 / 阻塞有了这些日志你就能回答一系列关键问题这个任务在哪一步最耗时哪个 Agent 经常返工平均一次代码变更消耗多少 token失败的任务卡在全线哪个节点这些问题在你后续优化流程时都是决策依据。没有日志的框架就像没有仪表盘的飞机飞起来全靠感觉。4. 决定成败的细节与关键参数4.1 上下文管理任务越完整输出越可靠AI Coding 中很多“灵异现象”其实源于上下文管理不当。比如 Agent 明明只需要改 A 文件上的一个函数但你把整个仓库的代码都塞进它的上下文里它可能被无关代码干扰改出一堆不相干的东西。反过来如果你只给它一个函数名不给函数定义、调用方、相关测试它又可能凭空捏造一个错误的实现。我们在实践中总结了一条经验输入上下文的边界必须和任务边界严格对齐。执行“给某个模块写测试”的任务时给 Agent 的上下文应该包括该模块的源码、已有测试的目录结构、项目约定但不要包含整个平台的无关模块。这既是对正确性的保障也是对 token 成本的优化。上下文塞得越满模型注意力越分散生成的代码越容易“东张西望”。4.2 工具调用协议先读后写、先验证后报告每个 Agent 在调用工具时都应该遵守一套固定的协议顺序不能跳步。我们的标准协议是“读 → 思考 → 改 → 验 → 报”。具体来说读先读取目标文件和相关依赖确认改动基线和约束。思考基于读到的内容形成方案不直接写代码。改按方案修改一次只改一个逻辑单元。验运行相关测试或静态检查拿到结果再决定下一步。报输出结构化报告包含改动清单、验证结果、风险说明。这套协议看起来平淡无奇但它能有效防止两类高频问题一类是“AI 连文件都没读就开写改完全部报错”另一类是“AI 改完却不跑测试直接宣称任务完成”。协议本身可以写在 Agent 的系统提示词里也可以放在 Harness 的工具调用拦截层后者更可靠因为它不依赖 AI 自觉执行。4.3 重试与终止条件保护资源防止死循环Agent 在执行任务时经常会出现“反复自我推翻”的情况改一次测试挂了它换个思路再改再挂再换个思路如此反复。如果没有终止条件这一过程可以无限持续下去消耗的资源和时间都非常可观。所以每个执行节点都必须设置硬性终止条件我建议至少包含以下四个参数。参数推荐值作用max_iterations3~5限制 Agent 在同一任务上的自我修正次数timeout_sec300~600单节点超时保护超时强制终止max_token_cost视预算单个任务的 token 消耗上限error_threshold连续 2~3 次连续失败达到阈值后自动转人工这里面max_iterations最容易被忽视。我发现很多团队的 AI 任务不是“能力不够导致失败”而是“失败后盲目重试导致局面恶化”。重试应当发生在“失败原因与上次不同”的情况下如果把同样的错误重试三遍结果只会是白花钱。所以我们的策略是第一次失败可以重试第二次失败必须切换策略或降级到人工。4.4 多 Agent 协作时共享状态与并发控制当一个任务涉及多个 Agent 时它们之间的状态同步就变成了新的问题。最危险的操作是两个 Agent 同时修改同一个文件这在并发场景下会造成相互覆盖或冲突。我们的做法是引入一个共享状态存储它保存每个文件当前的“持有者”和“版本号”。Agent 在修改文件前必须先申请锁定修改完成后释放锁定并更新版本号其他 Agent 读到的永远是当前版本。另外一个经验是尽量用数据的单向流动代替多 Agent 实时交互。比如执行节点产出 diff 后审查节点只需要读这个 diff不需要通知执行节点“我已开始审查了”。把每个节点的输入输出都物化为可读取的对象工作流就会变得更简单、更好排查。共享状态只在真正需要并发协同的地方使用而不是全局铺开。5. 踩坑实录与高频问题排查5.1 高频问题速查表搭框架的过程中我先后遇到了下面几类高频问题整理成速查表供直接参考。症状根本原因处理办法Agent 改动了无关文件写权限范围过宽收紧 Harness 中允许写入的目录白名单Agent 据此编造了不存在的 API 或文件路径它没有读源码在凭印象发挥强制“先读后写”把 repo 读取工具设为必备工具同一个问题反复失败结果越来越差重试策略不区分失败类型限制重试次数连续失败自动转人工提示词稍微变化输出就大不相同提示词版本没有管理将提示词纳入版本库每次变更都要有记录人工审批形同虚设全部秒过审批信息太多或太少没法判断每个审批节点输出统一的审查简报突出关键风险上下文太长导致模型注意力漂移任务拆得太粗输入塞得太多用任务解析节点把大任务拆成子任务按需注入上下文5.2 三个容易被忽视的细节第一提示词也是代码要纳入版本管理。很多团队写提示词就像记备忘录改一版扔一版一旦模型输出变化根本说不清是模型升级导致的还是提示词改动导致的。正确的做法是把每个 Agent 的提示词作为独立文件放入仓库并给它们打上版本号。这样你可以在一次任务报告中记录“本次执行的 Agent 提示词版本是 v1.3”出了问题能精确回溯。第二成本控制要落到每个节点上。如果不单独统计每个节点的 token 消耗你会发现月底账单高得离谱但说不清钱花在哪。我们的做法是每跑完一个任务把各节点的 token_cost 汇总成一张报表按比例排序。通常最贵的是执行节点其次是审查节点。有了这个数据你才能做出理性决策是减少重试次数、压缩上下文还是换一个更便宜的模型。第三每个代码变更都要自动生成回滚预案。即便有 Harness 和人工审批AI 生成的代码还是可能在生产环境里出问题。所以我们要求执行节点在产出 diff 的同时还必须输出一个回滚方案一般是“将变更提交到独立分支 生成逆向 patch”。一旦线上出现异常可以一键回滚而不是手忙脚乱地翻历史版本。5.3 人工审批的门道放在关键变更前而不是最后我们把人工审批节点从流程末端挪到了关键变更前这是踩了多次坑之后总结出来的调整。早期流程是“AI 生成 → AI 自查 → 人工看一眼 → 合并”实际运行起来发现到了人工审批环节diff 已经很大了reviewer 根本看不过来最后基本变成了“因为是你生成的我就点了通过”。调整后的流程是任务解析之后先请人确认方向执行出第一版 diff 后再请人确认一次关键变更最后合并时只做轻量抽检。这样做的效果是问题在早期就被发现和纠正而不是在最终提交前集中爆发。人工资源要花在刀刃上所谓刀刃就是决定任务方向的那个节点和引入高风险动作的那个节点。5.4 当我们遇到灾难性失败时有一次我们跑一个“升级某模块依赖”的任务Agent 在更新依赖时自作主张删掉了一个老接口的兼容层导致下游十几个模块同时编译失败。当时 Harness 的反应是编译失败 → 执行节点重试 → 失败 → 触发人工审批流程。审批人看了简报之后发现情况不对立即终止任务。由于整个流程运行在独立分支上主分支完全没受影响回滚只需要删除那个分支即可。这件事给了我两个教训。第一Harness 的“失败处理”不是要把所有错误都拦截在流程里而是要保证任何错误都不会波及主分支和生产环境第二人工审批节点最大的价值不在“审批通过”而在“及时发现方向性错误”——当 AI 跑偏的时候一个能看懂大局的人只要扫一眼就知道有问题。所以不要试图把审批节点完全自动化至少在可预见的阶段人仍然是整套流程里不可替代的最后一道防线。我个人在实际操作中还有个体会这类框架的价值不是一上来就全自动而是先跑通一条最小路径积累日志和数据再逐步放宽约束。我们第一次跑通“自动生成单元测试”用了两周但正是这两周跑出来的日志让团队信任了整体机制后面才敢把更复杂的重构任务交给框架。每一次放宽规则都要有数据和复盘撑着而不是拍脑袋说“这次试试让它多改点文件吧”。工程化 AI Coding本质上是在用管理者的耐心收敛一个创造者的野性。