← q52 Labs

Agentic Deer-Hunting Field Guide Architecture

Isometric illustration of an outdoor outfitter store with hunting maps and gear

How four Claude agents read public lidar, aerial photos and hunting regulations to answer one question: where can I legally sit this week, and on what wind?

Field guide map: ranked deer stands, scouting findings and hunt-area status over MassGIS leaf-off aerial imagery for a sample area in Massachusetts
The field guide’s interactive map from the first run: ranked stands, scouting findings and hunt-area status over the MassGIS 2023 leaf-off orthophoto. Place names are replaced with generic labels.

1. Overview

The field guide is a small multi-agent system that scouts white-tailed deer hunting locations in a chosen part of Massachusetts. The first run covers a sample area of four towns in north-central Massachusetts. It does what a good guide does before the season: it works out which land is legal to hunt, reads the terrain and the aerial photos for bedding areas and travel routes, checks the wind forecast, and turns all of that into a short list of stand sites with a parking spot and a walk-in route for each.

Everything comes from public, keyless data: MassGIS lidar and leaf-off orthophotos, USGS topo quads, Sentinel-2 land cover, the MassGIS open-space layer, USFWS refuge hunt units, and the National Weather Service. There are no API keys and no paid data. The design principle is the one we use across q52 Labs: agents research and recommend, the human decides. No agent ever recommends a stand on private land or inside a 500 ft building safety zone, and anything the agents could not confirm is listed as unverified rather than guessed.


2. Solution Brief

Platform: Claude Code as the orchestrator, with four subagents defined in .claude/agents/ and one skill (/hunt-plan) that runs the pipeline. About 1,500 lines of Python tools do the deterministic work: ArcGIS REST fetches, a numpy/scipy terrain model on a 6 m grid, scikit-image least-cost paths, grid-labelled imagery tiles, and a self-contained Leaflet map. The survey and regulations agents run on Sonnet; the scouting and field-guide agents, which do the judgement calls, run on Opus.

Each run goes: regulations ∥ survey → scouting → field guide → browser check → map and export. The first two stages run in parallel. A run is started with /hunt-plan, with optional fresh, refresh-regs or area=<towns> arguments. Moving the scouting area is a one-line change to config/area.json.

2.1 Regulations Agent (Sonnet)

  • Question answered: Where and when is it legal to hunt deer here, and on what terms?
  • Inputs: The MassGIS open-space layer (owner, manager, purpose, public access) and the USFWS refuge hunt units for the area, plus web research: the Wildlife Management Zone, this year’s archery, shotgun and primitive dates, statewide rules (the 500 ft safety zone, no Sunday hunting, blaze orange, baiting, tree stands), refuge permit rules, WMA, DCR and town conservation policies, and land-trust policies.
  • Output: regulations.json with season dates, rules, sources and an area_overrides list that reclassifies parcels by owner, site name or ID. The deterministic hunt_areas.py applies the overrides and gives every parcel one status: open | permit | restricted | verify | private_permission | closed.
  • Hard rule: Anything not confirmed from a source goes in unverified. Ownership defaults are a starting point, never the final answer.
  • Known gap: mass.gov returns 403 to automated fetches, so the orchestrator re-checks season dates and new rules in a real browser before the report goes out.

2.2 Survey Agent (Sonnet) and the Terrain Model

  • Question answered: Where should the scout look?
  • Inputs: Wetlands, building footprints, roads, DCR trails and large-wildlife crossings from ArcGIS REST; the MassGIS lidar DEM and Sentinel-2 land cover as rasters.
  • Terrain model (deterministic): Slope, aspect and cover give scored bedding areas, food edges and saddles. A travel cost surface penalises steep ground, buildings and roads, and least-cost paths from each bedding area to nearby food give modelled corridors. Pinch points are where corridors bunch together. Every building gets a 500 ft safety buffer. The model is a heuristic prior, not ground truth.
  • Output: A ranked list of hunt areas and private “hot spots”, then 6–10 focus areas, each with four grid-labelled images: 2023 leaf-off orthophoto, 2025 colour-infrared, lidar hillshade and USGS topo, all on the same 10×10 grid.
  • Hard rule: The survey agent does not interpret deer sign. Its job is coverage and clean data. A failing service is retried once, then logged in data_gaps.

2.3 Scouting Agent (Opus, vision)

  • Question answered: Where are the deer bedding, travelling, feeding and staging?
  • Inputs: All four images for every focus area, the model’s candidate points and corridors, and locate.py, which describes any spot: land status, safety zone, elevation, slope, aspect, cover, model scores and nearest road.
  • How it reads the images: Dark green in the leaf-off photo is conifer cover; bright red in colour-infrared is vigorous vegetation and food; the hillshade shows benches, saddles, ditches and hummocks in swamps; the topo shows contour pinches and drainages. The agent compares all four on the shared grid and cites the cells it used.
  • Output: scouting.json: each finding has a type (bedding | travel | pinch | food | staging), grid cells, a confidence score and the evidence from each layer. locate.py resolve converts the cells to coordinates and GeoJSON.

2.4 Field Guide Agent (Opus)

  • Question answered: Sit here, on this wind, and walk in this way.
  • Legal filter: A stand must be on land that is open, permit or restricted for that season (or verify, clearly flagged), and at least 160 m from any building: the 152.4 m legal limit plus margin.
  • Placement by season: Archery stands go near bedding and staging on saddles, benches and inside corners. Shotgun stands go on escape routes into the thickest, wettest cover. Primitive stands go on late-season food and thermal cover.
  • Wind and access: The agent pulls the NWS forecast and gives each stand the winds that carry scent away from both the bed and the deer’s approach, then names the days in the forecast that fit. Each stand gets a parking point and an approach path that avoids bedding and stays on legal land.
  • Output: field_guide.json and a plain-English report.md: a bottom line, a wind table, ranked stands, a season plan, permits and to-dos, private-land “ask permission” targets and the unverified items.

2.5 Hunter Review (human gate)

  • Interactive map: A single-file Leaflet map with the stands, approach paths, parking, scouting findings, hunt-area status, refuge units, and bedding, corridor and safety-zone heat layers over the leaf-off orthophoto.
  • Conversational follow-up: export_chatgpt.py copies the brief, the map and the key JSON into a ChatGPT project folder, so the hunter can ask follow-up questions from a phone.
  • The hunter’s job: Get the licence, stamps and refuge permit; call the landowners and town boards on the verify list; walk the ground before sitting it. The guide never contacts anyone or applies for anything.

2.6 Orchestration (Claude Code skill)

  • The /hunt-plan skill: creates or reuses a run folder, launches regulations and survey together, re-applies the regulation overrides once both finish, then runs scouting and the field guide in order.
  • Verification: The orchestrator opens the regulations page in a real browser to check dates, renders the map, opens it to take a screenshot, and confirms every output file exists before reporting.
  • Partial reruns: refresh-regs reruns only regulations and the field guide; new imagery or a single spot check is one tool call.

3. Run State Contract

Each run lives in one folder, runs/<id>/. Agents talk to each other only through these files, and each file has one writer.

FileWritten byRead by
aoi.json, vector/*.geojsongis_fetch.pyEvery stage
regulations.json (seasons, rules, sources, area_overrides, unverified)Regulations agent; orchestrator after the browser checkhunt_areas.py, Field guide
hunt_areas.geojson (status per parcel)hunt_areas.pySurvey, Scouting, Field guide, Map
terrain/ (candidates, corridors, heat overlays)terrain.pySurvey, Scouting, Field guide, Map
survey/ (area ranking, focus areas), imagery/Survey agent, focus_rank.py, imagery_fetch.pyScouting
scouting.json, scouting.geojsonScouting agent, locate.py resolveField guide, Map
field_guide.json, report.mdField guide agent; orchestrator saves the reportMap, ChatGPT export, Hunter
map.html, ChatGPT latest/render_map.py, export_chatgpt.pyHunter

Conventions: coordinates are [lon, lat] WGS84 everywhere; seasons are archery | shotgun | primitive; bulk vector data and terrain caches are git-ignored and regenerate on the next run.


4. Legal Routing Matrix

ConditionAction
Parcel open (e.g. a MassWildlife WMA)Stand allowed in every season the rules allow
Parcel permit (e.g. a refuge unit)Stand allowed; permit added to the to-do list with the refuge’s own rules (one marked tree stand, no screw-in steps, weapon limits)
Parcel restrictedStand allowed only for the weapons and seasons named in the rules
Parcel verify (policy unknown)Listed as an alternate, clearly flagged “confirm policy first”, with who to call
Parcel private_permissionNever a stand; listed as an “ask permission” target only
Parcel closedExcluded
Within 160 m of a buildingExcluded, whatever the land status; parking inside a zone gets a “walk in before loading” note
Regulation fact not confirmed from a sourceKept out of the advice and listed under Unverified

5. First Run

The first full run, on 12 November 2026, covered the four sample towns plus a 2.5 km buffer.

MeasureResult
Terrain grid3,123 × 5,244 cells at 6 m; 61% tree cover, 28% built
Land inside a 500 ft building safety zone66% of the area
Parcels classified963: 17 open, 9 permit, 101 restricted, 477 verify, 298 private, 61 closed
Model candidates80 bedding areas, 120 food edges, 60 saddles, 60 pinch points, 111 corridors
Focus areas imaged9, at four layers each
Scouting findings56: 24 bedding, 14 travel, 13 pinch, 3 food, 2 staging
Recommendations8 stands, 2 verify-land alternates, 7 private “ask permission” targets

The two numbers that matter most are the 66% and the 17. Two-thirds of the area is off-limits because of houses, and only 17 parcels are plainly open. A guide that ignores the safety zone would mostly recommend illegal spots. The other lesson is the size of the verify list: town and land-trust hunting policies are rarely published in a form an agent can confirm, so the honest output is a call list, not a guess.


6. Design Decisions

  • Model first, eyes second. The terrain model is cheap and covers every acre; vision is expensive and good at nuance. The model picks where to look, and the scouting agent confirms or rejects with the imagery.
  • Four layers on one grid. Every focus image carries the same A–J, 1–10 grid, so the agent can say “hemlock in D4 on the ortho, bench in D4 on the hillshade”, and code can turn the cell back into coordinates.
  • Legality is a filter, not a score. A great pinch point on private land does not become a stand at a lower rank. It becomes a different kind of output: a landowner to ask.
  • A real browser for the source of truth. The state regulations site blocks automated fetches, so the pipeline treats the browser check as a required step rather than trusting search snippets.
  • Hand off to a chat. The heavy analysis runs once at a desk; the results go to a ChatGPT project so follow-up questions in the field need no tools.

7. Prompt-Engineering Patterns

  1. Give each agent one question. Legal? Where to look? Where are the deer? Where to sit? Each agent’s prompt opens with its role and the one thing other agents rely on it for.
  2. Teach the reading, not just the task. The scouting prompt explains what conifer, CIR red, a bench or a hummock looks like in each layer, and where New England deer bed and travel, so the vision model has a field scout’s checklist.
  3. Evidence with every claim. Each finding carries grid cells, a confidence score and the evidence from each layer; each regulation carries a source. The field guide can then weigh them instead of trusting them.
  4. Hard rules stated as numbers. “Outside the safety zone” becomes dist_building_m ≥ 160; “near bedding” becomes “15–25 yards to the route”. Numbers survive across agents; adjectives don’t.
  5. Unverified is a first-class output. Every agent may say “I couldn’t confirm this”, and the report shows those items to the hunter.
  6. Tools over memory. Agents call locate.py for any spot instead of estimating land status, distance or slope from the picture.

8. Plumbing Patterns

  • Keyless public services only: every endpoint is listed in config/sources.json, verified, and attributed on the map.
  • Cache the slow parts: the first terrain run downloads about ten minutes of lidar; later runs reuse the cached grid.
  • Parallel where independent: regulations and survey share no inputs, so they run at the same time.
  • One folder per run: every intermediate file is kept, so any stage can be rerun or audited.
  • Self-contained outputs: the map is a single HTML file and the brief is Markdown, so both travel anywhere.

9. Risks and Recommendations

  • Regulations change every year. Rerun with refresh-regs before each season and read the official MassWildlife and refuge pages yourself. The guide is a research aid, not legal advice.
  • Open-space data lags reality. Ownership and access in the state layer can be out of date, and posted signs on the ground win. Treat every verify parcel as closed until someone confirms it.
  • The model is a prior. Bedding and corridor scores come from terrain and cover, not deer sign. Scout on foot (tracks, rubs, beds) before committing to a stand.
  • Building data misses some structures. Sheds, new houses and camps may not be in the footprint layer, so confirm the 500 ft distance in person.
  • Next steps: feed back trail-camera and harvest observations to tune the model weights, and reuse the same pattern (public GIS, model prior, vision scout, rules filter) for turkey season or for fishing access.

10. Diagram

flowchart TD
    %% Legend
    %%   Rounded node    = LLM agent (blue)
    %%   Rectangle       = deterministic code (gray)
    %%   Diamond         = router or control gate (amber)
    %%   Hexagon         = human hunter (red)
    %%   Cylinder        = run-folder state (green)
    %%   Stadium, dashed = start or end (white)
    %%   Solid arrows = control flow; dotted arrows = reads and writes to state

    classDef agent fill:#e8f0fe,stroke:#1a73e8,color:#0b2a5b
    classDef det fill:#f1f3f4,stroke:#5f6368,color:#202124
    classDef router fill:#fef7e0,stroke:#f9ab00,color:#4a3500
    classDef human fill:#fce8e6,stroke:#d93025,color:#5c0a05
    classDef state fill:#e6f4ea,stroke:#188038,color:#0d3b1a
    classDef term fill:#ffffff,stroke:#5f6368,color:#202124,stroke-dasharray:5 4

    START(["/hunt-plan: towns from config/area.json"]):::term
    FETCH["gis_fetch.py: towns, open space, refuge units"]:::det

    subgraph S1["Stage 1a: Regulations"]
        REGS("Sonnet: zone, season dates, rules, refuge, town and land-trust policy"):::agent
        HA["hunt_areas.py: status per parcel"]:::det
        REGS --> HA
    end

    subgraph S2["Stage 1b: Survey"]
        TER["terrain.py: lidar + land cover, bedding, food, saddles, corridors, pinches, safety zones"]:::det
        RANK["focus_rank.py: rank areas and private hot spots"]:::det
        SURV("Sonnet: choose 6-10 focus areas"):::agent
        IMG["imagery_fetch.py: leaf-off, CIR, hillshade, topo on one grid"]:::det
        TER --> RANK --> SURV --> IMG
    end

    subgraph S3["Stage 2: Scouting"]
        SCOUT("Opus vision: bedding, travel, pinch, food, staging with evidence"):::agent
        LOC["locate.py: cells to coordinates, spot checks"]:::det
        SCOUT --> LOC
    end

    subgraph S4["Stage 3: Field guide"]
        LEGAL{"Legal land and 160 m from buildings?"}:::router
        PRIV(["Private: ask-permission target only"]):::term
        WIND["NWS forecast: best wind per stand"]:::det
        GUIDE("Opus: stands, approach routes, season plan, unverified list"):::agent
        LEGAL -- "no" --> PRIV
        LEGAL -- "yes" --> GUIDE
        WIND --> GUIDE
    end

    CHECK["Orchestrator: check regulations in a real browser"]:::det
    OUT["render_map.py + export_chatgpt.py"]:::det
    HUNTER{{"Hunter: permits, calls, scouts on foot, decides"}}:::human
    END(["Sits the stand"]):::term

    subgraph STATE["Run folder runs/id"]
        direction LR
        R1[("regulations.json, hunt_areas.geojson")]:::state
        R2[("terrain/, survey/, imagery/")]:::state
        R3[("scouting.json, field_guide.json, report.md, map.html")]:::state
    end

    START --> FETCH
    FETCH --> REGS
    FETCH --> TER
    HA --> SCOUT
    IMG --> SCOUT
    LOC --> LEGAL
    GUIDE --> CHECK --> OUT --> HUNTER --> END

    HA -.-> R1
    IMG -.-> R2
    LOC -.-> R3
    GUIDE -.-> R3
    R1 -.-> LEGAL

Discover more from q52.ai

Subscribe now to keep reading and get access to the full archive.

Continue reading