-
-
ShelfSentry AI
-
Inspection page - Upload a shelf photo or run the bundled sample.
-
Inspection complete - Run example (bundled sample) - OpenCV 5 vision + an agentic decision loop - and returns an evidence-backed report
-
View full report - decision trace (Perception → decision → action, recorded) via restricted MCP tools.
-
History - Inspection history, read live from DynamoDB - demonstrating persistence, retrieval, and filtering.
-
Evaluation - Honest evaluation with failure cases and documented limitations - labeled 'Demo data' because the labels are synthetic.
-
ShelfSentry AI - Architecture
Inspiration
Retail shelves go empty, and the usual computer-vision answer is a one-shot detector: it returns a box and a confidence number and stops. That bothered me, because a real store associate doesn't work that way. When a shelf looks empty, a person leans in, checks whether it's actually a gap or just shadow, a divider, or a product pushed back, and only then decides. I wanted to build perception that behaves like that - a system that treats an uncertain finding as a reason to look again, not a reason to guess. And I wanted the vision to be real classical CV doing measurable work, not an LLM hallucinating boxes.
What it does
ShelfSentry AI inspects a retail shelf image (or short video) and decides what's happening through an active-perception loop:
- Real OpenCV 5 analysis finds candidate empty regions with measured metrics (empty-space ratio) and a quantified uncertainty for each region.
- A bounded agent (Amazon Bedrock as the primary decision model) reads those findings and chooses the next action: zoom into an uncertain region, analyze another frame, ask for a better image, escalate to a human, or finalize.
- The agent reaches perception only through four restricted MCP tools no filesystem, URL, shell, or credential surface.
- Every decision and tool call is recorded in an ordered decision trace, and the final report cites it.
- It's honest by construction: heuristic scores are kept separate from probabilities, every empty-looking region carries a visual caveat, and it never asserts "out of stock" as proven.
It runs in two modes from the same code: a local/offline pipeline, and an AWS mode (S3 + DynamoDB + Lambda + API Gateway + Bedrock).
Example results (real output from a live run)
A sample inspection on the deployed stack produced the annotated output image plus three measured candidate regions. The values below are real measured output, not illustrative placeholders:
| Region | Empty-space ratio (estimate) | Uncertainty value | Heuristic score |
|---|---|---|---|
| region_0000 | 77.8% | 0.428 | 0.654 |
| region_0001 | 18.9% | 0.539 | 0.340 |
| region_0002 | 22.3% | 0.446 | 0.354 |
Each region carries a visual caveat, e.g. "This region is an estimate of empty shelf space, not a confirmed out-of-stock condition. An empty-looking region may instead be shadow, occlusion, shelf dividers, or poor image quality." The empty-space ratio is a measured value, the heuristic score is an ordinal (not a probability), and the uncertainty value has a documented interpretation - the system never asserts out-of-stock as proven.
How I built it
- Vision: a fixed, deterministic OpenCV 5 pipeline (
cv2 5.0.0) Sobel/Hough shelf-band detection, Otsu + adaptive thresholding, morphology, Canny edge density, and contour analysis producing typed findings with uncertainty. - Agent: a bounded Strands-style loop (max 4 iterations) with Amazon Bedrock (Nova Lite via the Converse API) as the primary decision path and a deterministic policy as a recorded fallback.
- Tool boundary: a restricted MCP server exposing exactly four vision tools.
- Contracts: Pydantic v2 typed models at every seam, versioned response envelopes.
- Frontend: React + TypeScript + Vite + Tailwind, with a typed API client.
- Infrastructure: a single CloudFormation template (no CDK/Terraform) deploying a container-image Lambda (OpenCV 5 doesn't fit a zip), API Gateway HTTP API, DynamoDB, a private encrypted S3 bucket, least-privilege IAM, and CloudWatch.
Challenges I ran into
- OpenCV 5 won't fit in a Lambda zip. Its native libraries exceed the 250 MB unzipped limit, so I moved to a container-image Lambda (ARM64/Graviton) built and pushed to ECR and made the Docker build fail if
cv2.__version__isn't 5.x. - Model access reality. The target account didn't have Claude enabled, so I switched the decision model to Amazon Nova Lite. Because the agent uses the model-agnostic Converse API, that was a config change, not a code change.
- Live deploy surfaced real bugs which I fixed, not hid. CloudWatch logs caught a DynamoDB key-schema mismatch (table keyed on the wrong attribute), a missing
bedrock:InvokeModelpermission on the API role, a multipart upload parser that mangled binary PNG bytes, and aKeyErrorwhen the agent legitimately choserequest_better_image. Each was diagnosed from logs and corrected. - Keeping it honest under pressure. It would have been easy to fake a vision result or a metric. I kept the recorded-fallback design so the trace always shows why a decision was made.
Accomplishments that I'm proud of
- A genuinely agentic loop where OpenCV uncertainty causally drives the next action, verified live on AWS Nova Lite chose
inspect_crop, the MCP tool ran a real crop, and the agent then finalized. - Real, measured evaluation surfaced in the product (not fabricated): 8 synthetic samples, decision correctness 1.0 over the labeled subset, with the 2 false negatives and all limitations reported openly.
- One template, full stack, least-privilege, reaching
CREATE_COMPLETE/UPDATE_COMPLETEwith a working public API endpoint. - A hard honesty line held throughout: no fabricated confidence, caveats on every empty region, measured-only eval records.
What I learned
- Active perception is a design discipline, not a feature the value is in when the system refuses to conclude and gathers more evidence.
- "It built" should mean something: baking the OpenCV 5 version assertion into the image turned a possible silent regression into a loud build failure.
- The Converse API's model-agnosticism is a real portability win swapping Claude → Nova was trivial.
- Honest fallbacks beat confident guesses. Seeing
used_fallback: truewith a reason in the trace is more trustworthy than a smooth but unexplained answer.
What's next for ShelfSentry AI
- Tighten the Nova prompt to reduce occasional non-JSON responses (which currently trigger the recorded fallback).
- Replace the history Scan with a GSI-backed query for scale.
- Move long-video processing to ECS/Fargate when a job can exceed Lambda's 15-minute limit (the container image already supports it).
- Evaluate against real shelf imagery with human-labeled ground truth, so numbers reflect production accuracy rather than synthetic labels.
- Add annotated-evidence serving in AWS mode and a reviewer workflow for the
needs_reviewoutcomes.
Built With
- amazon-api-gateway
- amazon-bedrock
- amazon-cloudwatch
- amazon-dynamodb
- amazon-ecr
- amazon-web-services
- aws-cloudformation
- aws-iam
- aws-lambda
- docker
- model-context-protocol
- numpy
- opencv
- pydantic
- pytest
- python
- react
- react-router
- strands-agents
- tailwindcss
- typescript
- vite
- vitest

Log in or sign up for Devpost to join the conversation.