MiniVLM 事件生成模块详细设计
对应项目待办事项 P1-4. MiniVLM 事件生成模块详细设计。
负责人:@Tetoisnothuman
协助:@hexianglong
状态:核心层、采样调度、Frigate 帧获取、图片预处理、MySQL 存储均已实现;OpenAI-compatible VLM 后端子命令已接入;性能预警 MQTT 发布已实现
相关模块:Frigate NVR、Genapsed、时间线存储、LLM 查询引擎、插件系统
1. 模块定位
MiniVLM 事件生成模块是本项目的核心组件,负责“把监控画面翻译成自然语言事件”。
项目整体目标是将传统监控系统从“录像文件 + 人工拖进度条”的模式,转变为“自然语言时间线 + AI 查询 + 精准回放”的模式。因此,MiniVLM 的直接产出不是最终用户问答结果,而是供时间线存储、LLM 查询引擎和插件系统共同使用的基础事件文本。
在系统中,MiniVLM 主要承担以下职责:
- 接收 Genapsed 路由来的分析任务,任务内容来自 NVR-Bridge 归一化后的 Frigate 事件、截图和短视频片段引用;
- 调用轻量视觉语言模型(VLM)理解画面内容;当前开发期 worker 默认使用
StaticVLMBackend打通链路; - 生成面向用户和大模型都可读的自然语言事件描述;
- 对模型输出进行基础解析、校验、去重和合并;
- 通过 MQTT 将事件结果发布给 Genapsed,由后续模块写入时间线或触发插件逻辑。
MiniVLM 可以理解为系统的“视觉翻译层”:它不直接回答用户问题,也不负责复杂业务判断,而是把原始监控画面转写成可以长期存储、检索和推理的文本事件。
2. 设计目标与非目标
2.1 设计目标
MiniVLM 的设计目标如下:
-
低成本持续运行
能够在家庭 NAS、迷你主机或低功耗 GPU 设备上长期运行,避免对高算力云端服务形成强依赖。 -
生成可读的自然语言时间线
输出应优先服务“人能读懂”和“LLM 能推理”,而不是追求复杂、脆弱的全结构化视觉识别结果。 -
支持事件触发与低频巡检混合采样
在有 Frigate 事件时提高分析频率;无事件时保持低频巡检,尽量减少算力浪费。 -
通过 MQTT 与系统解耦
MiniVLM 不直接耦合时间线数据库。当前 worker 已通过 MQTT 发布timeline_event_generated消息信封给 Genapsed,由 Genapsed 调用 Timeline 处理后续存储和路由。 -
支持场景化 Prompt 扩展
Prompt 应支持基础规则、摄像头上下文、插件关注点三层组合,使系统可以适配客厅、厨房、门口、老人看护等不同场景。 -
避免时间线刷屏
通过事件去重、硬合并和软合并,减少连续帧中重复描述同一事件的问题。 -
可观测、可降级、可配置
模块需要提供日志、状态、预警和关键参数配置,便于用户根据设备性能调整运行频率。
2.2 非目标
MiniVLM 不负责以下事项:
-
不负责摄像头接入和录像管理
摄像头流、录像、截图、目标检测等由 Frigate NVR 或其他视频底层组件负责。 -
不直接存储正式时间线数据库
MiniVLM 只生成事件结果,不直接写入时间线数据库。运行时适配层通过 MQTT 交给 Genapsed 后,由 Genapsed 调用 Timeline 入库。 -
不承担最终自然语言问答
用户问题的理解、跨事件推理和视频片段定位由 LLM 查询引擎负责。 -
不替代插件业务逻辑
例如老人跌倒判断、厨房安全告警、智能家居联动等应主要由插件处理。MiniVLM 只提供基础视觉事件描述和必要的候选判断信息。 -
不直接传输大文件
模块遵循系统“控制面/数据面分离”的原则:MQTT 只传 JSON 和文件引用,图片、视频等大文件通过文件系统或 HTTP 引用传递。
3. 上下游关系
3.1 系统位置
3.2 上游输入
MiniVLM 的上游主要包括:
| 来源 | 输入内容 | 说明 |
|---|---|---|
| NVR-Bridge | 由 Frigate 事件归一化得到的分析任务 payload,包含事件 ID、截图和视频片段引用 | 当前直接任务来源 |
| Genapsed | MQTT 分析任务、配置、插件关注点、系统状态 | 负责调度与路由 |
| Frigate NVR | 运动检测、对象检测、事件 ID、截图、视频片段引用 | 第一阶段视频底座来源,经 NVR-Bridge 转换后进入 MiniVLM |
| 摄像头配置 | 摄像头 ID、位置、房间说明、关注区域 | 用于构建 Prompt 上下文 |
| 插件系统 | 特定插件希望关注的风险、对象或行为 | 例如老人看护、厨房安全 |
3.3 下游输出
MiniVLM 的下游主要包括:
| 下游 | 使用方式 |
|---|---|
| Genapsed | 接收 MiniVLM 结果,调用 Timeline 入库并向插件或 UI 发布结果 |
| 时间线存储模块 | 提供事件模型、入库和查询能力,由 Genapsed 编排写入 |
| LLM 查询引擎 | 读取自然语言时间线,回答用户问题 |
| 插件系统 | 基于事件描述和类型判断触发业务逻辑 |
| UI/通知模块 | 展示时间线、提示运行异常或性能预警 |
4. 输入数据设计
MiniVLM 接收的输入消息应尽量轻量,只包含必要的上下文和媒体引用。
4.1 分析任务消息示例
{
"msg_id": "uuid-v4",
"source": "genapsed",
"target": "minivlm",
"type": "analyze_frame",
"priority": "normal",
"timestamp": 1741000000,
"payload": {
"camera_id": "living_room",
"frigate_event_id": "1741000000.123456-abcd",
"trigger_type": "frigate_event",
"media_refs": {
"image": "temp/share/public/msg-uuid-001/1741000000.jpg",
"video": "temp/share/private/msg-uuid-001/1741000000.mp4"
},
"frigate_objects": [
{
"label": "person",
"confidence": 0.93,
"box": [120, 80, 430, 720]
}
],
"camera_context": {
"name": "客厅摄像头",
"location": "客厅",
"description": "画面主要覆盖沙发、茶几和客厅入口。"
},
"plugin_focus": [
"关注是否有人进入客厅",
"关注是否有物品被拿起或放下"
]
},
"metadata": {
"requires_ack": true,
"ttl": 300,
"trace_id": "trace-uuid"
}
}
4.2 字段说明
| 字段 | 说明 | 填写方 |
|---|---|---|
camera_id | 摄像头唯一标识 | Frigate/Genapsed |
frigate_event_id | Frigate 事件 ID,可为空 | Frigate |
trigger_type | 触发方式,如 scheduled_scan、frigate_event、manual_request | Genapsed |
media_refs.image | 待分析图片路径或 URL | Frigate/Genapsed |
media_refs.video | 相关视频片段路径或 URL | Frigate/Genapsed |
frigate_objects | Frigate 已检测到的对象 | Frigate |
camera_context | 摄像头位置与场景描述 | 配置系统 |
plugin_focus | 插件追加关注点 | 插件系统/Genapsed |
trace_id | 全链路追踪 ID | Genapsed |
5. 输出事件设计
5.1 输出原则
MiniVLM 采用“自然语言为主、结构化字段为辅”的混合输出方案。
但需要注意:模型本身不负责输出完整系统元数据。模型只输出:
- 对画面发生内容的自然语言描述;
- 少量需要视觉判断或语义判断的字段,例如事件类型、是否需要关注、候选对象、候选动作等。
其他字段,例如事件 ID、摄像头 ID、时间戳、Frigate event_id、媒体引用、trace_id、处理耗时等,应由 MiniVLM 系统逻辑或上游消息自动填写,避免让模型编造系统信息。
5.2 模型建议输出格式
模型输出可以采用简化 JSON,便于程序解析:
{
"summary": "一名男子从客厅入口进入,将一个快递盒放在茶几上。",
"event_type": "person_enter_and_put_object",
"subjects": ["person"],
"objects": ["快递盒", "茶几"],
"actions": ["进入", "放下物品"],
"attention_required": false,
"confidence": "medium"
}
其中:
| 字段 | 说明 |
|---|---|
summary | 面向时间线展示的自然语言描述,必须有 |
event_type | 模型判断的事件类型,可为空或 unknown。事件类型支持系统预置,也允许用户或插件注册扩展类型 |
subjects | 画面中的主要主体,如人、猫、老人、儿童 |
objects | 与事件相关的物体 |
actions | 动作或状态变化 |
attention_required | 是否可能需要后续关注或插件进一步判断;插件关注点可以影响该字段 |
confidence | 模型自评置信度,建议使用 low / medium / high |
5.3 事件类型注册机制
event_type 用于给时间线索引、插件订阅和 UI 过滤提供稳定分类。系统应提供一组基础事件类型,同时允许用户或插件注册新的事件类型。
基础事件类型建议:
| 类型 | 说明 |
|---|---|
no_significant_event | 画面无明显事件 |
person_enter | 有人进入画面或区域 |
person_leave | 有人离开画面或区域 |
person_present | 有人在画面中停留 |
object_moved | 物品位置发生变化 |
object_put_down | 有人放下物品 |
object_picked_up | 有人拿起物品 |
pet_activity | 宠物活动 |
risk_detected | 疑似风险事件 |
unknown | 无法判断或不属于已知类型 |
用户或插件可以注册扩展类型,例如:
minivlm:
event_types:
custom:
- id: "elderly_fall_suspected"
name: "疑似老人跌倒"
description: "画面中有人突然倒地、长时间躺卧或姿态异常。"
owner: "plugin.elderly_care"
attention_required_default: true
- id: "kitchen_fire_risk"
name: "厨房火源风险"
description: "画面中存在明火、烟雾或锅具无人看管。"
owner: "plugin.kitchen_safety"
attention_required_default: true
注册规则:
- 事件类型 ID 应使用小写英文、数字和下划线,例如
elderly_fall_suspected; - 插件只能注册自己命名空间下的事件类型,或由 Genapsed 审核后写入全局事件类型表;
- 同一 ID 不允许被多个插件重复注册;
- 扩展类型应提供中文名称和描述,便于 UI 展示和 Prompt 注入;
- 注册事件类型可以声明默认是否需要关注,例如
attention_required_default: true; - MiniVLM 构建 Prompt 时应把当前启用的事件类型说明注入给模型,避免模型随意创造不受控的类型。
5.4 MiniVLM 系统最终消息格式
MiniVLM 解析模型输出后,由系统补齐元数据并生成可发布消息信封:
{
"msg_id": "uuid-v4",
"source": "minivlm",
"target": "timeline",
"type": "timeline_event_generated",
"priority": "normal",
"timestamp": 1741000000,
"payload": {
"event_id": "evt_1741000000_living_room_xxxx",
"camera_id": "living_room",
"frigate_event_id": "1741000000.123456-abcd",
"trigger_type": "frigate_event",
"time_range": {
"start": 1741000000,
"end": 1741000060
},
"summary": "一名男子从客厅入口进入,将一个快递盒放在茶几上。",
"event_type": "person_enter_and_put_object",
"subjects": ["person"],
"objects": ["快递盒", "茶几"],
"actions": ["进入", "放下物品"],
"attention_required": false,
"confidence": "medium",
"media_refs": {
"image": "temp/share/public/msg-uuid-001/1741000000.jpg",
"video": "temp/share/private/msg-uuid-001/1741000000.mp4"
},
"merge_info": {
"merged": false,
"parent_event_id": null,
"merge_reason": null
},
"processing": {
"model": "Qwen3-VL-4B",
"duration_ms": 8200,
"prompt_version": "minivlm-v1"
}
},
"metadata": {
"requires_ack": true,
"ttl": 300,
"trace_id": "trace-uuid"
}
}
6. 整体处理流程
6.1 处理步骤
-
接收任务
MiniVLM 订阅指定 MQTT topic,接收由 Genapsed 路由来的分析任务。 -
加载上下文
根据camera_id加载摄像头位置、场景描述、用户配置和插件关注点。 -
读取媒体引用
根据media_refs读取图片或关键帧。视频不直接通过 MQTT 传输。 -
构建 Prompt
将基础 Prompt、摄像头上下文、Frigate 检测结果、插件关注点组合成最终 Prompt。 -
调用模型
使用 Qwen3-VL 或兼容的轻量 VLM 生成描述。 -
解析模型输出
提取summary、event_type、subjects、objects、actions、attention_required等字段。 -
补齐系统字段
添加时间戳、摄像头、事件 ID、媒体引用、trace_id、模型版本、耗时等系统元数据。 -
去重与合并
先进行硬合并,再进行软合并,减少重复事件。 -
发布结果
将最终事件通过 MQTT 发布给 Genapsed。
7. 画面采样策略
MiniVLM 设计上采用“混合采样”策略,并允许用户在配置文件中调整。
当前已实现运动感知调度器 SamplingScheduler,通过 tick() 按运动状态定时触发采样分析闭环。
7.1 默认策略
| 场景 | 默认触发频率 | 说明 |
|---|---|---|
| 无 Frigate 事件 | 每 30 分钟触发一次 | 用于低频巡检,记录长期静态状态或缓慢变化 |
| 有 Frigate 事件 | 每 1 分钟触发一次 | 事件活跃期间提高分析频率 |
| 手动查询/调试 | 立即触发 | 用户或开发者主动要求分析某帧/某片段 |
7.2 可配置参数
minivlm:
sampling:
idle_interval_seconds: 1800 # 无事件时默认 30 分钟
active_interval_seconds: 60 # 有事件时默认 1 分钟
manual_request_enabled: true
max_queue_size: 100
drop_low_priority_when_busy: true
7.3 事件活跃窗口
当 Frigate 发布事件后,MiniVLM 将该摄像头标记为活跃状态,并在活跃窗口内使用更高频率采样。
建议规则:
- 收到 Frigate 事件后进入活跃状态;
- 活跃状态下按
active_interval_seconds触发分析; - 如果连续一段时间没有新事件,则回到空闲状态;
- 活跃窗口时长可配置,例如 3~10 分钟。
minivlm:
sampling:
active_window_seconds: 300
7.4 模型输出过慢预警
如果模型单次输出耗时超过 50 秒,MiniVLM 应发布性能预警,提示用户当前采样频率可能过高,建议在配置中调高采样间隔或降低模型规模。
预警条件:
model_inference_duration > 50s
预警建议文本:
MiniVLM 单次识别耗时已超过 50 秒,当前设备可能无法稳定支撑现有采样频率。建议在配置中调高
idle_interval_seconds或active_interval_seconds,或切换更小/更低精度的模型。
预警消息示例:
{
"source": "minivlm",
"type": "performance_warning",
"priority": "normal",
"payload": {
"camera_id": "living_room",
"duration_ms": 52300,
"threshold_ms": 50000,
"suggestion": "MiniVLM 单次识别耗时超过 50 秒,建议调高采样间隔或切换更轻量模型。"
}
}
8. Qwen3-VL 模型调用方案
8.1 模型选择
MiniVLM 初期建议以 Qwen3-VL 系列轻量模型为主,优先考虑本地运行。同时系统所有 AI 功能均向用户开放端点自定义,MiniVLM 也不例外——用户可以配置自己的 VLM API 端点,系统提供几组预设。
候选模型:
| 模型 | 适用场景 |
|---|---|
| Qwen3-VL-4B | 默认推荐,适合低功耗设备或量化部署 |
| Qwen3-VL-8B | 画面理解效果更强,适合有独立 GPU 的设备 |
| 其他 OpenAI-compatible VLM | 用户自定义端点,作为替换后端 |
端点预设方案:
| 预设 | 类型 | 说明 |
|---|---|---|
| 本地 Qwen3-VL(默认) | 本地推理 | 通过 Transformers / llama.cpp / vLLM 本地运行 |
| DeepSeek VL API | 云端 API | DeepSeek 视觉模型 API |
| OpenAI-compatible | 云端/自建 | 任何兼容 OpenAI Vision API 的端点 |
| 自定义端点 | 用户配置 | 用户提供 API 地址和密钥,兼容 OpenAI 接口格式 |
用户可在管理端的 AI 配置页面切换预设或添加自定义端点。
8.2 推理后端抽象
MiniVLM 不应将业务逻辑绑定到某一个具体推理框架,而应抽象出统一接口。
class VLMBackend:
def analyze(self, image_ref: str, prompt: str, options: dict) -> dict:
"""返回模型输出结果。"""
raise NotImplementedError
可选后端:
- 本地 Transformers;
- llama.cpp / GGUF 兼容后端(如果模型支持);
- vLLM / SGLang 等本地服务;
- OpenAI-compatible HTTP API;
- 云端 VLM API(作为低优先级备选)。
8.3 输入预处理
为降低推理成本,应在送入模型前对图片做预处理:
- 限制最大分辨率;
- 根据 Frigate 检测框裁剪重点区域;
- 保留一张全局图,避免只看局部导致误判;
- 对夜间、逆光、模糊画面添加质量标记;
- 对连续相似帧可跳过分析。
9. Prompt 设计方案
MiniVLM Prompt 采用三层组合:
最终 Prompt = 基础 Prompt + 摄像头上下文 + 插件关注点
9.1 基础 Prompt
基础 Prompt 负责约束模型输出风格、观察重点和 JSON 格式。
示例:
你是家庭监控时间线生成助手。请根据输入画面,用简洁、客观的中文描述画面中正在发生的事情。
要求:
1. 只描述画面中可以观察到的事实,不要编造身份、关系、原因或意图。
2. 优先关注人的出现、离开、移动、拿起/放下物品、异常姿态、明显风险。
3. 如果画面没有明显变化,请说明“画面无明显事件”。
4. 输出必须是 JSON,不要输出 Markdown。
5. summary 字段应适合作为时间线中的一句话记录。
6. event_type、subjects、objects、actions 等字段只填写你能从画面中判断的内容。
9.2 摄像头上下文
摄像头上下文用于告诉模型画面含义,避免模型误解场景。
示例:
摄像头上下文:
- 摄像头名称:客厅摄像头
- 位置:客厅
- 画面覆盖:沙发、茶几、客厅入口
- 常见物体:茶几、水杯、快递盒、沙发、电视柜
9.3 插件关注点
插件可以追加关注点,但不应破坏基础输出格式。
插件关注点允许影响模型输出中的 attention_required 字段。例如老人看护插件注册了“疑似跌倒”关注点后,模型在发现疑似跌倒、长时间躺卧或姿态异常时,可以将 attention_required 标记为 true。MiniVLM 在解析结果时也可以结合插件注册事件类型中的 attention_required_default 对该字段进行二次校正。
示例:
插件关注点:
- 老人看护插件:关注是否有人跌倒、长时间躺在地上、行动明显异常。
- 厨房安全插件:关注是否有明火、烟雾、锅具无人看管。
9.4 输出格式约束
请严格输出如下 JSON:
{
"summary": "一句中文事件描述",
"event_type": "事件类型,只能从已注册事件类型中选择;无法判断时使用 unknown",
"subjects": ["主体列表"],
"objects": ["相关物体列表"],
"actions": ["动作列表"],
"attention_required": true 或 false,
"confidence": "low / medium / high"
}
注意:不要自行创造未注册的 event_type。如果画面内容不属于当前事件类型表,请使用 unknown 或 no_significant_event。
9.5 Prompt 版本管理
Prompt 应带版本号,方便后续评估和回滚。
minivlm:
prompt:
version: "minivlm-v1"
base_prompt_file: "prompts/minivlm/base.zh.md"
10. 事件去重与合并逻辑
连续采样会导致同一事件被模型重复描述,因此 MiniVLM 必须在生成消息前进行去重与合并。
合并分为两层:
- 硬合并:基于确定性字段快速合并;
- 软合并:基于语义相似度或轻量 LLM 判断合并。
10.1 硬合并
优先使用以下字段:
frigate_event_id;- 时间窗口;
camera_id。
如果两条事件满足以下条件,可直接视为同一事件的连续描述:
same(camera_id)
AND same(frigate_event_id)
AND time_delta < hard_merge_window_seconds
默认配置示例:
minivlm:
merge:
hard_merge_window_seconds: 180
硬合并处理方式:
- 保留最早开始时间;
- 更新结束时间;
- 保留更完整或置信度更高的
summary; - 合并 subjects、objects、actions;
- 记录
merge_reason = "same_frigate_event"。
10.2 软合并
对于没有相同 Frigate event_id,但时间接近、摄像头相同、语义相似的事件,可进行软合并。
判断依据:
- 摄像头相同;
- 时间距离较短;
- 事件类型相同或相近;
- summary 语义相似;
- subjects / objects / actions 高度重合。
可选实现方式:
| 方法 | 说明 |
|---|---|
| 文本 embedding 相似度 | 使用轻量向量模型计算 summary 相似度 |
| 规则判断 | event_type、subjects、objects、actions 交集判断 |
| 轻量 LLM 判断 | 对边界案例询问小模型“这两条是否为同一事件” |
默认建议:先使用规则 + embedding,相似度不足但疑似重复时再调用轻量 LLM。
10.3 不应合并的情况
以下情况不应合并:
- 摄像头不同,除非后续明确设计跨摄像头追踪;
- 时间相距过长;
- 事件主体或动作明显不同;
- 一个事件是风险告警,另一个只是普通状态;
- 插件明确要求保留独立事件。
11. 时间线索引字段建议
MiniVLM 不直接写数据库,但它生成的事件应包含足够字段,方便 Genapsed 调用 Timeline 索引。
建议时间线模块至少索引以下字段:
| 字段 | 用途 |
|---|---|
event_id | 事件唯一标识 |
camera_id | 按摄像头查询 |
time_range.start / time_range.end | 按时间范围查询 |
summary | 全文搜索与 LLM 上下文 |
event_type | 按事件类型过滤 |
subjects | 查询“人、猫、老人”等主体 |
objects | 查询“水杯、钥匙、快递”等物体 |
actions | 查询“进入、离开、拿起、放下”等动作 |
attention_required | 插件和 UI 优先展示 |
media_refs.video | 定位回放片段 |
trace_id | 调试链路追踪 |
12. MQTT Topic 与消息格式
MiniVLM 遵循 Genapsed 现有 Topic 规范:
everpast/<component>/<message_type>/<priority>[/target]
12.1 建议 Topic
| Topic | 方向 | 说明 |
|---|---|---|
everpast/genapsed/cmd/normal/minivlm | Genapsed → MiniVLM | 下发分析任务或配置更新 |
everpast/minivlm/event/normal | MiniVLM 运行时适配层 → Genapsed | 发布普通时间线事件 |
everpast/minivlm/event/high | MiniVLM 运行时适配层 → Genapsed | 发布需要关注的事件 |
everpast/minivlm/ack/normal | MiniVLM 运行时适配层 → Genapsed | 分析任务处理 ACK |
everpast/minivlm/status/normal | MiniVLM → Genapsed | 状态心跳 |
everpast/minivlm/log/low | MiniVLM → Genapsed | 普通日志 |
everpast/minivlm/log/normal | MiniVLM → Genapsed | 性能预警或可恢复异常 |
注:现有 Genapsed 文档中曾出现
minilm,此处建议统一使用minivlm,避免与 MiniLM 文本模型混淆。若系统已有旧 topic,可在迁移期兼容两个名称。
12.2 QoS 建议
| 消息类型 | QoS | 说明 |
|---|---|---|
| 分析任务 | 1 | 不应丢失,但允许通过 msg_id 去重 |
| 时间线事件 | 1 | 事件不可丢失,重复可由下游去重 |
| 控制命令 | 2 | 配置更新、暂停/恢复等必须准确送达 |
| 状态心跳 | 0 | 可丢失,最新状态优先 |
| 日志 | 0 | 可丢失 |
| 性能预警 | 1 | 建议可靠送达 |
13. 性能优化方案
13.1 采样层优化
- 默认无事件 30 分钟一次,避免长期空转;
- 有事件时 1 分钟一次,避免连续帧高频刷模型;
- 支持按摄像头配置不同频率;
- 支持在设备忙碌时丢弃低优先级巡检任务;
- 对连续高度相似画面跳过 VLM 推理。
13.2 模型层优化
- 使用量化模型;
- 限制输入图片分辨率;
- 对重点区域裁剪,必要时同时保留全局图;
- 支持更小模型和更大模型切换;
- 支持本地推理服务复用模型进程,避免重复加载。
13.3 队列与限流
MiniVLM 应维护任务队列,避免事件高峰期任务无限堆积。
建议配置:
minivlm:
queue:
max_size: 100
max_concurrent_inference: 1
drop_policy: "drop_idle_scan_first"
队列优先级建议:
- 用户手动请求;
- 高优先级 Frigate 事件;
- 普通 Frigate 事件;
- 空闲巡检。
13.4 性能预警
除单次推理超过 50 秒外,还可以监控:
| 指标 | 预警条件 |
|---|---|
| 队列长度 | 超过 max_queue_size * 0.8 |
| 连续失败次数 | 超过 3 次 |
| 模型加载失败 | 立即预警 |
| 图片读取失败 | 记录并告警 |
| MQTT 发布失败 | 重试后仍失败则告警 |
14. 异常处理与降级策略
| 异常 | 处理方式 |
|---|---|
| 图片路径不存在 | 发布错误日志,跳过该任务 |
| 视频引用不可读 | 保留事件文本,但标记 media_refs 不完整 |
| 模型输出不是 JSON | 尝试修复解析;失败则将原文放入 summary 并标记 parse_failed |
| 模型超时 | 中断任务,发布性能预警 |
| MQTT 断开 | 本地暂存结果,重连后补发 |
| 任务积压 | 丢弃低优先级空闲巡检任务 |
| 模型不可用 | 发布状态异常,暂停分析任务 |
14.1 模型输出修复
如果模型输出格式不严格,可执行以下修复:
- 提取第一个 JSON 对象;
- 尝试补齐缺失引号或尾逗号;
- 如果仍失败,则将模型原始文本作为
summary; - 标记
processing.parse_status = "parse_failed"。
15. 配置项设计
目标形态配置文件示例:
minivlm:
enabled: true
mqtt:
command_topic: "everpast/genapsed/cmd/normal/minivlm"
event_topic: "everpast/minivlm/event/normal"
high_priority_event_topic: "everpast/minivlm/event/high"
status_topic: "everpast/minivlm/status/normal"
model:
backend: "local_transformers"
name: "Qwen3-VL-4B"
device: "auto"
quantization: "int4"
timeout_seconds: 60
warning_threshold_seconds: 50
sampling:
idle_interval_seconds: 1800
active_interval_seconds: 60
active_window_seconds: 300
manual_request_enabled: true
prompt:
version: "minivlm-v1"
base_prompt_file: "prompts/minivlm/base.zh.md"
camera_context_enabled: true
plugin_focus_enabled: true
event_types_enabled: true
merge:
hard_merge_window_seconds: 180
soft_merge_window_seconds: 300
embedding_similarity_threshold: 0.86
use_light_llm_for_uncertain_merge: true
queue:
max_size: 100
max_concurrent_inference: 1
drop_policy: "drop_idle_scan_first"
output:
publish_raw_model_output: true # 开发阶段默认开启;后期可关闭
include_processing_metadata: true
15.1 原始模型输出保留策略
开发阶段建议开启 publish_raw_model_output 或至少在本地日志中保留原始模型输出,方便排查 Prompt、JSON 解析、幻觉和事件分类问题。
后期进入稳定版本后,该选项应允许关闭,原因包括:
- 原始输出可能包含冗余文本,增加日志体积;
- 原始输出可能包含模型误判或敏感描述,不一定适合长期保留;
- 生产环境通常只需要保留解析后的
summary和结构化候选字段。
建议策略:
| 阶段 | 默认值 | 说明 |
|---|---|---|
| 开发阶段 | true | 便于调试 Prompt 和解析逻辑 |
| 内测阶段 | true 或按摄像头开启 | 便于收集失败样例 |
| 正式运行 | false | 默认关闭,需要排查问题时临时开启 |
15.2 当前代码已实现配置
当前配置分为核心管线配置和开发期 worker 配置。MiniVLMConfig 覆盖核心单任务管线所需配置:
| 配置对象 | 字段 | 默认值 | 状态 |
|---|---|---|---|
model | backend | static | 已实现,通过 create_backend 工厂选择后端 |
model | name | Qwen3-VL-4B | 已实现,用于处理元数据 |
model | timeout_seconds | 60 | 已实现基础校验 |
model | warning_threshold_seconds | 50 | 已实现,超时后发布性能预警 MQTT 日志 |
model | options | {} | 已实现,原样传给 VLMBackend.analyze() |
prompt | version | minivlm-v1 | 已实现 |
prompt | camera_context_enabled | true | 已实现 |
prompt | plugin_focus_enabled | true | 已实现 |
prompt | event_types_enabled | true | 已实现 |
merge | hard_merge_window_seconds | 180 | 已实现 |
output | publish_raw_model_output | true | 已实现 |
output | include_processing_metadata | true | 已实现配置字段,当前输出始终包含处理元数据 |
scheduler | motion_interval_sec | 60 | 已实现,运动态采样间隔 |
scheduler | idle_interval_sec | 1800 | 已实现,空闲态采样间隔 |
scheduler | motion_timeout_sec | 300 | 已实现,运动超时后切回空闲 |
scheduler | enabled_cameras | () | 已实现,空表示全部启用 |
frame_source | frigate_api_url | http://127.0.0.1:5000 | 已实现,Frigate REST API |
frame_source | frame_height | 720 | 已实现,缩放高度 |
frame_source | frame_quality | 70 | 已实现,JPEG 质量 |
frame_source | fetch_timeout_sec | 10 | 已实现 |
frame_source | fetch_max_retries | 2 | 已实现 |
preprocess | max_height | 1080 | 已实现,等比缩放上限 |
preprocess | min_quality_score | 0.3 | 已实现,亮度方差质量评估 |
preprocess | jpeg_quality | 85 | 已实现,重编码质量 |
database | database_url | "" | 已实现,连接 MySQL |
database | echo | False | 已实现,SQL 日志开关 |
MiniVLMWorkerConfig 当前由 Genapsed 渲染 data/minivlm/config.json 后读取,已实现字段如下:
| 字段 | 默认值 | 状态 |
|---|---|---|
component_id | minivlm | 已实现,用于状态、日志和 ACK topic |
mqtt_client_id | minivlm-worker | 已实现,用于 MQTT 连接 |
command_topic_filter | everpast/genapsed/cmd/+/minivlm | 已实现,订阅 Genapsed 下发的分析任务 |
status_interval_seconds | 30 | 已实现,控制健康心跳上报间隔 |
static_model_output | 开发期 JSON 摘要 | 已实现,用于 StaticVLMBackend 打通链路 |
model_name | static-vlm 或配置中的模型名 | 已实现,当前仅写入处理元数据 |
data_root_dir | "" | 已实现,日志初始化路径 |
queue、软合并等仍属于后续运行时能力,尚未在当前 worker 中实现。
16. 测试与评估指标
16.1 功能测试
- 能否接收 Genapsed 下发的分析任务;
- 能否读取图片/视频引用;
- 能否正确调用 VLM;
- 能否解析模型输出;
- 能否发布 MQTT 事件;
- 能否根据 Frigate event_id 合并重复事件;
- 能否在模型超过 50 秒时发布预警。
16.2 效果评估
| 指标 | 说明 |
|---|---|
| 描述准确率 | summary 是否客观描述画面 |
| 事件召回率 | 重要事件是否被记录 |
| 重复率 | 时间线中重复事件比例 |
| 误报率 | 无明显事件却生成重要事件的比例 |
| 查询可用性 | LLM 能否根据时间线回答用户问题 |
16.3 性能评估
| 指标 | 目标 |
|---|---|
| 单次推理耗时 | 正常情况下低于 50 秒 |
| 空闲功耗 | 不明显影响家庭设备日常运行 |
| 队列积压 | 长时间运行不无限增长 |
| 内存占用 | 模型加载后保持稳定 |
| MQTT 成功率 | 事件发布可靠 |
17. 后续扩展方向
-
多帧理解
从单帧描述升级为短片段多帧理解,提升“拿起/放下/进入/离开”等动作判断准确率。 -
跨摄像头事件关联
支持同一主体从门口进入客厅等跨摄像头连续事件追踪。 -
插件自定义 Prompt 模板
允许插件提供 Prompt 片段和输出字段扩展,但需要经过 Genapsed 权限和安全校验。 -
本地事件类型分类器
对 VLM 输出后的 summary 使用轻量文本分类模型,减少大模型判断成本。 -
隐私脱敏
在事件进入 LLM 查询层前,对人名、敏感区域、隐私物体等进行可配置脱敏。 -
用户反馈闭环
用户可以标记“这条描述不准确”或“这两条应合并”,系统后续用于优化 Prompt 和合并规则。
18. 待确认问题
以下问题需要在原型测试或下一轮设计中继续确认:
- Qwen3-VL 具体使用哪个规模、量化格式和推理后端;
- 是否需要 MiniVLM 在生成事件前调用轻量 embedding 模型;
- 是否需要对不同摄像头配置不同模型或不同采样频率;
- 性能预警最终由 UI 弹出、日志显示,还是通知插件推送给用户。
19. 初步开发落地架构
MiniVLM 当前代码包含核心层和 MQTT worker 运行时。核心层负责帧获取、图片预处理、VLM 后端抽象、采样调度、Prompt 组合、模型输出解析和数据库存储;运行时适配层复用 Share MQTT 和 HttpImageFetcher,通过 FrigateFrameSource 主动拉取摄像头帧,经 ImagePreprocessor 处理后调用 MiniVLMPipeline 执行 VLM 分析,结果写入 MySQL 并通过 MQTT 发布 timeline_event_generated。
当前代码结构:
MiniVLM/
├── pyproject.toml
├── requirements.txt
├── src/minivlm/
│ ├── alembic/ # 数据库迁移
│ │ ├── env.py
│ │ └── versions/
│ │ └── 0001_create_minivlm_tables.py
│ ├── cli.py
│ ├── core/
│ │ ├── backends.py # VLM 后端协议、静态后端、工厂
│ │ ├── config.py # 核心配置 + 子配置对象
│ │ ├── event_types.py # 事件类型注册表
│ │ ├── exceptions.py # 核心异常
│ │ ├── frame_source.py # Frigate 帧获取
│ │ ├── image_preprocess.py# Pillow 图片预处理
│ │ ├── merger.py # 基于 Frigate event_id 的硬合并
│ │ ├── models.py # 分析任务、模型事件、时间线事件
│ │ ├── parser.py # 模型 JSON 输出解析
│ │ ├── pipeline.py # 单任务处理编排(含错误恢复)
│ │ ├── prompts.py # Prompt 组装
│ │ ├── scheduler.py # 运动感知采样调度器
│ │ └── topics.py # MiniVLM Topic 常量
│ ├── database/
│ │ ├── __init__.py
│ │ ├── migrator.py # Alembic 迁移封装
│ │ ├── schema.py # 数据库表结构定义
│ │ └── store.py # 分析任务/帧/推理 CRUD
│ └── runtime/
│ └── worker.py # MQTT worker(完整采样闭环)
└── tests/
当前核心处理链路(定时采样闭环):
SamplingScheduler.tick()
-> FrigateFrameSource.fetch_latest_frame()
-> ImagePreprocessor.process()
-> MiniVLMPipeline.process()
-> PromptBuilder
-> VLMBackend.analyze()
-> ModelOutputParser
-> HardEventMerger
-> MiniVLMStore.save_*(记录)
-> timeline_event_generated MQTT 发布
19.1 已确定实现边界
- 核心层使用 Pillow 进行图片预处理(质量评估、缩放、重编码),通过 SQLAlchemy 写入 MySQL;
VLMBackend是唯一模型调用抽象,通过create_backend工厂构建,支持static和openai_compat两种后端;- 模型输出必须经过
ModelOutputParser,未注册event_type会显式归一为unknown并记录解析警告; - 非 JSON 模型输出不会静默吞掉,而是标记
parse_status = "parse_failed",并保留原始文本用于开发期排查; - 事件类型由
EventTypeRegistry控制,Prompt 会注入当前已注册类型,限制模型随意创造分类; - 硬合并条件为
same(camera_id) + same(frigate_event_id) + time_delta <= hard_merge_window_seconds; - MQTT worker 负责定时采样闭环和即时分析命令处理,结果写入 MySQL 后通过 MQTT 发布
timeline_event_generated。
19.2 当前消息输出行为
MiniVLMPipeline.process() 要求 AnalyzeTask.media_refs.image 非空,否则返回 success=False 的 PipelineResult。
处理成功后会生成 timeline_event_generated 信封:
source固定为minivlm;target固定为timeline;metadata.requires_ack固定为true;metadata.ttl和metadata.trace_id来自分析任务;- 当
attention_required为true时发布 Topic 为everpast/minivlm/event/high,否则为everpast/minivlm/event/normal。
当前事件 ID 格式为 evt_<timestamp>_<camera_id>_<8位随机后缀>,时间范围默认以任务时间戳作为起止时间;
硬合并命中时由 HardEventMerger 更新结束时间和合并信息。
19.3 当前测试范围
开荒阶段不追求高覆盖率,但已为会影响后续模块契约的核心能力补测试:
- 事件类型注册与重复注册校验;
- 模型输出解析、未注册事件类型处理和非 JSON 输出标记;
- 单任务处理管线生成
timeline_event_generated消息; - 高关注事件发布到 high priority topic;
- 同一 Frigate 事件窗口内的硬合并。
本地验证命令:
cd MiniVLM
PYTHONPATH=src python -m unittest discover -s tests
19.4 后续模块顺序
已实现项(本轮已完成):
- ✅ OpenAI-compatible VLM HTTP 后端(
OpenAICompatVLMBackend+create_backend工厂) - ✅ 图片预处理与图片质量标记(
ImagePreprocessor) - ✅ MQTT 运行时适配层(
MiniVLMWorker定时采样闭环) - ✅ MiniVLM 调试 CLI 扩展(
migrate-db子命令) - ✅ 数据库存储(
MiniVLMStore+ Alembic 迁移)
建议继续按以下顺序推进:
- 本地 Qwen3-VL / Transformers 后端;
- WebUI 配置与调试页面;
- 软合并和可选 embedding 判断;
- 多帧理解与跨摄像头事件关联;
- 隐私脱敏和用户反馈闭环。