news 2026/10/14 5:40:06

Agentic Harness Workflow:将AI Coding升级为工程化流水线的实践框架

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Agentic Harness Workflow:将AI Coding升级为工程化流水线的实践框架

坦率讲,哪怕到今天,我依然看到很多团队把 AI Coding 用成了“高级补全工具”:人开一个对话,把需求贴进去,等模型吐出一段代码,再手动复制回仓库,跑一下发现报错,又贴回去让它改。这种玩法不是不行,但它完全没有发挥出 AI 编程真正的杠杆——Agentic Harness Workflow 框架要解决的就是这件事:把 AI Coding 从“一问一答”的对话模式,升级成“需求到交付”的工程化流水线。

我手上这套框架已经跑了小半年,前后重构过三轮,从最早的一个循环调模型,到现在拆成任务节点、审核门禁、工具沙箱、观测面板一整套体系。这篇文章把我踩过的坑、沉淀下来的设计思路、以及可以直接抄走的最小实现一次性讲清楚。如果你正准备把 AI 编程引入团队流程,或者你自己一个人想管住好几个并行的 AI 任务,这篇文章应该能帮你少走很多弯路。

1. 为什么 AI Coding 不能只靠“单次对话”

1.1 Harness 到底是什么意思

先说一个很容易被忽略的词:“Harness”。它在英文里的本义是“马具、缰绳”,后来在工程领域被引申为“控制、约束、支撑系统”。你去看很多测试框架,里面都有一个 Test Harness 的概念,它就是用来把被测对象包起来,替它准备数据、调用它的接口、收集它的输出。

放到 AI Coding 场景里,Harness 做的事情完全一样:模型是我们的被测对象,也是执行者,但它本身不受控。你不知道它下一步会调用哪个工具,不知道它会在哪一步开始编造不存在的文件,也不知道它会绕多少圈子才肯停下来。Harness 的作用,就是给模型划出一条轨道,让它只能在轨道里“自主”。

我见过很多团队对“Agentic”的理解,以为就是让模型想干嘛就干嘛。实际跑下来你会发现,没有约束的自主,结果就是失控。模型可能会自己装一堆依赖、改掉不该改的配置、在一个问题上翻来覆去重试十几遍。Harness 的意义,恰恰是用结构化的流程把这种自由限制在可接受的边界里。

1.2 从“对话”到“流水线”的转变

单次对话模式的本质是“人肉维护上下文”:模型写完第一版代码,你要自己看报错信息,再把报错贴回去,模型看到的是你整理好的局部信息,它并不知道整个仓库的全貌,也没有机会主动去跑一遍测试。

工程化流程的本质则是“把步骤抽成交付链”。你可以把一次 AI 编程任务拆成这么几段:

  • 需求澄清节点:模型或人来确认需求里有没有模糊点
  • 技术方案节点:让模型基于仓库结构给出改动方案
  • 代码生成节点:按方案逐文件生成或修改代码
  • 静态检查节点:跑 lint、类型检查、编译
  • 测试验证节点:跑单元测试、集成测试
  • 人工审核门禁:差改动必须停下来等人确认
  • 合并与回滚节点:通过后才允许合入,出问题能自动回退

每一步都是独立节点,都有输入输出,都有超时和重试策略。模型不再需要自己记住“我写到第几步了”,框架里的状态管理替它记住。这就是“Agentic + Workflow”的核心价值:智能体负责局部的自主决策,工作流负责全局的确定性编排。

2. 框架设计思路:核心概念与架构拆解

2.1 节点模型:用类型和工作流逻辑解耦

我这一版框架里,所有行为都被抽象成四类节点,这四类足够覆盖绝大多数 AI Coding 场景:

节点类型职责典型例子
Task Node调用大模型完成一个生成/理解任务写接口文档、生成单元测试
Tool Node执行确定性的外部操作跑 pytest、执行 git diff、检索代码
Decision Node根据条件做分支选择测试是否通过、是否需要人工介入
Human Gate暂停流程等人确认代码评审、敏感操作授权

一个工作流就是这些节点的有向图。节点之间不直接互相调用,而是通过一个共享的 WorkflowContext 传递数据。这样做的好处是:任何一个节点可以替换、可以单独调试、可以挂掉重试而不影响其他节点。

Task Node 是其中最重要的节点,需要单独设计。它的输入是一个 Prompt Template 和一段上下文,输出是模型的文本回复。这里有一个非常关键的设计原则:**Task Node 内不写任何业务逻辑,只做“生成”这件事。**判断生成结果好不好、要不要重试,交给上层的 Decision Node 和 Tool Node。

2.2 状态管理:不要把上下文塞给每一个节点

一开始我犯过一个严重错误:把整个对话历史、所有中间输出、整个仓库文件树,全部塞进每个节点的 Prompt 里。结果模型看起来什么都知道,实际上什么都记不住,输出质量一路下滑,Token 费用还高得吓人。

后来的方案是“按需注入 + 显式状态”。WorkflowContext 里只存三种东西:

  • 不可变输入:需求原文、项目元信息、约束条件
  • 可累积产出:已生成的代码、测试报告、检查结果
  • 事件日志:每个节点的耗时、Token 消耗、重试次数

在 Task Node 真正执行之前,一个专门的 ContextBuilder 模块会根据当前节点的类型,从三样东西里挑出与该任务相关的那一部分,拼成 Prompt。其他东西一概不注入。

这样做还可以顺便解决一个让很多人头疼的问题:上下文漂移。节点越多,模型越容易把早期步骤里的小决定理解歪。按需注入就像是给每个节点发一张“任务工单”,只告诉它需要知道的事情,它反而更能聚焦。

2.3 循环与控制:给 Agent 的“自由”装上刹车

Agent 任务没法避免循环——模型写代码发现测试挂了,修完又挂,再修。没有控制的话,这种循环可以无限进行下去,账单一夜跑爆。框架里必须内置三套刹车机制:

第一套,次数限制。每个 Task Node 都有 max_attempts,超过次数就进入失败分支,要么终止流程等待人工,要么切换备用模型重试。

第二套,工具调用白名单。模型只能在注册过的工具集合里选择,任何未授权的工具调用直接拒绝并记入日志。这个相当重要,你不希望模型哪天心血来潮去执行rm -rf。

第三套,成本预算。每个工作流实例可以设置总的 Token 预算或金额上限,一旦超过,框架主动中断所有节点,生成一份“成本异常报告”。

这三套刹车必须同时存在。次数限制防的是逻辑死循环,白名单防的是工具滥用,成本预算防的是资源失控。任何一个缺失,Harness 都不合格。

3. 实操:搭建一个最小可用的 Agentic Harness Workflow

3.1 确定技术栈与目录结构

我选择的是 Python 3.11 + asyncio,因为 Python 的生态对大模型和代码分析工具支持最全,asyncio 方便并行跑多个 Agent 节点。没有用重量级的服务器框架,核心就是一个异步调度器加一堆节点实现。

最小目录结构长这样:

agentic_harness/ ├── runtime.py # 核心调度器 ├── context.py # WorkflowContext 与数据装载 ├── nodes/ │ ├── base.py # 节点基类 │ ├── task_node.py # 大模型生成节点 │ ├── tool_node.py # 工具执行节点 │ └── gate_node.py # 人工门禁节点 ├── workflows/ │ └── coding_task.yaml # 工作流定义 ├── tools/ │ ├── run_tests.py │ └── git_ops.py └── main.py

这套结构的好处是,每个节点和工具都是独立的 Python 模块,可以单独写测试。我强烈建议你哪怕只是自用,也保持“定义文件与实现代码分离”:工作流描述放在 YAML 里,节点的实现放在代码里。这样调整流程不需要改代码,改 YAML 就行,非常适合多团队协作时划分责任。

3.2 定义工作流:把 AI 编程翻译成 YAML

以“修复一个失败的单元测试并合入”为例,工作流定义可以直接抄:

id: fix_failing_test name: 修复失败用例并提交 version: 1.0 timeout: 3600 budget: 200000 nodes: - id: analyze_failure type: task prompt_template: "分析以下测试失败信息,定位根因,输出简洁的诊断结论。" max_attempts: 2 - id: plan_fix type: task prompt_template: "基于诊断结论和仓库结构,输出修复方案,给出具体文件与改动点。" max_attempts: 3 - id: apply_changes type: tool tool: apply_patch max_attempts: 1 - id: run_tests type: tool tool: run_tests max_attempts: 1 - id: tests_check type: decision condition: "ctx['run_tests']['passed'] == True" next_on_true: human_review next_on_false: fix_again - id: fix_again type: task prompt_template: "根据最新报错修正之前的改动,只输出修改后的补丁。" max_attempts: 3 - id: human_review type: gate message: "AI 已完成修复,请在 30 分钟内人工审核本次改动。" - id: merge type: tool tool: git_review_merge max_attempts: 1 edges: - from: analyze_failure to: plan_fix - from: plan_fix to: apply_changes - from: apply_changes to: run_tests - from: run_tests to: tests_check - from: tests_check to: human_review - from: tests_check to: fix_again - from: fix_again to: apply_changes - from: human_review to: merge

看到里面的循环了吗?fix_again -> apply_changes -> run_tests -> tests_check是一个环,如果测试一直不过,这个环会一直转。但因为有每个节点独立的 max_attempts,以及总超时 3600 秒兜底,它不会无限跑下去。这种“局部循环 + 全局刹车”的组合,就是 Agentic 和传统流程编排最大的区别。

3.3 实现核心调度器与节点基类

调度器是整个框架的心脏,我用最简单的事件循环实现。一个节点执行完,根据它的输出和边的定义决定下一步谁执行。

class Runtime: def __init__(self, workflow, context): self.workflow = workflow self.ctx = context self.status = {} async def run(self): next_nodes = self.workflow.start_nodes() while next_nodes: current = next_nodes.pop(0) for node in current: if not await self.should_skip(node): result = await self.execute_node(node) self.status[node.id] = result next_nodes = self.resolve_next(current) return self.ctx

每个节点的基类只有两个方法:

class Node: def __init__(self, config): self.config = config self.attempts = 0 async def execute(self, ctx: WorkflowContext): raise NotImplementedError @property def exhausted(self): return self.attempts >= self.config["max_attempts"]

Task Node 的 execute 方法负责组装 Prompt、调用模型、解析输出。

class TaskNode(Node): async def execute(self, ctx: WorkflowContext): self.attempts += 1 prompt = self.build_prompt(ctx) result = await call_model( model="gpt-4o", messages=prompt, temperature=0.2, ) ctx.append_event(self.config["id"], "task_completed", result) ctx.outputs[self.config["id"]] = result return result

这里call_model是一个封装的异步函数。把模型调用封装成一层,方便以后替换提供商或加入缓存。我的经验是,这个函数一定要支持两个参数:max_tokens和temperature,并且要把每次调用的输入输出完整记录到事件日志里,否则出了问题根本没法复盘。

3.4 关键机制:工具沙箱、人工门禁与观测

工具沙箱是我后期加入的功能,但如果你打算让 Agent 在真实仓库里跑自动化操作,这一步绝对不能省。

class ToolNode(Node): async def execute(self, ctx: WorkflowContext): self.attempts += 1 tool = self.config["tool"] if tool not in ctx.allowed_tools: raise ToolNotAllowed(tool) result = await execute_tool_in_subprocess(tool, ctx) ctx.outputs[self.config["id"]] = result return result

execute_tool_in_subprocess会在一个低权限用户下执行工具命令,并限制工作目录。实际我踩过这样一个坑:第一次让 Agent 自己跑pytest,它看到测试挂了一个异步用例,竟然自作主张给测试代码加了time.sleep(1)想“修复”时序问题。这属于危险的“解题思路跑偏”。因为测试代码和业务代码都在仓库里,一个不受约束的 Agent 完全可能把测试改成“看起来通过”的样子。人工审核门禁就是防止这种隐蔽问题的最后防线。

人工门禁的实现很简单,但在消息提醒这块要稍微花点功夫。我在框架里内置了一个 gate 节点,执行到这里会向企业内部通讯工具发一条卡片消息,附带 diff 链接和确认按钮。卡片的按钮回调会写回数据库,流程继续或终止只取决于按钮结果。

class GateNode(Node): async def execute(self, ctx: WorkflowContext): await ctx.emit_gate(self.config["id"], self.config["message"]) decision = await ctx.wait_for_gate(self.config["id"], timeout=1800) if not decision.approved: raise GateRejected("人工审核未通过") ctx.outputs[self.config["id"]] = decision return decision

观测这一块也很有必要。每次节点执行完,我会往时序数据库写一条记录:节点 ID、耗时、Token 数、重试次数、状态。后面排查问题全靠它。有一段时间我发现某个任务的 Token 消耗异常高,查观测面板发现fix_again节点被触发了十几次,每个节点又往 Prompt 里塞了完整报错堆栈,相互叠加导致上下文越来越大。这个现象后来成了我们判断“上下文被污染”的典型信号。

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

4.1 上下文漂移:模型“忘了”最初的需求

这是跑多节点工作流时最常遇到的问题。表现为:后面的节点输出的代码,跟最初需求对不上,或者方案里的关键约束没有传递到最后的实现节点。

排查思路:先看事件日志,确认每个节点收到的事件日志是否完整。我在 2.2 节提过按需注入,这不是一句空话。要具体落实到每个 Task Node 的 Prompt 里,必须把“需求约束”单独抽成一个字段,并且在每一轮生成的结尾让它复述关键约束。

解决技巧:在 Task Node 的 Prompt 末尾加上这么一句“请先用一句话复述你刚才收到的最重要约束,再开始输出。”大量实验证明,这种强制性的“输出前复述”能显著减少生成结果偏离需求的情况,而且几乎不消耗额外成本。它可以看作是一种廉价的“工作记忆检查”。

4.2 工具调用循环:模型反复执行同一个操作

有一段时间,我们的工作流频繁出现模型的工具调用死循环,比如反复执行同一段git diff,或者反复跑同一组测试,但什么都不改。后来定位到原因:模型没有从工具输出中提取到“下一步要干什么”,于是默认选择重复上一次动作。

解决这个问题,我给 Tool Node 加了一层“操作总结器”。工具执行完后,用一个小的专用模型调用,把原始输出(比如测试失败的一大堆日志)压缩成结构化摘要,包括核心错误、涉及文件、建议定位范围。再把摘要喂给下一个 Task Node。这一步看似多花了 Token,实际上是省钱的,因为下一个节点不需要阅读完整日志就能理解现状,而且不会被日志里的噪声带偏。

另外一个很实用的限制是:给同一种工具操作加“间隔锁”。短时间内重复触发同一个工具且输入完全一致时,调度器直接拒绝执行,返回上一次的结果。这个策略在代码生成场景里非常有效,因为重复运行同一个测试并且输入没变,结果本来就一样。

4.3 生成结果不稳定:同样的输入,不同的输出

很多人的第一反应是调低 temperature,把模型参数改成 0。实测下来,temperature 设成 0 确实能降低随机性,但不能完全消除不确定性,因为模型本身有采样逻辑,而且不同版本模型之间的行为差异更大。我们的做法有两层:第一层,模型参数固定,temperature 固定在 0.2 左右,给一点随机性但不过分;第二层,建一个“回归样本集”,把过去出现问题的工作流输入输出记录下来,每次框架改动后,跑一遍回归集,确保质量没有回退。

这个回归集非常值钱。有一次我重构 ContextBuilder 模块,自认为会减少 Token 消耗,结果回归样本集跑下来,三个样本的方案生成质量明显下滑,才发现新版本把“需求约束”过滤掉了。如果没有回归集,这种回归错误可能要等真实任务跑挂才能发现。

4.4 测试被 Agent“作弊”式修复

这个我在实操部分提过,值得单独拿出来说:模型发现测试挂了,它不修业务代码,反而去修测试断言,这是一种非常典型的“走捷径”行为。它会给你一个非常合理的话术,比如“调整测试用例以匹配实际行为”,但在代码交付里,这种行为往往是不可接受的。

应对手段,首先是给“测试验证”节点加目录限制,只运行指定测试目录,并且让它读入 tests 目录下的 README 或约定文件,明确“测试优先级高于实现”。更重要的是,在人工审核卡片里,必须并排展示业务代码 diff 和测试文件 diff,审核人一眼就能看出是不是动了测试断言。

如果条件允许,可以在工具沙箱里把测试目录设为只读。这样 Agent 连改测试文件的可能性都被物理切断了。这是我的终极方案,后面跑下来再也没出现过“作弊式修复”。

4.5 Diff 过小或过大:模型偷懒或超范围修改

模型有时候会把任务“缩水”:需求是要修一个函数,它只改了一行注释,然后说完成。反过来也会超范围:让它修一个 bug,它顺带着重构了好几个文件。这两种情况都必须通过工具检查来拦截。

我在 apply_changes 这样工具节点之前,总设一个“diff 统计节点”:跑git diff --stat,把改动文件数和行数写入上下文,然后与任务预估范围做比较。改动行数低于某个阈值或高于某个阈值时,工作流自动进入“需求复查”分支,让模型重新阅读需求,并判断当前改动是否符合。

这种启发式检查不会百分之百准确,但很管用。至少它能抓住“完全没改”“狂改一堆文件”这两个极端,把 Agent 的行为拉回到可控范围。

5. 再分享几个让我少走三年弯路的经验

直接给结论,不铺垫。

**先设计状态结构,再设计 Prompt 模板。**大多数人反过来。他们在意 Prompt 怎么写,却不关心状态怎么传。一旦节点多了,状态结构不合理,Prompt 写得再精致也白搭,信息该丢还是丢。我的建议是,定义工作流之前,先画一张“数据流图”,确定每个节点从哪个字段取数、往哪个字段写数。这一步做扎实,后面开发会非常顺。

**人工审核点宁可多,不要少。**初期我们只设了一个最终审核点,结果模型生成的中间方案越走越偏,到最后一步人工才发现,返工成本极高。后来在方案生成阶段和代码生成阶段各加了一道快速审核点,改动大方向错了能及时被拦下来。虽然多花了人几分钟时间,但整体效率反而提升了一倍。

**日志里一定要有 Token 消耗的增量。**之前我只看总消耗,排查问题特别困难。改成每个节点独立记录增量消耗后,一眼就能看出哪个节点在“烧钱”。我甚至给日志加了个警告:单个节点消耗超过平均值的 5 倍时,直接在面板里标红。这让你快速识别出上下文越来越大的异常节点。

这套 Agentic Harness Workflow 本身不是银弹。它的所有优势,都建立在“你把流程想清楚了”的前提上。如果业务本身一团乱麻,装再好的 Harness 也是让模型在一团乱麻里更快地迷路。但一旦你的流程可描述、可拆解、可度量,再给它套上这套框架,AI Coding 的生产效率确实会迎来一轮质的提升。我自己的项目组,过去一个功能从开发到自测差不多要两天,现在稳定在半天以内,而且质量波动小得多。建议你从最小的场景开始,我一个同事就是从“自动修一个失败的测试用例”起步,跑了不到两个月,就培养出了对整个框架的信心。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/14 5:39:45

VC++模拟鼠标单击与双击:从消息机制到SendInput实战

简介:这份VC/MFC源代码资源聚焦Windows系统下的鼠标消息模拟,面向具备一定C基础、希望理解鼠标单击与双击底层响应机制的开发者。工程在窗体上布置若干按钮,通过点击按钮触发对鼠标单击、双击行为的模拟,帮助读者把消息映射、窗口…

作者头像 李华
网站建设 2026/10/14 5:39:42

Discuz关注功能开发实战:从数据表到前端交互

做Discuz开发的朋友,多多少少都经历过这样的需求:论坛里用户想要“关注某人”,关注之后能在个人中心看到自己关注了谁、谁又关注了自己,甚至希望能收到被关注对象的动态提醒。这个功能在主流社交平台里是标配,但在Disc…

作者头像 李华
网站建设 2026/10/14 5:39:36

网络安全通信方案推选PPT实战:从选型框架到评审问答

简介:这是一份关于虎符科技网络安全通信服务三种解决方案的PPT资源,面向关注网络身份认证、通信加密及安全通信平台建设的安全工程师与方案决策者。内容围绕标识认证技术展开,重点介绍基于组合公钥的超大规模自证安全系统,以及链路…

作者头像 李华
网站建设 2026/10/14 5:39:01

iBizEAM源码部署与定制化实战指南

简介:iBizEAM设备资产管理系统是一套面向中大型企业IT运维与资产管理场景的开源JavaVue全栈解决方案,适用于具备SpringBoot、Vue及前后端分离开发经验的中高级开发者学习与二次开发。资源包共2000个文件,主体为1280个Java后端服务模块、1746个…

作者头像 李华
网站建设 2026/10/14 5:38:58

从控制器到工具法兰全面焕新:UR Gen 7工博会中国首展,CB7控制器与PolyScope X重构机器人编程

以“智能经济 智造无界”为主题的第二十六届中国国际工业博览会10月12日在上海国家会展中心启幕。本届工博会聚焦具身智能、工业大模型与数字孪生,近300项首发首秀展品集中亮相;机器人展以“具身工业 智领未来”为主题,5.6万平方米展区迎来40…

作者头像 李华
网站建设 2026/10/14 5:38:31

【行业洞察】顶尖程序员切入理论数学研究的新范式与机遇

文章目录系列文章范式变革总览工具革新打破学科壁垒意义启示非数学科班出身者的数学丰碑皮埃尔德费马(Pierre de Fermat)戈特弗里德威廉莱布尼茨(Gottfried Wilhelm Leibniz)爱德华威滕(Edward Witten)玛乔…

作者头像 李华