Skip to content
Worked example

Turn a photo folder into a SQL catalog.

Your filenames don’t tell you which cameras you used or which photos contain location data. Ask your coding agent to bring that metadata into DuckDB, where you can filter it, group it, and join it to other data.

A folder of photosCamera, date, location
VGIYour agent’s workerReads metadata locally
A DuckDB catalogphotos.main.files(…)
01 · Give your agent the skill

Start in a fresh project.

The skill teaches your agent how to build a VGI worker and test it through DuckDB. Install it in your project, selecting your coding agent when prompted.

Install in your project’s terminal
npx skills add Query-farm/vgi-skills --skill vgi-worker

The installer uses Node.js. The saved Python example below also uses uv. Agent-specific installation help →

02 · Give it something concrete to read

Use the sample photos.

Download the sample inputs and unzip them in your project. You’ll get an inputs/photos folder and an empty folder for testing.

north.jpgCamera A · 2025 · GPS
south.jpgCamera B · 2024 · GPS
plain.jpgNo EXIF metadata

These are tiny synthetic images with fictional metadata. The folder also contains a corrupt file and a nested photo: useful tests of what the worker should skip.

03 · Describe the outcome

Paste the request. Choose your language.

This is the same request offered on the quickstart. It leaves the folder and language open, so the agent should ask before writing code.

Your first message
Use the vgi-worker skill to make my photo metadata queryable in DuckDB: file paths, camera models, dates, and GPS coordinates where available. Ask me which folder and language to use. Keep the files unchanged and the data local. Build and test the worker, then show me how to ATTACH and query it.
The agent asks for the missing information

Which folder should it read? Which language should it use? What should happen with subfolders or unreadable files?

Summary of the questions from the verified Claude Code run.

In this run, we chose Python and narrowed the scope to JPEGs directly in one folder. You can choose any supported language for your own build.

  • C#
  • Go
  • Java
  • Python
  • Rust
  • TypeScript
Our reply · Python for this worked run
Use Python. Keep this local and use stock DuckDB with VGI from query_farm. The photo folder is inputs/photos. For this first version read .jpg and .jpeg files directly in that folder, without recursing or following symlinks. Keep every input unchanged. Valid photos without EXIF get a row with NULL metadata; skip corrupt files with a diagnostic on stderr.
Expose photos.main.files(folder VARCHAR), with columns path, size_bytes, camera_model, taken_at, latitude, and longitude. NULL folder should return no rows. I want to group photos by camera and year and find ones with GPS coordinates. Include the ATTACH and example queries when it works.

Replace inputs/photos with your folder’s path. The example reply uses a relative path; the evaluation supplied the same folder as an absolute path.

This saved build predates the starter-kit workflow. Current skills begin with the kit for your chosen language; use the paths and launch command your agent returns for that project.

04 · Let the agent build and verify

A worker you can inspect and run.

The agent creates a program that reads EXIF metadata and exposes photos.main.files(folder). But a missing date or unreadable file must not silently become a believable value. The tests check those cases through real SQL.

Claude CodePython Verified in DuckDB
  • Photos without EXIF return NULL metadata.
  • Corrupt files are skipped with a diagnostic; subfolders and symlinks are not followed.
  • Empty and NULL folder inputs return no rows.
  • A newly added photo appears on the next scan.
  • The original input files remain unchanged.

The saved worker passed nine agent-written SQL checks and nine independent checks. Its source was copied unchanged into the downloadable example. The build was verified on 2026-11-07; it is one measured example, not a completion-time guarantee.

Inspect the source, tests, and build provenance →

05 · Bring the catalog into your session

ATTACH the worker. Ask a useful question.

Your agent returns the launch command for its worker. After VGI is loaded, ATTACH adds the photos catalog to your DuckDB session. Its main schema contains the files(folder) table function.

Saved Python example · run DuckDB from the example directory
ATTACH 'photos' AS photos (
    TYPE vgi,
    LOCATION 'uv run --frozen --project photo_worker python photo_worker/photo_worker.py'
);

Now ask which cameras and years are represented in the folder:

Query the photo metadata
SELECT
    coalesce(camera_model, '(unknown)') AS camera,
    year(taken_at) AS year,
    count(*) AS photos
FROM photos.main.files('inputs/photos')
GROUP BY ALL
ORDER BY camera;
camerayearphotos
(unknown)NULL1
Fixture Camera A20251
Fixture Camera B20241

Actual SQL results from the supplied synthetic images. The photo without metadata stays visible as an unknown camera and year.

The same catalog can answer another question: which photos contain location coordinates?

Find photos with location metadata
SELECT path, latitude, longitude
FROM photos.main.files('inputs/photos')
WHERE latitude IS NOT NULL AND longitude IS NOT NULL;

The agent’s work is done. DuckDB calls the worker when you query it; the agent is no longer part of that execution.

Reproduce the result

Run the exact worker from this example.

Download the complete example, unzip it, and run this from its directory:

Verify the saved worker and run the query
uv run demo.py

The runner sets up VGI from query_farm, runs the saved SQL assertions, and prints the ATTACH statement and result for your local paths. Package and extension downloads need network access during setup; photo metadata processing stays local.

Download sizes and SHA-256 checksums · Onboarding evaluation results