By Daniel RogulinSYSTEMS18 min0
AI in Revit as a Working Tool: How I Build an Intelligent Assistant into a Plugin
A practical breakdown of AI integration in Revit: turning an LLM from a decorative chat into a controlled model QA tool with constrained context, verifiable output, and explicit user confirmation.
Any idea about AI in Revit can become decorative surprisingly fast.
It is not hard to build a chat panel into a plugin. It is not hard to connect a model, send a few requests, return a text response, and demonstrate that you can now “talk to a BIM model.” In a demo, that almost always looks impressive. In a real project, it quickly becomes obvious that impressive and useful are not the same thing.
The problem is not AI itself. The problem is that AI is too often introduced into Revit as a kind of universal magic. As if attaching an LLM to the model is enough, and from that moment on it will somehow start helping designers, understanding context, suggesting decisions, and saving time.
That is usually the point where things begin to fall apart.
Because in an engineering environment, the cost of error is too high and the cost of ambiguity is too high as well. “Looks mostly correct” is not enough. A tool has to behave predictably, it must not replace established rules with guesswork, it must not create false confidence, and it must not turn the working model into a space of interpretation.
That is why, if AI is going into Revit at all, it should not be added as an open-ended chat interface for wow effect. It should be added as a narrow, controlled layer around a specific task.
One of the more realistic scenarios is an AI assistant for working with the model itself. Not a universal conversational agent, but a tool that answers questions only about the current document, only on the basis of extracted data, and only within clearly defined boundaries. It does not invent. It does not guess. It does not wander outside the context it was given. In other words, it does not try to replace the Revit interface. It simply adds another way to get answers out of model data more quickly.
That already looks like a real engineering problem.
Imagine a user opens a project and wants to answer something simple but annoying to check manually. How many doors in the model are missing a Mark value. Which rooms have no name. Which wall elements have Comments filled in but no Assembly Code. Which types appear least often in the project. Where there are values that look almost identical but not quite. Which elements do not match the accepted naming convention.
Formally, all of this can be done without AI. That is an important point. If a task can be solved with ordinary filtering, validation, or rule-based logic, then AI is unnecessary. But the moment the user moves from rigid conditions to natural-language questions, the situation changes. It is no longer just “find elements where Mark is null,” but “show me doors whose identification looks suspicious,” or “do we have rooms with possible duplicates,” or “which parameters look inconsistent.” That is where conventional rules either grow into a fragile pile of exceptions or become too blunt to be useful.
So useful AI in Revit is not a generator of answers to any question. It is an interpretation layer on top of already extracted and structured model data.
Architecturally, that is far less glamorous than AI integrations are usually imagined to be. And probably for that exact reason, far more reliable.
Inside Revit, an add-in works with the active document. It does not send the entire model outside the environment, and it does not try to make the LLM into “a second Revit.” Its role is much narrower: collect a safe, limited context for the user’s query. For example, select a subset of elements from a relevant category, extract only a handful of parameters, normalize the values, build a compact payload, and send that to an external service. The backend then decides how AI should be used: classification, summarization, anomaly detection, explanation generation, matching user input to a standard, or answering the user’s question — but only within the boundaries of the supplied data.
The most important principle here is that AI should not look directly “into Revit.” It should work with a carefully prepared representation of model data. That reduces noise, makes the system easier to verify, and prevents the model from becoming the source of uncontrolled interpretation.
At a high level, the architecture looks like this:
If you remove the AI gloss, what remains is a very ordinary engineering chain: context extraction, external service, controlled response, review interface, and explicit user confirmation. That simplicity is exactly what gives the approach its strength.
Take a concrete example. A user opens a model and asks:
“Show me doors with suspicious naming or incomplete identification.”
In the UI, this may look like a simple text input inside the plugin panel. But internally, there is no magic. There is just careful, well-scoped work.
First, the add-in collects only doors, and only the fields relevant to the task. For example: ElementId, Name, Family, Type Name, Mark, Comments, and Type Comments. Everything else can be left out from the start. There is no reason to send anything unrelated to the request.
A simple C# example inside a Revit add-in might look like this:
public class DoorModelItem
{
public int Id { get; set; }
public string Name { get; set; }
public string FamilyName { get; set; }
public string TypeName { get; set; }
public string Mark { get; set; }
public string Comments { get; set; }
public string TypeComments { get; set; }
}
public List<DoorModelItem> CollectDoors(Document doc)
{
var result = new List<DoorModelItem>();
var collector = new FilteredElementCollector(doc)
.OfCategory(BuiltInCategory.OST_Doors)
.WhereElementIsNotElementType();
foreach (var element in collector)
{
var type = doc.GetElement(element.GetTypeId()) as ElementType;
result.Add(new DoorModelItem
{
Id = element.Id.IntegerValue,
Name = element.Name ?? string.Empty,
FamilyName = type?.FamilyName ?? string.Empty,
TypeName = type?.Name ?? string.Empty,
Mark = element.LookupParameter("Mark")?.AsString() ?? string.Empty,
Comments = element.LookupParameter("Comments")?.AsString() ?? string.Empty,
TypeComments = type?.LookupParameter("Type Comments")?.AsString() ?? string.Empty
});
}
return result;
}There is nothing “intelligent” in this code, and that is exactly the point. Its purpose is to prepare data carefully, not to hide weak architecture behind improvised logic. Once the data is collected, the add-in can build a request object and send it to a backend.
public class AiQuestionRequest
{
public string Query { get; set; }
public string Category { get; set; }
public List<DoorModelItem> Items { get; set; }
}
public async Task<string> AskAiAsync(AiQuestionRequest request)
{
var json = JsonConvert.SerializeObject(request);
using var client = new HttpClient();
var content = new StringContent(json, Encoding.UTF8, "application/json");
var response = await client.PostAsync("https://your-api.example.com/revit/ask", content);
response.EnsureSuccessStatusCode();
return await response.Content.ReadAsStringAsync();
}At this point, many articles jump straight to “now call an LLM and get a smart answer.” But this is actually where it is worth slowing down, because in a real implementation the backend is not just a pass-through proxy. It is where the system stops being a convenient demo and starts becoming real engineering.
First, this is where input can be validated and context size can be strictly limited. Second, this is where the system can decide whether AI is even necessary. A missing Mark value is not an AI problem. It is ordinary validation. But “suspicious naming” is a more interesting case, because it may require a combination of deterministic rules and semantic interpretation.
A backend example in Python using FastAPI could look like this:
from fastapi import FastAPI
from pydantic import BaseModel
from typing import List
import re
app = FastAPI()
class DoorModelItem(BaseModel):
Id: int
Name: str
FamilyName: str
TypeName: str
Mark: str
Comments: str
TypeComments: str
class AiQuestionRequest(BaseModel):
Query: str
Category: str
Items: List[DoorModelItem]
@app.post("/revit/ask")
def ask_revit_ai(request: AiQuestionRequest):
suspicious = []
for item in request.Items:
reasons = []
if not item.Mark.strip():
reasons.append("Missing Mark")
if item.Mark and not re.match(r"^[A-Z0-9\-]+$", item.Mark):
reasons.append("Mark format looks inconsistent")
if "door" not in item.Name.lower() and "door" not in item.TypeName.lower():
reasons.append("Naming may not match expected door convention")
if reasons:
suspicious.append({
"id": item.Id,
"name": item.Name,
"mark": item.Mark,
"issues": reasons
})
return {
"answer": "Found doors with potentially incomplete or inconsistent identification.",
"items": suspicious[:25]
}At first glance, this is not AI at all. It is just server-side logic. And there is no contradiction in that. Good AI integration almost always starts with a very practical question: where do ordinary rules stop being enough? In many cases, the real value does not come from a pure LLM call. It comes from a hybrid design. Rigid checks remain deterministic. AI is used only where ambiguity begins: interpreting free text, finding similar but non-identical wording, explaining results, normalizing naming patterns, or ranking suspicious cases.
So the right question is not “how do I add AI to Revit,” but “which part of the task should actually be given to AI, and which part should not.”
That is where an article about development becomes more interesting than a simple API demo. In a real system, AI should almost never be the first layer of logic. It should be the second one. First come filtering, rules, validation, and context narrowing. Then comes intelligent interpretation, only where formal code is either too rigid or too expensive to maintain.
If an LLM layer is added at all, it should not be used in the form of “send the whole model and ask what it thinks.” It should be used through a tightly constrained prompt. For example, the backend may already have selected 20 suspicious doors and now asks the model not to discover issues from scratch, but to explain why the records appear inconsistent and suggest a normalized description.
A server-side prompt builder could look like this:
def build_prompt(items: list[dict]) -> str:
lines = []
for item in items:
lines.append(
f"Id={item['id']}, Name={item['name']}, Mark={item['mark']}, Issues={', '.join(item['issues'])}"
)
return f"""
You are assisting with model QA in a Revit workflow.
Review the following door records and explain briefly why each one may be inconsistent.
Do not invent missing project data.
Return a short structured explanation per item.
Records:
{chr(10).join(lines)}
""".strip()And the result should ideally come back not as free-form prose, but as a structured payload that the UI can display safely.
At that point, the plugin can present the result not as a chat answer, but as a working table inside Revit: element Id, name, current value, detected issue, explanation, recommended action. That is one of the most important parts of the entire architecture. If the user receives the AI output only as a nice paragraph of text, it is not especially useful in practice. But if they receive a concrete list of elements, a clear explanation, and the ability to navigate directly to those objects in the model, then AI stops being a toy and becomes part of a working tool.
The UI matters more here than it may seem. Not because the design has to be fancy, but because the interface is what keeps the system within safe limits. A good integration should never allow AI to silently modify the model. At most, it should suggest, explain, and allow the user to confirm or reject an action. In an engineering environment, human-in-the-loop is not bureaucracy. It is how you avoid losing trust in the system after the first bad answer.
If you want to go one step further and allow updates, it should happen only after explicit confirmation. For example, the user sees that five doors are missing Mark values and chooses to apply generated values or normalize names. Only then does the Revit add-in perform the change inside a transaction.
public void ApplyMark(Document doc, int elementId, string newMark)
{
var element = doc.GetElement(new ElementId(elementId));
if (element == null) return;
var markParam = element.LookupParameter("Mark");
if (markParam == null || markParam.IsReadOnly) return;
using var tx = new Transaction(doc, "Apply AI suggestion");
tx.Start();
markParam.Set(newMark);
tx.Commit();
}This looks simple, but that simplicity is exactly what matters. AI does not touch the model directly. It does not “act.” It recommends. Everything else remains inside the normal, verifiable, controllable logic of the plugin.
That is where the line between a nice demo and a proper solution really appears.
If I had to integrate AI into a Revit-based project, I would not start with the question, “what else could a model answer?” I would start with a much more practical one: where does the user face repeated ambiguity today that rigid rules cannot cover well enough? That is where AI can offer real value. Not by replacing the interface. Not by turning the model into an open-ended chatbot. And not by generating text for the sake of generating text. But by handling ambiguity carefully.
That might mean identifying inconsistent naming, matching free-text values to a corporate standard, explaining model QA results, finding similar but non-identical records, or interpreting a user’s natural-language question and translating it into a meaningful set of filters and conditions.
In all of those cases, AI does not replace the system. It simply helps the user move faster through the part of the workflow where conventional logic is either too rigid or too cumbersome.
That is why the strongest kind of AI integration in Revit is not chat for the sake of chat. It is a controlled layer built on top of a clear scenario, where the boundaries are known, the cost of error is understood, and the human role in decision-making is preserved.
For engineering tools, that is usually enough.
Everything else tends to remain a very good demo.
Next step
Continue reading or jump to projects where these ideas are applied in practice.