# AI 事件确认应如何保留未关闭的事件

代理确认告警，只说明了一件有用的事：某个人或某个系统已经接手调查。代理并没有证明用户已经恢复正常、告警条件已经消失，或下一位响应人员可以停止待命。任何把确认直接变成关闭的集成，都会抹掉这一差别，并在事件记录里写下不实信息。

应该把事件操作设计成相互独立的状态转换，并为每种转换设置单独权限。确认、解决、抑制和路由在告警界面里可能挨在一起，但它们回答的是不同的运维问题。代理应调用范围最窄的操作；当制度有要求时，由人批准影响较大的转换；审计记录则必须同时写明两类参与者。

## 确认记录责任归属，而不是服务恢复

确认表示响应人员已经接下告警并开始调查。它会改变接下来应由谁行动，也常常会改变升级行为，但它没有说明故障是否仍然存在。

PagerDuty 的 Incidents 文档明确区分了这两件事：已确认事件正在处理中，但尚未解决。确认会认领责任，并在确认超时前停止升级；解决才表示问题已经修复。这个超时机制很重要。如果超时前没有人解决事件，PagerDuty 可以把事件重新改成已触发并继续升级。代理在确认时关闭事件，会悄悄移除这层保护。

Google Cloud Monitoring 使用的术语略有不同，但保留了同样的边界。其文档把 Acknowledged 定义为仍处于打开状态、有人在调查时手动标记的事件。文档还说明，确认不会停止重复通知。休眠或策略变更负责控制通知；而关闭则依赖恢复观测、人工关闭或自动关闭条件。这个行为提醒我们，不能假定告警系统里的所有动词都有相同副作用。

实际契约应该很明确：`acknowledge` 记录负责人、确认时间以及接下工作的参与者。如果告警系统支持，它可以暂停升级路径。它必须让事件的恢复状态保持打开，保留仍活跃的告警，并且不得修改通知或路由规则，除非调用方通过另一个控制明确请求另一项操作。

这条边界也能保护事件指标。如果确认同时关闭记录，确认时间和恢复时间会落在同一个时间戳上。团队随后会奖励快速点按钮的行为，却失去真正反映用户受影响时长的指标。事后复盘也会继承同样的错误，因为时间线会声称缓解措施生效前服务就已经恢复。

## 一个代理动词只能对应一次转换

每个代理工具都应公开一个运维动词和一个主要效果。把多个动作捆在一起，在平静的设计评审中看似方便，真正发生故障时却很难推理。

确认回答“谁接下了工作”。它的主要效果是记录责任和确认时间，不能暗示服务已恢复或告警已消除。解决回答“事件是否已经结束”。它在取得恢复证据后关闭记录，不能顺带静默未来的告警或改变负责人。

抑制回答“选定信号是否应创建事件或发送通知”。它在指定范围内阻止部分通知或事件创建，但不会让已经打开的事件恢复。路由回答“谁应该收到或负责这项工作”。它会改变服务、团队、升级路径或负责人，却不能声称目标方已经确认或解决任何问题。

这些定义共同构成授权边界。人们可能愿意让代理确认分配给本团队的任何告警，但要求真人批准解决操作。同一个人也可能只允许在已声明的维护窗口内抑制告警，并且只允许在两个服务之间路由。一个宽泛的 `manage_incident` 能力无法表达这些选择，除非在内部藏入难以看见的分支。

不要设计带有 `ack`、`mute`、`assign` 和 `close` 标志的 `handle_page` 工具。它会让模型根据自然语言选择动作组合，一句被误解的话就可能触发多个修改。分开的工具迫使调用方明确意图，也让网关能分别授权每次转换。

这种分离必须贯穿不同供应商的适配器。如果一家供应商把静默操作称作 snooze，另一家称作 downtime，适配器可以把两者映射到共同的 `suppress_notifications` 意图。不能因为它们都能让告警器安静，就假装抑制和解决可以互换。

## 安全的 API 应让非法组合无法表示

一个范围明确的请求模式，比聪明的提示词更能防止事故。下面的请求只能确认一个现有事件，无法偷偷夹带关闭、静默或重新分配操作。

```json
{
  "operation": "incident.acknowledge",
  "incident_id": "inc_01JQ8K4Q6M",
  "expected_version": 17,
  "actor": {
    "type": "agent",
    "session_id": "ses_01JQ8JY2P3"
  },
  "reason": "Accepted investigation after the database latency page"
}
```

服务器应补充它能够验证的身份事实，而不是让代理提供。模型可以填写原因，但不能信任它声明自己的可执行文件哈希、代码签名身份、用户账号或审批结果。网关应从经过认证的进程和审批通道中取得这些值。

成功响应应说明确切的转换，以及仍然保留的状态：

```json
{
  "operation_id": "op_01JQ8K5D7A",
  "incident_id": "inc_01JQ8K4Q6M",
  "transition": "triggered_to_acknowledged",
  "incident_status": "acknowledged",
  "recovery_status": "open",
  "version": 18,
  "approved_by": "usr_2048",
  "approved_at": "2026-07-24T02:14:31Z"
}
```

返回 `recovery_status: open` 看起来可能有些重复，但应该保留。代理常常根据最近一次响应继续推理，明确状态比让模型猜测供应商语义成本更低。这个响应还给编排器提供了一个稳定的测试断言：确认成功，而且恢复状态仍然打开。

不要在确认请求中接受 `resolve_if_healthy`、`mute_for` 或 `route_to` 之类相互矛盾的便利字段。这些字段会把单一端点重新变成动作包。如果工作流需要第二个动作，就应发起第二个请求，获得单独决策，并留下单独审计事件。

## 审批必须绑定到具体转换

审批应授权一个描述清楚的状态变化，而不是笼统地认可某个代理。会话审批可以确认一个已知进程可以运行，但不应自动授权该进程后来发现的每一种事件修改。

每次转换都要记录拟议操作、目标事件、决策前观测到的状态、请求范围、发起请求的代理会话、批准人、决策时间和审批方式。还要保存批准人当时看到的状态版本或供应商事件标识符。没有这种绑定，为事件 A 显示的审批卡可能被用于事件 B；在已触发状态下作出的批准，也可能在别人已经解决事件后才执行。

审批界面应使用运维人员能直接理解的语言。`确认数据库延迟事件 inc_01JQ8K4Q6M，并把调查分配给支付值班组` 是可以审查的。`允许代理操作` 则不行。解决操作应展示代理引用的恢复证据，并说明哪些状态仍然活跃。抑制操作应展示确切的信号范围、持续时间和过期时间。路由操作应同时展示当前目标和建议目标。

不要把所有人都压进一个模糊的 `actor` 字段，要保留四种身份：

- 请求者是提出变更的代理会话。
- 批准者是在需要审批时授权操作的人。
- 执行者是调用供应商的网关或集成身份。
- 主体是发生变化的事件、告警、服务或路由。

这些身份在审查中回答不同问题。请求者解释自动化为何开始，批准者证明人的授权，执行者把事件与凭证及供应商日志对应起来，主体则防止有效决策漂移到另一个资源。

当存储的密钥要求保护时，Sallyport 可以把 HTTP API 和 SSH 操作置于逐次审批之后，而凭证始终留在加密保险库中，不会交给代理。这个机制很适合事件转换，因为审批可以绑定到具体供应商请求，而不是模型写下的一句自由文本承诺。

## 竞态会把看似合理的自动化变成虚假时间线

代理推理、等待审批或重试网络调用时，事件状态仍会变化。忽视这种延迟的设计，迟早会确认一个已经解决的事件，把工作从正在处理的响应人员手里路由走，或者在维护结束后应用过期抑制。

应使用乐观并发控制。代理读取版本 17，针对版本 17 提出确认，只有当供应商记录仍符合相关状态时，网关才执行。如果另一位响应人员先改变了事件，就返回说明差异的冲突，不要默默把操作应用到新状态。

```json
{
  "error": "state_conflict",
  "incident_id": "inc_01JQ8K4Q6M",
  "expected_version": 17,
  "actual_version": 19,
  "actual_status": "resolved",
  "retryable": false
}
```

不要教代理重试每一种冲突。传输超时可能允许使用同一个操作标识符进行幂等重试，状态冲突却要求重新读取，而且通常要重新决策。如果事件已解决，确认就已经过时。如果事件已路由到另一团队，原审批也未必覆盖新的目标。

一种常见故障始于迟迟未处理的审批。代理读取到数据库告警，要求 Alice 批准确认。审批卡等待期间，Bob 缓解问题并解决事件。随后 Alice 批准了过期卡片。粗糙的适配器发送 `acknowledge`，接收供应商特有的成功响应，甚至把事件强行改回活跃状态，并把 Alice 记录为一项已经结束的工作的负责人。版本绑定会在时间线变得荒谬之前拦住操作。

幂等性解决的是另一个问题。为每个拟议转换分配不可变的操作 ID，并持久保存结果。如果供应商已经应用确认，而网关丢失 HTTP 响应，重试就返回已存结果，而不是添加第二条时间线记录或再次通知。并发保护让意图不被变化后的状态扭曲；幂等性则防止同一意图执行两次。

## 路由和抑制需要各自的权限

路由改变责任落在谁身上，抑制改变哪些信号会产生噪声。两者都不能证明恢复，也都不应藏在确认操作里。

如果当前服务明显错误，而且还没有人接手，应先路由再确认。确认之后再路由要格外小心，因为响应人员可能已经开始调查。转换应说明责任是否转移、原响应人员是否继续订阅，以及目标方的升级策略是否启动。代理不能从一个团队名称推断这些效果。

抑制必须有明确范围和过期时间。PagerDuty 的 Event Management 文档说明，受到抑制的告警仍可用于取证，但不会创建事件。其 Event Orchestration 文档还说明，不匹配的事件可以路由到某个服务，也可以通过兜底路径受到抑制。这些都是事件进入系统时的决策，不是关闭现有事件的同义词。

Datadog 的 Downtimes 文档还划出另一条有用边界：停机期会静默告警和通知，但不会阻止监控状态转换。如果停机期结束时监控仍处于告警状态，通知可以恢复。这通常才是维护期间真正需要的行为。系统应记住不健康状态，而不是仅仅为了停止通知就把它改写成健康。

因此，抑制请求应包含信号选择器、开始和结束时间、原因、创建者以及到期行为。像整个服务这样的宽范围选择器，应比单个监控组需要更严格的审批。无限期抑制应被拒绝，或走明确的例外流程。代理不能制造一个安静却无人负责的故障。

不要仅仅因为响应人员接下了告警，就在确认后自动抑制。有些系统会在确认时暂停升级，另一些仍会继续重复通知。Google Cloud Monitoring 明确说明确认不会停止重复通知。应保留供应商原有行为，或单独请求抑制，让审查者看得见噪声为何停止。

## 解决必须依据证据，而不是代理的信心

只有恢复证据符合预先声明的条件时，才能解决事件。一条修复命令成功，只能证明动作执行过，不能证明服务恢复。

这一区别能抓住一种常见自动化故障。代理重启进程，收到退出码零，然后关闭事件。进程启动后未通过就绪检查，三十秒后再次崩溃。命令成功了，服务却从未恢复。根据命令成功关闭事件，会产生两个事件、两次告警和一个虚假的恢复时间，而不是一段连续故障。

应在事件类型附近定义解决证据。可用性事件可能要求告警条件消失并在一个评估窗口内持续正常。队列事件可能要求积压量和最旧消息时长都降到阈值以下。证书告警只有在已部署端点实际呈现预期证书后才能解决，而不是某台主机写入文件后就关闭。

代理可以收集证据并提出解决。网关应在审批中附上观测值、时间戳、来源和任何数据空缺。如果监控仍报告活跃条件，就拒绝关闭。Google Cloud Monitoring 对指标事件正是这样处理：其文档指出，当近期数据仍违反策略时会返回 `Unable to close incident with active conditions` 错误。

人工解决仍有必要。监控可能延迟，遥测可能失效，响应人员也可能知道受影响服务已按计划退役。覆盖正常流程时要写明原因和批准人，并保留最后一次不健康观测。覆盖是可追责的例外，不能成为削弱常规转换的借口。

重新打开的行为也属于契约。要明确重复信号是重新打开同一事件，还是创建新事件；无论哪种情况都要保留关联标识符。代理不能假设 `resolve` 会静默下一次事件。PagerDuty 说明，如果还需要处理工作，已解决事件可以重新打开；Datadog 则说明，手动解决监控只会把状态设为 OK，直到下一次评估。下一次不健康评估仍可能再次告警。

## 审计记录必须保存决策过程

供应商活动日志会显示某个 API 凭证调用了一个端点，但通常没有足够上下文解释代理提出了什么、谁批准了什么，以及批准人当时看见了什么状态。应保留独立的决策记录，并把它连接到供应商事件。

完整的转换事件应包含不可变操作 ID、请求负载哈希、标准化动作、目标、变化前后状态、请求者身份、批准者身份、执行者身份、审批方式、决策时间戳、供应商响应标识符和结果。被拒绝和已过期的提案也要记录。一次被拒绝的抑制能解释为何通知继续；过期审批则能解释拟议确认为何没有运行。

要防止记录顺序被悄悄修改。只追加存储、受限写入者、保留控制和密码学链分别对付不同威胁。Sallyport 从同一份加密哈希链审计日志生成 Sessions 和 Activity 两套日志，`sp audit verify` 可以在没有解密密钥的情况下离线验证密文链。事件集成因而能保留代理运行与每次外部调用的证据，又无需把凭证暴露给代理。

不要只保存代理的自然语言解释。模型可能写出自信但不准确的摘要，所以旁边还要保留结构化事实。`已接下延迟告警` 这样的原因方便人快速浏览；事件 ID、状态版本、HTTP 请求哈希和审批身份则让调查人员能够验证。

随着集成演进，审计读取者仍需要稳定语义。应为事件模式和标准化动作名称设置版本。如果适配器改变了供应商映射，要记录执行每次调用的适配器版本。否则，新版本改变副作用后，旧的 `acknowledge` 事件会变得含糊。

有权读取审计记录，不应等于有权读取事件凭证。把验证事件顺序的能力与解密敏感负载字段的能力分开。秘密应在进入记录前就删除，而不是指望每位读取者以后都能安全处理。

## 把状态转换当作恶劣工作流来测试

顺利路径测试只能证明端点在没有意外时可以工作。事件自动化需要在读取、审批、调用和响应之间的每个位置中断流程并进行测试。

先确立五条不变量：

1. 确认绝不会把恢复状态从打开改成关闭。
2. 解决绝不会创建或延长抑制。
3. 路由绝不表示目标方已经确认事件。
4. 每个获批的修改都写明请求者、批准者、执行者和主体。
5. 过期审批不能在不同状态版本或目标上执行。

然后让适配器对接一个可在调用之间改变状态的模拟供应商。在确认等待审批时解决事件；在抑制执行前改变路由；接受请求后返回超时，再用相同操作 ID 发送一次；批准后、执行前撤销代理会话。每种情况都应得到确定结果和一条审计事件。

应测试供应商差异，而不是把差异抹掉。一个告警系统可能在确认时停止升级，另一个可能继续重复通知。标准化响应可以把 `escalation_paused` 和 `notifications_suppressed` 作为独立事实暴露。适配器无法验证的通用副作用就不要承诺。

审查工具描述时，要像审查代码一样保持怀疑。`处理这个事件` 会邀请模型自行选择结果。`记录支付值班组已接手调查，并让恢复状态保持打开` 描述的是有边界的转换。工具文本会影响代理尝试发送的请求，因此它也是控制面的一部分。

最后还要测试没有权限的情况。只有确认权限的代理调用解决、路由或抑制时，应收到明确拒绝。拒绝不应自动提供会执行另一种修改的后备方案。安全失败必须让事件保持可见且不变。

## 后果越大，代理权限越应收紧

权限设计应依据每个动作的影响，而不是依据调用它的工作流是否方便。读取事件、确认已分配工作、转移责任、静默信号以及宣布恢复，在后果不同时就应该得到逐步收紧的授权。

先使用同时写明动词和范围的能力。仅针对支付服务的 `incident.acknowledge` 比覆盖整个生产环境的 `incident.write` 更窄。路由授权可以把目标限制在同一小组拥有的服务。抑制授权可以限制选择器和持续时间。解决授权可以要求已批准的证据配置。网关必须根据结构化请求字段评估限制，不能依赖代理的解释。

授权还应包含时间和进程身份。为一项编程任务启动的代理会话，不应在进程退出后继续拥有事件权限。如果审批等待期间有人撤销会话，即便之前的决策当时有效，执行也应失败。审批确认的是一个拟议转换，不能复活已经失去授权的请求者。

权限和审批要分开。权限回答这个请求者能否尝试某类动作，审批回答这一次具体尝试现在能否继续。要求审批无法修补过宽权限，因为审查者最终会产生审批疲劳，尤其当卡片隐藏范围时。同样，如果组织规定影响较大的转换必须由人决定，狭窄权限也不能取代审批。

降低可见性的操作应受到更严格处理。确认增加负责人并让故障保持可见。路由可能把告警移出当前团队的视野。抑制可能让人们听不到持续故障。解决可能把事件从活跃队列移除，并改变绩效报告。这种顺序不是放之四海皆准，但把它写下来能让分歧在代理深夜遇到之前暴露出来。

凭证也应遵守同样边界。如果供应商提供不同角色或令牌，不要仅为确认事件就给执行者一个能够管理排班和服务的账号。如果供应商只提供宽权限凭证，就在网关限制具体操作，并把这个限制明确写入威胁模型。供应商审计显示凭证可以做什么，网关记录必须显示本次请求被允许做什么。

撤销需要可预测的结果。撤销代理会话，应阻止该进程的一切新操作。撤销审批，应在执行尚未开始时阻止所绑定的操作。撤销路由或抑制授权，应阻止后续请求；已有抑制则通过明确取消转换处理。不要悄悄删除旧决策存在过的证据。

团队经常建议在事件开始时只做一次人工审批，让代理处理其余工作。这个想法流行，是因为重复提示会打断响应人员，但它不适合后果混杂的操作。可以为日常访问审批一次代理会话，再把转换审批留给广泛抑制、跨团队路由和解决之类的动作。这样既能让低风险工作继续，也不会让最初仓促的一次点击变成一小时后关闭事件的权力。

部署前，应写一张以动作为行、范围为列的授权矩阵。每个单元格都要决定代理能读取、提出、无需新决策执行，还是只能在审批后执行。加入最大抑制时长、允许的路由目标、可接受的解决证据，以及批准人不操作时的处理方式。空白单元格应拒绝操作，不能从更宽泛的标签继承权限。

要用真实供应商凭证演练这张矩阵。如果网关策略拒绝解决，但另一个已暴露的 HTTP 工具允许代理直接调用供应商端点，这套策略仍然不完整。盘点通往告警系统的每条路径，包括通用请求工具、命令运行器、Webhook 中继和已存脚本。能绕过网关的凭证不应交给代理进程。

审批提示还需要速率控制，但限速必须安全失败。如果代理反复提交相同确认请求，合并相同提案或让多余提案过期。不能用自动批准、自动解决或隐藏事件来应付审批过载。要记录多余请求，以便团队修复制造它们的循环。

## 告警器安静时，事件仍可以保持打开

告警噪声、响应责任、服务健康和团队路由是相互独立的维度。把它们压缩成一个状态，会让界面看起来简单，却把歧义转移到自动化、指标和事后复盘里。

即使代理完美执行了确认，也要让确认保持狭窄。它可以接下工作、记录谁批准了这次认领，并让事件继续打开。如果工作流还需要新路由、临时抑制，或在验证恢复后解决，就把每一步都变成可见的独立请求和决策。

这种设计会多花几次工具调用，却能换来一条凌晨两点的事件指挥官也能信任的时间线：谁接下了告警，谁允许状态转换，当时系统显示了什么，以及事件最终为何关闭。
