GitHub Agentic Workflows 现在会在 Agent 启动之前,先去问一个策略服务器
2026 年 10 月 27 日,GitHub 的 gh-aw(Agentic Workflows)团队发了一篇设计说明,讲的是一个很小但很关键的缺口:一个定时工作流已经准备好了,触发条件匹配,授权检查通过,提示词也编译完了——但现在应该再启动一个 Agent 会话吗?这个问题可能不只取决于一个仓库:它牵涉组织级预算、工作流优先级,或者一个集中管理的额度。他们的答案叫 activation gate(激活门禁),在 gh-aw 配置里写作 admission,用 on.admission: true 打开。值得注意的是他们自己划的那条边界:gh-aw 只实现工作流侧的客户端和编译器接线,真正持有策略、验证身份、做可信用量记账的,是一个独立部署的 admission authority。
门禁在推理之前决策,而不是推理过程之中:策略、身份与额度都在上游算完。
一、门禁放在哪:推理之前,而不是推理之中
对于一个已经 opt-in 且配置了端点的工作流,编译器会生成一个 admission 任务,在激活之前跑。它先为 authority 的 audience 申请一个 GitHub Actions OIDC 身份令牌,然后把调用身份和编译后的工作流哈希 POST 到 /api/admission。authority 验证身份、评估策略,返回批准或者拒绝。批准意味着「允许以这个额度开始」,拒绝意味着「Agent 根本不会启动」。这个位置很重要:它不是运行中熔断,而是启动前准入。运行中的花费、前几十秒的 token 消耗、以及一个跑歪了但已经开始的会话,这些都没有机会发生——因为决策发生在时钟之前,而不是时钟之后。
# Opt in per workflow. Nothing else changes about your triggers.
on:
workflow_dispatch:
schedule:
- cron: "0 6 * * *"
admission: true # <- the new key
permissions:
contents: read
# id-token: write is NOT added here. The compiler grants it to the
# isolated admission and completion-callback jobs, not to the agent.
# Or opt in repository-wide, preserving the existing object:
# .github/workflows/aw.json
# { "admission": true, "other_setting": "..." }
# A workflow-level `admission: false` cannot override a repo opt-in.二、三个额度数字:按组件替换,不是共享池
批准返回的是每个组件的上限:max_ai_credits 给主 Agent,max_ai_credits_threat_detection 给威胁检测,max_ai_credits_evals 给评测。文档特意强调这三条规则,因为误读会很贵:第一,每一个提供的额度会替换那个组件的运行时上限,即使它比工作流自身配置的上限更高;第二,省略的可选额度保持原有组件上限不变;第三,这三个数字不是一个共享池。还有一条容易被忽略的语义:一个有效的批准会取代该次激活的本地「每日额度检查」,所以任何共享预算的判断都应该放在 authority 里,而不是留在工作流里。反过来说,如果你想让本地检查继续当兜底,那就要想清楚两者谁先。
# What the compiler-owned admission job does before activation.
# 1) mint a GitHub Actions OIDC identity token for the authority's audience
export TOKEN=$(curl -s \
-H "Authorization: bearer $ACTIONS_ID_TOKEN_REQUEST_TOKEN" \
"$ACTIONS_ID_TOKEN_REQUEST_URL&audience=$GH_AW_DEFAULT_ADMISSION_AUDIENCE" \
| jq -r .value)
# 2) post invocation identity + the compiled workflow hash
curl -sS -X POST "$GH_AW_DEFAULT_ADMISSION_URL" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d "{\"workflow_hash\":\"$COMPILED_HASH\",\"run_id\":\"$GITHUB_RUN_ID\",\"repo\":\"$GITHUB_REPOSITORY\"}"
# 3) approval -> start with allowances. denial -> never start.
# On outage the behaviour is your choice: ignore (fall back to local
# daily-credit check) or block (do not activate).三、怎么接:一个 key,三个变量,一次重编译
工作流侧的开销极小。在现有 triggers 旁边加一行 admission: true 就行;或者在 .github/workflows/aw.json 里加 "admission": true 做仓库级 opt-in(要保留它的其它设置),而且工作流级的 false 覆盖不了仓库级的 opt-in——这个方向是对的,否则一个被改过的工作流就能自己退出治理。改完要跑 gh aw compile,并提交源配置和生成的 .lock.yml。有一个坑必须说:admission 任务默认不生成,只设端点不会凭空生成它,必须先编译进去。运行时配置来自组织或仓库的 Actions 变量:GH_AW_DEFAULT_ADMISSION_URL(必须是 HTTPS,且以 /api/admission 结尾)、GH_AW_DEFAULT_ADMISSION_AUDIENCE(默认等于端点 URL)、GH_AW_DEFAULT_ADMISSION_ON_OUTAGE(默认 ignore,或设 block)。端点为空的语义是「跳过准入、保留现有本地检查」;仓库变量优先于组织变量;这些变量改完不需要重新编译。
admission 策略服务器不属于 gh-aw。它得你自己部署。
# An approval supplies per-component maximums.
# These are illustrative allowances, not built-in tiers.
{
"max_ai_credits": 1000, # primary agent
"max_ai_credits_threat_detection": 100,
"max_ai_credits_evals": 50
}
# Rules that matter in practice:
# - each supplied value REPLACES that component's runtime limit,
# even when it is higher than the workflow's configured limit
# - omitted optional values leave the existing component limit alone
# - the three numbers are NOT a shared pool
# - a valid approval supersedes the local daily-credit check for
# that activation, so any shared-budget decision belongs upstream四、身份用 OIDC,不用 PAT
这条设计细节值得单独拿出来:认证走 GitHub Actions OIDC,不是 PAT,也不是模型提供商凭据。id-token: write 只授予隔离的 admission 任务和完成回调任务,不会因为开了 admission 就给 Agent——权限最小化的方向很清楚。手写 reusable workflow 的调用方还要自己允许这个 scope,不然拿不到令牌,门禁会静默失效或者报错。闭环那一半叫 conclusion_admission:普通 conclusion 任务发布统一会话产物之后,这个隔离任务用一枚新的 OIDC 令牌,把有界的紧凑元数据和产物 ID 发给 authority 同源下的 /api/conclusion。要说清楚它的边界:这是会话结束时的投递,不是实时流式上报,也不是结算;投递是 best-effort,回调失败只会告警,不会改变已经完成的 Agent 结果或 safe-output。
# Runtime configuration lives in repository or organization
# Actions variables. Repository variables win.
GH_AW_DEFAULT_ADMISSION_URL=https://policy.example.com/api/admission
# ^ must be HTTPS and end in /api/admission
# ^ empty value skips admission entirely and keeps local checks
GH_AW_DEFAULT_ADMISSION_AUDIENCE=https://policy.example.com/api/admission
# ^ OIDC audience; defaults to the endpoint URL
GH_AW_DEFAULT_ADMISSION_ON_OUTAGE=ignore
# ^ ignore = transient outage falls back to existing local controls
# ^ block = do not activate (explicit denials block either way)
# Changing these does not require recompiling, but the admission jobs
# must already be compiled in: an endpoint alone generates nothing.
# gh aw compile && git add .github/workflows && git commit五、它是合作式门禁,不是硬上限
官方在最后一段主动拆自己的台,这段应该原样读给采购和安全听:admission 仍然是合作式的——工作流编辑者可以移除参与,仓库变量管理员可以覆盖组织默认值,并发运行可以超出共享预算。硬配额、预留和运行中取消需要独立实施的后端控制。中断行为是一个明确的可用性选择:默认 ignore 时,短暂的 authority 故障会退回现有本地控制;设 block 时就直接不激活;而明确的拒绝和无效批准在两种策略下都会阻断。同一周他们还发了 v0.93.0 到 v0.93.5 六个版本,方向是加固:safe-output 集成任务在需要时可以使用网络凭据,引擎驱动崩溃现在带有 secret-safe 的 stderr 诊断以便分诊,Pi CLI 1.4.0 成为默认,另外新增了 readerstringconv 之类的自定义 linter,并给编译期加了索引边界保护与 cron 步进溢出防护。把两边放一起看结论很清楚:门禁解决「该不该开始、以多少额度开始」,加固解决「开始了以后别乱来」,两者不能互相替代。
额度是「按组件替换运行时上限」,不是三个组件共用一个池。
# Policy authority, in the shape the contract implies.
# Keep policy server responsibilities separate from workflow execution.
def admit(request, oidc_token):
claims = verify_oidc(oidc_token, audience=EXPECTED_AUDIENCE)
if claims.repository != request.repo:
return Deny("identity/repo mismatch")
if request.workflow_hash not in APPROVED_HASHES[request.repo]:
return Deny("unreviewed workflow revision")
budget = shared_budget(org=claims.org) # org-wide, cross-repo
if budget.remaining < MIN_RUN_CREDITS:
return Deny("org budget exhausted")
return Approve(
max_ai_credits=min(budget.remaining, POLICY_CAP),
max_ai_credits_threat_detection=THREAT_CAP,
max_ai_credits_evals=EVAL_CAP,
)
# Sub-second decisions, but concurrency is not solved here:
# parallel runs can still overshoot a shared budget.📌 常见问题 FAQ
admission 和本地每日额度检查是什么关系?
一个有效的批准会取代该次激活的本地每日额度检查。所以共享预算、跨仓库配额这类判断应该上移到 authority;如果你希望本地检查继续作为兜底,就要明确它在什么情况下生效,别让两条逻辑互相打脸。
三个 max_ai_credits 是共享一个池子吗?
不是。官方明确说这三个值不是共享池:max_ai_credits 给主 Agent,threat detection 和 evals 是独立额度。每个提供的额度会替换对应组件的运行时上限,即使比工作流自己配的更高;省略的可选额度则保持原有限制。
为什么只配了端点却没有生成 admission 任务?
因为 admission 任务默认不生成——只配置 GH_AW_DEFAULT_ADMISSION_URL 不会产生任何任务。必须先用 gh aw compile 把任务编译进 .lock.yml 并提交。之后再改那三个 Actions 变量就不需要重新编译了。
authority 是 gh-aw 自带的吗?
不是。gh-aw 只提供工作流侧的客户端和编译器接线;admission authority 必须单独部署,由它持有策略、验证 OIDC 身份、做可信用量记账。官方也明确说安装 gh-aw 不等于部署了 CAO 或者一个托管准入服务器。
它能防止预算超支吗?
能降低但不能保证。官方自己说这是合作式门禁:编辑者可以退出、仓库管理员可以覆盖组织默认值、并发运行仍然可能超出共享预算。要硬保证,需要独立实施的后端配额、预留和运行中取消。