DEV Community

shashank ms
shashank ms

Posted on

Building Computer Vision Applications with LLMs: An Engineering Perspective

We are going to build a shelf-audit agent that ingests a retail shelf photo and returns a structured JSON inventory report plus a human-readable manager summary. It runs entirely on Oxlo.ai vision and chat models, so you get flat per-request pricing even when feeding large, high-resolution images. This is useful for retail operations teams that need to automate store compliance checks without building a custom CV pipeline.

What you'll need

  • Python 3.10+
  • pip install openai
  • An Oxlo.ai API key from https://portal.oxlo.ai
  • A sample shelf image named shelf.jpg

Step 1: Configure the Oxlo.ai client

I start every project by verifying the client and credentials. This snippet points the OpenAI SDK at Oxlo.ai and makes a lightweight ping to confirm the key works.

from openai import OpenAI
import os

client = OpenAI(
    base_url="https://api.oxlo.ai/v1",
    api_key=os.getenv("OXLO_API_KEY")
)

# Verify connectivity with a cheap model
response = client.chat.completions.create(
    model="deepseek-v3.2",
    messages=[{"role": "user", "content": "ping"}],
    max_tokens=5
)
print(response.choices[0].message.content)

Step 2: Encode the input image

The chat completions vision format expects a base64 data URL. This helper reads any local image and returns the encoded string we need.

import base64

def encode_image(image_path):
    with open(image_path, "rb") as image_file:
        return base64.b64encode(image_file.read()).decode("utf-8")

image_b64 = encode_image("shelf.jpg")
print(f"Image encoded: {len(image_b64)} characters")

Step 3: Write the auditor system prompt

A strict system prompt keeps the vision model from hallucinating products. I treat this as a contract, and I store it as a module-level constant so it versions cleanly with the codebase.

SYSTEM_PROMPT = """You are a retail shelf auditor. When a user provides a shelf image, analyze it and produce a strict JSON object with these keys:
- sku_count: integer, total visible product facings
- missing_skus: list of strings, expected products not visible
- compliance_score: integer 0-100
- restock_needed: boolean
- notes: list of strings, concise observations

Be precise. If you cannot see a product, do not hallucinate it."""

Step 4: Run vision analysis with Kimi K2.6

Kimi K2.6 supports vision, reasoning, and a 131K context window, so it can handle high-resolution shelf photos and detailed instructions in one shot. I send the base64 image and enforce JSON mode so the output is machine readable.

user_message = {
    "role": "user",
    "content": [
        {"type": "text", "text": "Analyze this shelf image and return the audit JSON."},
        {
            "type": "image_url",
            "image_url": {"url": f"data:image/jpeg;base64,{image_b64}"}
        }
    ]
}

response = client.chat.completions.create(
    model="kimi-k2.6",
    messages=[
        {"role": "system", "content": SYSTEM_PROMPT},
        user_message
    ],
    response_format={"type": "json_object"},
    max_tokens=1024
)

audit_json = response.choices[0].message.content
print(audit_json)

Step 5: Parse and validate the audit JSON

I validate the structure immediately to catch schema drift before it hits downstream logic. This small guard saves production debugging time later.

import json

audit = json.loads(audit_json)
assert isinstance(audit.get("sku_count"), int)
assert isinstance(audit.get("compliance_score"), int)

print(f"SKUs: {audit['sku_count']}, Compliance: {audit['compliance_score']}/100")

Step 6: Generate the manager summary with Qwen 3 32B

Raw JSON is not great for a store manager inbox, so I chain a text-only call to Qwen 3 32B to compress the audit into three plain-English bullets. Because Oxlo.ai uses flat request-based pricing, this follow-up does not inherit the image token cost from the vision step. You can see details at https://oxlo.ai/pricing.

summary_prompt = f"""Turn this shelf audit JSON into three bullet points for a store manager:
{json.dumps(audit, indent=2)}"""

summary_resp = client.chat.completions.create(
    model="qwen-3-32b",
    messages=[{"role": "user", "content": summary_prompt}],
    max_tokens=256
)

summary = summary_resp.choices[0].message.content
print(summary)

Run it

Here is the complete main block I run from the terminal. Below it is the actual output I captured from a test photo of a beverage aisle.

import os
import base64
import json
from openai import OpenAI

client = OpenAI(
    base_url="https://api.oxlo.ai/v1",
    api_key=os.getenv("OXLO_API_KEY")
)

SYSTEM_PROMPT = """You are a retail shelf auditor. When a user provides a shelf image, analyze it and produce a strict JSON object with these keys:
- sku_count: integer, total visible product facings
- missing_skus: list of strings, expected products not visible
- compliance_score: integer 0-100
- restock_needed: boolean
- notes: list of strings, concise observations

Be precise. If you cannot see a product, do not hallucinate it."""

def encode_image(image_path):
    with open(image_path, "rb") as f:
        return base64.b64encode(f.read()).decode("utf-8")

if __name__ == "__main__":
    image_b64 = encode_image("shelf.jpg")

    # Vision audit
    vision_resp = client.chat.completions.create(
        model="kimi-k2.6",
        messages=[
            {"role": "system", "content": SYSTEM_PROMPT},
            {"role": "user", "content": [
                {"type": "text", "text": "Analyze this shelf image and return the audit JSON."},
                {"type": "image_url", "image_url": {"url": f"data:image/jpeg;base64,{image_b64}"}}
            ]}
        ],
        response_format={"type": "json_object"},
        max_tokens=1024
    )

    audit = json.loads(vision_resp.choices[0].message.content)
    print("=== AUDIT JSON ===")
    print(json.dumps(audit, indent=2))

    # Manager summary
    summary_prompt = f"""Turn this shelf audit JSON into three bullet points for a store manager:
{json.dumps(audit, indent=2)}"""

    summary_resp = client.chat.completions.create(
        model="qwen-3-32b",
        messages=[{"role": "user", "content": summary_prompt}],
        max_tokens=256
    )

    print("\n=== MANAGER SUMMARY ===")
    print(summary_resp.choices[0].message.content)
=== AUDIT JSON ===
{
  "sku_count": 24,
  "missing_skus": ["Diet Cola 12-pack", "Sparkling Water Lime"],
  "compliance_score": 78,
  "restock_needed": true,
  "notes": [
    "Top shelf has two empty facings.",
    "Brand blocking is broken on the second shelf.",
    "Price tags are visible for all stocked items."
  ]
}

=== MANAGER SUMMARY ===
- We counted 24 facings but are missing Diet Cola 12-pack and Sparkling Water Lime.
- Compliance is at 78% because of empty space and broken brand blocking on the second shelf.
- Restock is needed immediately to close the gaps on the top shelf.

Wrap up

That is the core pipeline. To productionize it, expose the agent as a FastAPI endpoint so field staff can POST photos from a phone. You can also add a second vision pass with Gemma 3 27B to OCR price tags and cross-check shelf labels against the audit.

Top comments (0)