跳到主要内容

MiniVLM 事件生成模块详细设计

对应项目待办事项 P1-4. MiniVLM 事件生成模块详细设计

负责人:@Tetoisnothuman
协助:@hexianglong
状态:核心层、采样调度、Frigate 帧获取、图片预处理、MySQL 存储均已实现;OpenAI-compatible VLM 后端子命令已接入;性能预警 MQTT 发布已实现 相关模块:Frigate NVR、Genapsed、时间线存储、LLM 查询引擎、插件系统


1. 模块定位

MiniVLM 事件生成模块是本项目的核心组件,负责“把监控画面翻译成自然语言事件”。

项目整体目标是将传统监控系统从“录像文件 + 人工拖进度条”的模式,转变为“自然语言时间线 + AI 查询 + 精准回放”的模式。因此,MiniVLM 的直接产出不是最终用户问答结果,而是供时间线存储、LLM 查询引擎和插件系统共同使用的基础事件文本。

在系统中,MiniVLM 主要承担以下职责:

  1. 接收 Genapsed 路由来的分析任务,任务内容来自 NVR-Bridge 归一化后的 Frigate 事件、截图和短视频片段引用;
  2. 调用轻量视觉语言模型(VLM)理解画面内容;当前开发期 worker 默认使用 StaticVLMBackend 打通链路;
  3. 生成面向用户和大模型都可读的自然语言事件描述;
  4. 对模型输出进行基础解析、校验、去重和合并;
  5. 通过 MQTT 将事件结果发布给 Genapsed,由后续模块写入时间线或触发插件逻辑。

MiniVLM 可以理解为系统的“视觉翻译层”:它不直接回答用户问题,也不负责复杂业务判断,而是把原始监控画面转写成可以长期存储、检索和推理的文本事件。


2. 设计目标与非目标

2.1 设计目标

MiniVLM 的设计目标如下:

  1. 低成本持续运行
    能够在家庭 NAS、迷你主机或低功耗 GPU 设备上长期运行,避免对高算力云端服务形成强依赖。

  2. 生成可读的自然语言时间线
    输出应优先服务“人能读懂”和“LLM 能推理”,而不是追求复杂、脆弱的全结构化视觉识别结果。

  3. 支持事件触发与低频巡检混合采样
    在有 Frigate 事件时提高分析频率;无事件时保持低频巡检,尽量减少算力浪费。

  4. 通过 MQTT 与系统解耦
    MiniVLM 不直接耦合时间线数据库。当前 worker 已通过 MQTT 发布 timeline_event_generated 消息信封给 Genapsed,由 Genapsed 调用 Timeline 处理后续存储和路由。

  5. 支持场景化 Prompt 扩展
    Prompt 应支持基础规则、摄像头上下文、插件关注点三层组合,使系统可以适配客厅、厨房、门口、老人看护等不同场景。

  6. 避免时间线刷屏
    通过事件去重、硬合并和软合并,减少连续帧中重复描述同一事件的问题。

  7. 可观测、可降级、可配置
    模块需要提供日志、状态、预警和关键参数配置,便于用户根据设备性能调整运行频率。

2.2 非目标

MiniVLM 不负责以下事项:

  1. 不负责摄像头接入和录像管理
    摄像头流、录像、截图、目标检测等由 Frigate NVR 或其他视频底层组件负责。

  2. 不直接存储正式时间线数据库
    MiniVLM 只生成事件结果,不直接写入时间线数据库。运行时适配层通过 MQTT 交给 Genapsed 后,由 Genapsed 调用 Timeline 入库。

  3. 不承担最终自然语言问答
    用户问题的理解、跨事件推理和视频片段定位由 LLM 查询引擎负责。

  4. 不替代插件业务逻辑
    例如老人跌倒判断、厨房安全告警、智能家居联动等应主要由插件处理。MiniVLM 只提供基础视觉事件描述和必要的候选判断信息。

  5. 不直接传输大文件
    模块遵循系统“控制面/数据面分离”的原则:MQTT 只传 JSON 和文件引用,图片、视频等大文件通过文件系统或 HTTP 引用传递。


3. 上下游关系

3.1 系统位置

3.2 上游输入

MiniVLM 的上游主要包括:

来源输入内容说明
NVR-Bridge由 Frigate 事件归一化得到的分析任务 payload,包含事件 ID、截图和视频片段引用当前直接任务来源
GenapsedMQTT 分析任务、配置、插件关注点、系统状态负责调度与路由
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_idFrigate 事件 ID,可为空Frigate
trigger_type触发方式,如 scheduled_scanfrigate_eventmanual_requestGenapsed
media_refs.image待分析图片路径或 URLFrigate/Genapsed
media_refs.video相关视频片段路径或 URLFrigate/Genapsed
frigate_objectsFrigate 已检测到的对象Frigate
camera_context摄像头位置与场景描述配置系统
plugin_focus插件追加关注点插件系统/Genapsed
trace_id全链路追踪 IDGenapsed

5. 输出事件设计

5.1 输出原则

MiniVLM 采用“自然语言为主、结构化字段为辅”的混合输出方案。

但需要注意:模型本身不负责输出完整系统元数据。模型只输出:

  1. 对画面发生内容的自然语言描述;
  2. 少量需要视觉判断或语义判断的字段,例如事件类型、是否需要关注、候选对象、候选动作等。

其他字段,例如事件 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

注册规则:

  1. 事件类型 ID 应使用小写英文、数字和下划线,例如 elderly_fall_suspected
  2. 插件只能注册自己命名空间下的事件类型,或由 Genapsed 审核后写入全局事件类型表;
  3. 同一 ID 不允许被多个插件重复注册;
  4. 扩展类型应提供中文名称和描述,便于 UI 展示和 Prompt 注入;
  5. 注册事件类型可以声明默认是否需要关注,例如 attention_required_default: true
  6. 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 处理步骤

  1. 接收任务
    MiniVLM 订阅指定 MQTT topic,接收由 Genapsed 路由来的分析任务。

  2. 加载上下文
    根据 camera_id 加载摄像头位置、场景描述、用户配置和插件关注点。

  3. 读取媒体引用
    根据 media_refs 读取图片或关键帧。视频不直接通过 MQTT 传输。

  4. 构建 Prompt
    将基础 Prompt、摄像头上下文、Frigate 检测结果、插件关注点组合成最终 Prompt。

  5. 调用模型
    使用 Qwen3-VL 或兼容的轻量 VLM 生成描述。

  6. 解析模型输出
    提取 summaryevent_typesubjectsobjectsactionsattention_required 等字段。

  7. 补齐系统字段
    添加时间戳、摄像头、事件 ID、媒体引用、trace_id、模型版本、耗时等系统元数据。

  8. 去重与合并
    先进行硬合并,再进行软合并,减少重复事件。

  9. 发布结果
    将最终事件通过 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 将该摄像头标记为活跃状态,并在活跃窗口内使用更高频率采样。

建议规则:

  1. 收到 Frigate 事件后进入活跃状态;
  2. 活跃状态下按 active_interval_seconds 触发分析;
  3. 如果连续一段时间没有新事件,则回到空闲状态;
  4. 活跃窗口时长可配置,例如 3~10 分钟。
minivlm:
sampling:
active_window_seconds: 300

7.4 模型输出过慢预警

如果模型单次输出耗时超过 50 秒,MiniVLM 应发布性能预警,提示用户当前采样频率可能过高,建议在配置中调高采样间隔或降低模型规模。

预警条件:

model_inference_duration > 50s

预警建议文本:

MiniVLM 单次识别耗时已超过 50 秒,当前设备可能无法稳定支撑现有采样频率。建议在配置中调高 idle_interval_secondsactive_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云端 APIDeepSeek 视觉模型 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

可选后端:

  1. 本地 Transformers;
  2. llama.cpp / GGUF 兼容后端(如果模型支持);
  3. vLLM / SGLang 等本地服务;
  4. OpenAI-compatible HTTP API;
  5. 云端 VLM API(作为低优先级备选)。

8.3 输入预处理

为降低推理成本,应在送入模型前对图片做预处理:

  1. 限制最大分辨率;
  2. 根据 Frigate 检测框裁剪重点区域;
  3. 保留一张全局图,避免只看局部导致误判;
  4. 对夜间、逆光、模糊画面添加质量标记;
  5. 对连续相似帧可跳过分析。

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 必须在生成消息前进行去重与合并。

合并分为两层:

  1. 硬合并:基于确定性字段快速合并;
  2. 软合并:基于语义相似度或轻量 LLM 判断合并。

10.1 硬合并

优先使用以下字段:

  1. frigate_event_id
  2. 时间窗口;
  3. camera_id

如果两条事件满足以下条件,可直接视为同一事件的连续描述:

same(camera_id)
AND same(frigate_event_id)
AND time_delta < hard_merge_window_seconds

默认配置示例:

minivlm:
merge:
hard_merge_window_seconds: 180

硬合并处理方式:

  1. 保留最早开始时间;
  2. 更新结束时间;
  3. 保留更完整或置信度更高的 summary
  4. 合并 subjects、objects、actions;
  5. 记录 merge_reason = "same_frigate_event"

10.2 软合并

对于没有相同 Frigate event_id,但时间接近、摄像头相同、语义相似的事件,可进行软合并。

判断依据:

  1. 摄像头相同;
  2. 时间距离较短;
  3. 事件类型相同或相近;
  4. summary 语义相似;
  5. subjects / objects / actions 高度重合。

可选实现方式:

方法说明
文本 embedding 相似度使用轻量向量模型计算 summary 相似度
规则判断event_type、subjects、objects、actions 交集判断
轻量 LLM 判断对边界案例询问小模型“这两条是否为同一事件”

默认建议:先使用规则 + embedding,相似度不足但疑似重复时再调用轻量 LLM。

10.3 不应合并的情况

以下情况不应合并:

  1. 摄像头不同,除非后续明确设计跨摄像头追踪;
  2. 时间相距过长;
  3. 事件主体或动作明显不同;
  4. 一个事件是风险告警,另一个只是普通状态;
  5. 插件明确要求保留独立事件。

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/minivlmGenapsed → MiniVLM下发分析任务或配置更新
everpast/minivlm/event/normalMiniVLM 运行时适配层 → Genapsed发布普通时间线事件
everpast/minivlm/event/highMiniVLM 运行时适配层 → Genapsed发布需要关注的事件
everpast/minivlm/ack/normalMiniVLM 运行时适配层 → Genapsed分析任务处理 ACK
everpast/minivlm/status/normalMiniVLM → Genapsed状态心跳
everpast/minivlm/log/lowMiniVLM → Genapsed普通日志
everpast/minivlm/log/normalMiniVLM → Genapsed性能预警或可恢复异常

注:现有 Genapsed 文档中曾出现 minilm,此处建议统一使用 minivlm,避免与 MiniLM 文本模型混淆。若系统已有旧 topic,可在迁移期兼容两个名称。

12.2 QoS 建议

消息类型QoS说明
分析任务1不应丢失,但允许通过 msg_id 去重
时间线事件1事件不可丢失,重复可由下游去重
控制命令2配置更新、暂停/恢复等必须准确送达
状态心跳0可丢失,最新状态优先
日志0可丢失
性能预警1建议可靠送达

13. 性能优化方案

13.1 采样层优化

  1. 默认无事件 30 分钟一次,避免长期空转;
  2. 有事件时 1 分钟一次,避免连续帧高频刷模型;
  3. 支持按摄像头配置不同频率;
  4. 支持在设备忙碌时丢弃低优先级巡检任务;
  5. 对连续高度相似画面跳过 VLM 推理。

13.2 模型层优化

  1. 使用量化模型;
  2. 限制输入图片分辨率;
  3. 对重点区域裁剪,必要时同时保留全局图;
  4. 支持更小模型和更大模型切换;
  5. 支持本地推理服务复用模型进程,避免重复加载。

13.3 队列与限流

MiniVLM 应维护任务队列,避免事件高峰期任务无限堆积。

建议配置:

minivlm:
queue:
max_size: 100
max_concurrent_inference: 1
drop_policy: "drop_idle_scan_first"

队列优先级建议:

  1. 用户手动请求;
  2. 高优先级 Frigate 事件;
  3. 普通 Frigate 事件;
  4. 空闲巡检。

13.4 性能预警

除单次推理超过 50 秒外,还可以监控:

指标预警条件
队列长度超过 max_queue_size * 0.8
连续失败次数超过 3 次
模型加载失败立即预警
图片读取失败记录并告警
MQTT 发布失败重试后仍失败则告警

14. 异常处理与降级策略

异常处理方式
图片路径不存在发布错误日志,跳过该任务
视频引用不可读保留事件文本,但标记 media_refs 不完整
模型输出不是 JSON尝试修复解析;失败则将原文放入 summary 并标记 parse_failed
模型超时中断任务,发布性能预警
MQTT 断开本地暂存结果,重连后补发
任务积压丢弃低优先级空闲巡检任务
模型不可用发布状态异常,暂停分析任务

14.1 模型输出修复

如果模型输出格式不严格,可执行以下修复:

  1. 提取第一个 JSON 对象;
  2. 尝试补齐缺失引号或尾逗号;
  3. 如果仍失败,则将模型原始文本作为 summary
  4. 标记 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 解析、幻觉和事件分类问题。

后期进入稳定版本后,该选项应允许关闭,原因包括:

  1. 原始输出可能包含冗余文本,增加日志体积;
  2. 原始输出可能包含模型误判或敏感描述,不一定适合长期保留;
  3. 生产环境通常只需要保留解析后的 summary 和结构化候选字段。

建议策略:

阶段默认值说明
开发阶段true便于调试 Prompt 和解析逻辑
内测阶段true 或按摄像头开启便于收集失败样例
正式运行false默认关闭,需要排查问题时临时开启

15.2 当前代码已实现配置

当前配置分为核心管线配置和开发期 worker 配置。MiniVLMConfig 覆盖核心单任务管线所需配置:

配置对象字段默认值状态
modelbackendstatic已实现,通过 create_backend 工厂选择后端
modelnameQwen3-VL-4B已实现,用于处理元数据
modeltimeout_seconds60已实现基础校验
modelwarning_threshold_seconds50已实现,超时后发布性能预警 MQTT 日志
modeloptions{}已实现,原样传给 VLMBackend.analyze()
promptversionminivlm-v1已实现
promptcamera_context_enabledtrue已实现
promptplugin_focus_enabledtrue已实现
promptevent_types_enabledtrue已实现
mergehard_merge_window_seconds180已实现
outputpublish_raw_model_outputtrue已实现
outputinclude_processing_metadatatrue已实现配置字段,当前输出始终包含处理元数据
schedulermotion_interval_sec60已实现,运动态采样间隔
scheduleridle_interval_sec1800已实现,空闲态采样间隔
schedulermotion_timeout_sec300已实现,运动超时后切回空闲
schedulerenabled_cameras()已实现,空表示全部启用
frame_sourcefrigate_api_urlhttp://127.0.0.1:5000已实现,Frigate REST API
frame_sourceframe_height720已实现,缩放高度
frame_sourceframe_quality70已实现,JPEG 质量
frame_sourcefetch_timeout_sec10已实现
frame_sourcefetch_max_retries2已实现
preprocessmax_height1080已实现,等比缩放上限
preprocessmin_quality_score0.3已实现,亮度方差质量评估
preprocessjpeg_quality85已实现,重编码质量
databasedatabase_url""已实现,连接 MySQL
databaseechoFalse已实现,SQL 日志开关

MiniVLMWorkerConfig 当前由 Genapsed 渲染 data/minivlm/config.json 后读取,已实现字段如下:

字段默认值状态
component_idminivlm已实现,用于状态、日志和 ACK topic
mqtt_client_idminivlm-worker已实现,用于 MQTT 连接
command_topic_filtereverpast/genapsed/cmd/+/minivlm已实现,订阅 Genapsed 下发的分析任务
status_interval_seconds30已实现,控制健康心跳上报间隔
static_model_output开发期 JSON 摘要已实现,用于 StaticVLMBackend 打通链路
model_namestatic-vlm 或配置中的模型名已实现,当前仅写入处理元数据
data_root_dir""已实现,日志初始化路径

queue、软合并等仍属于后续运行时能力,尚未在当前 worker 中实现。


16. 测试与评估指标

16.1 功能测试

  1. 能否接收 Genapsed 下发的分析任务;
  2. 能否读取图片/视频引用;
  3. 能否正确调用 VLM;
  4. 能否解析模型输出;
  5. 能否发布 MQTT 事件;
  6. 能否根据 Frigate event_id 合并重复事件;
  7. 能否在模型超过 50 秒时发布预警。

16.2 效果评估

指标说明
描述准确率summary 是否客观描述画面
事件召回率重要事件是否被记录
重复率时间线中重复事件比例
误报率无明显事件却生成重要事件的比例
查询可用性LLM 能否根据时间线回答用户问题

16.3 性能评估

指标目标
单次推理耗时正常情况下低于 50 秒
空闲功耗不明显影响家庭设备日常运行
队列积压长时间运行不无限增长
内存占用模型加载后保持稳定
MQTT 成功率事件发布可靠

17. 后续扩展方向

  1. 多帧理解
    从单帧描述升级为短片段多帧理解,提升“拿起/放下/进入/离开”等动作判断准确率。

  2. 跨摄像头事件关联
    支持同一主体从门口进入客厅等跨摄像头连续事件追踪。

  3. 插件自定义 Prompt 模板
    允许插件提供 Prompt 片段和输出字段扩展,但需要经过 Genapsed 权限和安全校验。

  4. 本地事件类型分类器
    对 VLM 输出后的 summary 使用轻量文本分类模型,减少大模型判断成本。

  5. 隐私脱敏
    在事件进入 LLM 查询层前,对人名、敏感区域、隐私物体等进行可配置脱敏。

  6. 用户反馈闭环
    用户可以标记“这条描述不准确”或“这两条应合并”,系统后续用于优化 Prompt 和合并规则。


18. 待确认问题

以下问题需要在原型测试或下一轮设计中继续确认:

  1. Qwen3-VL 具体使用哪个规模、量化格式和推理后端;
  2. 是否需要 MiniVLM 在生成事件前调用轻量 embedding 模型;
  3. 是否需要对不同摄像头配置不同模型或不同采样频率;
  4. 性能预警最终由 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 已确定实现边界

  1. 核心层使用 Pillow 进行图片预处理(质量评估、缩放、重编码),通过 SQLAlchemy 写入 MySQL;
  2. VLMBackend 是唯一模型调用抽象,通过 create_backend 工厂构建,支持 staticopenai_compat 两种后端;
  3. 模型输出必须经过 ModelOutputParser,未注册 event_type 会显式归一为 unknown 并记录解析警告;
  4. 非 JSON 模型输出不会静默吞掉,而是标记 parse_status = "parse_failed",并保留原始文本用于开发期排查;
  5. 事件类型由 EventTypeRegistry 控制,Prompt 会注入当前已注册类型,限制模型随意创造分类;
  6. 硬合并条件为 same(camera_id) + same(frigate_event_id) + time_delta <= hard_merge_window_seconds
  7. MQTT worker 负责定时采样闭环和即时分析命令处理,结果写入 MySQL 后通过 MQTT 发布 timeline_event_generated

19.2 当前消息输出行为

MiniVLMPipeline.process() 要求 AnalyzeTask.media_refs.image 非空,否则返回 success=FalsePipelineResult。 处理成功后会生成 timeline_event_generated 信封:

  • source 固定为 minivlm
  • target 固定为 timeline
  • metadata.requires_ack 固定为 true
  • metadata.ttlmetadata.trace_id 来自分析任务;
  • attention_requiredtrue 时发布 Topic 为 everpast/minivlm/event/high,否则为 everpast/minivlm/event/normal

当前事件 ID 格式为 evt_<timestamp>_<camera_id>_<8位随机后缀>,时间范围默认以任务时间戳作为起止时间; 硬合并命中时由 HardEventMerger 更新结束时间和合并信息。

19.3 当前测试范围

开荒阶段不追求高覆盖率,但已为会影响后续模块契约的核心能力补测试:

  1. 事件类型注册与重复注册校验;
  2. 模型输出解析、未注册事件类型处理和非 JSON 输出标记;
  3. 单任务处理管线生成 timeline_event_generated 消息;
  4. 高关注事件发布到 high priority topic;
  5. 同一 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 迁移)

建议继续按以下顺序推进:

  1. 本地 Qwen3-VL / Transformers 后端;
  2. WebUI 配置与调试页面;
  3. 软合并和可选 embedding 判断;
  4. 多帧理解与跨摄像头事件关联;
  5. 隐私脱敏和用户反馈闭环。