AI 工程从零到一(中文)
从请求开始一直到故障恢复,审查一个 MCP 操作。
如果缺失,索取以下输入:
- 传输方式:stdio 或 Streamable HTTP。
- 操作语义和副作用。
- 空闲超时和绝对最大超时。
- 进度 token 行为和速率限制。
- 重试策略和业务幂等性存储。
- SSE 缓冲区、代理、保活和重连设置。
- 如果操作使用 Tasks 扩展,则说明持久化 Task 行为。
产出以下章节。
取消线路
- 对于 stdio,要求客户端发送引用活动请求 ID 的
notifications/cancelled通知。 - 对于 Streamable HTTP,要求关闭该请求的响应流。拒绝普通的取消 POST。
- 要求不为取消通知发送 JSON-RPC 响应。
- 为 stdio
subscriptions/listen拆除保留服务器发送的notifications/cancelled。 - 说明取消未知、格式错误、迟到或无法执行时会发生什么。
竞态表
为以下情况给出确定性结果:
| 事件顺序 | 服务器终态 | 最终响应 | 客户端行为 |
|---|---|---|---|
| 先取消后完成 | Cancelled | 被抑制 | 继续恢复 |
| 先完成后取消 | Completed | 被保留 | 忽略迟到取消 |
| 超时后迟到响应 | 取决于传输的取消 | 按观察到的顺序忽略或抑制 | 在不安全重试前先对账 |
| 进度期间断连 | 取消正在进行的请求 | 没有可恢复的流 | 重连并重新获取 |
截止时间策略
要求同时具备空闲超时和绝对最大超时。只有有效、单调递增且经过速率限制的进度才能重置空闲时间。保活注释不算语义进度。最大超时永不重置。
重试分类
每个操作返回一种分类:
SAFE:应用契约证明没有副作用。CONDITIONAL:变更有一个与相同参数一起复用的持久化幂等键。UNSAFE:变更缺少权威去重。
绝不把新的 JSON-RPC ID、工具注解或传输重连视为业务幂等性。
对于有条件的重试,要求存储参数指纹和已提交结果。拒绝以改变后的参数复用同一个键。
对键声明、参数指纹、业务副作用记录和已提交结果,要求一个原子且持久化的账本边界。进程本地字典或锁并不持久化,也不能协调副本。接受共享数据库事务、事务性发件箱,或强制执行相同键的上游提供方。说明实际位于该边界内的是哪一种副作用。
返回已提交结果的防御性副本。绝不暴露账本持有的可变对象。用并发同键夹具、重新打开夹具和变更别名夹具证明这两项性质。
流量控制
- 设置明确的每流或每客户端容量。
- 合并可替换的进度。
- 标记已丢弃进度需要从权威来源重新获取。
- 保留最终 JSON-RPC 响应。
- 为 SSE 设置
Content-Type: text/event-stream、Cache-Control: no-cache和X-Accel-Buffering: no。 - 单独定义保活节奏和操作进度。
- 拒绝无界队列。
重连计划
- 使用新 ID 打开新请求。
- 恢复订阅过滤器。
- 对 MCP 2026-07-28 不使用
Last-Event-ID。 - 重新获取受影响的资源、列表、prompt 或 Task。
- 不要自动重放不安全的变更。
- 使用带抖动且有上限的指数退避。
持久化 Tasks
存在 Task 时,将 tasks/cancel 与请求取消分开。完整确认只能证明取消意图已被接受。轮询或监听,直到持久化任务达到终态。
必需夹具
至少返回以下可执行场景:
- stdio 在完成前取消;
- stdio 在迟到取消前完成;
- HTTP 流在完成前关闭;
- 进度重置空闲超时,而最大超时仍会触发;
- 没有幂等键时,使用两个 JSON-RPC ID 的重复变更;
- 使用一个键和相同参数的重复变更;
- 用改变后的参数复用键;
- 慢消费者超过进度缓冲区;
- 流中断后建立新订阅并从权威来源重新获取;
tasks/cancel确认后出现延迟的 worker 取消;- 两个独立账本连接对一个键和相同参数发生竞态;
- 调用方修改返回结果后,从持久化记录得到干净的重放。
当重试策略只描述为“超时就重试”、账本是进程本地的、外部副作用位于所声称的原子边界之外却没有发件箱或上游键、返回记录别名化了可变存储,或未指定队列容量时,拒绝给出生产就绪的结论。