← Back to all work
Video search · Security · Company withheld

Natural-language search over CCTV archives, with an honest "nothing found".

An operator types a plain-language query and gets the right seconds of footage back, or an empty result instead of plausible garbage. Four LLM roles, three open models, one config file. The expensive model runs once at index time; search only pays for light text calls.

Security · Video analytics · Company withheld

Every frame becomes a paragraph. Every paragraph becomes searchable.

A vision-language model writes one dense description per frame or segment: every vehicle with type, exact colour and plate, every person, actions, crash signs. That paragraph is the searchable document. Everything downstream (embeddings, rewriting, reranking) works on text, so the GPU-heavy step is paid once per frame, not once per query.

In production
01 · VLM describer Qwen3-VL-8B vLLM on H200

Writes one paragraph per frame or segment: cars, people, actions, incidents. This is the indexed document.

02 · Embeddings Qwen3-Embedding-4B 2560-dim · cosine

Dense vectors for documents and queries. The instruct prefix is applied to queries only, never to stored text.

03 · Query rewriter Text LLM same language in, same language out

Expands the operator's query with visual synonyms so "truck" also finds "lorry" and "HGV" descriptions.

04 · Reranker Qwen3-Reranker-4B cross-encoder · cutoff 0.08

Scores candidates against the original query and cuts at a threshold. This is what produces "nothing found" instead of the nearest-looking wrong answer.

RewriteVisual synonyms EmbedQuery vector QdrantRecall ×5 candidates Rerank + cutoffDrop below 0.08 Temporal NMSMerge hits, return top_k
Production index

20,000 recordings, 10 million indexed segments

reranker score, top hit
Operator query Result Score
"truck at a junction" Correct clip 0.997
"road accident" Correct clip 0.991
"crowd gathering" Correct clip 0.739

Operators query in their own language. Queries shown translated to English; the rewriter keeps the query language unchanged.

Flow 1 · Events

Kafka → S3 → VLM → Qdrant

Face, plate, situational-analytics, sabotage and abandoned-object events arrive on Kafka. The matching frames are pulled from S3, described and written to the events collection.

Flow 2 · Uploaded video

ffmpeg → segments → parallel describe / embed

Uploads are sampled at 1 fps, split into segments and processed in parallel.

Flow 3 · Search

Rewrite → embed → Qdrant → rerank → NMS

Every query pays for a rewrite, an embedding and a rerank: small text calls on a text model. The vision model is never in the request path.

Have an archive nobody can search? Bring it to a call.

Discuss your use case