Dev.to AI 🤖 Ai 👁 0 📖 4 min read

Revolutionizing Urban Planning with LLM: Opportunities and Challenges

We are building a Zoning Compliance Agent that ingests a municipal zoning excerpt and a development proposal, then flags violations and suggests modifications. It helps city planners and developers cut initial review tim

We are building a Zoning Compliance Agent that ingests a municipal zoning excerpt and a development proposal, then flags violations and suggests modifications. It helps city planners and developers cut initial review time from days to minutes. I shipped a similar tool for a mid-sized city last quarter, and this tutorial covers the core engine I extracted from that project.

What you'll need

You need Python 3.10 or newer, the OpenAI SDK, and an Oxlo.ai API key from https://portal.oxlo.ai. Oxlo.ai uses flat per-request pricing, so feeding it multi-page zoning documents does not inflate cost the way token-based billing would. See https://oxlo.ai/pricing for details.

pip install openai

Step 1: Configure the Oxlo.ai client

First, import the OpenAI SDK and point it at Oxlo.ai. I keep the client in a dedicated module so I can swap models later without touching business logic.

from openai import OpenAI
import json

client = OpenAI(
    base_url="https://api.oxlo.ai/v1",
    api_key="YOUR_OXLO_API_KEY"  # from https://portal.oxlo.ai
)

Step 2: Load the zoning ordinance text

The agent needs context. I hard-code a realistic snippet here, but in production I pull this from a vector store or a parsed PDF.

ZONING_ORDINANCE = """
R-3 Residential District Regulations:
1. Minimum lot area: 6,000 square feet.
2. Maximum building height: 35 feet.
3. Front yard setback: 20 feet.
4. Rear yard setback: 25 feet.
5. Lot coverage: no more than 40 percent of total lot area.
6. Permitted uses: single-family detached, two-family detached, parks.
7. Prohibited uses: commercial retail, industrial, multi-family dwellings exceeding two units.
"""

Step 3: Define the system prompt

This prompt is the entire product. It tells the model how to act, what to output, and how to cite rules. I treat it like source code and version it in Git.

SYSTEM_PROMPT = """
You are a Zoning Compliance Officer. A developer will submit a project description.
Your task:
1. Compare the proposal against the provided zoning ordinance.
2. List every violation or potential concern.
3. Cite the specific ordinance section for each issue.
4. Suggest concrete changes that would bring the proposal into compliance.
5. If the proposal is fully compliant, state that clearly.

Output strict JSON with this schema:
{
  "compliant": boolean,
  "issues": [
    {
      "ordinance_section": string,
      "issue": string,
      "suggestion": string
    }
  ],
  "summary": string
}
"""

Step 4: Build the review function

I wrap the API call in a function that assembles the user message from the ordinance and proposal. I use qwen-3-32b because it handles long context and structured reasoning well, and Oxlo.ai charges per request rather than per token, so pasting the full ordinance is cheap.

def review_proposal(zoning_text: str, proposal_text: str) -> dict:
    user_message = f"""ZONING ORDINANCE:
{zoning_text}

DEVELOPER PROPOSAL:
{proposal_text}

Review the proposal and return JSON only.
"""

    response = client.chat.completions.create(
        model="qwen-3-32b",
        messages=[
            {"role": "system", "content": SYSTEM_PROMPT},
            {"role": "user", "content": user_message},
        ],
        response_format={"type": "json_object"},
        temperature=0.1,
    )

    raw = response.choices[0].message.content
    return json.loads(raw)

Step 5: Format the report

Raw JSON is fine for machines, but planners need readable text. A small formatter turns the structured output into bullet points.

def print_report(result: dict) -> None:
    print(f"Summary: {result['summary']}\n")

    if result["compliant"]:
        print("No issues found.")
        return

    for idx, issue in enumerate(result["issues"], 1):
        print(f"{idx}. {issue['issue']}")
        print(f"   Citation: {issue['ordinance_section']}")
        print(f"   Suggestion: {issue['suggestion']}\n")

Run it

Here is a realistic proposal that violates height and lot coverage rules. I run it through the agent and print the results.

PROPOSAL = """
We propose a three-story single-family home on a 5,500 sq ft lot in the R-3 district.
The building footprint will cover 2,500 sq ft.
Height will be 38 feet.
We plan to set the front porch 15 feet from the curb.
"""

if __name__ == "__main__":
    result = review_proposal(ZONING_ORDINANCE, PROPOSAL)
    print_report(result)

Example output:

Summary: The proposal contains three violations of the R-3 Residential District Regulations.

1. Minimum lot area requirement not met.
   Citation: Section 1, Minimum lot area: 6,000 square feet.
   Suggestion: Increase the lot size to at least 6,000 square feet or select a different lot.

2. Maximum building height exceeded.
   Citation: Section 2, Maximum building height: 35 feet.
   Suggestion: Reduce the building height to 35 feet or lower.

3. Lot coverage exceeds permitted percentage.
   Citation: Section 5, Lot coverage: no more than 40 percent of total lot area.
   Suggestion: Reduce the building footprint to 2,200 square feet or less, or increase the lot size to accommodate 2,500 sq ft within 40 percent coverage.

Wrap-up and next steps

This agent replaces the first-pass manual review that usually takes a planner half a day. Because Oxlo.ai prices by the request, you can stuff the entire zoning chapter into the prompt without watching the meter run on tokens.

Two concrete next steps: wire this function into a FastAPI endpoint so planners can upload PDFs, and add a retrieval step with BGE-Large embeddings via Oxlo.ai to pull only the relevant ordinance sections for cities with massive code volumes.

📰 Read the original article on Dev.to AI

Originally published by Dev.to AI. Aggregated on AIWithGhost for educational purposes — full credit and traffic to the original publisher.