Files
yovision/docs/04-architecture.md
QiuSW 8208118904
Harness governance / validate (pull_request) Has been cancelled
feat(bell): add alert acknowledgement vertical slice
2026-08-11 17:01:53 +08:00

16 KiB
Raw Permalink Blame History

架构设计

详细设计与决策依据见 raw/03-通用场景应用方案.md 和 raw/08-三系统职责划分.md。

1. 总体分层

YoVision 使用“通用底座 + 场景包”,按变化频率分为:

L0 基础设施 → L1 接入 → L2 流水线 → L3 能力 → L4 业务编排 → L5 场景包

  • L1–L4 是可复用平台能力。
  • L5 只包含规则模板、升级策略、话术和报表口径,必须能通过配置交付。
  • 规则位于推理之后、告警之前;不可编进模型或 MediaMTX 配置。

2. 三系统

系统 语言/状态 职责 不负责
Sense Go,有状态 设备台账(modality + capabilities)、ONVIF 能力探测、MediaMTX 控制、对账、探活、隧道、设备型触发、流分片、配额/Area 策略投影与准入执行、接入运维中心 AI 判定、事件业务、预警、Tenant/Site/Area/RBAC/全局审计真相
Brain Python/CUDA,业务无状态 解码/推理、检测/姿态/跟踪/ReID、时间窗判定、事件 mapper、像素级触发 设备真相源、告警升级、租户权限
Bell Go + Web,有状态 事件校验/存储、规则、预警状态机、投递、反馈、Tenant/Site/Area/RBAC、全局审计、配额与 capture_policy 真相源和统一管理端 媒体转发、模型执行、设备实际态

MediaMTX、PostgreSQL、MinIO、Prometheus 等作为独立基础设施部署。

3. 部署与系统边界

  • 首期每个客户部署一套私有实例,数据和事件证据留在客户环境;数据库实体、RBAC、配置与 API 从第一版携带 tenant_id 并保持 SaaS-ready 边界。
  • Bell 自研且是事件、Alert、ack、升级链、Tenant/Site/Area/RBAC、配额和全局审计的唯一业务真相源;客户平台通过版本化 OpenAPI/Webhook 集成,不反向接管核心状态机。Sense 只保存执行所需的版本化只读投影,不形成第二份组织/策略真相。
  • M1–M3 的视频入口只有 ONVIF/RTSP。现有 NVR 在售前盘点;仅支持 GB/T 28181 的项目必须建立独立适配器任务,不把国标信令混入 Sense 最小骨架。
  • M3 客户端为值班室 Web + 响应式移动 H5,可嵌入客户系统;是否开发原生 App 在 M4 后另行决定。

4. 核心数据流

摄像头/传感器
    │
    ▼
Sense ── 视频流/触发信号 ──> Brain
  │                            │
  │ 设备与切片 API             │ 事件契约 v0.1
  │                            ▼
  └────────────────────────── Bell ──> Web/H5/短信/语音/Webhook/本地声光
                                  └──> outcome/误报反馈回 Brain

主流程:

  1. Bell 持有站点、Area、配额与 capture_policy;首期在同一 PostgreSQL 实例内发布 bell.site_quota_v1 和 bell.area_policy_v1 两个版本化只读视图。T-009/T-010 已实现 Bell 源表/视图、最小权限和 Sense PostgreSQL repository;Sense 按 Area→Site 的固定 advisory-lock 顺序执行策略与配额准入并记录所用版本。未来分库必须发布新版本契约,不能静默改变 v1 语义。
  2. Sense 维护设备期望态,通过 MediaMTX API 和对账器收敛实际态;PostgreSQL 多实例以数据库时钟短租约和 fencing token 领取 due row,过期 worker 不得提交结果。
  3. Brain 消费视频与触发信号,产生符合冻结契约的事件候选。T-017 已建立单路 frame source、可替换 detector、轻量 track、多边形进入判定和回环可视化工程原型;T-019 把候选先写入仓库外 SQLite Outbox,再经 producer-bound HMAC 内部 HTTP 投递。合成 fixture 与 OpenCV HOG 均不是生产模型,Brain 只生成 source_event_id,不自报平台 id。T-018 的 Sense 回环控制台不参与推理或事件生成。
  4. Bell 做身份/Area 隐私、schema 与代码级断言,生成平台 ULID,保存不可变事件。T-015 已实现内部 candidate→final event factory、append-only PostgreSQL repository 和独立 outcome 事实;T-019 增加数字事件身份到 Bell/Sense 逻辑身份的受控绑定、永久来源收据和短期 nonce 收据。该 ingress 是内部适配器,Bell 公共 API/JWT/OIDC 仍未冻结。
  5. T-020 已实现规则→Alert→ack/close 最小纵切:仓库外规则按 hash 发布不可变版本,worker 对每个 Event 写 durable sweep/evaluation;命中后由 Bell 创建独立 alt_ ULID、Event 关联和初始 open transition。ack 首个竞争者获胜,后到者读取实际 actor/time;当前尚未实现投递或升级。
  6. Bell 发起 pre-roll 证据回捞,Sense 提供切片接口。
  7. 用户标记 outcome,反馈进入 Brain 的数据闭环。

首个 M3 数据流部署在 S2 民办寄宿学校的 16 路高风险点位,只运行越线、危险区域和聚集等匿名规则,不加载人脸底库。

5. 十三条不可越界的决定

  1. MediaMTX 独立运行,Sense 管配置与生命周期。
  2. 设备型触发源归 Sense;需要解码的像素级触发归 Brain。
  3. pre-roll 由 Bell 发起、Sense 切片;Brain 不直接管理录像。
  4. 平台事件 ULID 由 Bell 生成;Brain 只填 source_event_id。
  5. 一个 PostgreSQL 实例,sense/bell schema 分离;Brain 无业务 schema。
  6. 16/128 都不是单机保证;媒体与推理按独立分片横向扩展。
  7. Bell 拥有 Site/Area、site.max_video_channels 与 capture_policy,并拥有两个 v1 投影视图;Sense 角色只获得视图 SELECT,在设备写路径执行准入,不能写 Bell schema 或读取 Bell 源表。
  8. 事件片段写入客户侧 MinIO/S3,常态录像留在客户 NVR;元数据/审计、人脸和训练样本使用独立生命周期。
  9. 投递状态机只依赖 Bell provider 接口,不直接依赖某家短信或语音 SDK;生产前至少两条独立路径并能故障切换。
  10. 设备领域模型使用 modality + capabilities,页面不以摄像头作为唯一根实体;未实现协议适配器明确为 adapter_not_ready,不得用模拟遥测伪装交付。
  11. Tenant/Site/Area/RBAC、配额、capture_policy 与全局审计属于 Bell;Sense Control API v1 只管理 Device 期望态与收敛查询,Sense 只读消费版本化投影并在设备写路径执行,投影不可用时只阻断相关新变更,不静默切断已有链路。
  12. Sense 的设备操作审计先写本地持久化 Outbox,再由幂等 relay 异步送入 Bell 全局审计;不得使用“先执行高风险操作、再尽力入队”的顺序。T-016 已实现 HMAC/nonce 内部 HTTP relay、数据库时钟 lease/fencing、逐项确认与 Bell 不可变全局事实;Sense 不获得 Bell schema 权限,Bell 不读取 Sense Outbox。
  13. Brain 的业务事件同样先写仓库外持久 Outbox,再异步投递;Bell 以 (producer_id, source_event_id) 永久收据而非短期 nonce 保证跨重启幂等。只有 accepted/duplicate 可确认本地 delivered;事件 ID 始终由 Bell 生成,稳定冲突不得自动改写来源 ID 或 payload。

6. 容量架构

  • site.max_video_channels 默认 16、最大 128。
  • media_shard.max_streams 初始建议 32,可按故障域降为 16,最终由压测确定。
  • inference_profile.max_sources 由模型、FPS、分辨率、batch 和硬件基准决定。
  • 流绑定必须记录 mtx_instance/分片归属;路由变化不改判定与业务代码。
  • 单分片故障不能扩散到其他分片。
  • 管理端默认查看 16 路,但按 128 路设计分页、虚拟列表、筛选和批量操作。

T-018 先实现其中的回环工程纵切:设备 cursor 默认每页 16 项,同时预览最多 4 路且默认不播放;只有 video_capture + enabled + online + converged 的设备允许打开预览。该上限是浏览器工程台保护值,不是站点视频配额或 MediaMTX 分片容量。常态录像仍由客户现有 NVR 承担,Sense 不因提供监看页而变成媒体代理或录像真相源。

T-014 已在单台 Windows 主机上用隔离 PostgreSQL、真实 Control API、单个生产 MediaMTX 和 16 个独立低码率合成 publisher 完成 16 → 0 → 16 批量收敛、四路发布故障隔离/恢复和 1800.1 s / 180 样本稳定观察,最大/最终 unconverged=0。这只证明默认 16 路的本地软件控制面与拉流基线,不改变上述分片架构:media_shard.max_streams=32 仍是待 64/128 路目标环境压测的初始建议,不能从 T-014 推导单机、真实摄像头、AI/GPU、存储或生产 SLA。完整证据见 research/sense-16-stream-capacity.md。

7. 一致性与失败处理

  • PostgreSQL sense schema 是生产期望态真相源;SQLite 只保留为 M1 本地开发/回归路径。MediaMTX、推理 worker 和对象存储是可对账的实际态。
  • 对账器水平触发、幂等、指数退避、限制并发;PostgreSQL 使用 FOR UPDATE SKIP LOCKED、每项续租和 fencing token,SQLite 只保留单进程开发语义。部分失败不做跨系统回滚,只持续收敛。
  • MediaMTX Path 扫描把“Sense 历史拥有但当前失配”和“从未归属 Sense”分开;未知归属永不自动删除。历史拥有项也只允许在 15 分钟二次快照、1~128 项和 候选 × 100 <= 当前 Path 总数 × 10 全部通过时由本地运维命令逐项处置,不提供绕过。
  • bell.site_quota_v1 行缺失、数值越界、版本回退或读取失败只阻止视频设备新增/启用,不中断已有流;降低配额导致超限时不自动停用,后续准入返回稳定错误并产生运维信号。多 Sense 实例使用 PostgreSQL transaction-scoped advisory lock 串行化同 tenant/site 的计数与写入,不能用进程内锁替代。
  • bell.area_policy_v1 缺失、非法、版本回退或读取失败时,PostgreSQL repository 拒绝相关新增/启用;non_imaging_only 允许非成像设备但拒绝具有 video_capture 的设备。已有设备保持原状态,策略冲突由 Bell 管理端显式迁移或取消。同库实时视图不以源记录年龄误判 freshness。
  • 设备创建和期望态受理在本地事务内同时写脱敏 sense.device_operation_outbox;Outbox 失败回滚业务写入,相同期望态不增加 generation 但仍审计。relay 最多领取 100 行,以 30 秒数据库 lease 和单调 fencing token 防止过期 worker 确认;成功和 dead letter 都保留本地事实。
  • Bell 先验证时间窗、nonce 和 constant-time HMAC,再逐项校验 v1/v2 事件;同 nonce/同摘要重放原结果,同 nonce/不同摘要拒绝。bell.audit_events 只追加且不自动清理,只有 10 分钟幂等收据允许 Bell runtime 删除过期行。
  • Bell 最终事件写入 bell.events;同平台 ID/同摘要仅视为幂等重放,同 ID/不同摘要拒绝。bell_runtime 只有 SELECT/INSERT,事件与 outcome 的 UPDATE/DELETE 另由数据库 trigger 拒绝;后续人工/自动 outcome 追加到独立表,不改写事件 payload。
  • T-019 的 Brain SQLite Outbox 与推理内存环分离:最多 10,000 条待投递,网络/5xx/认证故障按 1~300 秒重试,最多 100 次;稳定 4xx 进入 dead letter。Outbox 写入失败时不得在演示状态中伪装为已排队或已投递。
  • Bell 先按 HMAC key 绑定 producer,再解析候选;永久来源收据、最终 event 和成功 nonce 响应同事务提交。同来源/同 canonical hash 返回原 Bell ID,同来源/不同 hash 返回冲突。T-017 的 100 项内存环仍只服务页面显示,不承担可靠投递。
  • Alert 身份、Event 多对多关联与 open → acknowledged → closed transition 均只追加;当前状态从最新 transition 推导。规则 evaluation、Alert 创建和初始 transition 在同一事务中提交;无规则/未命中也写 durable sweep。命令永久幂等收据保证重启后同 key 重放原响应。投递和升级链尚未实现,页面不得伪造倒计时或送达事实。
  • 值班排班发布前必须按 Site 时区校验班次空档、重叠、联系人停用和通道验证;排班以新版本和未来生效时间发布,不原地改写历史。交接班是进行中 Alert 的显式责任转移事件,不替代排班版本变更。
  • 事件证据技术默认保留 30 天并按生命周期删除;客户/法务在 M3 生产上线前确认法规适用性和最终期限,技术默认值不能覆盖其结论。

8. 数据与契约

  • Bell 核心实体:Tenant → Site → Area(含 capture_policy)以及 Role/Binding/Quota/Audit;Sense 核心实体:Device(含 modality + capabilities)→ StreamBinding/Zone,以及只记录已观察版本的 SiteQuota/AreaPolicyProjection。两个 schema 以稳定逻辑 ID 关联,不跨 schema 写入;配额 v1 为五列,Area v1 固定为 tenant_id/site_id/area_id/capture_policy/source_version/source_updated_at 六列。
  • Sense Control API v1 使用站点作用域路径、认证上下文 tenant、HMAC cursor 分页、PostgreSQL 幂等收据与资源 ETag;敏感连接引用只写不读。T-011 已实现 7 个 handler,并以 feature flag 限定到 PostgreSQL 路径。T-016 审计 relay 与 T-019 Brain 事件 ingress 各用独立外部 HMAC key/端点,均不等同于 Bell 公共管理认证。正式签名和兼容规则以 contracts/ 为准。
  • 业务实体:Rule → Event → Alert → DeliveryAttempt/Ack;Event 与 Alert 不合并。
  • Bell 通知域分为三个聚合:Contact/Team 保存身份、成员关系和已验证通道;OnCallSchedule/ScheduleVersion/ShiftException 保存时区、轮换与例外;EscalationPolicy/Step 通过 person / team / on_call_schedule 类型化 target_ref 引用目标。三者共享逻辑 ID,不复制手机号、班次或轮换字段。
  • 每个 DeliveryAttempt 创建时解析当时生效的排班版本,并保存实际收件人、通道、schedule_version 和解析时间快照;之后联系人或排班修改不得回写既有投递事实。
  • 一个事件可触发多次预警与投递,一次预警也可聚合多个事件。
  • 事件 v0.1 以 raw/contracts/event-v0.1.schema.json 与 raw/contracts/README.md 为准;未知顶层字段拒绝,只允许通过 ext 扩展。
  • v0.1 还需代码校验时间自洽、confidence 当前为 null、证据文件名隐私、唯一 primary sensor 等跨字段约束。
  • S2 MVP 不处理人脸。首个人脸试点最早 M5,只允许经法务/客户门禁确认的 S4 成人访客/承包商白名单(≤10,000 人),并提供非人脸替代方式。

9. 目录目标

Sense/cmd + Sense/internal/{device,onvif,mtx,reconcile,orphan,metrics,probe,trigger,tunnel,auth,store}
Brain/{pipeline,models,judge,emit,trigger,contracts}
Bell/cmd + Bell/internal/{ingest,event,rule,alert,deliver,feedback,tenant,audit,store}
Bell/{web,packs,contracts}
deploy/postgres/{001_roles.sql,...,019_privileges_bell_alerts.sql,tests}

Sense 脚手架和 PostgreSQL 001~019 已实现;Bell 已有事件校验/不可变存储、Sense 审计 relay、默认关闭的 Brain 事件 ingress,以及规则/Alert/ack 回环工程纵切,但没有公共管理 API。Brain 已有单路工程原型和仓库外 SQLite 可靠事件 Outbox;尚无生产模型、GPU pipeline、证据切片、升级或通知链。

10. 开发顺序

  • M0 不写生产代码。
  • M1 只动 Sense,以 1 路 T-001 准入实机 + 至少 4 路独立合成 RTSP 源完成五路接入骨架与 MediaMTX;设备模型从此时起保持模态/能力可扩展,但不提前实现非视频适配器。真实多设备现场门禁移到 T-007,阻塞生产试点但不阻塞本地开发。
  • M2 仍以 Sense 为主;Control API、对账、多租户投影和本地 16 路开通/停用基线已完成,隧道等待客户网络条件后补验。
  • M3 Brain 与 Bell 同时起步;T-015~T-020 已打通不可变事件消费者、审计 relay、Brain 单路候选、Sense 回环管理纵切、Brain→Bell 可靠事件 ingress 和 Bell 规则→Alert→ack/close 工程纵切。下一步应独立实现 Bell→Sense 异步证据/pre-roll 切片,不与已经冻结的 Alert 状态机混交。
  • M4/M5 再做 64/128 路分片、完整管理端和多个场景包;M6 接入雷达、门磁、按钮和可穿戴等非视频适配器。

M3 先执行不少于 2 周的 dry-run,冻结现场标注集,按规则报告召回率和每路每天误报数;现场基线评审后才把数值阈值写入站点验收附件。算法效果指标与系统 SLA 分开验收。

任何任务若违反顺序或跨越系统边界,必须先修改架构决策并经评审,不得“先写再说”。