From caa7adb27c76947309f20d4f6b84343dbce98686 Mon Sep 17 00:00:00 2001 From: 16337 <1633794139@qq.com> Date: Thu, 9 Apr 2026 10:00:56 +0800 Subject: [PATCH 1/4] =?UTF-8?q?=E6=96=B0=E5=A2=9E:=20=E9=9D=9E=E6=9C=BA?= =?UTF-8?q?=E5=8A=A8=E8=BD=A6=E8=BF=9D=E5=81=9C=E5=91=8A=E8=AD=A6=E7=B1=BB?= =?UTF-8?q?=E5=9E=8B=E5=92=8CVLM=E5=A4=8D=E6=A0=B8=E6=8F=90=E7=A4=BA?= =?UTF-8?q?=E8=AF=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/constants.py | 10 +++++++--- app/services/vlm_service.py | 12 ++++++++++++ 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/app/constants.py b/app/constants.py index 617d08f..d44ccd2 100644 --- a/app/constants.py +++ b/app/constants.py @@ -34,6 +34,7 @@ class AlarmType(str, Enum): INTRUSION = "intrusion" ILLEGAL_PARKING = "illegal_parking" VEHICLE_CONGESTION = "vehicle_congestion" + NON_MOTOR_VEHICLE_PARKING = "non_motor_vehicle_parking" ALARM_TYPE_NAMES: Dict[str, str] = { @@ -41,6 +42,7 @@ ALARM_TYPE_NAMES: Dict[str, str] = { AlarmType.INTRUSION: "周界入侵", AlarmType.ILLEGAL_PARKING: "车辆违停", AlarmType.VEHICLE_CONGESTION: "车辆拥堵", + AlarmType.NON_MOTOR_VEHICLE_PARKING: "非机动车违停", } # VLM 场景下的简短名称(用于截图分析提示词,尽量精炼) @@ -49,6 +51,7 @@ ALARM_TYPE_SHORT_NAMES: Dict[str, str] = { AlarmType.INTRUSION: "入侵", AlarmType.ILLEGAL_PARKING: "违停", AlarmType.VEHICLE_CONGESTION: "拥堵", + AlarmType.NON_MOTOR_VEHICLE_PARKING: "非机动车违停", } @@ -145,10 +148,11 @@ ALARM_LEVEL_NAMES: Dict[int, str] = { # 各算法的默认告警等级 ALARM_TYPE_DEFAULT_LEVEL: Dict[str, int] = { - AlarmType.INTRUSION: 1, # 重要 - AlarmType.LEAVE_POST: 2, # 普通 - AlarmType.ILLEGAL_PARKING: 2, # 普通 + AlarmType.INTRUSION: 1, # 重要 + AlarmType.LEAVE_POST: 2, # 普通 + AlarmType.ILLEGAL_PARKING: 1, # 重要(与 edge 端一致) AlarmType.VEHICLE_CONGESTION: 2, # 普通 + AlarmType.NON_MOTOR_VEHICLE_PARKING: 2, # 普通 } diff --git a/app/services/vlm_service.py b/app/services/vlm_service.py index bd7dd64..19942c3 100644 --- a/app/services/vlm_service.py +++ b/app/services/vlm_service.py @@ -19,6 +19,7 @@ VLM_TYPE_NAMES = { "intrusion": "周界入侵", "illegal_parking": "车辆违停", "vehicle_congestion": "车辆拥堵", + "non_motor_vehicle_parking": "非机动车违停", } # 算法类型 → VLM Prompt 模板 @@ -58,6 +59,17 @@ description要求:≤15字,直接说结论,注明大致车辆数。 告警成立示例:"约5辆车拥堵在路口" 误报示例:"车辆正常通行无拥堵" 仅输出JSON:{{"confirmed":true,"description":"..."}}""", + + "non_motor_vehicle_parking": """你是安防监控AI复核员。算法类型:非机动车违停检测,监控区域:{roi_name}。 +截图显示时间:{timestamp}。 +任务:判断图中是否有非机动车(自行车、电动车、摩托车等)违规停放在禁停区域。 +分析要点: +1. 是否存在非机动车(自行车、电动车、共享单车等) +2. 非机动车是否处于静止停放状态(而非骑行经过) +3. 是否在禁停区域/消防通道内 +4. 停放是否造成通道阻塞 + +请用JSON回复:{{"is_real": true/false, "confidence": 0.0-1.0, "reason": "判断依据"}}""", } # 通用降级 prompt(未知算法类型时使用) From f8b4b65ced905e475e61a83be0bf5b9268a1bba1 Mon Sep 17 00:00:00 2001 From: 16337 <1633794139@qq.com> Date: Thu, 9 Apr 2026 17:55:59 +0800 Subject: [PATCH 2/4] =?UTF-8?q?=E6=96=B0=E5=A2=9E:=20=E7=AE=97=E6=B3=95?= =?UTF-8?q?=E5=85=A8=E5=B1=80=E5=8F=82=E6=95=B0=E8=8F=9C=E5=8D=95=E9=A1=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/routers/yudao_auth.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/app/routers/yudao_auth.py b/app/routers/yudao_auth.py index b6804a4..b67985c 100644 --- a/app/routers/yudao_auth.py +++ b/app/routers/yudao_auth.py @@ -328,6 +328,18 @@ def _build_aiot_menus(): "visible": True, "keepAlive": True, }, + { + "id": 2030, + "parentId": 2000, + "name": "算法全局参数", + "path": "device/algorithm", + "component": "aiot/device/algorithm/index", + "componentName": "AiotDeviceAlgorithm", + "icon": "ep:setting", + "sort": 5, + "visible": True, + "keepAlive": True, + }, { "id": 2040, "parentId": 2000, From 33e272cbc704fed125172db6dc415defe2690077 Mon Sep 17 00:00:00 2001 From: 16337 <1633794139@qq.com> Date: Mon, 13 Apr 2026 10:21:33 +0800 Subject: [PATCH 3/4] =?UTF-8?q?=E4=BF=AE=E5=A4=8D:=20VLM=20=E9=9D=9E?= =?UTF-8?q?=E6=9C=BA=E5=8A=A8=E8=BD=A6=E8=BF=9D=E5=81=9C=20prompt=20?= =?UTF-8?q?=E5=8F=82=E6=95=B0=E7=BC=BA=E5=A4=B1=20+=20=E8=BF=94=E5=9B=9E?= =?UTF-8?q?=E6=A0=BC=E5=BC=8F=E5=85=BC=E5=AE=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 移除 non_motor_vehicle_parking prompt 中未传递的 {timestamp} 占位符 - 统一该算法的 prompt 输出格式为 confirmed/description(与其他算法一致) - 解析时兼容 is_real/reason 字段,防止旧版 prompt 或模型返回不一致 --- app/services/vlm_service.py | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/app/services/vlm_service.py b/app/services/vlm_service.py index 19942c3..06b8735 100644 --- a/app/services/vlm_service.py +++ b/app/services/vlm_service.py @@ -61,15 +61,18 @@ description要求:≤15字,直接说结论,注明大致车辆数。 仅输出JSON:{{"confirmed":true,"description":"..."}}""", "non_motor_vehicle_parking": """你是安防监控AI复核员。算法类型:非机动车违停检测,监控区域:{roi_name}。 -截图显示时间:{timestamp}。 任务:判断图中是否有非机动车(自行车、电动车、摩托车等)违规停放在禁停区域。 分析要点: 1. 是否存在非机动车(自行车、电动车、共享单车等) 2. 非机动车是否处于静止停放状态(而非骑行经过) 3. 是否在禁停区域/消防通道内 4. 停放是否造成通道阻塞 - -请用JSON回复:{{"is_real": true/false, "confidence": 0.0-1.0, "reason": "判断依据"}}""", +- confirmed=true:有非机动车违停(告警成立) +- confirmed=false:无非机动车违停(误报) +description要求:≤15字,直接说结论。 + 告警成立示例:"电动车违停在消防通道" + 误报示例:"该区域无非机动车违停" +仅输出JSON:{{"confirmed":true,"description":"..."}}""", } # 通用降级 prompt(未知算法类型时使用) @@ -184,13 +187,16 @@ class VLMService: content = content.strip() result = json.loads(content) + # 兼容不同 prompt 返回格式(is_real/reason vs confirmed/description) + confirmed = result.get("confirmed") if "confirmed" in result else result.get("is_real", True) + description = result.get("description") or result.get("reason", "") logger.info( - f"VLM 复核完成: confirmed={result.get('confirmed')}, " - f"desc={result.get('description', '')[:30]}" + f"VLM 复核完成: confirmed={confirmed}, " + f"desc={description[:30]}" ) return { - "confirmed": result.get("confirmed", True), - "description": result.get("description", ""), + "confirmed": confirmed, + "description": description, "skipped": False, } From 736b0a05d56b886118016a124b0aaca0cfa068a5 Mon Sep 17 00:00:00 2001 From: 16337 <1633794139@qq.com> Date: Wed, 15 Apr 2026 11:36:40 +0800 Subject: [PATCH 4/4] =?UTF-8?q?=E4=BF=AE=E5=A4=8D:=20=E6=97=A5=E6=8A=A5?= =?UTF-8?q?=E9=A6=96=E5=93=8D=E8=BF=87=E6=BB=A4=E4=B8=8A=E9=99=90=20360?= =?UTF-8?q?=E2=86=9260=20=E5=88=86=E9=92=9F=EF=BC=8C=E5=AE=8C=E7=BB=93?= =?UTF-8?q?=E8=BF=87=E6=BB=A4=E4=B8=8A=E9=99=90=2024h=E2=86=926h?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/services/daily_report_service.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/app/services/daily_report_service.py b/app/services/daily_report_service.py index 164a7d3..59f452b 100644 --- a/app/services/daily_report_service.py +++ b/app/services/daily_report_service.py @@ -193,11 +193,11 @@ async def _build_daily_report_data() -> Optional[Dict]: false_alarm_count += 1 if sec_ext.dispatched_time and sec_ext.confirmed_time: delta = (sec_ext.confirmed_time - sec_ext.dispatched_time).total_seconds() / 60.0 - if 0 <= delta <= 360: + if 0 <= delta <= 60: response_times.append(delta) if sec_ext.dispatched_time and sec_ext.completed_time: delta = (sec_ext.completed_time - sec_ext.dispatched_time).total_seconds() / 60.0 - if 0 <= delta <= 24 * 60: + if 0 <= delta <= 6 * 60: close_times.append(delta) clean_ext = clean_ext_map.get(order.id) @@ -207,11 +207,11 @@ async def _build_daily_report_data() -> Optional[Dict]: dispatch_time = clean_ext.first_dispatched_time or clean_ext.dispatched_time if dispatch_time and clean_ext.arrived_time: delta = (clean_ext.arrived_time - dispatch_time).total_seconds() / 60.0 - if 0 <= delta <= 360: + if 0 <= delta <= 60: response_times.append(delta) if dispatch_time and clean_ext.completed_time: delta = (clean_ext.completed_time - dispatch_time).total_seconds() / 60.0 - if 0 <= delta <= 24 * 60: + if 0 <= delta <= 6 * 60: close_times.append(delta) camera_counter = Counter()