运行时协议:外部能依赖什么
编排器、看板、审批流和监控不能依赖你的 prompt,也不能依赖框架内部结构。给它们一层稳定对象——Thread、Run、Step、Event、Checkpoint——有稳定 ID、可版本化、换框架不用改。
你的 Agent 系统迟早要被别的系统依赖:编排器要触发它,看板要展示它,审批流要在它中间插一道门,监控要读它的成本与因果。这些系统不能依赖你的 prompt——那是给模型看的,每个版本都在改;也不能依赖你框架的内部结构——那是给你自己看的,换框架就没了。它们需要的是一层稳定对象和状态迁移的契约:Runtime Protocol。
协议管外部,运行时管内部
把两件事分开:Runtime 负责内部调度、持久化、权限和执行;Protocol 定义外部系统可依赖的对象、操作和状态迁移。框架不直接等于协议——框架通过 Adapter 实现协议 agent-trend-2026。
这个切分决定了一次换框架的成本。看板、监控、审批流挂在协议对象上,换 LangGraph 还是自研 runtime,它们一行不改;挂在框架内部结构上,每次迁移都是一次全链路改造。MCP 之所以能成为工具接入的事实标准,靠的也是同一件事:它只约定系统边界与语义,不管你怎么实现 mcp-spec。
十二个稳定对象
| 对象 | 职责 |
|---|---|
| Agent | 能力提供者、版本、权限和调用入口 |
| Thread | 长期上下文、参与者和会话边界 |
| Run | 一次执行、预算、状态和取消边界 |
| Step | 模型、Tool、Gate、Handoff、子任务等可观测执行单元 |
| Message | 用户、Agent、Tool 和系统交换的内容 |
| Event | 状态、消息、Tool、产物、错误和审批增量 |
| Artifact | 文件、报告、结构化输出、代码 Diff 和证据包 |
| Checkpoint | 可恢复状态、版本和待处理动作 |
| Interrupt | 等待输入、授权或审批的持久状态 |
| Workspace | 文件、代码、浏览器和执行环境边界 |
| Trace | Run 与 Step 的因果、成本和审计关系 |
| Error | 失败类别、可恢复性和建议动作 |
值得花时间的是几个容易混的对象。Thread 和 Run 的区别就是会话和执行的区别:Thread 承载长期上下文,Run 是其中一次有预算、可取消的执行。Interrupt 把「等输入」「等授权」表达为持久状态而不是内存里的一个标志位——进程重启后它还在,事件只在安全点被消费 才有落脚处。Error 必须带失败类别和可恢复性,因为「值不值得重试」是恢复决策的第一问,这正是 故障要先分类再计数 的入口。
协议的五条硬规则
- 稳定 ID:Thread、Run、Step、Event、Artifact、Checkpoint 全部有稳定 ID,
trace_id/event_id/run_id/step_id内建 otel-genai。 - 归属明确:每个 Event、Artifact、Error 和 Approval 都归属到具体 Run,因果链可追溯。
- 版本化:协议 Schema、状态机和错误码都要有版本,消费方按版本演进,不被你的升级打断。
- 传输无关:同一套语义可以跑在 HTTP、消息队列或 CLI 上,不同传输绑定实现同一协议。
- 一等对象:Workspace 和 Artifact 作为一等对象管理,文件和产物不是运行时的私产 langgraph-platform。
别把框架内部漏进协议
协议里不该出现的:框架内部的 Node、Reducer、模型私有推理。一旦外部系统开始消费这些,你就把实现细节升格成了契约——以后每一次内部重构都变成对外破坏性变更,外部挂得越多,你被锁得越死。
判断一条暴露该不该进协议,就问一句:换掉框架之后,消费方还需要它吗?需要,它是协议语义,进;不需要,它是实现细节,留在 runtime 里。这和 长任务要有交接物和终态 是同一个道理的两种尺度——交接包让下一段会话不依赖上一段对话,协议让外部系统不依赖你的内部。真正的可替换性不是宣传出来的,是靠「外面挂的东西不动」证明的。
