Kami Video Search
/install kami-video-search
Kamivision Video Recorder & Search
An intelligent video surveillance skill that records RTSP/RTMP camera streams, generates AI descriptions for each video segment using the Kamivision API, and supports natural language search across recorded footage.
Capabilities
- Start recording — launch background stream recording
- Stop recording — stop background recording
- Check status — view recording process status
- Search video — find video clips by natural language description (with optional time range)
- List recent events — show recent non-static video events
- View logs — display recording logs for troubleshooting
Trigger Words (Intent Routing)
Match user intent using these trigger words first, then fall back to semantic understanding:
| Intent | Trigger Words (EN) | Trigger Words (ZH) |
|---|---|---|
| Start recording | start record, start recording, start camera, start monitor, start stream, launch recording | 开始录制,启动录制,启动监控,开始监控,启动摄像头 |
| Stop recording | stop record, stop recording, stop camera, stop monitor, stop stream, kill recording | 停止录制,关闭录制,停止监控,关闭监控,关闭摄像头 |
| Check status | recording status, camera status, monitor status, is recording, check status | 录制状态,监控状态,运行状态,查看状态 |
| Search video | search video, find video, find clip, look for, search footage, video search | 搜索视频,查找视频,找视频,视频搜索,搜索片段 |
| List recent | recent events, recent clips, list events, what happened, show recent | 最近事件,最近录制,最近发生了什么,查看事件 |
| View logs | show logs, view logs, recording logs, check logs | 查看日志,显示日志,录制日志 |
If no trigger word matches, use semantic understanding to route to the closest intent.
First-Time Setup Flow
IMPORTANT: On first use, you MUST complete the setup flow before any other operation.
DO NOT use system Python directly. This skill requires its own isolated virtual environment (.venv) in the skill directory.
Step 0: Setup Flow (Follow in Order)
Follow these steps in exact order. Do not skip ahead.
Step 0.1: Check System Python (MUST be >= 3.8)
First, check if Python 3.8+ is installed on the system:
python3 --version
If Python 3.8+ is NOT found (command fails or version is below 3.8):
Tell the user:
🐍 Python 3.8 or higher is required but was not found on your system.
Install Python:
- macOS:
brew install python3or download from https://www.python.org/downloads/- Ubuntu/Debian:
sudo apt update && sudo apt install python3 python3-pip- Windows: Download from https://www.python.org/downloads/ and check "Add to PATH"
After installing, run
python3 --versionagain to confirm.
DO NOT proceed until Python 3.8+ is confirmed.
Step 0.2: Check/Create Virtual Environment in Skill Directory
Once Python 3.8+ is confirmed, check if .venv exists in the skill directory:
ls -la {baseDir}/.venv/bin/python3
If .venv does NOT exist:
Tell the user:
📦 This skill needs its own isolated Python environment. I'll guide you to create it.
Step 1: Create the virtual environment
cd {baseDir} python3 -m venv .venvStep 2: Verify it was created
ls -la .venv/bin/You should see:
python,python3,pip,pip3,activate, etc.Step 3: Activate and install dependencies
source .venv/bin/activate pip install -r requirements.txtStep 4: Verify installation
.venv/bin/python -c "import numpy; import requests; import cv2; print('All dependencies OK')"If this prints "All dependencies OK", setup is complete!
DO NOT proceed until the user confirms .venv is created and dependencies are installed.
If .venv already exists:
- Verify the Python version:
{baseDir}/.venv/bin/python3 --version
- Verify dependencies are installed:
{baseDir}/.venv/bin/python -c "import numpy; import requests; import cv2; print('All dependencies OK')"
- If imports succeed, proceed to configuration.
- If imports fail, tell the user:
⚠️ Dependencies are missing or broken. Let's reinstall:
cd {baseDir} source .venv/bin/activate pip install -r requirements.txt
Pre-Recording Configuration
⚠️ MANDATORY: Before EVERY recording start, you MUST confirm all Required Parameters with the user.
DO NOT start recording without explicit user confirmation of these parameters. This is a mandatory step every single time, even if the values haven't changed since the last recording.
Mandatory Confirmation Flow
⚠️ CRITICAL: Display FULL detailed explanations for EVERY parameter. Do NOT use shortened summaries.
- Read current config from
{baseDir}/stream_config.json - Display ALL Required Parameters to the user with COMPLETE detailed explanations including:
- Current value (当前值)
- What it does / 作用: Full explanation of the parameter's purpose and how it's used in the code
- How to get it / 如何获取: Step-by-step guidance on obtaining or choosing the right value
- Format / 格式: Expected format with examples
- Range / 取值范围: Valid values and constraints
- Default / 默认值: If applicable
- Impact of different values / 不同值的影响: Detailed breakdown of how different values affect behavior, costs, storage, API usage, etc. Include scenarios and recommendations.
- Error behavior / 错误后果: What happens if configured incorrectly
- Ask for explicit confirmation (use the user's language):
- English: "Please confirm if the above configuration is correct. See the detailed explanations above for each parameter. Let me know if you need to modify any parameters."
- Chinese: "请确认以上配置是否正确?每个参数的详细说明见上文。如有需要修改的参数请告诉我。"
- Wait for user response:
- If user says "yes"/"确认"/"开始" → proceed to start recording
- If user provides new values → update config, then confirm again
- Only after confirmation → execute the start recording command
When displaying parameters, use this format for each:
Language Rule: Match the user's language. If the user writes in English, use English labels and explanations. If the user writes in Chinese, use Chinese labels and explanations.
English format:
📹 STREAM_URL = \x3Ccurrent_value>
What it does: \x3Cfull explanation>
How to get it: \x3Cstep-by-step guidance>
Format: \x3Cformat with examples>
Range: \x3Cvalid values>
Default: \x3Cdefault value if applicable>
Impact of different values: \x3Cdetailed breakdown with scenarios>
Error behavior: \x3Cconsequences>
Chinese format:
📹 STREAM_URL = \x3Ccurrent_value>
作用:\x3C完整解释>
如何获取:\x3C逐步指导>
格式:\x3C格式及示例>
取值范围:\x3C有效值>
默认值:\x3C默认值(如适用)>
不同值的影响:\x3C详细的场景分析和影响说明>
错误后果:\x3C配置错误的后果>
IMPORTANT: Use the complete parameter explanations from the "Required Parameters" section below. Do NOT summarize or shorten them. The user needs full context to make informed decisions.
Required Parameters (MUST confirm every time with full explanations)
Language Rule: Display parameter explanations in the same language the user uses.
- English users: Use English labels (What it does, Format, Range, Default, Error behavior) and English explanations.
- Chinese users: Use Chinese labels (作用,格式,取值范围,默认值,错误后果) and Chinese explanations.
English Version (for English-speaking users)
-
STREAM_URL (Camera stream address)
- What it does: The RTSP/RTMP stream URL of your camera. The code passes this directly to
cv2.VideoCapture(STREAM_URL)to open the video stream. It supports RTSP, RTMP, and HTTP protocols. - How to get it:
- Check your camera's admin web interface (usually at
http://camera-ip/) - Look for "RTSP URL" or "Stream URL" in camera settings
- Common formats: Hikvision
rtsp://user:pass@ip:554/Streaming/Channels/1, Dahuartsp://user:pass@ip:554/cam/realmonitor?channel=1&subtype=0 - Contact your camera manufacturer's support if unsure
- Check your camera's admin web interface (usually at
- Format:
rtsp://username:password@IP:port/path(e.g.,rtsp://admin:[email protected]:554/stream1) - Range: Any valid stream URL. Validate: MUST start with
rtsp://. Reject any other format and ask user to correct it. - Impact of different values:
- Correct URL with valid credentials → Smooth recording, stable frames
- Wrong IP/port → Connection timeout, recording fails after reconnection attempts
- Wrong credentials → Authentication failure, no video stream
- Unreachable camera (offline/network issue) → Same as wrong URL, recording stops
- Error behavior: If the URL is wrong or unreachable,
cv2.VideoCapturewill fail to open. After 100 consecutive frame read failures, the code treats it as a stream disconnection and attempts reconnection (up toMAX_RECONNECTtimes, default 3). If all reconnections fail, recording stops entirely.
- What it does: The RTSP/RTMP stream URL of your camera. The code passes this directly to
-
KAMI_API_KEY (Kamivision API key)
- What it does: Authentication key sent in the
X-API-KeyHTTP header when calling the Kamivisionhttps://kamiclaw-skill-api.kamihome.com/v1/detectAPI for video description generation and text embedding. Without it, all API calls will return authentication errors, and no descriptions or embeddings will be generated — video search will not work. - How to get it:
- Visit https://kamiclaw-skill.kamihome.com
- Sign up / log in to your Kamivision account
- Navigate to API Keys section
- Create a new key or copy existing one (starts with
sk_live_) - Keep this key secret — do not share publicly
- Format: String starting with
sk_live_(e.g.,sk_live_xxxxxxxxxxxxxxxx) - Impact of different values:
- Valid key → API calls succeed, video descriptions generated, search works
- Invalid/expired key → API returns 401/403, no descriptions generated, recording continues but search won't work
- Empty key → Same as invalid, all API calls fail immediately
- Rate limit exceeded → API returns 429, code retries per
KAMI_API_RETRY, may lose some descriptions
- Error behavior: If empty or invalid, the API returns a non-200 code. The code retries up to
KAMI_API_RETRYtimes (default 3). After all retries fail, aRuntimeErroris raised, the worker thread sendsSIGTERMto the process, and recording stops.
- What it does: Authentication key sent in the
-
DEVICE_ID (Camera identifier)
- What it does: A label for this camera. Used in three places: (1) video file naming pattern
{DEVICE_ID}_{date}_{time}_{index}.mp4, (2) storage directory structure{DATA_DIR}/{DEVICE_ID}/{date}/{hour}/, (3) thedevice_idfield in the SQLite index database. - How to get it:
- Choose a meaningful name for this camera location (e.g.,
front-door,living-room,warehouse-1) - If you have multiple cameras, use unique IDs for each (e.g.,
CAM-001,CAM-002) - Use existing camera name from your NVR/DVR system for consistency
- Choose a meaningful name for this camera location (e.g.,
- Default:
CAM-001 - Range: Any string that is valid as a file/directory name. Avoid special characters like
/,\,:,*,?,",\x3C,>,|. - Impact of different values:
- Descriptive name (e.g.,
front-door) → Easy to identify videos, organized file structure - Generic name (e.g.,
CAM-001) → Works fine, but less identifiable when managing multiple cameras - Changing DEVICE_ID later → Creates new subdirectory; old videos remain under old DEVICE_ID folder (manual migration needed if you want to consolidate)
- Special characters in name → File/directory creation fails, recording cannot start
- Descriptive name (e.g.,
- Error behavior: If it contains invalid filesystem characters,
mkdiror file creation will fail with anOSError, and recording cannot start.
- What it does: A label for this camera. Used in three places: (1) video file naming pattern
-
SKIP_STATIC (Skip static frames)
- What it does: Controls whether the AI description API is called for static (no-motion) video segments. Regardless of this setting, all video segments are always saved to disk. When
true, each segment is analyzed byis_static_video()— it samples frame pairs, converts to grayscale, and computes mean absolute pixel difference. If belowSTATIC_THRESHOLD, the segment is markedis_static=Truein the database, and the API call is skipped (no description or embedding generated). This means static segments are still recorded on disk but are invisible to search and list queries — they have no description or embedding, sosearchcannot match them andlist_recentexplicitly filters them out (not rec.get("is_static", False)). - Default:
true(recommended) - Range:
trueorfalse(boolean) - Impact of different values:
true(recommended for most cases):- ✅ Saves API costs — only sends clips with actual motion to Kamivision
- ✅ Faster search — fewer indexed entries, queries are quicker
- ⚠️ Video files are still saved to disk — no disk space savings on video storage
- ⚠️ Static segments are unsearchable — they exist on disk but cannot be found via search or list commands
- ⚠️ May miss subtle events — very slight movements might be classified as static and become unsearchable
- Best for: 24/7 recording, low-traffic areas, cost-conscious setups
false(describe everything):- ✅ Every segment gets a description and embedding — all clips are searchable
- ✅ Complete searchable timeline — no gaps in search results
- ❌ Higher API costs — every segment calls Kamivision API (including boring static footage)
- ❌ Larger database — more index entries, slightly slower search
- ⚠️ Disk usage is the same — video files are saved regardless
- Best for: High-security areas, short-term recording, unlimited API budget
- Error behavior: If set to a non-boolean value, Python's truthy evaluation applies — any non-empty string or non-zero number is treated as
true. Setting tofalsemeans every segment will be sent to the API, which increases API usage and database size (but not video disk usage).
- What it does: Controls whether the AI description API is called for static (no-motion) video segments. Regardless of this setting, all video segments are always saved to disk. When
-
STATIC_THRESHOLD (Motion sensitivity)
- What it does: The threshold used by
is_static_video()to decide if a video segment is "static". The function computesnp.mean(np.abs(frame1 - frame2))for sampled grayscale frame pairs (pixel values 0–255). If the average of all pair differences is below this threshold, the segment is considered static. - Default:
5.0 - Range:
0.0–255.0(theoretical). Practical range:1.0–20.0. - Only effective when:
SKIP_STATICistrue - Impact of different values:
- Very low (0.0 – 1.0):
- Almost nothing is skipped — even completely still scenes pass through
- API calls: Maximum (nearly 100% of segments sent)
- Use case: Maximum sensitivity, don't miss anything (e.g., monitoring delicate equipment)
- Downside: High API costs, defeats the purpose of SKIP_STATIC
- Low (1.0 – 5.0):
- Catches subtle changes — lighting shifts, leaves swaying, small animals
- API calls: High (70-90% of segments sent)
- Use case: Outdoor scenes with wind, areas with frequent minor movements
- Downside: May still send many "unimportant" clips
- Medium (5.0 – 15.0) — RECOMMENDED:
- Normal motion detection — people walking, vehicles passing, doors opening
- API calls: Moderate (20-50% of segments sent, depends on location)
- Use case: Indoor rooms, entrances, hallways, parking lots
- Best balance: Catches meaningful events while skipping empty periods
- High (15.0 – 50.0):
- Only significant motion — multiple people, fast vehicles, major activity
- API calls: Low (5-20% of segments sent)
- Use case: Very busy areas where you only care about major events
- Downside: May miss single-person entries or slow movements
- Very high (> 50.0):
- Only extreme motion — crowds, chaos, rapid movement
- API calls: Minimal (\x3C 5% of segments sent)
- Use case: Almost never recommended — you'll miss most events
- Downside: Essentially records only when there's intense activity
- Very low (0.0 – 1.0):
- Tuning tips:
- Start with
5.0, monitor for a day, check what got skipped - If too many false positives (static clips sent): increase by 2-3
- If missing important events: decrease by 2-3
- Different locations need different values — test and adjust
- Start with
- Error behavior: No validation in code. A negative value means nothing is ever static (all segments processed). An extremely high value (e.g., 200) means almost everything is skipped. Non-numeric values will cause a
TypeErrorcrash innp.mean()comparison.
- What it does: The threshold used by
-
DATA_DIR (Storage directory)
- What it does: Root directory for all recorded data. The code creates a hierarchical structure:
{DATA_DIR}/{DEVICE_ID}/{YYYYMMDD}/{HH}/for video files, and{DATA_DIR}/{DEVICE_ID}/index.dbfor the SQLite index. Directories are created automatically viaPath.mkdir(parents=True, exist_ok=True). - How to get it:
- Choose a directory with sufficient disk space
- For Linux/Mac:
/home/youruser/video_dataor/mnt/storage/videos - For Windows:
D:\video_dataorC:\Users\YourName\video_data - Use external drive or NAS for long-term storage
- Ensure the user running the recorder has write permissions
- Default:
./video_data(relative to working directory) - Range: Any valid filesystem path (absolute or relative). Ensure the path is writable and has sufficient disk space. Each 10-second clip at 640×360 is roughly 200KB–1MB.
- Impact of different values:
- Local disk (fast SSD):
- ✅ Fast write speeds, reliable recording
- ✅ Quick search and playback
- ⚠️ Limited capacity (check free space:
df -h) - Estimate: 1 hour ≈ 72-360 clips ≈ 15-360 MB (depends on motion)
- External HDD / NAS:
- ✅ Large capacity for long-term storage
- ⚠️ Slower write speeds — ensure stable network for NAS
- ⚠️ Network issues can interrupt recording
- Best for: Archive storage, multi-camera setups
- Small partition (\x3C 10 GB free):
- ⚠️ Will fill up quickly — monitor disk space
- With
RETENTION_DAYS=3: Should auto-clean, but verify regularly - Risk: If disk fills, recording produces corrupted files
- Changing DATA_DIR later:
- Old videos stay in old directory (manual migration needed)
- New recordings go to new directory
- Index database is per DEVICE_ID, so search only sees new videos
- Local disk (fast SSD):
- Disk space estimation:
- Per 10-second clip: ~200KB (static) to ~1MB (high motion)
- Per hour: ~72 clips × average size
- Example: 50% motion, 500KB avg → 72 × 500KB ≈ 36 MB/hour
- Per day: ~864 MB (continuous recording, moderate motion)
- With SKIP_STATIC=true: Can reduce by 50-80% in low-traffic areas
- Error behavior: If the path is not writable (permission denied),
Path.mkdir()orcv2.VideoWriter()raisesOSError/PermissionErrorand recording fails. If disk space runs out,cv2.VideoWriter.write()silently produces corrupted files (0 bytes), which are then skipped by the processing queue.
- What it does: Root directory for all recorded data. The code creates a hierarchical structure:
-
RETENTION_DAYS (Data retention period)
- What it does: Controls automatic cleanup of old recordings. A background thread runs
index.purge_expired(RETENTION_DAYS)every hour. It queries the SQLite database for records wherecreated_at \x3C (now - RETENTION_DAYS), deletes the corresponding video files from disk, removes the database records, and cleans up empty directories. After purging, it runsVACUUMto reclaim database file space. - Default:
3(days) - Range: Integer
>= 0. Set to0to disable auto-cleanup (keep forever). No upper limit, but very large values effectively disable cleanup. - Impact of different values:
- 0 (disable cleanup):
- ✅ Keep everything forever — complete archive
- ❌ Disk space grows indefinitely — will eventually fill up
- ❌ Database grows larger — search may slow down over time
- Use case: Short-term testing, external backup system handles cleanup
- ⚠️ WARNING: Monitor disk space manually!
- 1-2 days:
- ✅ Minimal disk usage — aggressive cleanup
- ❌ May lose important footage if you don't check daily
- ❌ Not enough time to review and export important clips
- Use case: High-motion areas, limited disk space, daily review habit
- 3-7 days — RECOMMENDED:
- ✅ Balanced approach — enough time to review and export
- ✅ Reasonable disk usage for most setups
- ✅ Weekly review cycle works well
- Use case: Most home/office security setups
- Estimate: 7 days × ~1 GB/day ≈ 7 GB per camera (moderate motion, SKIP_STATIC=true)
- 8-30 days:
- ✅ Extended review window — don't miss old events
- ❌ Higher disk usage — ensure adequate capacity
- ❌ Larger database — slightly slower search
- Use case: Compliance requirements, incident investigation, low-motion areas
- > 30 days:
- ✅ Long-term archive on local disk
- ❌ Significant disk space required
- ❌ Consider external storage or cloud backup instead
- Use case: Legal/compliance requirements, very low motion areas
- 0 (disable cleanup):
- Disk space planning examples (per camera, SKIP_STATIC=true, moderate motion):
- RETENTION_DAYS=3: ~3 GB
- RETENTION_DAYS=7: ~7 GB
- RETENTION_DAYS=14: ~14 GB
- RETENTION_DAYS=30: ~30 GB
- Multiply by number of cameras for total estimate
- Manual override: You can manually delete old videos anytime by removing files from
{DATA_DIR}/{DEVICE_ID}/— the database will auto-cleanup orphaned entries on next scan - Error behavior: Negative values behave the same as
0(no cleanup) because the cutoff time would be in the future. Non-integer values are used directly intimedelta(days=...)— floats work (e.g.,0.5= 12 hours), but strings cause aTypeErrorcrash.
- What it does: Controls automatic cleanup of old recordings. A background thread runs
中文版本 (for Chinese-speaking users)
-
STREAM_URL (摄像头流地址)
- 作用:摄像头的 RTSP/RTMP 视频流地址。代码将此值直接传给
cv2.VideoCapture(STREAM_URL)来打开视频流,支持 RTSP、RTMP、HTTP 协议。 - 如何获取:
- 查看摄像头管理后台(通常访问
http://摄像头 IP/) - 在摄像头设置中查找"RTSP URL"或"流地址"
- 常见格式:海康威视
rtsp://user:pass@ip:554/Streaming/Channels/1,大华rtsp://user:pass@ip:554/cam/realmonitor?channel=1&subtype=0 - 如不确定,联系摄像头厂商客服
- 查看摄像头管理后台(通常访问
- 格式:
rtsp://用户名:密码@IP:端口/路径(如rtsp://admin:[email protected]:554/stream1) - 取值范围:任何有效的流地址。校验:必须以
rtsp://开头,否则拒绝并要求用户修正。 - 不同值的影响:
- 正确的 URL + 有效凭证 → 录制流畅,帧稳定
- 错误的 IP/端口 → 连接超时,重连尝试后录制失败
- 错误的凭证 → 认证失败,无视频流
- 摄像头不可达(离线/网络问题)→ 同错误 URL,录制停止
- 错误后果:如果地址错误或不可达,
cv2.VideoCapture将无法打开。连续 100 帧读取失败后,代码判定为断流并尝试重连(最多MAX_RECONNECT次,默认 3 次)。重连全部失败则录制彻底停止。
- 作用:摄像头的 RTSP/RTMP 视频流地址。代码将此值直接传给
-
KAMI_API_KEY (Kamivision API 密钥)
- 作用:调用 Kamivision
https://kamiclaw-skill-api.kamihome.com/v1/detectAPI 时通过X-API-KeyHTTP 头发送的认证密钥,用于生成视频描述和文本向量。没有它,所有 API 调用都会返回认证错误,不会生成描述和向量,视频搜索功能将不可用。 - 如何获取:
- 访问 https://kamiclaw-skill.kamihome.com
- 注册/登录 Kamivision 账户
- 进入 API Keys 页面
- 创建新密钥或复制现有密钥(以
sk_live_开头) - 保密此密钥——不要公开分享
- 格式:以
sk_live_开头的字符串(如sk_live_xxxxxxxxxxxxxxxx) - 不同值的影响:
- 有效密钥 → API 调用成功,生成视频描述,搜索功能正常
- 无效/过期密钥 → API 返回 401/403,无描述生成,录制继续但搜索不可用
- 空密钥 → 同无效,所有 API 调用立即失败
- 超出速率限制 → API 返回 429,代码按
KAMI_API_RETRY重试,可能丢失部分描述
- 错误后果:如果为空或无效,API 返回非 200 状态码。代码会重试
KAMI_API_RETRY次(默认 3 次)。全部重试失败后抛出RuntimeError,工作线程向进程发送SIGTERM,录制停止。
- 作用:调用 Kamivision
-
DEVICE_ID (摄像头标识)
- 作用:摄像头的标签名。用于三个地方:(1) 视频文件命名
{DEVICE_ID}_{日期}_{时间}_{序号}.mp4,(2) 存储目录结构{DATA_DIR}/{DEVICE_ID}/{日期}/{小时}/,(3) SQLite 索引数据库中的device_id字段。 - 如何获取:
- 为摄像头位置选择一个有意义的名称(如
front-door大门,living-room客厅,warehouse-11 号仓库) - 如有多个摄像头,为每个使用唯一 ID(如
CAM-001,CAM-002) - 为保持一致性,可使用 NVR/DVR 系统中现有的摄像头名称
- 为摄像头位置选择一个有意义的名称(如
- 默认值:
CAM-001 - 取值范围:任何可作为文件/目录名的字符串。避免
/、\、:、*、?、"、\x3C、>、|等特殊字符。 - 不同值的影响:
- 描述性名称(如
front-door)→ 易于识别视频,文件结构清晰 - 通用名称(如
CAM-001)→ 可用,但管理多个摄像头时不易识别 - 后期更改 DEVICE_ID → 创建新子目录;旧视频保留在旧 DEVICE_ID 文件夹下(如需合并需手动迁移)
- 名称含特殊字符 → 文件/目录创建失败,录制无法启动
- 描述性名称(如
- 错误后果:如果包含非法文件系统字符,
mkdir或文件创建会抛出OSError,录制无法启动。
- 作用:摄像头的标签名。用于三个地方:(1) 视频文件命名
-
SKIP_STATIC (跳过静止画面)
- 作用:控制是否对静止(无运动)视频片段调用 AI 描述 API。无论此设置如何,所有视频片段都会保存到磁盘。 设为
true时,每个片段会经过is_static_video()分析——抽取帧对,转为灰度图,计算像素差均值。如果低于STATIC_THRESHOLD,该片段在数据库中标记为is_static=True,跳过 API 调用(不生成描述和向量)。这意味着静止片段仍然录制在磁盘上,但搜索和列表查询无法找到它们——因为没有描述和向量,search无法匹配,list_recent也会显式过滤掉(not rec.get("is_static", False))。 - 默认值:
true(推荐) - 取值范围:
true或false(布尔值) - 不同值的影响:
true(推荐大多数情况):- ✅ 节省 API 费用——只发送有实际运动的片段给 Kamivision
- ✅ 搜索更快——索引条目更少,查询更迅速
- ⚠️ 视频文件仍然保存到磁盘——不节省视频存储空间
- ⚠️ 静止片段不可搜索——它们存在于磁盘上,但无法通过搜索或列表命令找到
- ⚠️ 可能错过细微事件——非常轻微的运动可能被判定为静止,从而变得不可搜索
- 适用场景:24/7 录制、低流量区域、注重成本的设置
false(为所有片段生成描述):- ✅ 每个片段都有描述和向量——所有片段均可搜索
- ✅ 完整的可搜索时间线——搜索结果无空白
- ❌ API 费用更高——每个片段都调用 Kamivision API(包括无聊的静止画面)
- ❌ 数据库更大——更多索引条目,搜索稍慢
- ⚠️ 磁盘用量相同——无论如何视频文件都会保存
- 适用场景:高安全区域、短期录制、API 预算充足
- 错误后果:如果设为非布尔值,Python 的真值判断生效——任何非空字符串或非零数字都视为
true。设为false意味着每个片段都会调用 API,增加 API 用量和数据库大小(但不影响视频磁盘用量)。
- 作用:控制是否对静止(无运动)视频片段调用 AI 描述 API。无论此设置如何,所有视频片段都会保存到磁盘。 设为
-
STATIC_THRESHOLD (运动灵敏度)
- 作用:
is_static_video()函数用来判断片段是否"静止"的阈值, 值越大越容易被检测为静止片段, 越省费用。函数对抽样的灰度帧对计算np.mean(np.abs(frame1 - frame2))(像素值范围 0–255)。如果所有帧对差值的平均值低于此阈值,则判定为静止。 - 默认值:
5.0 - 取值范围:理论范围
0.0–255.0,实用范围1.0–20.0。 - 仅在以下条件生效:
SKIP_STATIC为true - 不同值的影响:
- 极低 (0.0 – 1.0):
- 几乎不跳过任何片段——即使完全静止的场景也会通过
- API 调用:最多(近 100% 片段发送)
- 适用场景:最高灵敏度,不要错过任何事(如监控精密设备)
- 缺点:API 费用高,违背 SKIP_STATIC 的初衷
- 低 (1.0 – 5.0):
- 捕捉细微变化——光线变化、树叶晃动、小动物
- API 调用:高(70-90% 片段发送)
- 适用场景:有风的户外场景、频繁轻微运动的区域
- 缺点:可能仍发送许多"不重要"的片段
- 中 (5.0 – 15.0) — 推荐:
- 正常运动检测——行人走动、车辆经过、门打开
- API 调用:中等(20-50% 片段发送,取决于位置)
- 适用场景:室内房间、入口、走廊、停车场
- 最佳平衡:捕捉有意义事件的同时跳过空闲时段
- 高 (15.0 – 50.0):
- 仅显著运动——多人、快速车辆、主要活动
- API 调用:低(5-20% 片段发送)
- 适用场景:非常繁忙的区域,只关心重大事件
- 缺点:可能错过单人进入或缓慢移动
- 极高 (> 50.0):
- 仅极端运动——人群、混乱、快速移动
- API 调用:最少(\x3C 5% 片段发送)
- 适用场景:几乎不推荐——会错过大多数事件
- 缺点:基本上只在有激烈活动时录制
- 极低 (0.0 – 1.0):
- 调优技巧:
- 从
5.0开始,监控一天,检查什么被跳过了 - 如果误报太多(静止片段被发送):增加 2-3
- 如果错过重要事件:减少 2-3
- 不同位置需要不同值——测试并调整
- 从
- 错误后果:代码中无校验。负值意味着没有片段被判为静止(全部处理)。极高值(如 200)意味着几乎所有片段被跳过。非数字值会在
np.mean()比较时引发TypeError崩溃。
- 作用:
-
DATA_DIR (存储目录)
- 作用:所有录制数据的根目录。代码创建层级结构:
{DATA_DIR}/{DEVICE_ID}/{YYYYMMDD}/{HH}/存放视频文件,{DATA_DIR}/{DEVICE_ID}/index.db存放 SQLite 索引。目录通过Path.mkdir(parents=True, exist_ok=True)自动创建。 - 如何获取:
- 选择有足够磁盘空间的目录
- Linux/Mac:
/home/你的用户名/video_data或/mnt/storage/videos - Windows:
D:\video_data或C:\Users\你的用户名\video_data - 长期使用可使用外接硬盘或 NAS
- 确保运行录制器的用户有写入权限
- 默认值:
./video_data(相对于工作目录) - 取值范围:任何有效的文件系统路径(绝对或相对)。确保路径可写且有足够磁盘空间。每个 640×360 的 10 秒片段约 200KB–1MB。
- 不同值的影响:
- 本地磁盘 (快速 SSD):
- ✅ 写入速度快,录制可靠
- ✅ 搜索和播放迅速
- ⚠️ 容量有限(检查可用空间:
df -h) - 估算:1 小时 ≈ 72-360 个片段 ≈ 15-360 MB(取决于运动量)
- 外接硬盘 / NAS:
- ✅ 大容量,适合长期存储
- ⚠️ 写入速度较慢——NAS 需确保网络稳定
- ⚠️ 网络问题可能中断录制
- 适用场景:归档存储、多摄像头设置
- 小分区 (\x3C 10 GB 可用):
- ⚠️ 会快速填满——需监控磁盘空间
- 使用
RETENTION_DAYS=3:应自动清理,但需定期检查 - 风险:磁盘满时,录制产生损坏文件
- 后期更改 DATA_DIR:
- 旧视频保留在旧目录(需手动迁移)
- 新录制进入新目录
- 索引数据库按 DEVICE_ID 分离,搜索只看到新视频
- 本地磁盘 (快速 SSD):
- 磁盘空间估算:
- 每 10 秒片段:~200KB(静止)到 ~1MB(高运动)
- 每小时:~72 个片段 × 平均大小
- 示例:50% 运动,平均 500KB → 72 × 500KB ≈ 36 MB/小时
- 每天:~864 MB(连续录制,中等运动)
- 使用 SKIP_STATIC=true:低流量区域可减少 50-80%
- 错误后果:如果路径不可写(权限不足),
Path.mkdir()或cv2.VideoWriter()抛出OSError/PermissionError,录制失败。如果磁盘空间耗尽,cv2.VideoWriter.write()会静默产生损坏文件(0 字节),处理队列会跳过这些文件。
- 作用:所有录制数据的根目录。代码创建层级结构:
-
RETENTION_DAYS (数据保留天数)
- 作用:控制旧录像的自动清理。后台线程每小时执行一次
index.purge_expired(RETENTION_DAYS)。它查询 SQLite 数据库中created_at \x3C (当前时间 - RETENTION_DAYS)的记录,删除对应的视频文件,移除数据库记录,并清理空目录。清理后执行VACUUM回收数据库文件空间。 - 默认值:
3(天) - 取值范围:整数
>= 0。设为0禁用自动清理(永久保留)。无上限,但极大值等同于禁用清理。 - 不同值的影响:
- 0(禁用清理):
- ✅ 永久保留所有内容——完整归档
- ❌ 磁盘空间无限增长——最终会填满
- ❌ 数据库变大——搜索可能随时间变慢
- 适用场景:短期测试、外部备份系统处理清理
- ⚠️ 警告:需手动监控磁盘空间!
- 1-2 天:
- ✅ 磁盘用量最小——激进清理
- ❌ 如不每天检查可能丢失重要片段
- ❌ 没有足够时间审查和导出重要片段
- 适用场景:高运动区域、磁盘空间有限、有每天审查习惯
- 3-7 天 — 推荐:
- ✅ 平衡方案——有足够时间审查和导出
- ✅ 对大多数设置来说磁盘用量合理
- ✅ 适合每周审查周期
- 适用场景:大多数家庭/办公室安防设置
- 估算:7 天 × ~1 GB/天 ≈ 7 GB 每摄像头(中等运动,SKIP_STATIC=true)
- 8-30 天:
- ✅ 延长审查窗口——不会错过旧事件
- ❌ 磁盘用量更高——确保容量充足
- ❌ 数据库更大——搜索稍慢
- 适用场景:合规要求、事件调查、低运动区域
- > 30 天:
- ✅ 本地磁盘长期归档
- ❌ 需要大量磁盘空间
- ❌ 建议改用外部存储或云备份
- 适用场景:法律/合规要求、极低运动区域
- 0(禁用清理):
- 磁盘空间规划示例(每摄像头,SKIP_STATIC=true,中等运动):
- RETENTION_DAYS=3: ~3 GB
- RETENTION_DAYS=7: ~7 GB
- RETENTION_DAYS=14: ~14 GB
- RETENTION_DAYS=30: ~30 GB
- 乘以摄像头数量得到总估算
- 手动覆盖:你可以随时手动删除旧视频——从
{DATA_DIR}/{DEVICE_ID}/移除文件,数据库会在下次扫描时自动清理孤立条目 - 错误后果:负值与
0行为相同(不清理),因为截止时间会在未来。非整数值直接传给timedelta(days=...)——浮点数可用(如0.5= 12 小时),但字符串会导致TypeError崩溃。
- 作用:控制旧录像的自动清理。后台线程每小时执行一次
Configuration Update
IMPORTANT: Only update config and start recording AFTER explicit user confirmation.
After the user confirms the parameters (or provides updated values):
- Read the current config from
{baseDir}/stream_config.json - Update only the confirmed/changed fields
- Write the updated config back
- Then proceed to execute the start recording command
Never skip the confirmation step or assume previous values are still valid.
Command Execution
All commands MUST use the virtual environment Python interpreter at {baseDir}/.venv/bin/python.
All commands use the script at {baseDir}/stream_recoder2.py with config at {baseDir}/stream_config.json.
Start Recording
{baseDir}/.venv/bin/python {baseDir}/stream_recoder2.py --config {baseDir}/stream_config.json --start-daemon --log-file {baseDir}/stream_recorder.log
After running, parse the JSON output:
- If
statusis"started": Tell user recording has started, show the PID - If
statusis"already_running": Tell user recording is already running, show the PID - On error: Show the error message and ask user to check their STREAM_URL and network
Stop Recording
{baseDir}/.venv/bin/python {baseDir}/stream_recoder2.py --config {baseDir}/stream_config.json --stop-daemon
Parse the JSON output:
- If
statusis"stopped": Confirm recording has stopped - If
statusis"not_running": Tell user there's no active recording to stop
Check Status
{baseDir}/.venv/bin/python {baseDir}/stream_recoder2.py --config {baseDir}/stream_config.json --status
Parse the JSON output and report the status to the user in a friendly way.
Search Video
The user may provide a search query in natural language, optionally with a time range.
Time range parsing:
- Natural language: "today morning", "yesterday afternoon", "this morning 8 to 12" → convert to
YYYY-MM-DD_HH:MM:SSformat - Fixed format: accept
YYYY-MM-DD_HH:MM:SSorYYYY-MM-DD HH:MM:SSdirectly - If no time range specified, search all recorded footage
{baseDir}/.venv/bin/python {baseDir}/stream_recoder2.py --config {baseDir}/stream_config.json --search "QUERY_TEXT" --json
With time range:
{baseDir}/.venv/bin/python {baseDir}/stream_recoder2.py --config {baseDir}/stream_config.json --search "QUERY_TEXT" --time-start "YYYY-MM-DD_HH:MM:SS" --time-end "YYYY-MM-DD_HH:MM:SS" --json
Parse the JSON output and present results to the user:
- Show the number of matches
- For each result: time, description, score, and full video file path
- If no results: tell the user no matching clips were found, suggest broadening the query or time range
List Recent Events
{baseDir}/.venv/bin/python {baseDir}/stream_recoder2.py --config {baseDir}/stream_config.json --list HOURS --json
Default HOURS to 24 if user doesn't specify. Parse and present results showing time, description, and video path.
View Logs
tail -100 {baseDir}/stream_recorder.log
Show the last 100 lines of the log file. If the user asks for more, increase the line count. If the log file doesn't exist, tell the user recording hasn't been started yet.
Error Handling
- STREAM_URL connection failure: Tell the user to check the camera address, network connectivity, and credentials. Do NOT retry automatically.
- Invalid API Key: Tell the user "Your Kamivision API key appears to be invalid. Please check it at https://kamiclaw-skill.kamihome.com". Do NOT retry.
- Python not found: Guide installation per Step 0 above.
- Virtual environment missing: Create
.venvper Step 0, then install dependencies. - Dependency missing: Re-run
{baseDir}/.venv/bin/pip install -r {baseDir}/requirements.txt. - Permission denied: Suggest running with appropriate permissions or changing DATA_DIR to a writable location.
Language
Respond in the same language the user uses. If the user writes in Chinese, respond in Chinese. If in English, respond in English.
For parameter explanations during recording setup:
- English users: Use the English version of all 7 parameter explanations with English labels (What it does, Format, Range, Default, Error behavior).
- Chinese users: Use the Chinese version of all 7 parameter explanations with Chinese labels (作用,格式,取值范围,默认值,错误后果).
- Do not mix languages — if the user asks in English, show all explanations in English; if in Chinese, show all in Chinese.
- 确保已安装 OpenClaw(本地或 Docker 部署)
- 在对话框中输入安装命令:
/install kami-video-search - 安装完成后,直接呼叫该 Skill 的名称或使用
/kami-video-search触发 - 根据 Skill 的参数说明提供必要输入,即可获得结构化输出
Kami Video Search 是什么?
RTSP/RTMP camera stream recording with AI-powered video search. Start/stop background recording, check status, search video clips by natural language descrip... 它是一个面向 Claude Code / OpenClaw 的 AI Agent Skill 插件,目前累计下载 27 次。
如何安装 Kami Video Search?
在 OpenClaw 或 Claude Code 对话框中运行命令「/install kami-video-search」即可一键安装,无需额外配置。
Kami Video Search 是免费的吗?
是的,Kami Video Search 完全免费,采用 MIT-0 许可证,可自由下载、安装和使用。
Kami Video Search 支持哪些平台?
Kami Video Search 跨平台运行,可在任意部署了 OpenClaw / Claude Code 的环境中使用(cross-platform)。
谁开发了 Kami Video Search?
由 KamiVision(@13681882136)开发并维护,当前版本 v1.0.0。