We are building a zoning compliance agent on Oxlo.ai that ingests a plain-text development proposal and emits a structured violation report. It is aimed at civic tech teams and planning consultants who need to pre-screen site plans against municipal code without reading hundreds of pages of ordinance.
What you'll need
You need Python 3.10 or newer, the OpenAI SDK, and an Oxlo.ai API key. Install the SDK and grab your key from the Oxlo.ai portal. Oxlo.ai uses request-based pricing, so long prompts containing full zoning chapters do not inflate your cost the way token-based providers do. That makes it practical to pass entire ordinance sections on every call.
pip install openai
Step 1: Instantiate the Oxlo.ai client
I start with a standard OpenAI client pointed at Oxlo.ai. Because the API is fully compatible, this is the only Oxlo.ai-specific line in the whole project.
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: Encode a sample zoning ordinance
Instead of hard-coding logic, I store the rules in a dictionary so the LLM can reason over them. These values mirror a typical downtown mixed-use (MU-3) district.
ZONING_ORDINANCE = {
"district": "MU-3 Downtown Mixed-Use",
"max_height_ft": 120,
"max_far": 3.0,
"setback_front_ft": 10,
"setback_rear_ft": 15,
"parking_ratio": {
"residential": 1.0,
"commercial": 1.5 # spaces per 1000 sq ft
},
"open_space_pct": 10,
"affordable_housing_pct": 15,
"prohibited_uses": ["heavy manufacturing", "adult entertainment"]
}
Step 3: Write the planner system prompt
The system prompt is the entire reasoning engine. It tells the model how to compare a narrative proposal against the numeric rules and what JSON schema to return.
SYSTEM_PROMPT = """You are a senior city planner conducting a preliminary zoning review.
You have access to the following ordinance for the district in question:
""" + json.dumps(ZONING_ORDINANCE, indent=2) + """
When you receive a development proposal, analyze it carefully.
Return ONLY a JSON object with this exact structure:
{
"compliant": boolean,
"violations": [
{"rule": string, "required": string, "proposed": string, "severity": "minor"|"major"}
],
"notes": [string]
}
Be strict. If a value is not mentioned in the proposal, note it as missing rather than assuming compliance.
Do not include markdown formatting or explanation outside the JSON.
"""
Step 4: Build the compliance checker
This function wraps the Oxlo.ai call. I use JSON mode so the response is guaranteed parseable, which keeps downstream code clean.
def check_compliance(proposal_text: str) -> dict:
response = client.chat.completions.create(
model="llama-3.3-70b",
messages=[
{"role": "system", "content": SYSTEM_PROMPT},
{"role": "user", "content": proposal_text},
],
response_format={"type": "json_object"},
temperature=0.1,
)
raw = response.choices[0].message.content
return json.loads(raw)
Step 5: Submit a sample proposal
Here is a realistic proposal that deliberately violates height and parking rules. I print the raw JSON so you can see exactly what the model returns.
PROPOSAL = """
Development Proposal: 400 Main Street
A 14-story mixed-use building with 85 residential units and 12,000 sq ft of ground-floor retail.
Height: 155 feet.
Parking: 60 spaces total.
Open space: 8% of lot.
Affordable units: 10% of total.
"""
result = check_compliance(PROPOSAL)
print(json.dumps(result, indent=2))
When I ran this, the model returned:
{
"compliant": false,
"violations": [
{
"rule": "max_height_ft",
"required": "120 ft",
"proposed": "155 ft",
"severity": "major"
},
{
"rule": "parking_ratio",
"required": "85 residential + 12k commercial = 85 + 18 = 103 spaces",
"proposed": "60 spaces",
"severity": "major"
},
{
"rule": "open_space_pct",
"required": "10%",
"proposed": "8%",
"severity": "minor"
},
{
"rule": "affordable_housing_pct",
"required": "15%",
"proposed": "10%",
"severity": "major"
}
],
"notes": [
"Setbacks are not specified in the proposal and must be confirmed.",
"No prohibited uses are mentioned."
]
}
Step 6: Generate a staff memo
Raw JSON is useful for databases, but planners need prose. I feed the structured result back to the model with a terse prompt to produce a staff memo.
def generate_memo(compliance_result: dict) -> str:
prompt = f"""Write a one-paragraph planning staff memo summarizing this compliance review.
Be direct and list required corrections. Data: {json.dumps(compliance_result)}"""
response = client.chat.completions.create(
model="llama-3.3-70b",
messages=[{"role": "user", "content": prompt}],
temperature=0.3,
max_tokens=300,
)
return response.choices[0].message.content.strip()
memo = generate_memo(result)
print(memo)
Example output:
The proposal for 400 Main Street is non-compliant with the MU-3 Downtown Mixed-Use district. The building height must be reduced from 155 feet to the 120-foot maximum. Parking must be increased from 60 spaces to at least 103 spaces to meet the combined residential and commercial requirements. The affordable housing contribution must rise from 10% to 15%, and open space must increase from 8% to 10%. Front and rear setbacks were not provided and must be submitted for further review.
Run it
The full script is below. Save it as zoning_agent.py, export your OXLO_API_KEY, and run it. Everything from the ordinance encoding to the final memo runs in under five seconds on Oxlo.ai with no cold start.
from openai import OpenAI
import json
import os
client = OpenAI(
base_url="https://api.oxlo.ai/v1",
api_key=os.environ["OXLO_API_KEY"]
)
ZONING_ORDINANCE = {
"district": "MU-3 Downtown Mixed-Use",
"max_height_ft": 120,
"max_far": 3.0,
"setback_front_ft": 10,
"setback_rear_ft": 15,
"parking_ratio": {
"residential": 1.0,
"commercial": 1.5
},
"open_space_pct": 10,
"affordable_housing_pct": 15,
"prohibited_uses": ["heavy manufacturing", "adult entertainment"]
}
SYSTEM_PROMPT = """You are a senior city planner conducting a preliminary zoning review.
Ordinance:
""" + json.dumps(ZONING_ORDINANCE, indent=2) + """
Return ONLY JSON:
{
"compliant": boolean,
"violations": [{"rule": string, "required": string, "proposed": string, "severity": "minor"|"major"}],
"notes": [string]
}
"""
def check_compliance(proposal_text: str) -> dict:
response = client.chat.completions.create(
model="llama-3.3-70b",
messages=[
{"role": "system", "content": SYSTEM_PROMPT},
{"role": "user", "content": proposal_text},
],
response_format={"type": "json_object"},
temperature=0.1,
)
return json.loads(response.choices[0].message.content)
def generate_memo(compliance_result: dict) -> str:
prompt = f"Write a one-paragraph planning staff memo summarizing this review. Data: {json.dumps(compliance_result)}"
response = client.chat.completions.create(
model="llama-3.3-70b",
messages=[{"role": "user", "content": prompt}],
temperature=0.3,
max_tokens=300,
)
return response.choices[0].message.content.strip()
if __name__ == "__main__":
proposal = """
Development Proposal: 400 Main Street
A 14-story mixed-use building with 85 residential units and 12,000 sq ft of ground-floor retail.
Height: 155 feet.
Parking: 60 spaces total.
Open space: 8% of lot.
Affordable units: 10% of total.
"""
result = check_compliance(proposal)
print("=== COMPLIANCE JSON ===")
print(json.dumps(result, indent=2))
print("\n=== STAFF MEMO ===")
print(generate_memo(result))
Wrap-up
Two directions to take this next. First, connect the agent to a parcel database API so it pulls the correct zoning district automatically instead of hard-coding the ordinance. Second, add vision support by switching to kimi-k2.6 and passing uploaded site plans or floor plates so the model can verify square footage and unit counts directly from drawings. Both are straightforward because Oxlo.ai exposes the same OpenAI-compatible endpoints for chat and vision, and the flat per-request pricing keeps costs predictable even when you add large document context.
For pricing details, see https://oxlo.ai/pricing.
Top comments (0)