DEV Community

shashank ms
shashank ms

Posted on

LLM for Architecture: Exploring the Technical and Creative Possibilities

We are going to build an architectural concept assistant that reads a client brief, analyzes a site photograph, and produces a structured spatial program with design rationale. It helps solo practitioners and small firms automate early-stage schematic thinking without replacing the architect's judgment.

What you'll need

Python 3.10 or newer, the OpenAI SDK installed with pip install openai, and an Oxlo.ai API key from https://portal.oxlo.ai. You will also need a site photo in JPEG or PNG format to analyze.

Step 1: Configure the client

Set up the OpenAI SDK to point at Oxlo.ai. I keep my key in an environment variable so it does not leak into source control.

import os
from openai import OpenAI

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

print("Client ready:", client.base_url)

Step 2: Write the system prompt

The system prompt defines the agent's role as a senior design architect. It constrains output to practical, buildable concepts and asks for structured reasoning. I treat this as the single most important file in the project.

SYSTEM_PROMPT = """You are a senior architectural designer acting as a concept assistant.
Your job is to help architects translate client briefs and site constraints into spatial strategies.
Rules:
- Always ground suggestions in the site context, climate, and brief.
- Favor passive design strategies before active systems.
- When analyzing images, describe circulation, solar orientation, and adjacencies you observe.
- Output structured data when requested, using exact field names.
- Do not invent zoning codes. If unknown, note the gap and suggest what to verify.
"""

Step 3: Analyze site photos with vision

We use Kimi K2.6 because it handles vision and long context. The agent receives the brief and a site photo, then returns observations about topography, access, and solar exposure. I encode the image as a base64 data URI.

import base64

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

def analyze_site(brief_text, image_path):
    b64_image = encode_image(image_path)
    data_uri = f"data:image/jpeg;base64,{b64_image}"

    messages = [
        {"role": "system", "content": SYSTEM_PROMPT},
        {
            "role": "user",
            "content": [
                {"type": "text", "text": f"Client brief: {brief_text}"},
                {"type": "image_url", "image_url": {"url": data_uri}},
                {
                    "type": "text",
                    "text": (
                        "Analyze this site photo. Describe topography, apparent solar orientation, "
                        "access points, and any constraints that should shape the massing."
                    ),
                },
            ],
        },
    ]

    response = client.chat.completions.create(
        model="kimi-k2.6",
        messages=messages,
        max_tokens=1024,
    )
    return response.choices[0].message.content

# Example usage
site_notes = analyze_site(
    "Small mixed-use building on a corner lot. Needs ground-floor retail and two residential units above.",
    "site_photo.jpg",
)
print(site_notes)

Step 4: Generate structured program data

Now we turn the brief and site notes into a structured spatial program. We use JSON mode so the downstream report generator can rely on field names. I use Llama 3.3 70B here because it follows instructions for structured output reliably.

import json

def generate_program(brief_text, site_notes):
    user_content = (
        f"Client brief: {brief_text}\n\n"
        f"Site analysis notes: {site_notes}\n\n"
        "Generate a spatial program as JSON with these exact top-level keys: "
        "building_summary, spaces (list of objects with name, area_sqm, adjacencies, daylighting_strategy), "
        "material_palette (list), sustainability_notes (list). "
        "Do not include markdown fences."
    )

    response = client.chat.completions.create(
        model="llama-3.3-70b",
        messages=[
            {"role": "system", "content": SYSTEM_PROMPT},
            {"role": "user", "content": user_content},
        ],
        response_format={"type": "json_object"},
        max_tokens=2048,
    )

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

program = generate_program(
    "Small mixed-use building on a corner lot. Needs ground-floor retail and two residential units above.",
    site_notes,
)
print(json.dumps(program, indent=2))

Step 5: Assemble the report

Finally, we combine everything into a readable markdown report. This function passes the JSON back through the LLM to write a narrative design rationale. I use Qwen 3 32B for fluent, structured prose.

def draft_report(brief_text, site_notes, program_json):
    prompt = (
        "Write a concise architectural concept report in markdown. "
        "Include: 1) An executive summary, 2) Site response paragraph, 3) Spatial strategy paragraph, "
        "4) A bullet list of spaces with areas, 5) Sustainability priorities. "
        "Use the following data.\n\n"
        f"Client brief: {brief_text}\n\n"
        f"Site analysis: {site_notes}\n\n"
        f"Spatial program JSON: {json.dumps(program_json)}\n"
    )

    response = client.chat.completions.create(
        model="qwen-3-32b",
        messages=[
            {"role": "system", "content": SYSTEM_PROMPT},
            {"role": "user", "content": prompt},
        ],
        max_tokens=2048,
    )

    return response.choices[0].message.content

report = draft_report(
    "Small mixed-use building on a corner lot. Needs ground-floor retail and two residential units above.",
    site_notes,
    program,
)

with open("concept_report.md", "w") as f:
    f.write(report)

print("Report written to concept_report.md")

Run it

Here is the full script wired together. Save it as architect_agent.py, place a photo named site_photo.jpg in the same folder, and run python architect_agent.py.

import os
import json
import base64
from openai import OpenAI

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

SYSTEM_PROMPT = """You are a senior architectural designer acting as a concept assistant.
Your job is to help architects translate client briefs and site constraints into spatial strategies.
Rules:
- Always ground suggestions in the site context, climate, and brief.
- Favor passive design strategies before active systems.
- When analyzing images, describe circulation, solar orientation, and adjacencies you observe.
- Output structured data when requested, using exact field names.
- Do not invent zoning codes. If unknown, note the gap and suggest what to verify.
"""

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

def analyze_site(brief_text, image_path):
    b64_image = encode_image(image_path)
    data_uri = f"data:image/jpeg;base64,{b64_image}"

    messages = [
        {"role": "system", "content": SYSTEM_PROMPT},
        {
            "role": "user",
            "content": [
                {"type": "text", "text": f"Client brief: {brief_text}"},
                {"type": "image_url", "image_url": {"url": data_uri}},
                {
                    "type": "text",
                    "text": (
                        "Analyze this site photo. Describe topography, apparent solar orientation, "
                        "access points, and any constraints that should shape the massing."
                    ),
                },
            ],
        },
    ]

    response = client.chat.completions.create(
        model="kimi-k2.6",
        messages=messages,
        max_tokens=1024,
    )
    return response.choices[0].message.content

def generate_program(brief_text, site_notes):
    user_content = (
        f"Client brief: {brief_text}\n\n"
        f"Site analysis notes: {site_notes}\n\n"
        "Generate a spatial program as JSON with these exact top-level keys: "
        "building_summary, spaces (list of objects with name, area_sqm, adjacencies, daylighting_strategy), "
        "material_palette (list), sustainability_notes (list). "
        "Do not include markdown fences."
    )

    response = client.chat.completions.create(
        model="llama-3.3-70b",
        messages=[
            {"role": "system", "content": SYSTEM_PROMPT},
            {"role": "user", "content": user_content},
        ],
        response_format={"type": "json_object"},
        max_tokens=2048,
    )

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

def draft_report(brief_text, site_notes, program_json):
    prompt = (
        "Write a concise architectural concept report in markdown. "
        "Include: 1) An executive summary, 2) Site response paragraph, 3) Spatial strategy paragraph, "
        "4) A bullet list of spaces with areas, 5) Sustainability priorities. "
        "Use the following data.\n\n"
        f"Client brief: {brief_text}\n\n"
        f"Site analysis: {site_notes}\n\n"
        f"Spatial program JSON: {json.dumps(program_json)}\n"
    )

    response = client.chat.completions.create(
        model="qwen-3-32b",
        messages=[
            {"role": "system", "content": SYSTEM_PROMPT},
            {"role": "user", "content": prompt},
        ],
        max_tokens=2048,
    )

    return response.choices[0].message.content

if __name__ == "__main__":
    brief = (
        "Small mixed-use building on a corner lot. "
        "Needs ground-floor retail and two residential units above."
    )
    image_path = "site_photo.jpg"

    print("Analyzing site...")
    site_notes = analyze_site(brief, image_path)
    print(site_notes[:500], "...\n")

    print("Generating program...")
    program = generate_program(brief, site_notes)

    print("Drafting report...")
    report = draft_report(brief, site_notes, program)

    with open("concept_report.md", "w") as f:
        f.write(report)

    print("Done. See concept_report.md")

When I ran this against a corner lot photo, the site analysis began:

"The site appears relatively flat with a slight grade toward the southeast corner. The main street frontage faces southwest, suggesting afternoon solar gain on the primary facade. A narrow service alley runs along the north edge, offering secondary access and utility routing..."

The generated JSON contained:

{
  "building_summary": "Three-story mixed-use infill on a tight corner lot",
  "spaces": [
    {
      "name": "Ground-floor retail",
      "area_sqm": 85,
      "adjacencies": ["main street entry", "back of house"],
      "daylighting_strategy": "Large south-facing glazing with deep overhang"
    }
  ],
  "material_palette": ["reclaimed brick", "cross-laminated timber", "powder-coated steel"],
  "sustainability_notes": ["Passive solar shading via balcony overhangs", "Rainwater collection from flat roof"]
}

The final markdown report stitched these into a coherent two-page concept narrative.

Next steps

Wire the JSON program output into a Python script that generates a rough SketchUp or Blender mesh using the dimensions, turning the assistant into a geometry seeding tool. Alternatively, add a feedback loop where the architect edits the JSON and sends it back for a revised report, using Oxlo.ai's multi-turn context to maintain design continuity across iterations.

Because Oxlo.ai uses flat per-request pricing, running a multi-step agent pipeline with long briefs and image analysis does not inflate costs the way token-based providers do for long-context workloads. You can iterate freely. See https://oxlo.ai/pricing for plan details.

Top comments (0)