Files
yovision/docs/raw/02-需求分析.md
T

793 lines
45 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 🔍 YoVision 智能视频事件平台 — 需求分析文档
> 版本 v0.3 · 2026-08-03
> 上游:《01-需求收集》 · 下游:《03-通用场景应用方案》
> 本文档负责把原始需求**分层、规格化、发现矛盾、推导出架构性约束**,不负责技术选型细节
---
# 1. 分析结论摘要
先给结论,后面是推导过程。
| # | 结论 | 依据 |
| --- | --- | --- |
| C1 | 系统必须切成「**通用底座 + 场景包**」两层,场景包是配置与插件,不是分支代码 | 三个场景 60% 需求重合,但检测能力与升级策略完全不同(§3) |
| C2 | 事件(Event)与预警(Alert)是**两个独立实体**,不可合并 | 一次事件可产生多次预警、多条投递;一次预警可聚合多个事件(§5.2) |
| C3 | 「指定人群」的判定分四类并在**能力层解耦**;**ReID 为默认手段,人脸识别为按租户授权开启的可插拔能力** | 合规风险与技术依赖不同;多数"同一性"需求不需要知道"是谁"(§4.3、§4.4) |
| C4 | 规则引擎必须放在**推理之后、告警之前**的独立环节,不能编进模型插件 | 规则变更频率远高于模型,且需支持热更新与多级覆盖(§6) |
| C5 | 接入层必须**控制面/数据面分离**,且数据面为边缘主动推流 | 家庭场景摄像头在 NAT 后,中心不可达(§7.1) |
| C6 | 系统状态一致性靠**水平触发的对账器**,不靠事件驱动 | 三个下游系统(媒体路由、推理、存储)无分布式事务(§7.3) |
| C7 | 默认 16 路可用单媒体/推理分片起步;扩展到 128 路必须**横向分片**,触发式推理用于降低平均算力 | GPU 数量不能由路数直接推导,须按真实模型、分辨率、FPS、batch 与硬件压测(§8.1–8.2) |
| C8 | 业务预警系统的核心是 **ack 与升级**,不是"发消息" | 没有 ack 环节的告警系统等于没有(§9.2) |
| C9 | **误报反馈闭环是产品飞轮,必须在 M3 第一天就有**,不是后期优化项 | 真实误报案例是最稀缺的训练数据(§10) |
---
# 2. 需求分层
原始需求(《01》共 8 个场景需求组 + 36 条通用需求)按变化频率重新分层。**同一层内的东西一起变,不同层之间靠契约解耦。**
```mermaid
flowchart TD
L5["L5 场景包<br/>规则模板 · 升级策略 · 话术 · 报表口径"]
L4["L4 业务编排<br/>事件生成 · 去重防抖 · 预警状态机 · 处置流"]
L3["L3 能力层<br/>检测 · 姿态 · 跟踪 · ReID · 属性 · (人脸)"]
L2["L2 流水线层<br/>解码 · 抽帧 · 调度 · 故障隔离 · 触发式唤醒"]
L1["L1 接入层<br/>设备台账 · ONVIF · 媒体路由 · 隧道 · 对账"]
L0["L0 基础设施<br/>多租户 · RBAC · 存储 · 可观测 · 审计"]
L5 --> L4 --> L3 --> L2 --> L1 --> L0
```
| 层 | 变化频率 | 谁在改 | 改动方式 |
| --- | --- | --- | --- |
| L5 场景包 | 每周 | 实施/产品 | 配置,不发版 |
| L4 业务编排 | 每月 | 后端 | 发版 |
| L3 能力层 | 每季度 | 算法 | 模型热替换 |
| L2 流水线 | 半年 | 平台 | 发版 |
| L1 接入 | 半年 | 平台 | 发版 |
| L0 基础设施 | 年 | 平台 | 发版 |
> **这张表是本文档最重要的产出。** 如果一个需求变更需要同时改 L5 和 L1,说明分层错了。典型反例:把"上课时段"这个校历概念写进 mediamtx 的 path 配置。
---
# 3. 场景共性与差异分析
## 3.1 共性度量
把《01》§4.2 的场景需求逐条拆成原子能力,统计复用度:
| 原子能力 | S1 家庭 | S2 校园 | S3 社区 | 复用 |
| --- | :---: | :---: | :---: | :---: |
| 人体检测 + 跟踪 | ✅ | ✅ | ✅ | 3/3 |
| 姿态关键点 | ✅ | ✅ | ✅ | 3/3 |
| 跌倒判定 | ✅ | ✅ | ✅ | 3/3 |
| 区域入侵 / 越线 | ○ | ✅ | ✅ | 2/3 |
| 停留 / 徘徊时长 | ✅ | ✅ | ✅ | 3/3 |
| 静止 / 无活动 | ✅ | ○ | ✅ | 2/3 |
| 人群聚集 / 密度 | — | ✅ | ○ | 1.5/3 |
| 打架 / 剧烈动作 | — | ✅ | ○ | 1.5/3 |
| 攀爬 / 翻越 | — | ✅ | ✅ | 2/3 |
| 非人目标(电动车、堆物) | — | ○ | ✅ | 1.5/3 |
| ReID 跨镜跟踪(匿名) | ○ | ✅ | ✅ | 2.5/3 |
| 身份比对(人脸) | ○ | 🔐 | 🔐 | 授权后启用 |
| 高空抛物 | — | — | ✅ | 1/3 |
✅ 核心 ○ 可选 — 不需要 🔐 需租户授权 + 前置条件齐备(§4.4)
**结论**:前 6 项覆盖了三个场景的 P0 需求主体,构成**通用能力包**;后续按场景增量。高空抛物(RQ-S3-04)需要专用仰拍机位与独立轨迹算法,与其余能力无复用,**建议独立立项或后期外采,不进本期通用底座**。
## 3.2 差异分析(差异才是场景包要装的东西)
| 维度 | S1 家庭 | S2 校园 | S3 社区 |
| --- | --- | --- | --- |
| 单点位路数 | 1–3 | 100–500 | 50–300 |
| 部署形态 | 边缘盒子(必须) | 校内边缘或中心 | 混合 |
| 摄像头可达性 | NAT 后 | 专网可达 | 专网可达 |
| 预警接收人 | 家属(非专业、随时可能没看手机) | 安保值班室(7×24 有人) | 物业值班(夜间可能无人) |
| 升级链 | 4 级,含语音电话与呼叫中心 | 2 级,值班室 → 校领导 | 3 级,值班 → 主管 → 业主群 |
| 超时容忍 | 30s / 60s / 90s | 2min / 10min | 1min / 5min |
| 误报容忍 | **极低**(家属会卸载 App) | 中(有人筛) | 中 |
| 隐私敏感度 | **极高** | 高(未成年人) | 中 |
| 常态录像 | ❌ 禁止上云 | ✅ 允许 | ✅ 允许 |
> **关键洞察**:S1 的接收人是非专业个人,没有值班室兜底,所以升级链必须做到"穿透勿扰模式"级别;S2/S3 有值班室,升级链可以简化,但需要**批量事件的看板与工单**能力(S1 完全不需要)。**这两种形态的 UI 和交互是两个东西,不要试图用一套界面覆盖。**
---
# 4. 领域模型
## 4.1 核心实体关系
```mermaid
erDiagram
TENANT ||--o{ SITE : "拥有"
SITE ||--o{ AREA : "划分"
SITE ||--o{ DEVICE : "部署"
DEVICE ||--o{ STREAM_BINDING : "产生"
AREA ||--o{ ZONE : "标注(多边形/警戒线)"
DEVICE ||--o{ ZONE : "画在画面上"
TENANT ||--o{ RULE : "定义"
RULE }o--|| SCENE_PACK : "继承自"
RULE ||--o{ EVENT : "命中生成"
EVENT ||--o{ EVIDENCE : "附带"
EVENT }o--o{ ALERT : "触发/聚合"
ALERT ||--o{ DELIVERY : "投递"
ALERT ||--o{ ALERT_LOG : "审计"
SITE ||--o{ CONTACT_CHAIN : "配置"
ALERT }o--|| CONTACT_CHAIN : "按级投递"
```
## 4.2 实体职责界定
| 实体 | 定义 | 关键约束 |
| --- | --- | --- |
| **Tenant 租户** | 计费与数据隔离的最小单位 | 所有查询必须带 tenant_id,无例外 |
| **Site 站点** | 一个物理场所(一户 / 一所学校 / 一个小区) | 升级链、时段策略挂在这一层 |
| **Area 区域** | 站点内的逻辑分区(教学楼 / 单元 / 客厅) | 用于权限范围与报表口径 |
| **Device 设备** | **不叫 Camera**,`device_type` 取 camera/radar/door/button | 为异构传感器预留(继承参考项目 §4.5) |
| **Zone 画面区域** | 画在某路画面上的多边形或警戒线 | 与 Area 是两回事:Area 是物理概念,Zone 是像素坐标 |
| **Rule 规则** | 「主体 × 条件 × 时空 × 动作」的可配置组合 | 三级覆盖:租户默认 → 站点 → 设备 |
| **Event 事件** | 一次被判定成立的客观发生 | **不可变**。误判只能被标记,不能被删改 |
| **Alert 预警** | 一次需要人响应的通知过程 | 有生命周期状态机,可被 ack |
| **Delivery 投递** | 一次向某人某通道的发送尝试 | **独立实体**,一次 Alert 有多条 |
## 4.3 「指定人群」的建模
《01》§5 提出四类判定。分析后确定为**主体过滤器(Subject Filter)**,作用于跟踪目标而非规则整体:
```
Track(跟踪目标)
├─ class: person | vehicle | e_bike | object
├─ attributes: {age_group, posture, ppe, ...} ← A 类,模型直接产出
├─ states: {falling, running, loitering_sec, ...} ← B 类,时序分析产出
├─ anon_id: "pid_88213" | null ← B+ 类,ReID 匿名同一性,默认开
└─ identity: {ref_id, group, confidence} | null ← C 类,人脸,按租户授权开
```
| 判定类 | 实现位置 | 默认 | 开启条件 | 回答什么问题 |
| --- | --- | --- | --- | --- |
| A 属性 | 检测模型多头输出 / 独立分类器 | 开 | — | 他是什么样的人 |
| B 状态 | 时序窗口分析插件 | 开 | — | 他在做什么 |
| **B+ ReID** | ReID 模型,跨镜特征匹配 | **开(默认手段)** | — | **是不是同一个人** |
| C 身份 | 独立身份服务,异步比对后回填 | **关** | 租户授权 + §4.4 全部前置条件 | 他是谁 |
**设计原则(已定)**:
1. **能用 ReID 解决的不上人脸。** 徘徊、尾随、跟随、跨镜追踪这类"同一性"需求一律走 B+ 类。人脸只用于必须知道"是谁"的需求(白名单放行、关注名单告警、访客核验),且需逐条论证"为什么 ReID 不够"。
2. **`identity` 为 null 时规则必须仍能工作**,降级为"未知人员"。身份服务不可用、比对超时、人脸角度不佳都会产生 null——**不得因此漏报**。这条决定了规则 DSL 必须显式声明 `on_unavailable` 语义(见《03》§2.5)。
3. **`anon_id` 与 `identity` 是两个独立字段**,不可互相顶替。ReID 的 anon_id 只在会话/时间窗内有效,不是身份标识,也不应被持久化成长期人员画像。
## 4.4 身份识别(人脸)子系统的需求分析
客户已确认将授权使用人脸识别,因此该能力**进入方案范围**。但授权解决的是"能不能用",不解决"怎么用才安全"。分析出以下约束:
### 4.4.1 授权是租户级开关,不是全局开关
| 层级 | 语义 |
| --- | --- |
| 平台级 | 能力是否编译/部署(私有化交付时可整体不部署) |
| **租户级** | **主开关**。未授权租户在 API 与 UI 层均不可见该能力,不是"能看到但点不动" |
| 站点级 | 在已授权租户内进一步限定生效范围(如只在校门口,不在教室) |
| 规则级 | 单条规则是否使用 identity 过滤 |
> 未授权租户"看得到但用不了"是错误设计——它会诱导客户询问和绕过,也会让审计难以证明"该租户从未启用"。
### 4.4.2 底库是独立的高敏资产
| 需求 | 理由 |
| --- | --- |
| 底库与业务库**逻辑分离**,独立加密 | 泄露后果与一般业务数据不在一个量级 |
| **默认只存特征向量,不存原始人脸图** | 原图泄露可直接冒用;特征向量至少不可直接还原为可用照片。若业务必须留原图(如核验展示),原图单独分库 + 单独授权 |
| 人员档案必须有**有效期** | 学生毕业、住户搬离、访客当日有效——没有有效期的底库必然越积越脏 |
| 到期自动失效,不依赖人工清理 | 依赖运维记得清理是不可靠的 |
| 支持整体导出与彻底删除 | 客户终止服务时的义务 |
| 每次比对命中与每次底库查询均独立审计 | "谁查了谁"必须可追溯 |
### 4.4.3 比对是异步旁路,不在主判定链路上
```
主链路(必须实时):检测 → 跟踪 → 姿态/行为 → 规则 → 事件
↑
旁路(可延迟、可失败):人脸检测 → 质量筛选 → 特征提取 → 底库比对 → 回填 identity
```
**推导**:若把比对放进主链路,一次底库慢查询会拖垮整条 pipeline 的实时性;而人身安全类事件(跌倒、打架)根本不需要知道是谁。
派生需求:
| 编号 | 需求 |
| --- | --- |
| FR-ID-01 | 比对为异步旁路,超时/失败不阻塞事件生成 |
| FR-ID-02 | identity 回填有时间窗;超窗未回填则事件以 `identity: null` 定稿 |
| FR-ID-03 | 依赖 identity 的规则须声明 `on_unavailable`:`treat_as_unknown`(默认,倾向报警)或 `skip`(倾向不报) |
| FR-ID-04 | 人脸质量筛选(角度、清晰度、遮挡、尺寸)前置,低质量帧不送比对,避免误配 |
| FR-ID-05 | 置信度阈值可配;高风险动作(如触发告警、拒绝放行)前可要求人工复核 |
> FR-ID-03 的两个取值对应两种业务倾向。**默认必须是 `treat_as_unknown`**:在监护与安防场景,"不确定是谁"应当倾向于报警而非放过。若某条规则选 `skip`,需在规则上显式写明并有审批记录。
### 4.4.4 误识别的后果分级
人脸比对有两种错误,后果完全不同,阈值不能用同一个:
| 错误类型 | 场景 | 后果 | 阈值倾向 |
| --- | --- | --- | --- |
| 假阳性(认错人) | 白名单放行 | 陌生人被当成住户放行 → 安防失效 | **阈值调高**,宁可认不出 |
| 假阳性(认错人) | 关注名单告警 | 无辜者被当成关注对象告警 → **对个人的实质伤害** | **阈值调最高 + 强制人工复核** |
| 假阴性(认不出) | 白名单放行 | 住户被当成陌生人 → 体验差,但安全 | 可容忍 |
| 假阴性(认不出) | 关注名单告警 | 漏报 | 靠 ReID 与其他规则兜底 |
> **关注名单类规则不得自动触发对人的处置动作**(如自动锁门、自动通报),只能生成待人工确认的事件。这是产品红线,写进 L4 编排层的硬约束。
---
# 5. 核心流程分析
## 5.1 主流程
```mermaid
flowchart TD
A["摄像头 / 传感器"] --> B["接入层<br/>台账 · 隧道 · 媒体路由"]
B --> C{"触发判定<br/>常态 / 运动 / 传感器"}
C -->|无触发| C
C -->|唤醒| D["推理流水线<br/>检测 · 跟踪 · 姿态 · 属性"]
D --> E["规则引擎<br/>主体过滤 × 时空 × 条件"]
E -->|未命中| F["丢弃<br/>仅记指标"]
E -->|命中| G["事件生成器<br/>回捞 pre-roll · 抓拍 · 元数据"]
G --> H{"去重 / 防抖 / 聚合"}
H -->|抑制| I["静默记录"]
H -->|通过| J["事件实例落库"]
J --> K["管理系统<br/>看板 · 详情 · 处置"]
J --> L{"是否需要预警"}
L -->|是| M["预警状态机<br/>投递 → ack → 升级"]
M --> N["App / 短信 / 语音 / Webhook / 大屏"]
N --> O["处置结果回填"]
K --> O
O --> P["误报标记 → 标注队列 → 模型迭代"]
```
## 5.2 为什么 Event 和 Alert 必须分开(C2 的推导)
反例:把预警状态直接放在事件表上。会立刻遇到三个无法表达的情况:
| 情况 | 合并模型下的困境 |
| --- | --- |
| 客厅两个机位拍到**同一次**跌倒 | 两条事件、两次预警,家属收到两遍 |
| 消防通道堆物**持续存在** | 每帧都命中规则,要么事件爆炸,要么无法表达"仍未整改" |
| 一次事件在**不同时间**升级到不同人 | 事件表上放不下多级投递记录 |
| 值班员**批量确认** 20 条事件 | ack 语义作用在哪个粒度说不清 |
正确关系:
```
Event N ──┐
├──> Alert 1 ──> Delivery N
Event N ──┘
```
- **Event**:一次客观发生,不可变,是证据与统计的单位
- **Alert**:一次需要人响应的过程,可聚合多个事件,是 SLA 与考核的单位
## 5.3 预警生命周期状态机
```
[触发] ─→ [投递中] ─→ [已送达] ─→ [已确认 ack] ─→ [处置中] ─→ [关闭]
│ │ │ │
│ │ └─超时未 ack─→ [升级] ─→ 下一级投递 │
│ └─投递失败─→ [升级] │
└─防抖命中─→ [抑制] [标记误报] ←───┘
```
**硬约束(继承参考项目 §10.10)**:告警**先落库再投递**。进程崩溃重启后,扫描所有非终态告警并接续升级链——与 §7.3 对账器的"水平触发"是同一个思路。
## 5.4 三个不同的事实,不可混淆
| 事实 | 含义 | 字段 | 拿不到时的处理 |
| --- | --- | --- | --- |
| 发出去了 | 调用了上游 API | `sent_at` | — |
| 送达了 | 上游回执确认到达终端 | `receipt_at` | **当失败处理并升级**,不做乐观假设 |
| 被看到了 | 用户主动 ack | `acked_at` | 超时升级 |
---
# 6. 规则引擎需求分析
## 6.1 为什么必须独立(C4 的推导)
| 如果把规则写进模型插件 | 后果 |
| --- | --- |
| 客户要改一个停留时长阈值 | 要改 Python 代码、重启 pipeline、影响同机所有路 |
| 不同租户要不同阈值 | 插件里写 if tenant == ... |
| 要审计"这条事件是哪条规则判的" | 无从追溯 |
| 要做规则的灰度/回滚 | 做不了 |
**结论**:模型插件只输出**客观观测**(这里有个人、他的姿态是躺倒、他在多边形 A 内停留了 12 秒),规则引擎负责**主观判定**(这属于需要报警的情况)。这条边界要写进代码规范。
## 6.2 规则表达能力需求
一条规则必须能表达的五个维度:
| 维度 | 内容 | 例 |
| --- | --- | --- |
| **主体** | class + 属性过滤 + 身份过滤 | 人 且 未识别为本校学生 |
| **时空** | 哪些设备/Zone + 哪些时段/日历 | 实验室 B 区 且 非上课时段 |
| **条件** | 观测量的逻辑组合 + 持续时长 | 进入区域 且 停留 > 3s 且 无教师同行 |
| **抑制** | 冷却期、聚合键、静默窗口 | 同区域 5 分钟内不重复 |
| **动作** | 事件等级 + 证据规格 + 通知策略 | 高危 + 前 10s 后 15s + 升级链 A |
## 6.3 三级覆盖模型
```
场景包默认规则 ──继承──> 租户规则 ──覆盖──> 站点规则 ──覆盖──> 设备规则
```
覆盖语义必须明确定义(否则运维会被"为什么这条没生效"折磨死):
| 字段类型 | 覆盖语义 |
| --- | --- |
| 标量(阈值、时长) | 就近覆盖 |
| 列表(联系人、通道) | **替换**,不合并(合并会导致意外扩大通知面) |
| 开关(enabled) | 就近覆盖,且允许下级关闭上级下发的规则 |
| Zone 坐标 | 只能在设备级定义,上级不可下发 |
## 6.4 规则变更的非功能要求
| 要求 | 理由 |
| --- | --- |
| 热生效,不重启 pipeline | 一次重启影响同机全部路数 |
| 变更有版本与操作人记录 | 事故复盘时"谁把阈值改了" |
| 支持"试运行"模式:命中但不通知 | 新规则上线前评估误报量 |
| 支持按规则统计命中量与误报率 | 规则质量可度量,否则只能靠感觉调 |
> **试运行模式是被低估的需求。** 没有它,每条新规则上线都是拿真实用户做实验。
---
# 7. 接入层需求分析
## 7.1 NAT 约束推导(C5)
家庭场景摄像头在运营商 NAT + 家庭路由 NAT 之后,中心机房**无法主动发起连接**。校园/社区虽有专网,但不能假设每个项目都有公网 IP。
分析出的唯一可行结构:**控制面与数据面分离**(直接继承参考项目 §2.1)
| 平面 | 方向 | 通道 | 流量 |
| --- | --- | --- | --- |
| 控制面 | 中心 → 设备 | 边缘侧 WireGuard 隧道 | 极小,仅配置与探活 |
| 数据面 | 边缘 → 中心 | 边缘主动推流(RTSP announce / SRT / RTMP) | 大 |
派生需求:
| 编号 | 需求 |
| --- | --- |
| FR-ACC-01 | 每个站点分配不冲突的内网段,设备地址确定化(如 `10.<站点高位>.<站点低位>.0/24`,摄像头从 `.10` 起) |
| FR-ACC-02 | 控制面失联**不得**中断视频推流(两者已解耦),仅告警 |
| FR-ACC-03 | 边缘侧断网时本地缓存事件与证据,恢复后补传 |
| FR-ACC-04 | 推流鉴权走 HTTP 回调到自有服务,注销/欠费/隐私模式在业务库判断,媒体层零配置变更 |
## 7.2 设备身份与状态机
| 需求 | 理由 |
| --- | --- |
| 设备身份用**序列号**,不用 IP | DHCP 续租、Wi-Fi 漫游换 AP 都会换 IP |
| IP 变化时按序列号自动重定位并重连 | 家庭场景常态 |
| 设备状态含 `pending_activation` | 批量开通必然有凭据待补的中间态 |
| 探活用独立的 ONVIF 轻量调用,**不用流状态代替设备状态** | 「设备离线」与「无人订阅」在媒体层表现相同 |
设备状态机:
```
[录入] → [pending_activation] ──补齐凭据──> [active] ⇄ [offline]
│ │
└──────────────> [disabled] <──┘
```
## 7.3 对账器:一致性方案(C6 的推导)
一次设备开通需要在**多个独立系统**各做一次操作,且它们之间**没有分布式事务**:
```
数据库(唯一真相源)
├──> 媒体路由:建 path
├──> 推理流水线:挂 source
└──> 存储:建 bucket/前缀
```
### 为什么必须是水平触发而非事件驱动
| 方案 | 部分失败时 |
| --- | --- |
| 事件驱动(边沿触发) | 事件丢了就永久不一致,需要补偿事务,补偿也会失败 |
| **水平触发(对账)** | 不处理"事件",只不断比对期望与实际并向期望收敛。失败自动在下一轮重试 |
### 派生的硬性需求
| 编号 | 需求 | 理由 |
| --- | --- | --- |
| FR-REC-01 | 数据库是唯一真相源,下游均为被驱动的从属状态 | — |
| FR-REC-02 | 建 path 前先查是否存在,绝不盲目 add | 盲目 add 会踢掉正在推流的 publisher |
| FR-REC-03 | 改配置前比对 `desired_hash` / `applied_hash`,没变**绝不**调 API | 同上 |
| FR-REC-04 | 每个下游系统一行 sync_state,全部收敛才算开通成功 | 中间态必须可见 |
| FR-REC-05 | 开通顺序:媒体路由先 → 推理后;停用顺序:推理先 → 媒体路由后 | 建立与拆除逆序,写进代码注释 |
| FR-REC-06 | 部分失败**不回滚,只收敛**(退避重试) | 回滚本身会失败,导致回滚的回滚,无限递归 |
| FR-REC-07 | 每个下游系统独立限流预算(各 8 并发),不共享信号量 | 一个系统变慢会饿死另一个 |
| FR-REC-08 | 孤儿资源清理需有**安全闸**:单轮删除量 > 总量 10% 时停止并告警 | 防止"期望集合意外为空"把全部路数拆掉 |
| FR-REC-09 | 不设放弃阈值,持续重试 + 告警引入人工 | 对账器放弃 = 设备静默不工作,监护场景不可接受 |
| FR-REC-10 | 循环周期:正常 30s,有未收敛项降到 5s,另有 10min 全量对账(含孤儿扫描) | — |
### 必须暴露的指标
| 指标 | 用途 |
| --- | --- |
| `reconciler_unconverged_bindings{system}` | 核心健康指标,持续 > 0 即异常 |
| `reconciler_orphans_detected{type}` | 有人手改了生产系统 |
| `reconciler_safety_brake_triggered` | 安全闸触发,**应当直接呼人** |
| `reconciler_loop_duration_seconds` | 循环超时 = 规模到顶,该分片了 |
---
# 8. 非功能需求分析
## 8.1 容量模型
本项目以**单站点 16 路为默认交付规格**,支持 32 / 64 / 128 路扩展,单站点本阶段上限 128 路。16 是默认配额而非代码上限;128 路通过增加媒体分片与推理 Worker 实现,不把全部流压在一个进程或一块 GPU 上。
以下仅用于初步预算,最终以 M1–M5 的真实流水线压测为准:
| 项 | 默认 16 路 | 上限 128 路 | 说明 |
| --- | ---: | ---: | --- |
| 子码流 640×360 @ 512 kbps | ≈ 8 Mbps | ≈ 64 Mbps | 推理优先使用子码流 |
| 主码流按 4–6 Mbps | ≈ 64–96 Mbps | ≈ 512–768 Mbps | 常态录像与证据回捞的预算口径 |
| 抽帧 5 fps | 80 帧/秒 | 640 帧/秒 | GPU、解码器和 batch 均需实测 |
| 媒体分片 | 1 个 | 建议 4 个起步 | `media_shard.max_streams` 初始建议 32;可按故障域降为 16 |
| 推理分片 | 1 个逻辑分片 | 最多 8 个 16 路逻辑分片 | 一个 Worker 可承载几个分片由压测决定 |
容量必须按四个维度分别验收:接入/录像带宽、同时解码、AI 推理、证据存储。不能用“NVR 能录 128 路”推导“GPU 能分析 128 路”。连续录像时每 1 Mbps 约产生 10.8 GB/天;因此默认由客户已有 NVR 承担常态录像,YoVision 优先保存事件证据,避免重复存储全量视频。
> **实现约束**:`site.max_video_channels` 默认 16、最大 128;`media_shard.max_streams` 与 `inference_profile.max_sources` 为配置项。数据库、规则、数组、前端分页与批量操作中不得写死 16。
## 8.2 算力与触发式推理(C7 的推导)
| 模式 | 默认 16 路 | 扩展到 128 路 | 适用 |
| --- | --- | --- | --- |
| 常态全量推理 | 单逻辑推理分片起步 | 按 16 路切成最多 8 个逻辑分片,分配到多个 Worker | 高价值点位 |
| **触发式推理** | 保持同一分片结构,空闲时不解码/不推理 | 通过降低各分片占空比减少平均 GPU 需求 | 家庭、社区大部分点位 |
GPU 型号和数量不在需求阶段写死。M1 用 5 路打通,M3 以 16 路建立性能基线,再依次验证 64 路和 128 路;每档都以端到端 P95/P99 延迟、丢帧率和故障隔离为出口标准。
触发信号的三个来源(按成本与可靠性排序):
| 触发源 | 成本 | 可靠性 | 适用 |
| --- | --- | --- | --- |
| 运动侦测(软件,参考 Frigate 思路) | 零 | 中(光照变化、树叶晃动会误触) | 通用兜底 |
| 摄像头 ONVIF 事件订阅 | 零 | 中 | 支持的型号 |
| **毫米波雷达等传感器** | 硬件成本 | 高 | 家庭卧室/卫生间(且解决隐私) |
> **重要澄清**:流水线框架(Savant 等)主要解决多路调度、复用与故障隔离,不会自动降低单帧推理成本。触发式推理是降低平均算力的主要手段之一,但最终容量仍取决于模型、分辨率、FPS、batch、解码方式与硬件实测。
## 8.3 两级判定与误报(准确性需求的可实现形式)
单一信号源的误报都会让使用者很快失去信任:
| 信号源 | 典型误判 |
| --- | --- |
| 纯视觉 | 遮挡、逆光、坐下、弯腰、宠物、访客 |
| 纯雷达 | 快速坐下、弯腰 |
**两级串联是行业标准做法**:
- 一级(低成本、常开):宁可多报,不能漏报
- 二级(高成本、按需唤醒):审核一级的候选,剥离干扰
派生需求:架构必须支持"一级触发源"可插拔(运动侦测 / ONVIF 事件 / 雷达 / 门磁),且触发入口独立成 handler,接新传感器时不改动流控制逻辑。
## 8.4 分类粒度对误报的影响
参考实现的经验:`GajuuzZ/Human-Falling-Detect-Tracks` 对每个人轨迹每 30 帧预测动作,输出**站立/行走/坐下/躺下/起身/坐下动作/跌倒共 7 类**。
> **七分类而非二分类是降误报的关键。** 二分类模型面对"坐下"只能在"跌倒/非跌倒"之间硬选;七分类可以明确输出"这是坐下"。本项目所有行为判定模型均按此原则设计。
**负面经验**:`Y-B-Class-Projects/Human-Fall-Detection` 用约 500 张网络图片训 LSTM 完全学不会,最终退回 if-else 规则。→ **小数据量下时序模型学不动,先解决数据再上模型。**
## 8.5 可用性与降级
| 故障 | 要求 | 降级行为 |
| --- | --- | --- |
| 单路摄像头掉线 | 不影响其他路 | 告警 + 自动重连 |
| 推理节点崩溃 | 不影响接入与其他节点 | 该分片流量转移或降级为仅录制 |
| 算力不足 | 不得积压至崩溃 | **实时模式:主动丢帧优于积压** |
| 突发流量(早晨集中活动) | 不丢事件 | 持久化磁盘缓冲,抗突发 |
| 中心平台不可达 | 不丢事件 | 边缘缓存,恢复后补传 |
| 通知供应商故障 | 不丢预警 | 多供应商冗余,`provider` 字段为切换预留 |
## 8.6 安全需求
| 编号 | 需求 |
| --- | --- |
| NFR-SEC-01 | 设备凭据(ONVIF 用户名密码)加密存储,不落明文日志 |
| NFR-SEC-02 | 推流鉴权,未授权设备推不上来 |
| NFR-SEC-03 | 所有查询强制带 tenant_id,在数据访问层统一拦截,不依赖业务代码自觉 |
| NFR-SEC-04 | 证据文件访问用带时效的签名 URL,不可枚举 |
| NFR-SEC-05 | 管理界面不得对局域网默认无密码暴露(第三方面板的常见坑) |
| NFR-SEC-06 | 人脸底库(若启用)独立加密存储,独立审计,独立授权 |
## 8.7 合规需求
| 编号 | 需求 |
| --- | --- |
| NFR-CMP-01 | `alerts` 与 `alert_deliveries` **只追加不修改**,状态变更写事件表,独立备份 |
| NFR-CMP-02 | 每条预警须能回答:何时触发、基于什么证据、向谁发过、走什么通道、是否送达、谁在何时确认、处置结果、升到第几级、每级耗时 |
| NFR-CMP-03 | 证据留存期限可配置,到期自动清理并留清理记录 |
| NFR-CMP-04 | 数据主体的知情同意记录可查 |
| NFR-CMP-05 | 隐私区域(卧室、卫生间)在设备录入时即标记,系统层面拒绝配置摄像头类设备 |
> NFR-CMP-05 是**代码层面的硬约束**,不是流程约束。依赖实施人员"记得别装"是不可靠的。
---
# 9. 预警系统需求分析
## 9.1 两类告警必须彻底分开
| | 运维告警 | 业务预警 |
| --- | --- | --- |
| 触发源 | 设备离线、对账未收敛、GPU 过载、缓冲积压 | 规则命中 |
| 收件人 | 运维团队 | 家属 / 值班员 / 呼叫中心 |
| 延迟容忍 | 分钟级 | **秒级** |
| 漏报后果 | 服务降级 | 人身伤害 + 法律责任 |
| 方案 | Prometheus + Alertmanager(现成) | **自研状态机 + 复用投递通道** |
> **两套系统不得共用投递通道和值班配置**,否则运维噪声会淡化业务预警。这是本节最重要的一条。
## 9.2 ack 是核心,不是附加功能(C8)
「发出去」和「被人看到」是两回事。整个预警系统的设计围绕 **确认或升级** 这一对概念展开:
- 无 ack → 无法知道是否有人在处理 → 无法决定是否升级 → 升级链形同虚设
- 无 ack → 无法度量响应时长 → 无法做 SLA 与考核
- 无 ack → 出事后无法举证"我们报了且某人确认了"
## 9.3 升级链需求
| 需求 | 说明 |
| --- | --- |
| 层级数与超时值按站点可配 | 独居老人与有同住家属的策略应当不同 |
| 时段策略 | 白天找子女、夜间找同住者;校园白天找安保、夜间找宿管 |
| 通道冗余 | 至少两条独立路径,**且需有绕过互联网的通道** |
| 联系人可用性 | 支持轮值排班,而非固定人 |
**通道独立性分析**:推送依赖 APNs/FCM,短信依赖运营商,任何单一通道都可能整体故障。语音电话是唯一能穿透勿扰模式且不依赖互联网的通道,**在 S1 场景是必需项**。
## 9.4 去重与防抖
| 规则 | 参数 | 理由 |
| --- | --- | --- |
| 同设备冷却期 | 默认 5min | 避免同一事件多次触发 |
| 同站点聚合 | 多设备同时触发合并为一条 | 两个机位拍到同一次跌倒 |
| 已处置抑制 | 已 ack 期间不再新建同类 | 人已到现场 |
| 维护窗口 | 用户主动静默 | **必须强制限时(建议最长 4h)并到期前提醒** |
> **永久静默是这类系统最常见的事故成因。** 静默功能的限时是硬约束,不提供"永久"选项。
---
# 10. 数据闭环需求(C9)
## 10.1 为什么这是 P0 而非优化项
| 事实 | 后果 |
| --- | --- |
| 公开数据集(Le2i / UR Fall 等)是演员摆拍的表演性动作 | 与真实场景分布不符,训出来的模型现场表现差 |
| 真实误报案例是最稀缺的训练数据 | 只能靠运营积累,无法采购 |
| 没有闭环,模型不会随运营时间变好 | 产品没有护城河 |
## 10.2 闭环设计
```
用户点「误报」→ 自动把 evidence(视频片段 + 结构化观测序列)推进标注队列
用户确认「有效」→ 样本进正例库
↓
定期评估 → 规则阈值调整 / 模型重训 → 灰度上线(试运行模式)
```
**要求:从 M3 第一天就开始存,不要等到想训模型时才发现没数据。** `outcome = true_positive` 的样本同样是金矿。
## 10.3 派生的数据需求
| 编号 | 需求 |
| --- | --- |
| FR-DAT-01 | 事件证据须包含**结构化观测序列**(关键点、包围框、track_id 时序),不只是视频 |
| FR-DAT-02 | 误报标记须记录归因(遮挡/光照/宠物/坐下/其他),供分类统计 |
| FR-DAT-03 | 标注队列与训练数据的导出需符合 NFR-CMP-03 的留存与脱敏要求 |
| FR-DAT-04 | 每条规则的命中量、误报率可查询,作为规则质量指标 |
---
# 11. 数据模型(分析层)
在参考项目基础上扩展多租户与规则/事件。**字段级设计见《03》,此处只给结构与关键约束。**
```sql
-- ── 组织(Bell 为真相源;Sense 只读配额投影)────────
tenants(id, code, name, status, plan, created_at)
sites(id, tenant_id, code, name, scene_pack, subnet CIDR, timezone, status,
max_video_channels INT NOT NULL DEFAULT 16 CHECK (max_video_channels BETWEEN 1 AND 128))
areas(id, site_id, name, parent_id)
-- ── 设备(不叫 cameras,为异构传感器预留)────
devices(
id, tenant_id, site_id, area_id,
device_type, -- camera | radar | door | button
serial UNIQUE, -- ⚠️ 身份用序列号,不用 IP
vendor, model,
onvif_addr, onvif_user, onvif_secret, -- secret 加密存储
state, -- pending_activation | active | offline | disabled
privacy_flag, -- 隐私区域标记,为 true 时拒绝 camera 类型
last_seen_at
)
stream_bindings(id, device_id, mtx_instance, inference_shard, path_name, rtsp_url, profile_token, enabled,
UNIQUE(mtx_instance, path_name))
-- ── 对账(每下游系统一行)──────────────
sync_state(binding_id, system, desired_hash, applied_hash,
last_error, attempt, retry_after, updated_at,
PRIMARY KEY(binding_id, system))
-- system ∈ {'mediamtx','inference','storage'}
-- 全部行 desired_hash = applied_hash 才算真正开通
-- ── 规则 ──────────────────────────────
scene_packs(id, code, name, version, spec JSONB) -- 场景包模板
zones(id, device_id, name, geom_type, coords JSONB) -- 画面区域,仅设备级
rules(id, tenant_id, site_id, device_id, -- 三级覆盖,逐级可为 NULL
scene_pack_id, code, name, enabled, dry_run, -- dry_run = 试运行
spec JSONB, version, updated_by, updated_at)
rule_versions(rule_id, version, spec, updated_by, updated_at) -- 变更审计
-- ── 事件(不可变)──────────────────────
events(
id, tenant_id, site_id, device_id, rule_id, rule_version,
kind, severity, confidence,
occurred_at, detected_at, -- 发生时刻 vs 判定时刻,两者都要
subject JSONB, -- track_id / 属性 / anon_id(ReID) / identity + identity_status
observation JSONB, -- 触发时的客观观测量
evidence_uri, snapshot_uris,
dedup_key, aggregated_into, -- 聚合关系
outcome, -- true_positive | false_positive | unknown
outcome_reason, outcome_by, outcome_at
)
-- ── 身份识别(独立库/独立 schema,独立加密与审计)────
-- 仅在租户授权后创建;未授权租户无此 schema 的任何数据
tenant_features(tenant_id, feature, enabled, authorized_by,
authorized_at, expires_at, doc_uri) -- feature='face_recognition'
face_libraries(id, tenant_id, site_id, name, purpose, retention_days)
-- purpose ∈ {'whitelist','watchlist','visitor'};不同 purpose 的阈值与处置策略不同
persons(id, library_id, external_ref, display_name,
group_tags, valid_from, valid_until, -- ⚠️ 有效期必填,到期自动失效
consent_ref, -- 知情同意留档指针
created_by, created_at, revoked_at)
face_vectors(id, person_id, embedding VECTOR, model_ver,
quality_score, source, created_at) -- 默认只存向量
face_images(id, person_id, image_uri, created_at) -- 可选,单独授权才写入
face_match_logs(id, tenant_id, site_id, device_id, at,
person_id, score, threshold, decision, -- matched / below_threshold / no_candidate
event_id, reviewed_by, reviewed_at) -- 只追加,独立审计
-- ── 预警 ──────────────────────────────
alerts(id, tenant_id, site_id, kind, severity, state,
triggered_at, acked_by, acked_at, closed_at,
escalation_level, contact_chain_id)
alert_events(alert_id, event_id) -- 多对多
contact_chains(id, site_id, level, contact_name, channels JSONB,
active_hours, timeout_sec, UNIQUE(site_id, level))
alert_deliveries(id, alert_id, level, channel, provider,
sent_at, receipt_at, failed_reason)
alert_logs(id, alert_id, at, actor, from_state, to_state, detail JSONB) -- 只追加
CREATE INDEX ON alerts(state) WHERE state NOT IN ('closed','suppressed');
```
**关键约束复述**:
1. `devices` 而非 `cameras`——为雷达/门磁/按钮预留
2. 设备身份用 `serial`——IP 会变
3. `sync_state` 每系统一行——无分布式事务,中间态必须可见
4. `alert_deliveries` 是独立实体——一次预警多条投递,各自有回执
5. `events` 与 `alert_logs` 只追加——合规要求
6. `events.subject` 中 `anon_id`(ReID)与 `identity`(人脸)是两个独立字段,不可互相顶替
7. 人脸相关表独立 schema、独立加密、独立审计;`persons.valid_until` 必填,无"永久有效"选项
8. `sites.max_video_channels` 默认 16、最大 128;配额在写入设备时校验,分片容量由运行时配置与压测决定
9. `sites.max_video_channels` 由 Bell 持有,Sense 通过版本化内部 API 或只读投影校验设备新增/启用;不得跨 schema 直接写入
---
# 12. 优先级与里程碑映射
## 12.1 MoSCoW
| 级别 | 内容 |
| --- | --- |
| **Must** | 摄像头接入与台账、NAT 穿透、对账器、人体检测+跟踪+姿态、跌倒/区域入侵/停留三类规则、事件实例与证据、预警状态机与升级链、多租户与 RBAC、审计、误报反馈闭环 |
| **Should** | 触发式推理、**ReID(匿名同一性,默认手段)**、时段/日历策略、试运行模式、统计报表、边缘缓存补传、Grafana 看板 |
| **Could** | **人脸识别子系统**(租户授权开启:底库管理、异步比对旁路、审计)、人群聚集/打架检测、非人目标(电动车/堆物)、雷达接入、国标 GB/T 28181 对接、大屏与声光联动 |
| **Won't(本期)** | 高空抛物、S4 园区场景、车牌识别、**人脸在未成年人场景(S2)的启用**(待专项合规意见) |
> **人脸识别的排期说明**:客户已确认授权,能力进入方案范围,但它**不在主线关键路径上**(§4.4.3 已论证比对是异步旁路)。因此定为 Could 并安排在 M5——这样 M3 的端到端主线不被合规流程与底库模块阻塞。ReID 提到 Should 并在 M4 交付,先把"同一性"类需求兜住。
## 12.2 里程碑与出口标准
| 阶段 | 内容 | 出口标准 |
| --- | --- | --- |
| **M0** | 摄像头兼容性验证 + 需求定稿 | 可直接运行 MiBeeNvr 作为隔离实验室测试台;3–5 款型号跑通 GetProfiles/GetStreamUri/SetSystemDateAndTime,采购白名单归档;M0 代码不得作为生产基线;《01》§8 的 Q1–Q12 有答案 |
| **M1** | 接入骨架 + 媒体路由 | **MediaMTX 正式进入且不得由完整 MiBeeNvr 替代**;5 路自动建 path、探活、断线重建;设备台账可用 |
| **M2** | 对账器 + 多租户 + 隧道 | 10 个站点试点,至少一个站点完成 16 路开通/停用全流程;`unconverged = 0` 稳定 |
| **M3** | 推理接入 + 规则引擎 + 事件与预警 | **默认 16 路**端到端稳定运行;跑通一个场景的三类规则;拿到现场误报基线,反馈闭环已收数据 |
| **M4** | 64 路分片 + 灰度扩容 + 管理系统 | 64 路稳定运行;单分片故障不影响其他分片;看板、批量操作与处置流可用 |
| **M5** | 128 路容量验收 + 第二/第三场景包 + 触发式推理 + **人脸识别子系统** | **128 路通过横向分片稳定运行,扩容不改业务代码**;场景包为纯配置交付;人脸能力在一个已授权租户上跑通 |
| **M6(阶段二)** | 异构传感器接入 + 两级判定 | 误报率显著下降,视频常态采集下降 |
> **M5 的出口标准是本项目的架构验收点。** 如果新增场景需要改 L1–L4 的代码,说明 §2 的分层没做成,需要返工而不是继续叠加。
---
# 13. 风险清单
| 风险 | 影响 | 缓解 |
| --- | --- | --- |
| **Ultralytics YOLO 商业授权未按时到位** | 已决策沿用 YOLO;未购授权则落在 AGPL-3.0 第 13 条,需开源整个服务端 | **M3 前完成采购**;逃生通道:检测换 YOLOX(Apache-2.0,同属 YOLO 家族)、姿态换 RTMPose(COCO 17 点,判定算法不用改) |
| 授权范围未覆盖全部形态 | 私有化实例、边缘盒子内模型、二次训练权重可能不在授权内 | 采购时逐项书面确认 |
| **人脸误识别造成对个人的实质伤害** | 无辜者被当成关注对象告警 | §4.4.4 分级阈值;关注名单类规则**不得自动触发对人的处置动作**,只生成待人工确认事件 |
| **人脸底库泄露** | 生物识别信息泄露,后果与一般数据不在一个量级 | 独立 schema + 独立加密 + 默认只存特征向量不存原图 + 独立访问审计 |
| 底库越积越脏(毕业/搬离人员未清) | 误配率上升,合规风险累积 | `valid_until` 必填,到期自动失效,无"永久有效"选项 |
| 人脸能力拖累主线工期 | M3 被合规流程阻塞 | §4.4.3 比对为异步旁路;排期在 M5,不进 M3 关键路径 |
| 人脸模型权重许可与代码许可不一致 | 侵权 | 启用前逐个核对权重许可 |
| **公开数据集与真实场景脱节** | 模型上线后表现远低于论文指标 | M3 首要产出是**真实误报案例库**,不是模型指标;不承诺绝对准确率 |
| 摄像头 ONVIF 实现差异 | 部分型号无法接入 | 采购前真机验证(M0),白名单管控 |
| 摄像头时钟漂移 | 事件时间戳全乱,审计失效 | 开通时校时,定期校准 |
| NAT 隧道不稳 | 控制面失联 | 已与数据面解耦,告警但不阻断推流 |
| path reload 踢流 | 用户看到卡顿 | 变更前比对哈希,变更集中在维护窗口 |
| H.265 兼容性 | 浏览器无法原生预览 | 统一设为 H.264 |
| **隐私合规不过** | 项目无法落地 | 尽早引入触发式采集减少常态视频;隐私区域代码级硬约束;法务前置 |
| 推理框架绑定 NVIDIA | 硬件选型受限,信创环境不可用 | 推理代码与框架解耦,预留 ONNX Runtime / Pipeless 备选 |
| 场景包泛化失败,变成三套代码 | 维护成本三倍 | M5 出口标准强制验收 |
| 值班员被误报淹没后忽略全部告警 | 真实事件被漏 | 试运行模式 + 规则质量指标 + 误报率看板 |
| 客户要求"什么都能查"的全量录像 | 与隐私需求冲突且成本高 | 需求收集期即明确事件驱动采集的边界 |
---
# 14. 需求追溯矩阵(节选)
| 原始需求 | 分析结论 | 实现层 | 里程碑 |
| --- | --- | --- | --- |
| RQ-C-02 NAT 后接入 | C5 控制面/数据面分离 | L1 | M1/M2 |
| RQ-C-03 序列号身份 | §7.2 设备状态机 | L1 | M1 |
| RQ-C-11 规则组合表达 | C4 独立规则引擎,§6.2 五维度 | L4 | M3 |
| RQ-C-12 三级覆盖 | §6.3 覆盖语义表 | L4/L5 | M3 |
| RQ-C-16 人脸按租户授权 | C3 四类判定;§4.4 租户级主开关 | L3 | M5 |
| RQ-FR-01 未授权租户不可见 | §4.4.1 不是"看得到点不动" | L0/L3 | M5 |
| RQ-FR-03 底库有效期自动失效 | §4.4.2 无"永久有效" | L3 | M5 |
| RQ-FR-04 只存特征不存原图 | §4.4.2 | L3 | M5 |
| RQ-FR-06 不可用时不得漏报 | §4.3 原则 2 + FR-ID-03 `on_unavailable` | L3/L4 | M5 |
| RQ-S3-05 陌生人徘徊 | §4.3 原则 1:走 ReID,不上人脸 | L3 | M4 |
| RQ-S2-04 尾随进校 | 同上,ReID + 门禁事件融合 | L3/L4 | M4 |
| RQ-C-17 事件实例 | C2 Event/Alert 分离 | L4 | M3 |
| RQ-C-19 去重防抖 | §9.4 四条规则 | L4 | M3 |
| RQ-C-21 误报标记 | C9 数据闭环 | L4 | **M3(不可延后)** |
| RQ-C-23 ack 与升级 | C8 预警状态机 | L4 | M3 |
| RQ-C-25 通道独立 | §9.3 语音电话必需 | L4 | M3 |
| RQ-C-28 限时静默 | §9.4 硬约束,无永久选项 | L4 | M3 |
| RQ-C-29 两类告警分离 | §9.1 | L0/L4 | M2 |
| RQ-C-30 先落库再投递 | §5.3 硬约束 | L4 | M3 |
| RQ-C-31 多租户 | §11 强制 tenant_id 拦截 | L0 | M2 |
| RQ-S1-05 隐私区域禁摄像头 | NFR-CMP-05 代码级约束 | L0/L1 | M2 |
| RQ-S1-08 3 分钟人工介入 | §9.3 升级链,站点级配置 | L5 | M3 |
| RQ-S2-08 未成年人合规 | §7.3 法务阻塞项 | — | M0 |
| RQ-S3-04 高空抛物 | §3.1 无复用,建议独立立项 | — | 移出本期 |
---
> 本文档的分析结论(§1 的 C1–C9)是《03-通用场景应用方案》的输入约束。任何技术选型若与这些结论冲突,应先回来修改本文档,而不是在方案里悄悄绕过。