AI 时代软件的终局:界面退位,契约上位

0 阅读29分钟

一句话:AI 把「人看界面 → 程序查表」的两段式调用压成「意图 → 能力」的一段式,界面这个翻译层因此失去必要性;但它承载的责任、审计、事务、口径不会消失,只能上移到语义层与治理层。所以终局不是软件消失,而是软件的边界从「人怎么操作它」改成「代理在什么条件下被授权」。

先说结论,再谈论证。这篇文章回答一个问题:AI 时代软件的终局将何去何从。答案不是某个形态清单,而是一道授权题——谁能把不确定性关进可验证、可授权、可回退的契约里,并让业务为这份契约签字,谁才拿到入场券。全文分四段递进:为什么是授权题(1 章)、架构怎么分层(2 章)、投不投怎么算(3 章)、边界在哪里(4 章),然后落到可编译的工程(5 章)、门禁与回退(6 章),最后回答「凭什么说这是终局而不是一轮潮流」(7 章)。

1. 终局是授权题,不是预测题

1.1 六条形态预测,底层只有一个变量

行业里流传的形态判断大致六条:UI 退为兜底、表退居幕后、软件变成自进化 Agent、纵向烟囱压成横向两层、本体层决定真智能、终局是可编排能力 + 可信代理 + 统一语义。它们不是六件并列发生的事,而是同一个变量在不同层的投影:

调用主体从确定的人与程序,变成会推理、会误解、会自作主张的 Agent。

过去的调用者只有两类:人(有视觉、有常识、也是责任主体),确定性程序(参数写死、行为可枚举)。因此界面布局、字段含义、操作顺序、审批责任可以隐式存放在 UI、表结构与流程里,靠人与组织兜住。第三类调用者读自然语言、自己选工具、自己填参数、可能误解、可能重试、可能被注入——隐式约定一旦不被显式表达,不确定性就沿调用链放大。

1.png

由此得到全文第一个判断:凡是「退位」的都是形态,凡是「转移」的都是工作量。AI 化的净收益 ≈ 省下的界面与表设计成本 − 新增的语义与治理成本。多数项目失败在第二项压根没进预算表。判断一个团队是不是真的在做 AI 化,不看它有没有对话框,看它有没有人专职维护语义与治理。

术语口径(全文只用这一套,不再单列术语附录):能力指带契约的单一动作,有本体锚点、权限域、失败语义与档位;工具是能力的一种暴露方式,可以没有语义锚点;本体是对象 + 动作 + 规则 + 责任的统一表达,与「知识图谱」的区别在于它带可执行约束;编排是意图到能力的路由与顺序控制,含人机交接与降级;**技能(Skill)**是可加载的技能包(SKILL.md + 渐进式披露 + 可选确定性代码),与提示词模板的区别是有代码、有披露层级;制品是静态交付物,与「能力资产」的区别是后者有契约、有 Owner、有退役机制。

1.2 四条不变式,外加一条定量判据

① 调用主体律——不确定性进入调用链后,任何未被机器验证的约定等于不存在。约束的表达强度必须 ≥ 调用者的自由度。

② 三层契约律——能力、语义、治理是与关系,不可互相替代,也不存在「先把能力铺齐再补治理」的顺序。

2.png

③ 收敛律——动态生成必须沉淀为资产,否则是熵增。闭环是「重复需求 → 命中已沉淀能力 → 未命中才动态生成 → 评测 / 权限 / 灰度通过 → 沉淀为新能力 → 运行反馈反哺」。判据不是「能生成新能力」,而是能力集合的净增长率是否收敛、每次新增是否可回滚、总数是否受预算约束。

④ 作用域律——AI 可替换表现形态、调用编排、参数拼装与一次性胶水代码;不可替换事实源与强一致事务、权限与责任主体、合规删除与保留期限、对账与审计证据、安全边界。所以「存量系统被压扁为能力提供点」是接口层描述,不是存储层描述:烟囱不会因统一入口消失,只会从「用户可见的界面孤岛」变成「Agent 可见的语义孤岛」,而且更隐蔽——错误不再有 5xx。

四条之外有一条定量判据,它不改变不变式,只把它们换算成档位:可下放的最大自主度 = 结果可机器验证的程度 × 错误可回滚的程度 × 责任可追溯的程度,三个因子任一为零,等级归零。这条判据直接决定 3.6 的分级表与 5.3 的启动校验。

1.3 三个必须纠正的直觉

3.png

其中第一条要单独说清:MCP Tools 规范为 trust & safety 明确 SHOULD 始终保留 human-in-the-loop 与拒绝调用的能力(F1),A2UI 1.0 与 MCP Apps 把界面变成宿主渲染的声明式资源(F7 / F8)。但规范用的是 SHOULD,推不出「必然」。真正让确认界面不可省的是 G2 以上的责任归属与举证要求——失败不可逆时,必须有一个人签字担责的界面,这是内控与法律约束,不是模型能力问题。四类界面必须止步:需要人签字担责、异常与长尾处置、低频高风险操作、审计与法律留痕。

1.4 方向、路径、现状:三个尺度别混用

4.png

行业里绝大多数争论发生在现状,被讨论的那组形态预测谈的是方向;把 0–6 章的结论当成终局预告,是范畴错误。正确姿势不是「要不要转」,而是:用终局形态校验今天的架构决策,用现状约束校验今天的投资节奏。今天写下的每个接口、每张表、每条流程都该回答一句话:五年后它能否被 Agent 直接调用、直接理解、直接问责?能,是资产;不能,是债务。收敛速度由最慢的那层(治理与验证)决定,不由最快的那层(模型能力)决定。

2. 架构视角:不确定性进入调用链之后怎么分层

2.1 五层参考架构,治理是平面不是层

三平面纵贯
  交付平面  版本 / 评测门禁 / 灰度 / 回滚
  信任平面  身份透传 / 工具裁剪 / 审批 / 凭证托管 / 成本
  观测平面  轨迹 / 耗时 / 成功率 / 回归 / 接管

L4 呈现层  对话入口 + 生成式 UI(声明式蓝图)+ 固化的问责界面
L3 编排层  意图识别 → 计划 → 固化 Workflow → 预算 / 超时 → 兜底
L2 语义层  对象 / 关系 / 状态机 / 规则 / 动作 / 权限(可运行契约)
L1 能力层  原子能力(API / MCP Server / Skill)+ 注册表 + 元数据
L0 事实层  事务内核:账、库存、合同、主数据、不变量与幂等

两个反直觉约束:L0 不能被 L3 穿越——模型决定「做什么、按什么顺序做」,但任何改变事实的动作必须落到 L0 的确定性执行(幂等键、唯一约束、对账与补偿),Agent 负责编排,数据库负责真相;三个平面不是配套运维,而是架构本体——只有「模型网关 + RAG + 编排」的平台离生产还很远,评测、身份、工具裁剪、轨迹、成本、回滚缺一,等于把 demo 上架。

5.png

「两层架构」与「五层架构」不冲突:前者是按职能的粗切(能力 / 编排),后者是按职责与半衰期的细切。真正决定系统能否演进的分界线是 2.4 的半衰期;按职能切,切完还是两个烟囱。四条依赖纪律可落成 CI 规则:能力层代码不许引用编排层类型;编排层不许出现硬编码业务阈值;语义层每次变更触发一次编排层回归;治理层判定结果必须写进结构统一的审计事件。

2.2 一次调用的端到端时序:三层护栏

一次调用要依次穿过三层护栏,缺任一层都有对应的失效形态:

6.png

协议护栏与授权护栏的顺序本身就是契约:同一条请求,先判授权还是先判 B,会得到不同拒绝码。行业里常见的坏做法是把 B 校验写在执行器里(太晚,已经进了业务代码),或者把 scope 校验交给工具描述(等于没校验,客户端 MUST 视 annotations 为 untrusted,F2)。

2.3 协议信号:哪些在收敛,哪些还在晃

判断一个协议信号值不值得押注,看它有没有把「不确定性」显式化。已经稳定的是:工具描述与结构化结果的双向 schema(F4)、服务端 MUST 校验入参与限流(F2)、工具名字符集与确定性排序(F3)、2026-07-28 起移除协议级会话、结果带 resultType、列表带 ttlMs / cacheScope、OTel trace context(F5)、安全最佳实践里对 confused deputy 与令牌发放对象的硬要求(F6)。仍在晃的是呈现层:MCP Apps 用 ui:// 资源 + 沙箱 iframe + 扩展协商(F7),A2UI 1.0 用声明式消息 + 宿主可信组件目录(F8),两者都要求「不支持时优雅降级」。共同点是界面变成可降级的资源,而不是必经的路径——这正是 1.1 表格里「翻译层被削掉」的协议层证据。

2.4 真正的架构切分线是衰减速率,不是职能

这是本文与「能力 / 编排两层论」最实质的分歧:终局里软件按变更半衰期分相。

7.png

由此得三条硬规则:① 依赖方向由快到慢(快变层可以依赖慢变层,反之不行,否则一次提示词调整会震到审计 schema);② 慢变层不依赖快变层(语义层不许读编排层的运行期对象,治理层不许读提示词);③ 同一发布单元内半衰期方差越小越好(把「月度变的编排」和「永久不变的审计字段」放进一个发布单元,等于每次调编排都要重验审计)。

这三条比「能力层 / 编排层」的职能切分更硬:职能切分只是分工描述,半衰期切分是可测的——同一目录下两类文件的提交频率差一个数量级,就说明这个发布单元该拆了。

2.5 五类硬边界:不可被「统一入口」冲掉

8.png

3. 业务决策框架:把「要不要让它自己做」变成签字流程

架构师最容易犯的错,是把「能不能做」当成了「该不该做」。这一章给一套可签字的口径:三本账(3.1)、一个可解的模型(3.2–3.4)、一棵否决树(3.5)、一张分级表与 ADR(3.6)。

3.1 三本账:收益、代价、风险必须分开记

9.png

三本账合起来才是一个决策。只报收益账的项目,通常在第一季度末暴露出「省下的界面工作量,被新增的语义维护吃掉」。

3.2 可计算模型:T* 有闭式解,不是一句「收益凹、代价线性」

变量先定义清楚。λ 为日调用量,p 为单步成功率,n 为编排步数,A 为验证覆盖率,S 为拦截后的止损率,κ 为治理拦截率,c 为单对象错误代价,B 为单次错误动作的影响面,T 为自主度(0 全人工,1 全自动)。

P_chain ≈ p^n                                  复合成功率
P_eff   = P_chain × A × S                     有效成功率
q(T)    ≈ q0 + αT                             穿透到事实源的错误率【假设】
B(T)    ≜ B0 + βT                             单次错误动作的影响面(定义式)
L(T)    = λ · q(T) · B(T) · c = L0 + L1·T + L2·T²
            L0 = λc·q0·B0   L1 = λc(q0·β + α·B0)   L2 = λc·α·β
C(T)    = (k0 + H0 + L0) + (L1 − H0)·T + (k1 + L2)·T² + λ·C_run
T*      = clamp( (H0 − L1) / (2(k1 + L2)), 0, 1 )

四项成本各有归属:k1·T² 是契约 / 评测 / 治理投入(随自主度凸增,因为要覆盖的动作组合数超线性增长);L(T) 是错误损失;H0(1 − T) 是人工复核与返工;λ·C_run 是单位运行成本。

两个必须写明的【假设】:q(T) 的线性形式由「拦截率随自主度摊薄」导出(κ(T) = κ0 − γT),是拟合假设,不是恒等式,α 必须实测;P_chain ≈ p^n 隐含步间错误独立,真实场景会偏差,因此这个模型用于相对比较与定价,不用于宣称准确率。

10.png

这张表比模型本身更有用:把 p 从 0.95 提到 0.99(模型侧,贵且不可控),在 n=8 时把 0.663 提到 0.923;把 n 从 8 降到 3(架构侧,便宜且可控),在 p=0.95 时就得到 0.857。编排步数比单步准确率更值得优化,而且完全在架构师职权内——固化 Workflow 不是保守,是降低 n。这里要分清两个变量:m(可见工具数)影响 p(选对工具的概率),不进入 n;增加能力数会拉低 p、扩大语义冲突面与投毒面,但不会改掉 P_chain 的指数。混淆两者,能力预算就失去可度量的落点。

可授权性由三个不等式同时圈定:

①  L  ≤  L_max        业务签字的损失上界
②  κ  ≥  κ_min        合规签字的拦截率下限
③  B  ≤  B_max        架构签字的单次爆炸半径,且必须有闭环补偿

技术上能做但 L > L_max 的场景,正确答案是「不做」或「降到人工确认」,不是「换更强的模型」。c 只能由业务给:历史客诉与赔付均值、价差补偿与券核销的实际支出、风控单笔上限、A/B 里错误动作造成的转化损失;取不到就按最保守上界先跑,再用评测收紧。

3.3 端到端算例(全部数值为【假设】,须替换为实测)

某订单场景:业务签 L_max = 500 元/天,合规签 κ_min = 0.90,架构签 B_max = 10;取 λ = 1000、p = 0.98、n = 3、A = 0.95、S = 0.90、κ = 0.90、c = 2 元。

11.png

代入的是 B_max,所以 L̄ 是损失上界估计而非期望值;真实 E[L] 需要实测 B̄ ≤ B_max。用上界与上界比较偏保守,但口径清楚,全文凡写 L 都指这个上界。

敏感度只有三行,足够支撑决策:

12.png

一个必须提前说清的边界:运行期不估算 L。请求路径上只强制可机器判定的代理量(B 硬限、κ 的构成项、完整审计),L ≤ L_max 是离线门禁(6.2 的 E6)的事。把 L 塞进请求路径,等于让服务在每次调用时做一次无法举证的估算。

3.4 单位经济学:从调用量换成「已验证结果」

CPVO =(模型 + 工具与基础设施 + 重试 + 失败 + 人工复核与返工)÷ 已验证成功的结果数。分母口径只有一条:返工后由人完成的任务不计入 Agent 的成功数,但返工成本全额计入分子。把人工兜底算成 Agent 成功,会系统性高估便宜模型,CPVO 直接失真。

【假设】100 次任务的对比,不是基准数据。金额沿用模型 API 的美元报价口径(按 token 计价),不与 3.3 的「元 / 天」参数混算;CPVO 是比值口径,货币单位不影响两个方案的相对结论:

13.png

只算 AI 成本,便宜模型便宜一半;算上返工,贵模型便宜近八倍。便宜模型不等于便宜结果。CPVO 同时是选型公式:自研 / 外购 / 换模型 / 固化 Workflow / 只读副本 / 什么都不做,都折算成同一场景的 CPVO;「不自动化」的 CPVO 就是纯人工成本,必须永远留在比较里(3.6 的 O1)。

由此得到一条商业推论:卖得越靠近结果,就越必须自建验证器。卖结果本质是卖责任,责任的价格由评测覆盖率、事故率与回滚能力决定——这是「治理内建化」从技术口号变成商业必然的那一步,也是 7.2 第三条单调性的来源。

3.5 否决树 D0–D5:先问「该不该」,再问「能不能」

D0  是否存在高频重复的「意图 → 动作」链路?
      否 → 不值得做编排,先把流程理顺
D1  该动作失败后是否可逆或有闭环补偿?能否给出 B_max?
      否 → 只能「建议 + 人签」,不进自动执行
D2  语义能否写成「对象 + 动作 + 规则 + 责任」的可执行断言?
      否 → 先补本体,不要上编排
D3  能力是否已原子化、契约是否稳定、补偿链是否闭环?
      否 → 先拆能力(一条能力包办全流程时,编排没有意义)
D4  治理三件套(服务端身份与权限域、审计、回滚)是否内建?
      否 → No-Go:不可审计的自动执行不是效率,是风险敞口
D5  是否有评测集、热回退旧路径,且 E6 离线判定三个不等式成立?
      否 → No-Go:没有实测结论或没有旁路,等于没有退路

两个「否」的性质不同:D0 / D2 / D3 否掉是换顺序(先理顺流程、先补本体、先拆能力),项目不停;D4 / D5 否掉才是 No-Go。签字归属也要分清:语义可验证性由领域签,动作性质分级由架构签,组织能力(谁维护本体、谁拥有能力、谁审批沉淀)由技术负责人签;κ_min 由合规签,B_max 由架构签,c 与 L_max 只能由业务给,业务不给则 D4 直接否决。任何一级试图用配置绕过前一级(「语义还没统一,先拿 prompt 兜一下」),本质是把合同改成了默认条款。

3.6 分级表、方案矩阵与 ADR

14.png

B_max 是该级别的上限,单能力可取更严的值;任何比所属级别更宽松的配置必须在 ADR 里单独签字。G3 的「补偿」是人工冲正工单,不承诺自动撤销——不可逆动作不会因为写了个补偿函数就变可逆。四类操作必须止步:资金动作、权限变更、对外承诺、删除类;可以做「建议 + 人签」「预演 + 双人复核」,理由不是模型不够强,而是失败不可逆且责任无法归属。

六个选项,O1 永远在位:

15.png

O2 与 O4 常被当成对立面,其实解决的是不同问题:O2 是没有 API 时的兼容通道,O4 是有契约之后的呈现通道。判断 Computer Use 是否在倒退,不看用没用,看过渡期是否随存量系统减少而缩短:第一年兼容 40 个系统、第二年还有 45 个,说明能力层没建起来。

ADR 必须写到的六项(可直接归档):上下文与基线(O1 的人工指标);决策(档位、B_max、自主度 T、灰度范围);契约(SemanticRef、Owner、补偿能力名);治理(身份来源、令牌签发方、审计字段、热回退开关、评测集);边界(哪些动作一律 G3 以上由人决定);触发复审的条件(λ 翻倍、c 上调、契约版本升级、模型漂移、通过率下滑)。缺这六项里的任一项,半年后就没人能解释「当初为什么放它进来」。

授权合同模板(八条,可直接复印签字,与 3.5 的签字归属、3.6 的 ADR 三处对齐):

1. 授权动作清单   能力名 + 档位 + 单能力 B 上限(逐条列,不接受「按需调用」)
2. 损失与拦截口径 L_max(元/天,业务签)· κ_min(率,合规签)· c(元/每对象,业务签)
3. 语义锚点       每条能力绑定的 shape URI 与领域版本;shape 变更即合同变更
4. 补偿与回退     闭环补偿能力名 + 冲正工单入口 + 热降级开关
5. 审计与举证     19 字段审计 + 保留期 + 导出格式 + traceId 交付方式
6. 责任           第一责任人(业务 Owner)、平台责任(乙方)、审批人留痕
7. 复审触发       κ 低于 κ_min·0.95 / 契约变更 / 能力数翻倍 / 模型漂移 / 季度
8. 终止           能力下线流程、在途请求处理、审计证据保留至保留期结束

4. 落地边界:什么能先做、什么必须止步

4.1 四分类判定表

16.png

4.2 六个伪终局与失效判据

17.png

4.3 存量绞杀四步与最小可行本体

「孤岛被压扁为能力提供点」的工程顺序不可颠倒:

第 1 步 观测   用流量与变更频率给存量模块排序,识别「高价值 + 高稳定」接口面
第 2 步 契约化 先给现有 API 补语义(前置 / 后置 / 副作用 / 幂等),不改实现
第 3 步 绞杀   新功能一律走能力层,旧路径逐步转发,保留双写与影子校验
第 4 步 上收   把散在存储过程、前端校验、Excel 参数表里的规则抽到 L2

为什么先补契约再改实现:契约是评测集、权限、审计、CPVO 的共同地基。实现没契约照样能跑,Agent 没契约就只能猜;顺序反过来,重构风险与语义风险会叠在一起同时付。

最小可行本体(MVO)四步:① 只挑一条端到端主价值链(报价 → 订单 → 发货 → 收款);② 沿链列五件套——对象、关系、规则、动作(逐条绑定 L1 契约)、指标;③ 每条规则写成可执行断言并记录来源(哪份制度、谁确认、哪天验证);④ 规则总数控制在 60–150 条【假设】,先跑出可度量的收益,再争第二条链的预算。判据很硬:一条规则若无法写成断言(只能「视情况而定」),它应被标为人决策域,直接进 4.1 的「永不自动化」。

语义层最容易写成 PPT,所以这里给出能跑的形态:

@prefix sh: <http://www.w3.org/ns/shacl#> .
@prefix ex: <https://ontology.example.com/shapes/> .

ex:OrderShape a sh:NodeShape ;
  sh:targetClass ex:Order ;
  sh:property [ sh:path ex:status ;
                sh:in ("Draft" "Submitted" "Cancelled") ] ;
  sh:property [ sh:path ex:amount ; sh:minInclusive 0 ] ;
  sh:sparql [
    sh:message "超过审批限额的订单必须带财务批准" ;
    sh:select """
      SELECT $this WHERE {
        $this ex:amount ?a ; ex:approvalLimit ?l .
        FILTER(?a > ?l && NOT EXISTS { $this ex:financeApproval ?x })
      }
    """ ] .

SHACL 用 shapes graph 校验 RDF data graph,是 W3C Recommendation(F12)。三态集合与 5.2 的 Order/v1 登记一致(不含 Approved);?a > ?l 依赖数值字面量类型,数据以字符串写入会静默不匹配,生产须在 shape 里约束 xsd:decimal。5.2 的 SemanticShape 把 States / Preconditions / Postconditions 登记为断言名,启动校验要求它们非空、且每个能力的 SemanticRef 必须命中已登记 shape——SemanticRef 从此不是「像不像 URI」的字符串检查,而是「有没有可执行约束」的指针。示例只校验断言是否存在,生产须接 SHACL 处理器在写入前跑一遍;胜负手是维护而不是建设:本体与代码同仓、有 Owner、有变更评审、有回归集,否则它就是个更贵的注释。

4.4 五条现实约束与九项残余风险

概率系统无法天然提供业务确定性;海量存量债务不可能一夜重构;本体建设是人力密集且维护比建设贵;治理 / 合规 / 审计 / 权限是绕不开的门槛;成本与时延会随多步规划、多次工具调用、校验与确认叠加而恶化。这些不是技术迭代能短期消除的,只能承认并设计对冲。

18.png

还有一条不写在表里但必须说的:这套东西不承诺替代判断。它替代的是「信息处理 + 事务执行」这一段,判断、取舍、对外承诺仍然是人。任何宣称「把决策也交给模型」的方案,本质上是在逃避责任归属,而不是在提升自动化。

5. .NET 10 落地:把授权合同编译进代码结构

这一章只做一件事:证明 1–4 章的每条约束都能落成编译期或启动期可判定的代码,而不是文档里的承诺。代码为 net10.0 / ASP.NET Core,本机 0 错误 0 警告;本文内联的契约模型是完整文件,其余实现文件属于配套工程,不在正文展开。

5.1 五条不可绕过的设计决策

  1. 写入前先过治理拦截链,且拒绝码由最先命中的一层决定。顺序固定为:运行期开关 → 档位自述比对 → scope 覆盖 → 幂等键要求 → 确认令牌 → 硬限与 schema → 执行。顺序变更会改变对外拒绝语义,等于改合同。
  2. 幂等结果先落定,再消费令牌。TryGetCompleted → Begin(占位)→ Consume(令牌) → 执行 → Complete。反过来会在「令牌已用、结果未落」之间留出空窗,重试时既拿不到结果也拿不到令牌。同键重放不校验令牌(否则网络重试必然失败),换键则令牌失效。
  3. 硬限取三层最小值:min(能力 B, 档位 B, 全局硬顶),请求方申报的 requestedBlastRadius 只用于拒绝,不参与上限计算。
  4. 档位策略是启动期快照。TierPolicy 用 IOptions<AgentPolicy> 固定,硬顶只读快照;唯一走 IOptionsMonitor 的是「是否授权」这个开关。运行期热改 B 会被忽略并计一次漂移告警——能热切的只有「是否授权」,不能是「授权多大」。
  5. 补偿链闭环校验 + 输出契约校验。补偿目标必须已登记、有执行器、标记为补偿能力、档位不高于 G1、自身不再拉起补偿;outputSchema 不满足按执行失败处理,不把违约结果塞进 structuredContent。

5.2 契约模型:能力、语义、治理三层都在这一个文件里

按 5.1 的落盘清单(Contracts.cs / Registry.cs / Governance.cs / Wire.cs / Program.cs / appsettings.json)拼装,Contracts.cs 是唯一被协议无关化的领域模型——任何 MCP 线格式字段(resultType / _meta / ttlMs)都不许出现在这里,协议升级只改 Wire.cs。

// ============================================================================
// Contracts.cs —— 能力契约的领域模型
// 这一层只放「不随协议版本变化」的类型:档位、能力描述、请求、上下文、结果、审计。
// 任何 MCP 线格式字段(resultType / _meta / ttlMs)都不许出现在这里,
// 协议升级只改 Wire.cs,不改领域代码。
// ============================================================================

using System.Text.Json.Nodes;

namespace AgentContract;

// 自主度档位:由服务端注册表决定,绝不接受请求方自述。
// G0 只读 / G1 可逆写 / G2 可补偿写 / G3 不可逆外部动作 / G4 受监管决策(不进目录)。
public enum AutonomyTier { G0, G1, G2, G3, G4 }

// 副作用等级:用来判断「这个动作做错了能不能自己改回来」。
// 它决定档位、决定是否强制幂等键、决定补偿链是否必须闭环。
public enum SideEffect
{
    // 只读:不改变任何状态,出错只损失一次查询
    ReadOnly,
    // 可逆本地写:写下去还能撤回(草稿、偏好、临时标记)
    ReversibleWrite,
    // 可补偿写:不能撤回,但有一个对冲动作能把状态拉回来(下单 → 取消)
    CompensableWrite,
    // 不可逆外部动作:钱出了门、函件发出去了,没有任何程序化手段还原
    IrreversibleExternal
}

// 单个档位的配置规格:与 3.6 分级表逐格对齐,由 appsettings 的 Agent:Tiers 绑定。
public sealed class TierSpec
{
    // 该档位允许的单次爆炸半径上限(影响多少对象)
    public long MaxBlastRadius { get; set; }
    // 该档位要求的最低拦截率 κ_min,运行期只校验它的构成项是否齐备
    public decimal MinInterceptionRate { get; set; }
    // 该档位是否必须持服务端签发的确认令牌
    public bool RequiresConfirmation { get; set; }
    // 该档位是否允许自动执行(G3/G4 必须为 false)
    public bool AutoExecute { get; set; }
}

// 全局策略:绑定 appsettings 的 Agent 节,作为启动期契约快照被注入。
public sealed class AgentPolicy
{
    // 唯一允许热切的运行期开关:决定「是否还授权 Agent 调用」,不决定「授权多大」
    public bool Enabled { get; set; } = true;
    // 验收期探针开关:打开 /internal/* 端点,生产必须 false
    public bool ProbeEnabled { get; set; }
    // 全局爆炸半径硬顶:任何能力的 B 都不能超过它
    public long GlobalMaxBlastRadius { get; set; } = 100;
    // L_max:业务签字的每日损失上界【假设值,须替换】
    public decimal AcceptedLossPerDay { get; set; } = 500m;
    // λ:每日调用量【假设值,须替换】
    public long DailyInvocations { get; set; } = 1000;
    // c:单对象错误的代价(元)【假设值,须替换】
    public decimal UnitCost { get; set; } = 2m;
    // 契约版本:写进审计,用来事后解释「当时按哪版契约放行」
    public string ContractVersion { get; set; } = "contract-v1";
    // 模型版本:写进审计,用来把「模型漂移」与「契约变更」分开归因
    public string ModelVersion { get; set; } = "host-model-v1";
    // qMax 用哪一档的 B 上限:由 ADR 的落点决定,不写死在代码里(否则换档位算出的阈值会失真)
    public string ReportTier { get; set; } = "G2";
    // 逐档规格:键为 G0–G4,缺一档启动即失败
    public Dictionary<string, TierSpec> Tiers { get; set; } = new(StringComparer.Ordinal);
}

/// <summary>服务端唯一可信的能力描述;不读 MCP annotations 做鉴权。</summary>
public sealed record CapabilityDescriptor(
    // 能力名:1–128 字符,[A-Za-z0-9_.-],也是 tools/list 里的唯一键
    string Name,
    // 责任主体:出事时第一个被找的人 / 团队,不允许为空
    string Owner,
    // 语义锚点:必须命中已登记的 shape,否则启动校验失败
    string SemanticRef,
    // 档位:与副作用、B 上限共同决定这条能力能被自动调用到什么程度
    AutonomyTier Tier,
    // 副作用等级:与档位必须匹配(G0↔ReadOnly、G2↔CompensableWrite…)
    SideEffect SideEffect,
    // 需要的权限域:由服务端 ClaimsPrincipal 提供,请求体无法自报
    string[] RequiredScopes,
    // 是否必须持确认令牌:true 时无令牌一律 invalid_confirmation
    bool RequiresConfirmation,
    // 是否必须带幂等键:写操作必须 true,否则重试会重复下单
    bool RequiresIdempotencyKey,
    // 补偿能力名:写操作必须登记;补偿能力自身不得再拉起补偿链(深度恒为 1)
    string? Compensation,
    // 单能力爆炸半径上限:运行期取 min(能力上限, 档位上限, 全局硬顶)
    long MaxBlastRadius,
    // 入参结构:最小示例只校验 required,生产替换为完整 JSON Schema 2020-12
    JsonObject InputSchema,
    // 出参结构:返回不满足时按执行失败处理,不把违约结果塞进 structuredContent
    JsonObject OutputSchema,
    // 是否为补偿能力:为 true 时自身不再需要补偿,且档位不得高于 G1
    bool IsCompensation = false);

/// <summary>请求体只带业务意图。身份、scope、轨迹、契约/模型版本一律不在这里。</summary>
public sealed record InvocationRequest(
    // 业务参数:为 null 直接按协议错误拒绝(缺 arguments)
    JsonObject? Arguments,
    // 幂等键:写操作必填;同键重放命中成功结果,不再重复执行
    string? IdempotencyKey,
    // 请求方申报的爆炸半径:超过硬限即拒绝,申报值本身不参与上限计算
    long RequestedBlastRadius,
    // 服务端签发的不透明 handle,不是自造字符串;自造值一律 invalid_confirmation
    string? ConfirmationId,
    // 仅用于检测越权自述,不参与判定:与注册表档位不一致即 tier_override_attempt
    string? DeclaredTier,
    // 意图原文,仅用于审计,不参与判定
    string? IntentText);

/// <summary>服务端构造的调用上下文;任何字段都不来自请求体。</summary>
public sealed record CallerContext(
    // 已认证主体:来自 ClaimsPrincipal,不从请求体读取
    string Actor,
    // 权限域集合:来自认证令牌的 scope 声明,不从请求体读取
    string[] Scopes,
    // 轨迹 ID:取 HttpContext.TraceIdentifier,用作审计与回放的句柄
    string TraceId,
    // 契约版本:来自启动期配置快照,用于事后归因
    string ContractVersion,
    // 模型版本:来自启动期配置快照,用于把模型漂移与契约变更分开
    string ModelVersion,
    // 会话 ID:示例用 X-Demo-Session 解析,生产换真实身份提供方
    string SessionId);

// 【契约】协议错误走 JSON-RPC error;执行错误走 isError:true 结果(F4)。
// 这个三值枚举就是那条分界线:Kind 决定 Wire 层用哪种线格式。
public enum OutcomeKind { Success, ExecutionError, ProtocolError }

// 治理层的统一返回值:领域层不关心线格式,只表达「成功 / 执行失败 / 协议错误」。
public sealed record InvocationResult(
    // 结果类别:Wire 层据此决定回 isError 结果还是 JSON-RPC error
    OutcomeKind Kind,
    // 人类可读文本:拒绝时带修复提示(保留同一幂等键),不诱导换键重试
    string Text,
    // 通过 outputSchema 校验后的结构化内容;失败时为 null
    JsonNode? StructuredContent,
    // 协议错误码(-32601 / -32602 / -32001),执行错误为 null
    string? ErrorCode = null,
    // 审计决策串:allow / replay / deny:<reason> / failed:<reason> / cancelled
    string? AuditDecision = null);

/// <summary>验收期签发确认令牌的探针入参;生产应由审批系统签发,不暴露 HTTP 端点。</summary>
public sealed record ConfirmationProbe(
    // 令牌要绑定的能力名
    string Tool,
    // 令牌要绑定的幂等键:换键后令牌自动失效
    string IdempotencyKey,
    // 审批人:写进审计的 Approver 字段,用于事后追责
    string Approver);

/// <summary>E7 判据:任取一条记录,能只靠它重建决策链(十项语义 + 九项工程字段)。</summary>
public sealed record AuditEntry(
    // 回放句柄:同时回写给客户端(_meta.traceId),让举证不依赖翻服务端日志
    string TraceId,
    // 谁调的
    string Actor,
    // 带哪些权限域调的
    string Scopes,
    // 调的哪个能力
    string Tool,
    // 实际生效的档位(服务端判定结果,不是请求方自述)
    string EffectiveTier,
    // 审计决策:allow / replay / deny:<reason> / failed / cancelled
    string Decision,
    // ① 意图原文
    string IntentText,
    // ② 结构化意图(工具 + 脱敏参数)
    string StructuredIntent,
    // ③ 候选能力与打分
    string Candidates,
    // ⑧ 确认令牌(未走确认时为空的字符串)
    string ConfirmationId,
    // ⑧ 审批人:出事后第一个可追的人
    string Approver,
    // ⑦ 请求申报的爆炸半径
    long BlastRadius,
    // ⑤ 脱敏参数摘要(SHA-256 前 16 位),不存明文
    string ArgumentsDigest,
    // ⑥ 返回摘要:用于比对「当时返回了什么」
    string ResultDigest,
    // 当时生效的契约版本
    string ContractVersion,
    // 当时生效的模型版本
    string ModelVersion,
    // ⑨ 耗时,毫秒
    long ElapsedMs,
    // ⑨ 成本单位【假设】算式,生产接真实计费
    decimal CostUnits,
    // ⑩ 时间戳,UTC
    DateTimeOffset TimestampUtc);

三个设计点值得单独指出。档位枚举不对外:AutonomyTier 是服务端注册表的事实,请求体里只有 DeclaredTier,仅用于检测越权自述;两者不一致即 tier_override_attempt。AuditEntry 一次成型:19 个字段一次性写全,其中十项是语义(意图原文、结构化意图、候选与打分、确认令牌、审批人、参数摘要、结果摘要、爆炸半径、耗时成本、时间戳),九项是工程(轨迹、主体、scope、工具、档位、决策、契约版本、模型版本、成本)。字段不是「先留几个以后补」——补字段等于改 schema,等于改合同。OutcomeKind 三值即分界线:协议错误走 JSON-RPC error,执行错误走 isError:true 的结果,这条线决定客户端该重试还是该放弃。

5.3 授权层的判定顺序,与它的代码形态

下面这段是治理拦截链的核心(配套工程 Governance.cs 第 520 行起,逐字):

        // ③ 授权层:热开关 → 档位自述 → scope → 幂等键 → 确认令牌
        if (!agentSwitch.Enabled)
            return Deny(req, caller, name, tierName, "capability_disabled", sw,
                "能力已停用:保留同一幂等键,稍后重试");
        // 请求方自报档位且与注册表不一致:这是越权尝试,不是配置问题
        if (!string.IsNullOrEmpty(req.DeclaredTier) &&
            !string.Equals(req.DeclaredTier, tierName, StringComparison.Ordinal))
        {
            metrics.TierOverrideAttempts.Add(1);
            return Deny(req, caller, name, tierName,
                "tier_override_attempt:" + req.DeclaredTier, sw, "档位由服务端决定");
        }
        // scope 覆盖:能力的每一个 RequiredScope 都必须在可信上下文里
        if (!d.RequiredScopes.All(s => scopes.Contains(s, StringComparer.Ordinal)))
            return Deny(req, caller, name, tierName, "missing_scope", sw, "权限域不足");
        // 写操作缺幂等键:拒绝,而不是替它生成一个(生成等于替调用方许诺重试语义)
        if (d.RequiresIdempotencyKey && string.IsNullOrWhiteSpace(req.IdempotencyKey))
            return Deny(req, caller, name, tierName, "idempotency_key_required", sw,
                "写操作必须携带幂等键");
        // 确认令牌:能力要求或档位要求,任一为真就必须校验三元组
        if ((d.RequiresConfirmation || tier.RequiresConfirmation) &&
            !confirmations.Validate(req.ConfirmationId, caller, name, req.IdempotencyKey))
            return Deny(req, caller, name, tierName, "invalid_confirmation", sw,
                "确认令牌缺失、过期、已用或与工具/幂等键不匹配");

顺序不是注释,是这几行代码的先后:先确认「这条能力此刻是否被授权」,再确认「这次请求是否被授权,以及被授权到多大」。上面这段处理的是后者:申报档位与注册表比对、B 硬限、schema 校验。下面的启动期校验处理的是「注册表本身是否自洽」(Registry.cs 第 166 行起,逐字):

        // 交叉登记校验:补偿目标必须真实存在、可执行、且是可逆档位
        foreach (var d in All.Where(x => x.Compensation is not null))
        {
            // 按名回查补偿目标:查不到就是「幽灵补偿」——文档里有,代码里没有
            var target = Find(d.Compensation!);
            if (target is null)
            {
                errors.Add($"{d.Name}: 补偿能力 {d.Compensation} 未登记(幽灵补偿)");
                continue;
            }
            // 目标必须自己声明是补偿能力,否则它可能被当成正常业务动作再被编排
            if (!target.IsCompensation)
                errors.Add($"{d.Name}: 补偿目标 {target.Name} 未标记为补偿能力");
            // 补偿动作必须是可逆档位:用不可逆动作去冲正另一个不可逆动作,等于没补偿
            if (target.Tier is not (AutonomyTier.G0 or AutonomyTier.G1))
                errors.Add($"{d.Name}: 补偿目标 {target.Name} 档位过高,不可逆动作不能当补偿");
            // 有契约没有执行器,等于承诺了一个跑不起来的冲正
            if (!executor.IsMapped(target.Name))
                errors.Add($"{d.Name}: 补偿目标 {target.Name} 无执行器映射");
        }

这两段的分工正是 1.2 三层契约律的代码投影:PolicyGate 守运行期的每一次调用,Registry.Validate 守发布期的每一条声明。前者错了会放行不该放的动作,后者错了会把坏契约带上线——两者都不能靠「代码评审时看一眼」保证。

五分钟跑通(PowerShell 7,命令均在本机实跑过):

# 构建:-warnaserror 让警告也中断构建,避免把可空性问题带上线
dotnet build AgentContract.csproj -warnaserror

# 打开验收探针(只影响 /internal/*),并只监听回环地址
$env:Agent__ProbeEnabled = "true"
dotnet run --urls http://127.0.0.1:5199

# 另开窗口。用 curl.exe:PowerShell 的 curl 是 Invoke-WebRequest 别名,参数不同
# 无会话头 → 401;带会话头 → 7 条能力,_meta 只有 semanticRef 与 effectiveTier
curl.exe -i http://127.0.0.1:5199/agent/tools
curl.exe -H "X-Demo-Session: demo-order-operator" http://127.0.0.1:5199/agent/tools

# 阈值报告来自启动期快照:热改配置不会变
curl.exe -H "X-Demo-Session: demo-order-operator" http://127.0.0.1:5199/internal/contract/report

# 签发一次性令牌 → 调用 G2 → 用响应里的 traceId 反查审计(E7 的自助回放)
$id = (curl.exe -s -X POST -H "X-Demo-Session: demo-order-operator" -H "Content-Type: application/json" -d '{"tool":"orders.create","idempotencyKey":"K-1","approver":"lead@corp.example"}' http://127.0.0.1:5199/internal/confirmations/issue | ConvertFrom-Json).confirmationId
$body = '{"arguments":{"customerId":"C1","sku":"S","qty":1},"idempotencyKey":"K-1","requestedBlastRadius":1,"confirmationId":"' + $id + '"}'
$call = curl.exe -s -X POST -H "X-Demo-Session: demo-order-operator" -H "Content-Type: application/json" -d $body http://127.0.0.1:5199/agent/tools/orders.create/invoke
curl.exe -H "X-Demo-Session: demo-order-operator" ("http://127.0.0.1:5199/internal/audit/" + ($call | ConvertFrom-Json)._meta.traceId)

请求体用单引号拼接,是因为 PowerShell 双引号里反斜杠不是转义符——这一条踩过才知道,写在这里省读者一次调试。

5.4 线格式只在一个文件里变化

McpWire 只做三件事:ToTool 产出 inputSchema / outputSchema / annotations / _meta(不下发 Owner 与 B 上限,避免把内部治理信息暴露成攻击面);ToCallResult 产出 isError + resultType + 已校验的 structuredContent + _meta.traceId;ToProtocolError 产出 JSON-RPC error。协议演进的已知变化——2026-07-28 起移除协议级会话、结果带 resultType、列表带 ttlMs / cacheScope(F5)——都只影响这一个文件,领域模型不动。

19.png

【边界】示例工程只实现「能力端点按 capability_disabled 拒绝」这一半;把流量切回旧应用路径由宿主 / 网关负责,不在本工程内。E8 必须演练整条回退链,否则「热降级」只是拒绝服务,不是可执行的回退。【边界】生产环境的确认令牌应由审批系统签发,不挂 HTTP 端点;探针端点必须关闭或加鉴权。

6. 路线、门禁与回退

6.1 迁移路线:治理先行,自主度后放

四阶段,顺序不能反:① 契约化(只补语义与幂等,不改实现)→ ② 治理内建(身份、审计、回滚、令牌)→ ③ 低自主度上线(G0–G1,T≈0.3)→ ④ 按能力域放量(先 B 小、可补偿、有评测的业务链,逐域抬 T)。每一阶段都有退出条件,不是时间表:① 的退出条件是「主价值链的规则能写成断言」;② 是「每一类拒绝都能给出稳定拒绝码」;③ 是「E6 离线判定三个不等式成立」;④ 是「季度复测 p、κ 未退化」。

反过来走(先铺 Agent 再补治理)在 demo 阶段更快,但每次事故都会触发一次「回退」,而回退在这条路上通常是全停——因为没有别的路径可切。

6.2 E1–E8:可证伪门禁

20.png

E7 是最容易被当成「有日志就行」的一条,所以给出判据:取 traceId,能从审计里拿到意图原文、结构化意图、候选与打分、确认令牌与审批人、参数与结果摘要、契约与模型版本。缺一项,举证就要去翻日志文件,事故时慢的那几个小时就是成本。

6.3 三个可测量的量与告警线

21.png

示例(λ=1000、B=10、c=2、L_max=500):q_max = 0.025,警戒 0.0200,实测 q = 0.01953 即 占阈值 78.1%,比警戒线低 2.4%(0.01953 / 0.0200)。三个量同时恶化时,先降 T,不要先换模型——换模型改变的是 p 与成本,改变不了「损失上界已经超了」这个事实。

6.4 回退剧本:三种代价分开写

22.png

四种回退的前提不同:热关断要求「授权开关」与「事实写入」分离(5.1 第 4 条);切回旧路径要求旧系统在过渡期保留写能力——这是很多团队在「绞杀」第 3 步就犯的错,把旧写路径提前关掉,等于自断退路。

6.5 危险信号:出现任一条就停下来

  1. 评审会上没人能回答「这个 Agent 一天最多造成多少损失」。
  2. 确认令牌由请求方生成,或「令牌校验」写在 Prompt 里。
  3. 能力清单里写著「按需调用」「参数由模型决定」。
  4. 审计里只有自然语言日志,没有结构化字段与 traceId。
  5. 换模型后没有重跑评测集,却说「效果更好了」。
  6. 回退方案只有「重启服务」。
  7. 同一个动作在两个系统里有两个 Owner,且没人知道谁签字。
  8. 存量系统数量在过渡期内没有下降。

7. 终局:为什么契约体系是稳定吸引子

前六章给了判断、架构、算法与工程,这一章回答最后一个问题:凭什么说这不是一轮潮流。

7.1 不是应用消失,是价值位置迁移

三条价值链被切开重排:开发链(需求 → 实现 → 测试 → 发布)中,实现环节被压缩,需求表达与验收被放大;交付链从「交付一个带界面的系统」变成「交付一组带契约的能力 + 一套治理」;运营链从「培训人怎么用」变成「维护语义与授权边界」。三条链共同把价值位置从「界面与流程」推到「契约与治理」。

所以「软件消失」是个错误表述。消失的是为人的认知负荷而存在的那一层;留下的是事实源、事务、权限、责任——这些东西本来就不是为了适配人的视觉与记忆而存在的,它们是为了让世界保持一致而存在的。

7.2 三条成本单调性,把终局压成一个吸引子

① 界面成本随自主度单调下降,但语义成本单调上升,且上升得更慢。 每提升一档自主度,需要显式化的语义条目数增加,但一条语义可以服务多个界面、多个入口、多个 Agent;界面是一对一的,语义是一对多的。因此系统规模越大,语义的边际成本越低——这正是「沉淀」的经济学含义。

② 治理成本有一次性门槛,之后边际递减。 身份、审计、令牌、回滚这套东西第一次建起来很贵,但第二条能力复用同一套判定链,第三条几乎免费。所以治理是必须先付的固定成本,把它摊到单个功能上比较,永远得出「太重」的结论——这是很多团队反复推迟治理的真实原因:算错了摊销口径。

③ 卖得越靠近结果,验证器越是必需成本。 按席位或按调用量计费时,失败可以被「用户自己重试」吸收;按结果计费时,失败直接变成赔付。于是验证器从「可选的质量投入」变成「定价的前提」,而验证器一旦建成,又可以反过来支撑更高的自主度。这条正反馈是终局收敛的第三个推力。

三条合起来说明:终局不是某个团队的选择,而是成本结构压出来的落点。这解释了一个现象——无论从哪个方向出发(做 Agent 平台的、做本体/知识图谱的、做 RPA 的、做 MCP 工具的),做得足够深的团队最后都会撞上同一组东西:能力契约、语义锚点、授权与审计。撞上的不是同一个产品,是同一组约束。

7.3 两个反命题:终局论证必须防守的两面

反命题一:「模型足够强,契约就是多余的」。 若成立,需要同时满足三件事:模型能对不可逆动作的后果负责、能对语义口径的偏差负责、能在没有观测的情况下自证正确。这三件事都不是智能问题——第一件是法律与组织问题,第二件是主数据治理问题,第三件是观测问题。模型变强只会让「猜」更准,不会让「谁负责」有答案。能力提升改变的是概率,不改变责任结构。

反命题二:「模型足够便宜,人工复核就被替代」。 若成立,需要 CPVO_agent < CPVO_human 在含返工与事故的口径下成立。3.4 的算例已经说明:便宜模型在含返工时可能贵近八倍。模型降价压的是分子里的推理成本,压不动返工成本与事故成本——后两项由 p、κ、B 决定,都是架构与治理变量。所以正确的策略不是等降价,而是先把 p 与 κ 抬起来再谈规模。

两个反命题的共同结构:它们都把「智能水平」当成唯一变量,而终局的约束来自责任、语义与观测。这也是为什么 1.2 的四条不变式里没有一条是关于模型能力的。

7.4 三种可能的终局与可观察信号

23.png

第三种不是终局,是未完成态。它看起来像终局,因为局部都能跑;但一旦要求跨系统追责或跨域复用,成本会突然不可承受。判断自己在哪一态,只需回答 6.5 的八个问题。

7.5 三条可证伪的断言

24.png

这三条写出来是为了能被推翻。一个不允许被推翻的终局判断,不是判断,是信仰。

7.6 收束:一句话与一页卡

谁能把能力原子化、语义统一化、治理内建化,谁就能拿到入场券——这句只说了一半。补上可验收的另一半:谁能把不确定性关进可验证、可授权、可回退的契约里,并让业务为这份契约签字,谁才真正拿到入场券。而这份合同不会随模型变强而作废,因为它约束的从来不是模型有多聪明,是谁有权让一件事发生、发生了谁负责。

软件终于要对自己的输出负责:说清楚要做什么(语义)、确认做没做成(验证)、出事了找谁(责任)。这三件事里模型一件都替不了——它们本来就不是智能问题,是组织问题。加速一个个体不需要架构,改变一条流程才需要。

一页决策卡(开会时可以直接投屏):

25.png

附录 A 事实出处与采信口径

外部事实逐条标注「采信 / 收紧」,均截至 2026-11-14 核对;明确不采信的判断单独写在表后。发布前应逐条补当时可访问的链接与抓取日期,本文只标来源类型,避免给出会失效的深链。

26.png

三条采信纪律:规范里的 SHOULD 不当 MUST 用;厂商发布说明不当第三方实测用;用工结构判断不当技术形态判断用(「某某行业是夕阳产业」这类说法在本文不采信,因为它无法证伪,也无法指导架构决策)。

附录 B 验证说明

本文的数值与代码行为均在 net10.0(SDK 10.0.300)本机验证:dotnet build -warnaserror 0 警告 0 错误;启动期校验对坏配置(G2.B=50 且 G3.AutoExecute=true)一次性列出全部违规并拒绝启动;G2 无令牌拒绝、B=50 带合法令牌仍拒绝、同键重放成功、换键令牌失效;G3 走人工审批分支返回「不要为重试更换幂等键」;用响应 _meta.traceId 可取回同一条 19 字段审计。3.2–3.4 的全部数值是【假设】参数下的算例,不是基准数据,替换为实测前不得当作结论引用;q(T) 的线性形式是拟合假设,P_chain ≈ p^n 隐含步间独立,两者都必须在真实评测集上复核。本文未做的一件事:没有在真实业务系统上验证 κ 抽检口径与 SHACL 处理器的性能开销,这两项需要在接入时单独度量。