Interface Design

October 2026

Software Development

When an AI Interface Needs More Than a Chat Box

When an AI Interface Needs More Than a Chat Box

What I focus on when tool execution, approvals and application state become part of the user experience.

HK Lab Studio

CSV import validation showing duplicate and invalid customer records

A chat window is usually the most obvious starting point when designing an interface around AI. The interaction is familiar: somebody enters a request, the system responds and the conversation provides a natural history of what has happened. That model works well while the software is mainly answering questions, but it starts to become less useful once the system is expected to perform actual work.The difference became much clearer to me while developing interfaces around agent-style workflows, tool execution and approval states. As soon as a system can inspect something, call an external tool, wait for another service, ask for permission or move through several stages before producing a result, the conversation is no longer the whole interface. There is activity taking place between the user's instruction and the final response, and if none of that activity is visible, the user is left trying to work out whether the software is progressing, waiting or simply stuck.

That changed the way I approached these interfaces. I became less interested in making another polished chat window and more interested in the operational parts around it: how the software communicates progress, how tool activity is represented, when an approval should interrupt the workflow and how much technical information the user actually needs to see. Those details are not as visually obvious as the main conversation, but they have a much bigger influence on whether an AI-driven application feels controlled or unpredictable.

The problem starts when a conversation becomes a workflow

A simple conversational interface has a fairly predictable sequence. The user enters something, the application processes it and a response appears. Even if the response takes a few seconds, there is usually only one operation that the user is waiting for, so a basic loading state can be enough.The situation changes when one request starts several different operations. A user might ask the software to inspect a project, check a number of files, compare the results and then prepare a final report. The system may need to identify which tool is appropriate, execute it, wait for the result, process that information and then continue with another step. From a technical point of view those operations can be completely normal, but from the user's side there is a long period where very little may be happening visibly.

That is where a generic loading indicator starts to lose its value. A spinner tells the user that the application has not finished, but it does not explain whether the system has started the task, whether a tool is currently running, whether something has already completed successfully or whether the workflow is waiting for input. Once a task becomes longer or more complicated, those distinctions start to matter because the user needs some idea of where the system is in the process.I found it more useful to think of the conversation as one layer of the application rather than the application itself. The request and the final answer can still live naturally inside a chat-style interface, but the work happening between them needs its own visual language. That does not mean displaying every internal operation. It means identifying the stages that are useful enough for the user to understand what the software is doing without having to understand how the system was implemented.

Status has to explain progress without turning into a log

Status information looks simple until there are several different kinds of status to represent. A system might be ready, connecting, processing, running a tool, waiting for approval, recovering from a failure or finished. If all of those conditions are displayed as "Working", the interface technically has a status indicator but it is not providing much useful information.The opposite approach is not much better. Developer tools can produce a large amount of information about requests, connections, tool calls and internal events, and it is tempting to expose everything because it is available. That can be useful during development, but it does not necessarily belong in the main user experience. A person trying to complete a task should not have to read a stream of technical events just to work out whether the application is still doing what they asked.

The useful middle ground is to expose the states that affect the user's understanding of the workflow. If the system is checking something, I want that to be distinguishable from a state where it is waiting for permission. If a tool has completed successfully, that should not look the same as one that failed. If the workflow is finished, the interface should make that clear enough that the user does not continue waiting for something that has already happened. That sounds like a small interface detail, but it changes the relationship between the user and the software. When the application communicates its current state properly, waiting feels deliberate rather than uncertain. The user can see that progress is being made and, just as importantly, can recognize when progress has stopped because something requires their attention.

Tool execution introduced another layer of interface design

Tool calls became one of the more interesting parts of this work because they sit between the AI and the rest of the application. A generated response is relatively easy to understand as part of a conversation, but a tool call represents the system asking something else to perform an operation and return information. That can involve files, APIs, project data or another service entirely. The way that action should be shown depends heavily on who is using the software. In a developer-facing application it can be useful to show the actual tool name, execution status and some of the information being passed between operations. A less technical product may need to describe the same event in much simpler terms, such as "Checking project files" or "Retrieving account information." The underlying operation can be identical while the interface communicates it very differently.That distinction is important because technical transparency and useful transparency are not always the same thing. Showing every raw detail may technically expose more information, but it can also make the application harder to understand. I would rather show the user enough information to explain what is happening and keep the lower-level detail available when it is genuinely useful.

I started treating tool activity as a distinct interface state rather than another line of text inside the conversation. That makes it easier to communicate when the operation starts, whether it is still running and what happened when it finished. It also gives the interface somewhere to put useful secondary information without allowing technical output to overwhelm the primary workflow.

Approvals need to feel like real decisions

The importance of the interface becomes even clearer when the system can perform an action rather than simply retrieve information. There is a meaningful difference between asking software to analyze something and allowing it to change something, particularly when the action affects a file, a configuration, published content or an external service.For those situations I prefer approval to be represented as an explicit state rather than hidden inside the conversation. If the system is asking permission to perform an action, the user should be able to see what is about to happen and make a decision without searching backwards through several messages to understand the context. The design has to be careful here because confirmation can easily become excessive. Requiring approval for every small operation quickly turns an automated workflow into a sequence of interruptions. The purpose is not to make every action feel dangerous; it is to introduce human control at the points where a decision has a meaningful consequence. This is also where the wording of the interface matters. "Approve" and "Reject" are not very useful if the user does not understand what is being approved. I want the action itself to be clear enough that the decision can be made from the information immediately around it. When the approval is completed, the interface should also show that the workflow has moved on rather than leaving the previous decision looking active.

These are familiar ideas from conventional software design, but AI-driven workflows make them more visible because the path through the application can be less predictable. The system may decide that a tool needs to be called or that a particular operation is necessary based on the request it received. The user still needs a stable interface around those decisions even when the underlying workflow is dynamic.

Errors are more useful when they preserve what already happened

Failure states also become harder once a task contains several steps. In a simple operation, an error message can usually tell the user that the action failed and ask them to try again. In a longer workflow, some parts may already have completed successfully before the problem appears.A generic message such as "Something went wrong" throws away too much context in that situation. The user needs to know whether the entire operation failed or only one part of it, whether anything was already changed and whether retrying will repeat work that has already been completed. Those questions become especially important when external tools are involved because the application may not have complete control over every dependency.I do not think the interface needs to explain the technical cause of every failure. What it does need to explain is what the failure means for the user. If an operation can be retried safely, that option should be obvious. If something needs to be corrected first, the user should know what requires attention. If earlier work is still valid, there is little value in making the interface look as though the entire workflow has been lost.

Preserving context also makes the system easier to debug when something genuinely does need investigation. A failed tool call is much easier to understand when the surrounding state is still visible than when the whole interface collapses into a single error screen. That was one of the reasons I started thinking of failures as part of the workflow rather than as a separate state that replaces everything else.

Responsive design becomes a question of priority

These interfaces can become quite dense on a desktop screen. A conversation, current status, tool activity, approval controls and supporting information can all be useful at the same time, and there is usually enough space to establish a clear hierarchy between them. A smaller screen forces much harder decisions. Simply placing every desktop panel underneath the previous one can technically make the interface responsive while still producing something that is unpleasant to use. The user ends up scrolling through operational detail to reach the part of the application that actually needs their attention. I try to decide which information has to remain visible and which can become secondary when the available space decreases. A pending approval should generally have more visual priority than a completed tool call. The current task status is more useful than a long history of previous states. Technical details can move into expandable areas without disappearing completely.

The same applies to generated content. Long responses, tool names, code snippets and execution details can all produce widths and heights that are difficult to predict while designing the original component. The layout needs to tolerate those changes rather than being tuned around one carefully prepared demo. This is the same principle I apply to responsive websites generally, but AI interfaces make the consequences more obvious because there are more simultaneous states competing for attention. A useful mobile layout is not simply a smaller desktop layout. It is a decision about which parts of the workflow matter most at that particular moment.

Repeating the same states led me toward reusable components

After working through these interfaces several times, I noticed that the applications could be different while many of the interface problems remained the same. A tool still needed a running state and a completed state. An approval still needed to move from pending to accepted or rejected. A connection could be available or unavailable. An operation could succeed, fail or require another attempt.Those repeated states were more useful to me than designing another complete screen from scratch. If the behaviour of a good approval component had already been considered properly, there was little value in solving the same interaction differently in every project. The same applied to tool execution, operational feedback and connection status.

That thinking eventually became part of the HK AI Interface Collection. Rather than treating it as one finished application, I organized the work around reusable interface patterns for agent workflows, AI chat and tool calling, approvals, developer states and operational feedback. The collection currently brings those ideas together across three React and TypeScript UI kits, with the components designed around the states that appear repeatedly in this kind of software.I also wanted people to be able to see those states rather than only look at screenshots, which is why part of the collection is available as an interactive demonstration in the Tool Lab. A static image can show what an approval component looks like, but it cannot show how the state changes once the user makes a decision. The same is true of a tool call or an agent workflow. The transitions are part of the design, so being able to interact with them gives a much better idea of how the components are intended to behave.

Building the collection also reinforced the value of separating presentation from the actual AI service. The Tool Lab demonstration does not need to make live model or backend calls in order to show the interface behaviour. The states can be simulated locally, which makes it easier to concentrate on interaction, hierarchy and responsiveness without tying the UI to one particular model or API.

The interface should reduce uncertainty rather than pretend it does not exist

The more capable AI-driven software becomes, the less comfortable I am with interfaces that hide all of the activity behind a polished loading animation. There is a point where simplifying the interface too aggressively removes information the user genuinely needs. At the same time, I do not want the opposite result where somebody has to understand orchestration, context management or tool routing simply to use the application. The interface still has to do the work of translating the system into something that makes sense for the person in front of it. For me, the useful question is whether the user can tell what stage the software is in and whether anything is required from them. If the application is working, that should be apparent. If it is waiting, the reason should be understandable. If an action requires approval, the decision should be clear. If something failed, the user should know enough to work out what happens next.

That is a much less dramatic goal than making an AI interface look futuristic, but I think it produces better software. A capable system does not become easier to use by pretending that nothing complicated is happening. It becomes easier to use when the interface manages that complexity well enough that the user does not have to guess. The chat box still has an important place in that design. It is often the most natural way to give the system an instruction and receive an explanation. I simply no longer think it should be expected to carry the entire experience once the software starts acting on those instructions.

DEVELOPMENT DECISION

Treat tool execution, approvals, progress and failure as part of the interface architecture from the beginning instead of adding them around a finished chat screen later. The user does not need to see every internal event, but the states that affect their understanding or require a decision should always be clear.

WHAT I TOOK FROM THIS STAGE

Working on AI interface components has made me much more conscious of the parts of an application that sit between the initial request and the final result. Those intermediate states are easy to treat as secondary because they are not usually the part of the product shown in a screenshot, but they are often where the user decides whether the software feels reliable. The same design principles continue to apply regardless of which AI model or tool system sits behind the interface. Status has to be understandable, important actions need appropriate control, failures need context and the layout has to survive both unpredictable content and smaller screens. The technology behind these systems will continue changing, but the user still needs a clear explanation of what the software is doing and what is expected from them. That is the direction I took with the HK AI Interface Collection. The components are not intended to make an application look like AI software simply for the sake of it. They are built around the operational states that start to matter once AI moves beyond generating a response and becomes part of a larger workflow.