工作流 📋 6 个步骤 第 572 / 572 篇

用 Optio 自建 Agent 工作流平台:事件触发、三类工作体与用量账本实操

编码 Agent 跑出补丁不难,难的是补丁之后:CI 红了谁叫醒它、评审意见谁接住、全绿了谁来合并。Optio 是自托管控制面,把票追到合并,并用用量账本与三层月度预算管住花销。本教程六步落地。

2026-10-27· 11 分钟上手· 约 3631 字· 🛰️ Optio / 🔁 CI 闭环续跑

让编码 Agent 跑出一段补丁并不难,难的是补丁之后的事:CI 红了谁去把它叫醒继续修,评审提了意见谁去接住,全绿之后谁来合并。Optio 卖的正是这段「后半程」。它是一个自托管的编排控制面,跑在你自己的 Kubernetes 集群或任意装了守护进程的机器上,把票变成一次 squash 合并的 PR:CI 失败时带失败上下文自动续跑,评审要求修改时接住评论并推修复,全绿时合并并关闭 issue。0.20.0 把它从原本「CI 失败续跑」的单一工具重做成通用工作流平台,并新增了对 Pi 智能体的支持,基础镜像换到了带十月安全修复的 Node.js 26.12.1。它由 jonwiggins 开源,MIT 协议,TypeScript 实现。本教程按自建前置判断、三类工作体、写一份工作流、闭环续跑、Controller MCP、用量账本与预算六步把它落地。

🛰️ 本教程适合:已经在运维 Kubernetes、且因为数据驻留或租户隔离不能把源码与凭据交给托管 Agent 服务的平台团队。你需要一套合规集群(GKE、EKS、AKS 或任何符合规范的 K8s)、Postgres 与 Redis,以及一个 Agent 运行时(Claude Code、Codex、Gemini、Pi 等)。文中接口以官方仓库文档为准;0.20.0 刚完成通用化改造,接口仍在演进。

Step 1:先判断你该不该自建

1 没有集群的话,托管方案更简单

自建的收益是明确的:代码、密钥与 Agent 日志全部留在你控制的基础设施里,并且可以按仓库切换或对比不同 Agent 厂商。代价同样明确——你要跑 Helm、Postgres、Redis 与一个控制面,还要为它做升级、备份与告警。项目自己的判断也很坦率:如果团队没有集群、也不打算运维集群,托管选项更简单;如果 Agent 工作量小到开一个本地 CLI 会话就够,那么为了它跑一整套数据库与控制面并不划算。

# 自建之前先确认这几件事都成立
# 1) 已有合规 K8s:GKE / EKS / AKS / 任何符合规范的集群
# 2) 能长期运维 Postgres 与 Redis(备份、升级、监控)
# 3) 有明确的数据驻留或租户隔离要求,无法使用托管服务
# 4) Agent 工作量足够大,摊得平固定运维成本

# 部署形态:Helm chart + Postgres + Redis + 控制面
# 控制面用对账循环驱动,pod 状态不放在内存里

一个实现细节值得留意,因为它决定了它能不能扛住重启:控制面靠对账循环工作,pod 状态不保存在内存里。也就是说编排器重启之后,它会把实际状态与期望状态重新对一遍再继续,而不是「忘了自己在干什么」。这是把长流程交给机器跑的前提——一次部署不该让正在进行的任务人间蒸发。

它是控制面,不是 Agent:Optio 不提供模型能力,驱动的是你已有的 Agent CLI,因此你仍然要为 Claude Code、Codex 或其它运行时的订阅与用量付费。它的价值在触发、续跑、合并与记账这一层,不要指望装上它智能体就变聪明了。

Step 2:三类工作体:按形状分,不按功能分

2 任务、作业与常驻 Agent

Optio 把 Agent 的活按形状分成三类,而不是按功能罗列。Tasks 是「票变 PR」:手动提交,或从 GitHub Issues、Linear、Jira、Notion 进来,在 git worktree 里干活,产出 PR 并追到合并。Jobs 是不做仓库检出的参数化运行,面向报告、告警分诊、依赖审计、数据库查询与发 Slack。Persistent Agents 是长驻的命名进程,带稳定 slug、收件箱与一个循环状态机,随时可以被别的 Agent 或人发消息。0.20.0 把 Pi 智能体的支持加到了 pod 任务、作业、常驻 Agent 与 PR 审查四类上。

# 三类工作体的分工
# Tasks             票 → PR:手动 / GitHub Issues / Linear / Jira / Notion
#                   在独立 worktree 里工作,产出 PR 并追到合并
# Jobs              无仓库检出的参数化运行:报告、告警分诊、依赖审计、DB 查询、发消息
# Persistent Agents 长驻命名进程:稳定 slug + 收件箱 + 循环状态机

分类的意义在于选错形状会让后面处处别扭。需要改代码并留下 PR 的活,用 Task;只是定期跑一段查询并发个报告,用 Job——硬塞进 Task 会白搭一次仓库检出;需要长期在线、随时被点名的,才用常驻 Agent。判断标准就一条:这次运行要不要碰代码。

💡 从 Job 起步。它不涉及仓库、分支与合并权限,出问题的影响面最小;等你对触发、日志与记账都熟了,再上 Task 这种会真正动代码的形态。

Step 3:写一份工作流:when、where、who、what

3 四段说清什么时候跑、在哪跑、谁去跑、跑什么

一份工作流回答四个问题,加上收尾行为与权限设置。when 是触发:manual 立即开跑,schedule 用五段式 cron 加 IANA 时区(默认 UTC,并会正确处理夏令时),webhook 则接 Slack、Jira、Linear、GitHub、PagerDuty、Datadog 或 Sentry 的事件,把事件载荷变成提示词参数。where 是位置:跑在集群 pod 里,还是跑在任意装了守护进程的机器上。who 是运行时:claude-code、codex、gemini、pi 或其它。what 是提示词本身。工作流可以在 Web 界面里配,也可以写成 YAML 同步给编排器。

{
  "spec": {
    "name": "Daily results review",
    "when": {
      "type": "schedule",
      "config": { "cronExpression": "0 9 * * 1-5", "timezone": "America/Denver" }
    },
    "where": { "runTarget": "cluster" },
    "who":  { "runtime": "claude-code" },
    "what": { "prompt": "Review yesterday's work results and summarize failures and follow-ups." },
    "then": "exits",
    "settings": { "controllerAccess": "read" }
  }
}

时区这一项值得单独拎出来:不少定时任务事故都出在「以为用的是本地时间」。这里明确用五段式 cron 加 IANA 时区名,默认是 UTC,并且会处理夏令时;想让它在丹佛时间每个工作日早上九点跑,就把 timezone 写成 America/Denver,而不是自己换算成 UTC 再写死——后者在夏令时切换那天一定会错一次。写完先手工试跑一次,再去看运行列表与日志确认行为符合预期。

Webhook 载荷是不可信输入:它会被拼进提示词交给 Agent,而 Agent 手上有仓库写权限与工具。来自外部系统的标题、正文与评论都可能夹带指令。给这类工作流配上最小权限的运行时、限定可触及的仓库,并对合并动作保留人工环节——不要因为「反正只是个机器人」就让它自己合。

Step 4:从 CI 红到合并的闭环

4 卖的不是补丁,是补丁之后发生什么

把这一段跑通,才算真的用上了 Optio。Agent 在独立的 git worktree 里工作,产出一个 PR;随后控制面持续跟进 CI 与合并状态。CI 失败时,Agent 会带着失败上下文被自动唤醒继续修;评审人提出修改意见时,Agent 会接住这些评论并推送修复;一切通过之后,PR 被 squash 合并,关联的 issue 被关闭。这条环,而不是生成补丁本身,才是它真正的卖点。

# 一次 Task 的生命周期
# 1) 提交任务(手动 / GitHub Issues / Linear / Jira / Notion)
# 2) 在独立 git worktree 中工作,产出 PR
# 3) CI 失败  → 带失败上下文自动续跑
# 4) 评审意见 → 接住评论,推送修复
# 5) 全绿     → squash 合并,关闭 issue

独立 worktree 这个设计是承重墙:多个任务可以同时在同一个仓库上推进而互不干扰,每个任务看到的是自己那一份工作树。代价是它对仓库与 CI 有要求——测试得能在一次全新检出上跑通,构建产物不能依赖本地脏状态。上 Task 之前,先用一个真实的小任务在干净检出上验证一遍你的 CI,这比事后排查「为什么 Agent 那次总是红」要省事得多。

💡 先限定范围再放开。开始时只允许它处理标注过的 issue、只授予单个仓库的写权限、并要求合并前必须有人批准;跑顺两三周、看清续跑与修复的实际质量之后,再考虑放宽到自动合并。

Step 5:Controller MCP:让别的 Agent 也能派活

5 写操作是异步且非幂等的

除了 Web 界面与 YAML,Optio 还对外暴露一个 Controller MCP:外部客户端可以发现工作、创建工作、读取运行与日志,甚至让不同运行时的 Agent 互相发现与对话。常用工具是创建工作、试跑、列运行、取日志与列 Agent。列 Agent 会返回开启了 controller 权限的常驻 Agent 与活跃的任务或作业 Agent,可以按 kind(persistent-agent、repo-task、job-run)或 runtime 过滤,每条记录带地址、状态、投递方式与一个能否被发消息的标记。手机端也有原生 iOS 与 Android 应用,用来远程接管会话。

# 创建 → 试跑 → 看结果
optio_create_work     # 建一个工作(manual 类型立即开跑,schedule 类型等待触发)
optio_run_work        # 立即试跑一次
optio_list_runs       # 列出运行
optio_get_run_logs    # 取某次运行的日志

# 跨 Agent 发现
optio_list_agents     # 按 kind 或 runtime 过滤,带 address、状态、投递方式、canMessage

有一句必须记住:写操作是异步且非幂等的。发起之后你会拿回 work、run 或 message 的 id,请把它存下来再用它去查状态;如果响应在网络上丢了,正确做法是先去查列表或收件箱确认到底创建成功没有,然后再决定是否重发。凭「没收到回执」就重发,很可能得到两份重复的工作——而这两份都会真实地烧掉 token。

重发之前先查,不要凭回执判断:创建与派发都是异步非幂等操作。响应丢失不等于没创建。先查列表或收件箱确认状态,再决定重发,否则你会为一个已经在跑的任务付两次钱,并且可能得到两份互相冲突的 PR。

Step 6:用量账本与月度预算:把花销管在前面

6 删除任务不会抹掉账

Agent 和普通脚本不一样:它会重试、会思考、会一轮一轮地调模型。一个定时任务跑一个月,消耗可能远超想象。Optio 的应对是一本用量账本:每类任务的计数器在同一个数据库事务里写入,覆盖 Repo Task、Job 运行、PR 审查、常驻 Agent 对话与本机终端等所有来源,做到不重不漏。理解这几条记账规则,你看到的数字才可信。

# 账本规则(决定了你看到的数字是否可信)
# Token 口径   输入(含缓存读/写)+ 输出;缓存命中是子集,不重复计费
# 费用口径     模型上报真实成本 与 API 等价估算 分开标注,估算不是发票
# 归属规则     私人任务归 owner;组织任务归任务本身;子任务计入父任务
# 只增不减     删除运行记录不会抹掉已记账用量;重复或回退的快照不会拉低总额
# 历史数据     一次性回填的合计会标记 historical,与日常数据区分

在这本账之上是月度预算,可以分别设在组织、个人与单个工作三个层级,超支时可以选择暂停新任务。这把「月底收到一张看不懂的账单」变成了「跑到某个阈值就自己停下」。把权限与预算按层级对齐,是这套机制最实用的用法:组织层设总额上限兜底,个人层给成员各自的空间,单个工作层给那些可能失控的实验性任务单独一道闸。

💡 三层预算的用法:组织层是总闸,设在你能接受的最坏情况;个人层按成员数量摊;单个工作层专门留给新上的实验性工作流。新接一个 Agent 运行时或新开一类任务时,先给它一道单独的低额度闸,稳定之后再并入常规额度。

估算是估算,不是发票:账本把模型上报的真实成本与 API 等价估算分开标注,后者只是折算口径。对账请以模型厂商的实际账单为准。另外删除任务或运行记录都不会抹掉已记账用量,所以别指望靠删记录来「清理」账目——它恰恰是为了防止这件事而设计的。

常见问题速查

现象原因与处理
不知道该不该自建没有集群或工作量小到本地 CLI 就够,托管方案更简单
编排器重启后任务丢了靠对账循环驱动,pod 状态不在内存;检查集群侧资源与持久化存储
定时任务时间不对五段式 cron 加 IANA 时区,默认 UTC;不要手工换算成 UTC 写死
夏令时切换那天跑错点用 timezone 指定 IANA 时区名,框架会处理夏令时
Job 白跑一次仓库检出Job 不做仓库检出;需要改代码请用 Task
Agent 每次都在干净检出上失败先验证 CI 能否在全新检出上跑通,Task 依赖这一点
CI 红了没人管闭环本应带失败上下文自动续跑;检查工作流是否配了跟进 CI 状态
响应丢了不知道建没建成功写操作异步且非幂等;先查列表或收件箱再决定是否重发
产生了两份重复工作凭回执就重发导致的;务必先按 id 或列表确认状态
费用数字看不懂上报成本与 API 等价估算分开标注;对账以厂商实际账单为准
删了任务账还在账本只增不减是刻意设计,防止靠删记录改账
担心某个任务烧穿预算在单个工作层单独设月度预算,超支可选择暂停新任务
← 返回教程中心