vss-search-archive について
vss-search-archiveは、NVIDIA VSSの動画アーカイブを自然言語で検索するスキルです。動画ファイルやRTSPストリームを取り込み、属性検索とCosmos Embed1のベクトル検索を組み合わせて該当箇所を探します。アルファ機能のため、本番利用は推奨されていません。
主な機能
- 01自然言語による動画アーカイブ検索
- 02動画ファイルとRTSPストリームの取り込み
- 03動画埋め込みの生成とインデックス化
- 04属性検索とベクトル検索の併用
- 05ソースと検索データの削除
活用シーン
- 01アーカイブから対象物や動作を検索
- 02時間や場所で動画区間を絞り込み
- 03動画やRTSPソースを検索対象に追加
- 04ソースと関連する埋め込みを削除
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ promptingcompany/ nv-skills --skill vss-search-archiveインストール後、Claude Code で/vss-search-archiveを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
BENCHMARK.mdskill-card.mdskill.oms.sigevals/evals.jsonsearch.jsonreferences/discovery_modes.mdtroubleshooting.md詳細
目的
アーカイブされた動画に対して最上位のVSSフュージョン検索を実行し、新しいクリップ/RTSPストリームを検索対象として取り込み、検索用に取り込んだソースを削除します。
前提条件
$HOST_IPから到達可能な、稼働中のVSSデプロイメント(vss-deploy-profileおよびreferences/を参照)。vss-manage-video-io-storageスキルがインストールされていること(検索前に動画ソースを一覧表示、管理するために使用します)。- イメージをプルする場合は、
$NGC_CLI_API_KEYと$NVIDIA_API_KEYにNGC認証情報が設定されていること。 - 呼び出し元で
curl、jq、Dockerを使用できること。
手順
以下のルーティングテーブルと手順ごとのワークフローに従ってください。workflow、quick start、または flow で終わる各セクションは、上から順に実行することを想定しています。詳細なリファレンス資料は references/ にあります。
使用例
実行可能なエンドツーエンドの例は evals/ にあります(各 *.json マニフェストに、実行可能なシナリオが含まれています)。また、ワークフローごとの以下の curl ブロックにも例が記載されています。nv-base validate <this-skill-dir> --agent-eval を実行すると、Tier-3評価でこれらを再現できます。
制限事項
- 対応するVSSプロファイル/マイクロサービスがデプロイされ、呼び出し元から到達可能である必要があります。
- NGCホストモデルとNIMには、レート制限、GPUメモリ要件、ライセンス制限が適用される場合があります。
- 同時実行数、GPUメモリ、ストレージの制限は、ホストのハードウェアとプロファイルのcomposeファイルによって異なります。
トラブルシューティング
- エラー:REST呼び出しで接続拒否が返される。原因:対象のマイクロサービスが実行されていません。解決策:
/docsまたは/healthを確認し、vss-deploy-profileまたは対応するvss-deploy-*スキルを使って再デプロイします。 - エラー:NGCからのプルでHTTP 401/403が返される。原因:
NGC_CLI_API_KEYが未設定、または期限切れです。解決策:docker login nvcr.ioを実行し、キーを再度エクスポートしてから再試行します。 - エラー:コンテナがOOMになる、またはモデルの読み込みに失敗する。原因:選択したプロファイルに対してGPUメモリが不足しています。解決策:より小さいバリアントに切り替えるか、
docker compose downでGPUを解放します。
動画検索ワークフロー
アルファ機能 — 本番環境での使用は推奨されません。
Cosmos Embed1の埋め込みを使用して、自然言語で動画アーカイブを検索します。検索プロファイルが必要です。vss-deploy-profile スキル(-p search)を使ってデプロイしてください。検索対象の動画ソースには、取り込んだファイルまたはRTSPストリームを指定できます。
使用する場面
- 「フォークリフトが映っている場面をすべて検索」
- 「立ち入り禁止区域に人が入ったのはいつですか?」
- 「荷積み場付近にいる人を表示」
- 「午前8時から正午までの間に車両を検索」
- 動画アーカイブを対象とした、自然言語による検索全般
- 「検索用に
<file>を取り込んで」/「検索用にこの動画をアップロードして」 - 「このRTSPストリームを検索用に追加して」/「検索用に
<rtsp_url>を登録して」 - 「検索対象から
<file>を削除して」/「この動画と埋め込みを削除して」
デプロイの前提条件
このスキルを使用するには、ホスト上の $HOST_IP でVSSの search プロファイルが稼働している必要があります。リクエストを実行する前に、次の手順を行ってください。
-
スタックを確認します。
curl -sf --max-time 5 "http://${HOST_IP}:8000/docs" >/dev/null \ && curl -sf --max-time 5 "http://${HOST_IP}:9200/" >/dev/null(2つ目の確認では、Elasticsearchが稼働していることを確認します。これはsearchプロファイル固有です。)
-
確認に失敗した場合は、ユーザーに尋ねます。
「VSSの
searchプロファイルが$HOST_IPで稼働していません。/vss-deploy-profileスキルを-p searchで使用して、今すぐデプロイしますか?」- はいの場合 →
/vss-deploy-profileスキルに引き継ぎます。デプロイに成功したら、ここに戻ります。 - いいえの場合 → 中止します。スタックが存在しない、または誤ったプロファイルである状態で、このスキルを実行しないでください。
(呼び出し元から自律的なデプロイの明示的な事前承認を受けている場合 (たとえば、リクエストに「前提条件のデプロイは事前承認済み」とある場合や、 その権限を持つ非対話型の評価ハーネスで実行している場合)は、確認を省略して
/vss-deploy-profileを直接呼び出してください。) - はいの場合 →
-
確認に成功した場合は、続行します。
取り込みの前提条件(/generate の実行前に必須)
ソースを検索可能にするには、VIOS のみを使用するのではなく、VSS エージェントのバックエンド経由で取り込む必要があります。エージェントの取り込みルートは、VIOS へのアップロード、RTVI-CV への登録、RTVI-embed のパイプラインを1つのトランザクションとして実行します。VIOS への単独の PUT ではデータが保存されるだけで、Elasticsearch への登録は行われません。
まず、ソースが VIOS に存在することを確認してください(必須ワークフローのステップ2)。存在しない場合は、/generate を実行する前に、以下のいずれかの手順で取り込んでください。取り込みが成功すると、指定した名前で sensor/list にソースが表示され、エージェントが検索ツールの分解器に渡す自然言語クエリから参照できるようになります。構造化された video_sources ペイロードを自分で作成する必要はありません。
ファイルのアップロード — 汎用の3ステップフロー
以下のタイムスタンプ付きアップロードフォームを使用してください。VSS エージェント/検索プロファイルでは、アップロードした video_file の基準タイムスタンプとして
2025-01-01T00:00:00.000Z を使用します。
VIOS ストレージと埋め込みは同じタイムラインを共有する必要があります。共有されていない場合、スクリーンショット URL や critic のフレーム取得に失敗することがあります。
FILENAME="<filename.mp4>"
FILE_PATH="/path/to/${FILENAME}"
# 1. Ask the agent for the chunked-upload URL
UPLOAD_URL=$(curl -s -X POST "http://${HOST_IP}:8000/api/v1/videos" \
-H "Content-Type: application/json" \
-d "{\"filename\":\"${FILENAME}\"}" | jq -r .url)
# 2. Chunked POST the file to that VST URL (nvstreamer protocol).
# The final-chunk response carries sensorId.
IDENTIFIER=$(uuidgen 2>/dev/null || cat /proc/sys/kernel/random/uuid)
UPLOAD_RESPONSE=$(curl -s -X POST "${UPLOAD_URL}" \
-H "nvstreamer-chunk-number: 1" \
-H "nvstreamer-total-chunks: 1" \
-H "nvstreamer-is-last-chunk: true" \
-H "nvstreamer-identifier: ${IDENTIFIER}" \
-H "nvstreamer-file-name: ${FILENAME}" \
-F "mediaFile=@${FILE_PATH};filename=${FILENAME}" \
-F "filename=${FILENAME}" \
-F 'metadata={"timestamp":"2025-01-01T00:00:00"}')
# 3. Tell the agent the upload finished — this fans out to RTVI-CV + RTVI-embed
SENSOR=$(printf '%s' "${UPLOAD_RESPONSE}" | jq -r .sensorId)
[ -z "${SENSOR}" ] || [ "${SENSOR}" = "null" ] \
&& { echo "Upload failed: no sensorId in response: ${UPLOAD_RESPONSE}"; exit 1; }
printf '%s' "${UPLOAD_RESPONSE}" \
| jq --arg filename "${FILENAME}" '. + {filename: $filename}' \
| curl -s -X POST "http://${HOST_IP}:8000/api/v1/videos/${SENSOR}/complete" \
-H "Content-Type: application/json" \
-d @- | jq .
/complete の応答を待ってください(埋め込みが反映されると chunks_processed > 0 が返されます)。動画が検索可能になるのは、その後です。
非推奨の
PUT /api/v1/videos-for-search/{filename}ルートも、従来の呼び出し元向けに接続されています(エージェント主導の単一リクエスト方式)が、OpenAPI エントリーにはdeprecatedのフラグが付いています。新しい実装では、上記の3ステップフローを使用してください。
RTSP ストリーム — 単一エンドポイント
curl -s -X POST "http://${HOST_IP}:8000/api/v1/rtsp-streams/add" \
-H "Content-Type: application/json" \
-d '{
"sensorUrl": "rtsp://<host>:<port>/<path>",
"name": "<sensor-name>",
"username": "",
"password": "",
"location": "",
"tags": ""
}' | jq .
応答の形式は {status, message, error} で、sensorId は含まれません(エージェントは指定した name でストリームを識別します)。いずれかのステップが失敗すると、それ以前のステップはロールバックされます。start_embedding_generation ステップは、リクエストを受け付けて埋め込みパイプラインがバックグラウンドで実行中であることを確認するものであり、ストリームがすでに検索可能になったことを示すものではありません。Elasticsearch に十分な数のチャンクが反映されてから検索結果が表示され始めます。準備完了の目安が必要な場合は、数秒後に top_k を小さくしてクエリを実行し、状態を確認してください。
ソースの削除 — エージェント経由のクリーンアップ
VIOS ストレージと検索用埋め込みをまとめてクリーンアップするため、VIOS への単独のリクエストではなく、エージェントのバックエンド経由で削除してください。
# For video files: video_id is the VIOS sensor/video UUID
curl -s -X DELETE "http://${HOST_IP}:8000/api/v1/videos/<video_id>" | jq .
# For RTSP streams: name is the registered source name
curl -s -X DELETE "http://${HOST_IP}:8000/api/v1/rtsp-streams/delete/<name>" | jq .
検索の仕組み
- 取り込み — ファイルはエージェントの汎用3ステップフローから、RTSP ストリームは
/api/v1/rtsp-streams/addから取り込みます。どちらのルートもソースを RTVI-CV(属性検出)と RTVI-Embed(Cosmos Embed1)に渡し、動画セグメントのベクトル埋め込みを生成します。 - インデックス作成 — 埋め込みは Kafka パイプライン経由で Elasticsearch に保存されます。
- クエリ — 自然言語クエリが埋め込まれ、類似度に基づいて保存済みのベクトルと照合されます。
- 結果 — 関連度順に並べられたタイムスタンプ付き動画セグメントと、クリップの再生リンクが返されます。
VSS エージェントがオーケストレーションするこの検索では、次の3つの動作が発生します。
- 属性のみ:LLM がクエリを分解した結果、動作はなく、外見の属性のみが見つかった場合(例:「赤いジャケットを着た人」)
- 埋め込みのみ:クエリから抽出可能な属性がない場合(例:「フォークリフトを見せて」)
- 融合:クエリに動作と属性の両方がある場合(例:「赤いジャケットを着て走っている人」)、まず埋め込み検索を実行し、その後、属性検索を使って再ランキングします
必須のワークフロー
このスキルを使用する際は、必ず次の大まかなワークフローに従ってください。
-
ユーザーの指示から入力を解決します —
$HOST_IPが明示されていない場合は、処理を必ず停止してください。 下記の「入力の解決」を参照してください。localhost、127.0.0.1、エージェント自身の実行ホスト、その他の推測値を既定値として使用しないでください。ユーザーからエンドポイントの提示があるまで、POST http://.../generateリクエストを送信しないでください。HOST_IP/VSS エージェントのエンドポイントを尋ねる質問を 1 つだけユーザーに返し、回答を待ってください。 -
ソースを解決します —
/generateを呼び出す前に、必ず処理を停止してください。 ユーザーのクエリで特定の動画/センサー名(例:「空港の動画」、「warehouse_cam_3」、「サンプル倉庫」)が指定されている場合は、POST .../generateを実行する前に、VIOS に実際に登録されていることを確認してください。vss-manage-video-io-storageスキルを使ってソースを一覧表示してください。次に、以下のように進めてください。
- 指定されたソース(または明確に部分一致する名前)が一覧にある場合 → ステップ 3 に進みます。ユーザーの自然言語クエリをそのまま転送してください。エージェント自身の検索ツールの分解器(
services/agent/src/vss_agents/tools/search.py)が、利用可能なソースに基づいて文章からvideo_sourcesを抽出するため、このスキルで構造化されたvideo sourcesペイロードを作成する必要はありません。 - 指定されたソースが一覧にない場合 → 停止してください。調査目的で
/generateを実行しないでください。登録済みのソース名をユーザーに提示し、そのうちのいずれを指していたのか、不足しているソースを取り込みたいのか(取り込みの前提条件を案内し、該当するファイルまたは RTSP の手順を VIOS 単体ではなくエージェントバックエンド経由で実行してください)、それともクエリを中止するのかを尋ねてください。回答を待ってください。 - クエリで特定のソース名が指定されていない場合(「取り込み済みの動画からフォークリフトを探す」、「すべてのソースを横断して検索する」など)→ 部分一致の確認は省略します。ただし、
sensor/listが空でないことは必ず確認してください(空の場合、ソースが取り込まれていないため、処理を必ず停止してください)。
- 指定されたソース(または明確に部分一致する名前)が一覧にある場合 → ステップ 3 に進みます。ユーザーの自然言語クエリをそのまま転送してください。エージェント自身の検索ツールの分解器(
-
選択した方法で検索を実行します。
-
検索結果をユーザーのクエリに対して提示します。回答は専門的な検査レポートとして構成し、タイトルは
Video Search Resultsとしてください。 — 明確なセクション見出しを使用します。- 詳細な根拠を添えて、個々の結果を整理し、最後に概要をまとめます。
- 比較に役立つ場合は表を使用します。チャットメッセージではなく、技術レポートとして記述してください。
- criteria の結果が null でない場合は、列「Critic result」(「confirmed」 | 「rejected」 | 「skipped」)に加えて、この検索結果に対するすべての criteria を示す列「Criteria」({criteria_n}: ✓ | ✗)を含めてください。
-
重要:結果を検証し、その内容を簡潔にユーザーに説明してください。 検索が失敗した場合、または予期しない結果(ユーザーのクエリに一致していないと思われる動画、該当結果なし、動画の返却なし、エラーなど)が返された場合は、停止してください。結果が適切に見つかり、専門的な検査レポートとして提示されるまで、troubleshooting.md を読んで、フィードバックループを繰り返してください。
-
最終確認:
- 必ず、最終確認および追加の確認を実行できることをユーザーに伝えてください。これを
Verification Stepとして提示してください。 - ユーザーが同意した場合に限り、検索結果(JSON results)から最も有力な候補(一致度スコアが最も高いもの)の
screenshot_urlを使ってスクリーンショットを/tmpにダウンロードしてください。スクリーンショットを読み込み、ユーザーのクエリに対応しているかを確認してください。
- 必ず、最終確認および追加の確認を実行できることをユーザーに伝えてください。これを
入力の解決
これらの入力は会話またはユーザーのクエリからのみ推測してください(ユーザーから提供された場合を除き、その他のファイルは使用しないでください)。推測できない項目がある場合は、すぐにユーザーに尋ねてください。
- $HOST_IP:VSS エージェントのバックエンドが実行されている場所
注意事項
- 予期しないことが起きた場合は、必ずすぐにワークフローのトラブルシューティング手順に進み、troubleshooting.mdを読んでください。
- クエリは具体的な視覚的描写(物体、動作、場所)を含めると最適に機能します。必要に応じてユーザーのクエリを補足し、質問の質を高めるために詳細を広げてください。
- このスキルは、動画ソースがエージェントのバックエンド経由ですでに取り込まれていることを前提としています(取り込みの前提条件を参照)。ユーザーが明示的に依頼した場合(「検索用に
<file>を取り込む」、「検索用に<rtsp_url>を追加する」)、エージェントを使用した取り込みレシピを実行することがあります。ユーザーが指定していないファイルをローカルファイルシステムから検索することはなく、VIOS の PUT パスをそのまま使用することもありません(埋め込みは生成されません)。ワークフローのステップ 2 では、/generateの前提条件として「このソースが VIOS に存在する」ことの確認を引き続き必須とします。 vss-query-analyticsスキルを使用して、検索結果をインシデント/アラートデータと照合してください。
REST API 経由で検索する
ユーザーが別の方法を指定しない限り、デフォルトではこの REST API の方法を使用してください。
# Consider only ingested video file sources by default
curl -s -X POST http://${HOST_IP}:8000/generate \
-H "Content-Type: application/json" \
-d '{"input_message": "find all instances of forklifts"}' | jq .
その他の例
# Search by object
curl -s -X POST http://${HOST_IP}:8000/generate \
-H "Content-Type: application/json" \
-d '{"input_message": "find vehicles in the parking lot"}' | jq .
# Search by action
curl -s -X POST http://${HOST_IP}:8000/generate \
-H "Content-Type: application/json" \
-d '{"input_message": "show me people running"}' | jq .
# Search by time context
curl -s -X POST http://${HOST_IP}:8000/generate \
-H "Content-Type: application/json" \
-d '{"input_message": "what happened at the entrance between 2pm and 3pm?"}' | jq .
# Consider only RTSP sources with `search_source_type` filter i.e. live camera streams
curl -s -X POST http://${HOST_IP}:8000/generate \
-H "Content-Type: application/json" \
-d '{"input_message": "find all instances of forklifts", "search_source_type": "rtsp"}' | jq .
高度な制御オプション
ユーザーのクエリが曖昧な場合、ユーザーが詳しいガイダンスを求めている場合、または詳細な制御が必要な場合は、特定のオプションをプレーンテキストで明示してエージェントを希望する方向に導き、ユーザーのinput_messageを補足してください。利用可能な制御項目は次のとおりです。
| 項目 | 型 | デフォルト | 説明 |
|---|---|---|---|
video sources | string[] | null | 特定のカメラまたはセンサー名に絞り込みます |
top k | int | 10 | 最大結果数 |
minimum similarity | float | 0.0 | 類似度の最小しきい値。ノイズを除外するには値を上げます(例:0.3) |
critic usage | bool | true | VLM が各結果を検証し、誤検出を除外します |
description | string | null | メタデータが利用可能な場合、カメラのメタデータ(例:場所、カテゴリ)で絞り込みます |
これらの調整オプションから必要なものを選び、ユーザーの状況やクエリに応じて調整してください。 これらを活用した検索モードの例については、discovery_modes.mdを参照してください。
エージェント UI 経由で検索する
http://${HOST_IP}:3000/を開き、自然言語のクエリを入力します。
find all instances of forklifts
show me people near the loading dock
when did a truck arrive at the gate?
find someone wearing a red jacket
結果には、タイムスタンプ付きのクリップと類似度スコアが含まれます。
bump:2