vpp-ai-platform/docs/11-contracts.md
stewart hu 9d2ef6e432 Add interfaces and contracts reference (doc 11)
Consolidates plane-boundary ports, service/event contracts, and the
canonical domain-object table (merged with peer review §12), plus the
TS↔Python strategy: zod as schema source, committed JSON Schema
artifacts, generated pydantic models, golden-fixture contract tests
in both CIs, and cross-language data-representation rules.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019u5SLNweVio6ozJX7yfxQr

🔮 View transcript: https://logs.lojong.info/s/e8u90k3t33w590r7b5y7yzqh
2026-09-01 20:52:14 -04:00

8.2 KiB
Raw Blame History

11 · 接口与契约总册

全系统的边界接口清单 + 规范业务对象契约 + TypeScript↔Python 跨语言契约策略。 各篇的 YAML 片段是示意,本篇是接口的权威索引;字段级定义在代码仓 packages/domain 中。

1. 接口清单(按边界分类)

1.1 平面间端口(进程内 / 内部服务,TS interface)

借用同行评审(brainstorming.md §6)的端口风格——每个端口是一条信任边界的收口:

// 认知平面 → 可信执行链:唯一提交入口
interface ProposalSubmissionPort {
  submit(proposal: Proposal): Promise<{ caseId: CaseId; lifecycleRunId: RunId }>
}

// 可信执行链内部依赖(确定性服务,不暴露给 LLM)
interface PolicyEnginePort {
  check(proposal: Proposal, pack: PolicyPackRef): Promise<ValidationResult>
}
interface SimulationPort {
  simulate(proposal: Proposal): Promise<SimulationResult>
}
interface EnvelopePort {
  match(proposal: Proposal): Promise<EnvelopeMatch>        // 包络命中与边界检查
}

// 授权环节:现势复核 + 许可签发(03 篇 §2.2,I4)
interface AuthorityPort {
  authorize(p: Proposal, approvals: Approval[]): Promise<ExecutionPermit | StaleDenial>
  revoke(permitId: PermitId, reason: string): Promise<void>
}

// 网关:只认 (Proposal, Permit) 对
interface GatewayPort {
  dispatch(p: Proposal, permit: ExecutionPermit): Promise<ExecutionReceipt>
}

// 共享账本与证据
interface LedgerPort {
  read(scope: LedgerScope): Promise<LedgerView>            // 带版本号(乐观并发)
  append(update: PositionUpdate): Promise<LedgerVersion>
}
interface EvidencePort {
  record(evt: DomainEvent): Promise<EvidenceRef>           // 事件溯源写入口
}

// 运营工单台(02 篇 §5)
interface CaseDeskPort {
  open(kind: CaseKind, input: CaseInput): Promise<CaseId>
  read(caseId: CaseId): Promise<CaseView>
  apply(cmd: CaseCommand): Promise<CommandReceipt>         // 审批、叉分情景、关单…
  watch(caseId: CaseId): AsyncIterable<CaseEvent>
}

1.2 服务间接口(跨进程 / 跨语言)

接口 双方 协议 契约来源
Skill 调用 TS Runtime → Python Skill 服务 HTTP/JSON(FastAPI) domain 导出的 JSON Schema(见 §3)
审批/工单 API 前端/工单台 → Runtime HTTP/JSON 同上
决策案 API(外部系统) 外部系统 → 平台 HTTP/JSON,start/act/observe 三操作(同行方案 A) 二期再开放,一期仅内部
联邦网关 省级平台 ↔ 他省 签名工件交换(10 篇) FlexibilityEnvelope 等工件 schema
外部适配器 平台 ↔ 交易平台/调度/计量 对方规定(文件/WebService/消息) 防腐层内转换(06 篇),原始报文留痕
边缘控制链路 执行引擎 ↔ 边缘终端 国网标准协议栈(IEC 104 / MQTT + 国密等,按现场定) 不属于 JSON 契约域;计划/包络的语义仍来自 domain 对象

1.3 事件契约

事件总线上的每类事件 = 一个 domain 对象 + 信封:

interface EventEnvelope<T> {
  eventType: string; schemaVersion: string   // 语义化版本
  occurredAt: string                          // ISO8601 UTC
  causationId: string; correlationId: string  // 因果链(复盘/审计遍历用)
  payload: T
}

2. 规范业务对象总表

合并本文档集与同行评审 §12 的清单,去重后的权威列表(★ = 已在各篇定义):

对象 篇 权威存储 说明
Resource / ResourceProfile ★ 02 关系库(复用既有档案/设备中心主数据) 画像含可靠性评分
FlexibilityEnvelope ★ 04/10 关系库 能力向上;省间交换同 schema
PositionLedger / PositionUpdate ★ 00/04 关系库(账本服务) 约束级联载体
Commitment 10 关系库 用户承诺与省间承诺共用基型
ForecastBundle 05 时序库 + 快照 预测曲线 + 分位区间 + 模型版本
SituationReport ★ 02 事件日志 + 快照
Proposal ★(含 digest) 03 关系库 + 事件日志 状态机见 03
ConstraintSet 03/05 随 Proposal 快照 优化器输入的约束集合
ValidationResult / SimulationResult 03 快照 血缘组成部分
Approval ★ / ExecutionPermit ★ 03 关系库 绑定 digest
DispatchOrder 04 关系库 许可放行后的分解指令
ExecutionReceipt / ExecutionReport ★ 04 事件日志 幂等回执(I5)
Settlement 06 关系库 结算事实
DecisionCase ★ 02 关系库 工单聚合(引用,不复制)
PolicyPack ★ 05 版本库(随代码仓管理) 可执行规则 + 测试
Envelope ★(授权包络) 03 关系库 与 FlexibilityEnvelope 严格区分
ReviewFinding ★ 02 语义记忆库

3. TypeScript ↔ Python 契约策略

3.1 单一事实来源 + 双向生成

packages/domain (zod schemas, TS)          ← 唯一手写处
        │  构建时导出
        ▼
contracts/*.schema.json (JSON Schema)      ← 提交入库的中立工件(版本化)
        │  datamodel-code-generator
        ▼
skills-py/.../models.py (pydantic)         ← 生成物,禁止手改
  • 为什么 zod 为源:对象的产生地与主要校验点在 TS Runtime(工作流步骤 IO、 结构化输出、事件信封都用 zod);JSON Schema 作为中立层,Python 侧不依赖 TS 工具链;
  • 双端校验:TS 侧 registerSkill 出站校验(05/09 篇),Python FastAPI 用生成的 pydantic 模型入站校验——同一 schema,两端执行;
  • 备选方案(记录备查):JSON Schema 手写为源、两侧都生成——对 Python 团队更中立, 但失去 zod 的 TS 类型推导与重构联动;除非武大/电科院团队明确要求共管 schema 源文件, 维持 zod 为源。

3.2 契约测试(CI 强制)

  • contracts/fixtures/ 存每类对象的黄金样例(含边界值与历史真实脱敏样本);
  • TS CI 与 Python CI 各自加载全部样例做校验——两端对同一样例结论不一致即构建失败;
  • schema 变更走兼容性检查:对比上一版 JSON Schema,破坏性变更(删字段、改类型、 收紧约束)必须升主版本并附迁移说明;事件消费者按 schemaVersion 路由新旧处理器。

3.3 数据表示纪律(跨语言最容易翻车处)

项 约定
金额 / 电价 / 电量 字符串表示的定点小数("123.45"),禁止 JSON 浮点直传金额(TS number 与 Python float 的精度差异会在结算对账时爆炸);两侧分别用 decimal 库处理
单位 编进字段名:power_mw、energy_mwh、price_yuan_per_mwh——消灭「这个数是 kW 还是 MW」类事故
时间戳 ISO8601 UTC 存储与传输;市场时段语义(96 点、申报截止)统一按 Asia/Shanghai 解释,时段用 {date, interval_index} 而非裸时间戳
曲线 {interval_minutes: 15, start: ..., values: [...]} 定长数组,不用稀疏 map
标识 全部字符串 ID(防 JS 大整数精度损失);主数据 ID 引用既有档案中心编码
枚举 全大写字符串字面量,两侧生成为枚举类型

3.4 为什么不用 gRPC / protobuf

当前调用量级(申报级频次,非遥测流)下 HTTP/JSON 足够,且: 血缘快照人类可读(审计员直接看)、合作方接入门槛低、与 FastAPI/OpenAPI 生态顺滑。 遥测流是另一个协议域(边缘侧,国网标准协议栈),本就不在 JSON 契约范围内。 二期若省间联邦工件量大再评估签名 + 二进制序列化。

4. 一期落实清单

  1. packages/domain 建仓:先落 07 篇场景所需对象(Proposal / Approval / Permit / PositionLedger / ForecastBundle / SituationReport / DecisionCase);
  2. JSON Schema 导出与 pydantic 生成接入构建脚本;contracts/ 目录 + 黄金样例;
  3. 双端契约测试进 CI(M1 里程碑的一部分——数据先行包括契约先行);
  4. 与武大/电科院约定:Skill 服务只依赖 contracts/(JSON Schema + 生成的 pydantic), 不依赖 TS 仓的任何内部代码——组织边界与代码边界对齐。