建立无环的agent工作流

4 阅读7分钟

原文来自个人博客主站,纯技术分享

太长不看:

  • 不要指望 agent 一口气吃下一个复杂工作流。
  • 把流程拆成多个单向、短上下文、规则明确的子流程,稳定性会明显提高。
  • 有环流程不是不能做,但一定要显式限制循环次数和停止条件。

最近已经不满足于 agentic coding 了,沉迷于使用 coding agent 做自动化。

像 CC(Claude Code) 这样的工具,不仅仅是优秀的编码助手,在“听从指令”这件事上也很擅长,很适合接手固定、复杂、又重复的流程化工作。

这里有个非常重要的小提示:不要指望 agent 能一口气搭建好非常复杂的工作流

虽然 agent 的知识面很广,但是它的注意力是有限的。除非你能给出非常具体的规则条件,精确到每一个容易跑偏的小细节,否则它的注意力很容易被转移,工作流就会跑偏。

在用 coding agent 做开发自动化时,可能会遇到这些问题:

  • 流程一长就开始跑偏
  • 一旦需要反复回滚、重试、验收,结果就越来越不稳定
  • prompt 越写越长,但效果并没有变好

我的经验是,把复杂工作流拆成一个一个简单流程,先把简单流程稳定实现,再把整个流程串起来,成功率会高很多。

把频繁的UI交互流程做成自动化

上周我试着把 CC 应用到软件开发业务的工作流中,搭一套跟自己业务相关的 AI 自动化工作流。

软件团队人手不够,需求、测试、开发、验收全靠自己(纯纯一人公司了),有很多繁琐的流程化操作不得不去面对。

需求暂且不说,测试要负责“找问题”:跑场景、抓指标、提issue、复测验收。研发要负责“解问题”:复现、修复、单元测试、回归测试。

即便只是使用最简单的 gitlab 来维护项目,像“维护issue”这类工作,也需要大量重复性的键鼠交互操作。

所以,第一个自动化就拿它开刀。

把工作流的目标抽象成一条条规则

我这里说的自动化不涉及像 Github Action 这样的第三方 App,简单利用 skills/hooks 这样的原生机制就能制作很好用的自动化流程。

像 CC 这样的 agent 都具备自动构建 skills 的能力(skill-creator),你只需要把需求转化成它能看懂的 prompt 告诉它就行,它能自动完成转化。

当然,这样生成的自动化是否好用,完全取决于 user prompt 的质量高低。我强烈建议用 markdown 这种格式化的语言去写 prompt,并把自动化工作流用规则的形式明确表达出来

比如,可以模仿 skills 的通用模板,按照Objective, In-Scope, Out-of-Scope, Rules等标题去归纳、整理 prompt。

这样做的目的是让 agent 集中注意力,让它在你关注的所有流程细节上深入挖掘,避免工作流“跑偏”。

拿测试的需求举例,我希望 agent 能抓取、分析数据日志,从日志中提取性能指标并跟 benchmark 做对比,将不符合预期的 case 都在 GitLab 上用 issue 的形式整理出来。整个工作流是单向线性的,输入端是测试场景,输出端是 GitLab issues。

这个时候,prompt 里至少要显式写清楚这几类规则:

  • 不同测试分别看什么指标
  • 日志结构长什么样,关键字段在哪里
  • benchmark 是什么,超过多少算异常
  • issue 要用什么标题、正文结构和标签

如果当前工作流只是更大流程中的一个子流程,那还要额外考虑“给下游喂什么数据”。

比如,如果后续还有一个“自动修复 issue”或者“自动验收 issue”的子流程要接这个输出,那么在定义 issue 模板时,就应该提前把后续流程需要的信息也写进去(即便当前流程本身不需要这些信息),比如:

  • 测试数据源
  • 异常指标
  • 日志片段
  • 预期行为

很多时候,工作流不稳定,不是因为 agent 不够聪明,而是因为上游输出的信息对下游来说不够用。

拿我做的这件事举例,可以把一个无环工作流抽象成下面这种样子:

项目内容
Input测试场景、日志文件、benchmark 配置
Rules指标提取规则、阈值定义、issue 模板
Steps解析日志 -> 提取指标 -> 对比 benchmark -> 生成 issue
Output一组结构一致的 GitLab issues
Stop Condition当前批次所有 case 都已完成分析

prompt 可以按照这种格式去写:

# Objective

分析测试日志,找出异常 case,并生成 GitLab issue。

# Input

- 测试场景列表
- 日志目录
- benchmark 配置文件

# Rules

- 每种场景使用对应的指标集合
- 仅当指标超过阈值时创建 issue
- issue 标题格式固定
- issue 正文必须包含场景、指标、阈值、日志证据、复现建议

# Out-of-Scope

- 不负责修复问题
- 不负责回归测试

# Stop Condition

- 所有输入场景均完成处理

这样的 prompt 格式可以帮助 agent 更好的理解和完成工作流。

单向无环的工作流远比有环的工作流稳定

在实践中发现,越简单的工作流越容易实现,越不容易出错。

这也很符合工程设计的哲学:越稳定的系统,其内部设计往往越简单

什么是“单向无环”的工作流?所有的步骤只允许向前执行一次。这样的工作流往往是简单的。

“有环”的工作流就是不符合这一特征的流程,即流程可能会“回头”。比如,对于某些有验收标准的工作,很难一次性完成,就需要构造“验收 -> 不通过 -> 修改 -> 再验收”的环状流程。

我自己的感受是,这两类工作流的差异非常大:

对比项无环工作流有环工作流
步骤方向只向前走一次会回头、会重试
context 压力
规则复杂度相对简单通常更高
失败模式某一步失败即停止容易反复迭代后越来越偏
是否适合先自动化很适合适合拆小后再自动化

有环流程最麻烦的地方,不是“多了一步”,而是它会持续消耗 context。

当 agent 在同一个上下文里反复经历“判断 -> 修改 -> 再判断 -> 再修改”这类循环时,约束条件会逐渐松动,局部目标会逐渐覆盖全局目标,最后就开始出现一些很熟悉的症状:

  • 忘记最初的验收标准
  • 只盯着眼前报错,忽略更高层的约束
  • 输出格式越来越飘
  • 修着修着,把原本已经正确的东西也改坏了

这也是为什么我越来越倾向于把工作流做成能在单个 context 内闭环完成的小任务。

不是因为有环流程完全不可做,而是因为它对 agent 的记忆稳定性要求更高,而这恰恰是今天的 coding agent 最不稳定的一环。

所以,对于有环工作流,我现在的默认策略很简单:

  • 能拆成多个无环子流程,就先拆
  • 实在拆不掉,也必须限制最大循环次数
  • 每轮循环都要重新声明验收标准
  • 到达上限后,宁可中断交给人,也不要让它无节制地继续转

这样做当然有副作用。最直接的副作用就是:有些任务可能因为迭代次数不够,最终没能达到验收条件,工作流会提前中断。

但在工程上,这通常是个更好的失败方式。因为**“可控地失败”往往比“失控地继续”更容易被接受**。

一言以蔽之,简单、单向、无环的工作流,通常比复杂、有环、持续自我回写的工作流更稳定。