Install
openclaw skills install kami-suspicious-personDetect unregistered faces loitering in sensitive areas. Runs continuously, outputs alarm JSON to stdout each time a stranger exceeds the loiter threshold, then keeps monitoring. No local GPU needed for face detection (CPU inference via insightface).
openclaw skills install kami-suspicious-personDetect unregistered face loitering events in sensitive areas. The script runs continuously and outputs an alarm JSON line to stdout each time a stranger exceeds the loiter threshold. It does NOT exit after an alarm — it keeps monitoring. Set run_time: 0 for unlimited operation.
Uses ONNX models directly (no insightface package dependency) — works on Linux, macOS, and Windows:
det_10g.onnx) for face detection + 5-point landmarksw600k_r50.onnx) for 512-dim face embedding extractionsample_interval seconds.loiter_threshold, output alarm JSON to stdout with face snapshot. After a cooldown period, the same stranger can trigger again if still present.Use this skill when the user wants to:
bash setup.sh
This will:
.venv/ virtual environmentonnxruntime, opencv-python-headless, numpyalerts/, face_db/, models/ directoriesdet_10g.onnx) and ArcFace (w600k_r50.onnx) models from the insightface release (~180MB total)Idempotent — safe to run repeatedly. Works on Linux and macOS.
python3 and python3-venv installed on the systemsetup.sh has been run at least onceface_db/<person_name>/xxx.jpgPlace registered personnel photos in the face_db/ directory:
face_db/
├── Alice/
│ ├── photo1.jpg
│ └── photo2.jpg
├── Bob/
│ └── photo1.jpg
└── face_db.pkl (auto-generated cache)
To pre-build the database cache:
.venv/bin/python build_face_db.py --face_db ./face_db
Before running this skill, confirm the following parameters with the user:
| Parameter | Default | Description |
|---|---|---|
--rtsp_url | (required) | RTSP camera URL or local video file path |
--face_db | face_db/ | Registered face database directory |
--det_model | models/det_10g.onnx | SCRFD face detection model path |
--rec_model | models/w600k_r50.onnx | ArcFace face recognition model path |
--db_match_threshold | 0.4 | Cosine similarity threshold for database matching |
--stranger_match_threshold | 0.35 | Cosine similarity threshold for cross-frame stranger tracking |
--loiter_threshold | 300 | Loitering alert threshold in seconds (300 = 5 minutes) |
--sample_interval | 2.0 | Face detection sampling interval (seconds) |
--cooldown | 300 | Per-stranger alert cooldown (seconds); same stranger won't re-alert within this window |
--det_thresh | 0.5 | Face detection confidence threshold |
--min_face_size | 40 | Minimum face size in pixels |
--output_dir | alerts/ | Alert output directory |
--run_time | 0 | Max run time in seconds; 0 = unlimited |
--fps | 15 | Video stream frame rate |
--expire_seconds | 600 | Stranger tracking expiry (seconds since last seen) |
--inbox_file | alerts/pending.jsonl | Alarm inbox file consumed by the LLM heartbeat task |
--feishu_webhook | (env FEISHU_WEBHOOK_URL) | Feishu custom bot webhook URL — alarms are pushed directly to the user's phone |
--feishu_secret | (env FEISHU_WEBHOOK_SECRET) | Feishu webhook signing secret (only if the bot has signing enabled) |
Ask the user: do any parameters need to be changed?
Create a Feishu custom bot (自定义机器人) in the target group chat, copy its webhook URL, then either:
export FEISHU_WEBHOOK_URL="https://open.feishu.cn/open-apis/bot/v2/hook/xxxxxxxx"
# Optional, only if the bot has "签名校验" enabled:
export FEISHU_WEBHOOK_SECRET="your_secret_here"
Or pass --feishu_webhook / --feishu_secret on the CLI. When set, every alarm is POSTed to Feishu as an interactive card (title / stranger_id / duration / timestamp / snapshot path).
# Initialize environment (first time only)
bash setup.sh
# (Optional) Pre-build face database
.venv/bin/python build_face_db.py --face_db ./face_db
# Run with RTSP stream
.venv/bin/python suspicious_person_detector.py \
--rtsp_url rtsp://192.168.1.100/live/stream1
# Run with local video file (for testing)
.venv/bin/python suspicious_person_detector.py \
--rtsp_url /path/to/test_video.mp4
# Custom parameters
.venv/bin/python suspicious_person_detector.py \
--rtsp_url rtsp://192.168.1.100/live/stream1 \
--loiter_threshold 60 \
--sample_interval 1.0 \
--cooldown 120
The skill runs continuously and prints a JSON alarm line to stdout each time a stranger loitering event is detected. It does NOT stop after an alarm — it resets and keeps monitoring.
When alarm triggers:
{
"alarm": true,
"type": "stranger_loitering",
"timestamp": "2025-01-15T14:30:22.123456",
"stranger_id": "STR_0001",
"duration_seconds": 312.5,
"duration_display": "5m12s",
"face_image": "alerts/STR_0001_20250115_143022.jpg",
"hit_count": 48,
"message": "Warning: Stranger STR_0001 detected loitering in sensitive area for 5m12s, exceeding alert threshold. Face snapshot saved to alerts/STR_0001_20250115_143022.jpg. Please review and take appropriate action."
}
When no alarm (normal exit after run_time or stream end):
{
"alarm": false,
"type": null,
"detail": "No stranger loitering detected",
"run_seconds": 3600.0,
"source": "rtsp://192.168.1.100/live/stream1"
}
| Code | Meaning |
|---|---|
0 | Run time exceeded or stream ended (normal exit) |
1 | Video source failure or runtime error |
The skill runs continuously and prints a JSON alarm line to stdout each time a stranger loitering event is detected. It does NOT stop after an alarm — it resets and keeps monitoring. Set run_time: 0 for unlimited operation.
Check suspicious_person.log for detailed diagnostics.
RTSP/File → FrameGrabber thread (latest frame only)
└─ Latest frame → main thread (every sample_interval)
├─ insightface: detect faces + extract embeddings
├─ FaceDatabase: match against registered faces
│ ├─ Registered → skip
│ └─ Stranger → StrangerTracker
│ ├─ Known stranger → update last_seen, avg embedding
│ └─ New stranger → create tracking record
└─ Check loiter duration ≥ threshold?
├─ No → continue monitoring
└─ Yes (and not in cooldown) → print alarm JSON to stdout
→ save face snapshot
→ continue monitoring (do NOT exit)
Virtual environment not found
→ Run bash setup.sh
insightface model download fails
→ Check network connectivity. Model is downloaded to models/ on first run.
No faces detected
→ Check --det_thresh (try lowering to 0.3), ensure faces are large enough (--min_face_size), verify camera resolution.
Too many false stranger alerts
→ Increase --db_match_threshold (e.g., 0.45), add more photos per person to face_db/.
Same stranger triggers repeated alerts
→ Increase --cooldown (e.g., 600 for 10-minute cooldown between alerts for the same person).
These rules are mandatory. The agent MUST comply with every rule below without exception.
When the user requests stranger loitering detection or monitoring, start the script immediately as a background daemon (e.g. nohup ... & or a systemd service) so the agent is NOT blocked waiting on a foreground process. Do NOT ask unnecessary follow-up questions if the RTSP URL is already known.
The detector is a long-running process. The agent CANNOT watch its stdout in real time (a blocking tool call never returns, and the LLM is only invoked on user messages / heartbeats / cron). Therefore alarms are delivered via two out-of-band channels:
alerts/pending.jsonl (for LLM context) — the script appends every alarm here. A heartbeat task defined in space/HEARTBEAT.md polls this file and reports unreported alarms to the user.The agent's job is NOT to tail stdout. The agent's job is to make sure the heartbeat task in HEARTBEAT.md is healthy and that Feishu env vars are set before launching.
On each heartbeat, the agent MUST follow space/HEARTBEAT.md:
kami-suspicious-person/alerts/pending.jsonlalerts/consumed/<timestamp>.jsonlHEARTBEAT_OKNever reply HEARTBEAT_OK on a heartbeat that has unreported alarms.
Before launching the detector, the agent MUST verify:
FEISHU_WEBHOOK_URL is set (or --feishu_webhook is passed). If not, warn the user that phone push is disabled.alerts/ directory is writable.space/HEARTBEAT.md still contains the stranger-alert inbox task.alerts/pending.jsonl has been consumed (or archive it before starting, to avoid re-reporting stale alarms).If the script exits, check the exit code:
0: normal end. Tell the user and ask if they want to restart.1: error. Report the error (include tail of suspicious_person.log).RULE: Alarms flow via (a) Feishu webhook (script → user) and (b) inbox file (script → heartbeat → user). Never rely on tailing stdout.
RULE: Every heartbeat consumes alerts/pending.jsonl; non-empty → proactive message; empty → HEARTBEAT_OK.
RULE: Consumed alarms are MOVED to alerts/consumed/, not deleted.
RULE: Launch the detector as a background daemon so the agent is never blocked.
RULE: Verify FEISHU_WEBHOOK_URL before launch; warn if missing.