How to Design User Experience for Generative AI Applications: A 3-Stage Framework
Designing UX for generative AI applications requires balancing creativity, control, and trust through systematic user research, deliberate interaction patterns, and continuous human-in-the-loop iteration.
The microsoft/generative-ai-for-beginners repository provides a practical framework for building user-centric AI interfaces in lesson 12 – Designing UX for AI Applications. This guide translates complex generative model capabilities into intuitive interfaces by aligning technical constraints with genuine user needs.
Understand User Goals and Context
Effective AI UX begins with identifying tasks where generative models add measurable value, such as drafting text, brainstorming ideas, or querying large datasets. The curriculum emphasizes persona-driven scenarios to map specific user workflows and failure-mode analysis to anticipate when models might hallucinate or generate biased outputs.
Designers should document these scenarios in 12-designing-ux-for-ai-applications/README.md, creating decision trees that determine when to automate versus when to require human confirmation. This analysis directly informs the choice of interaction patterns and safety guardrails implemented in the interface.
Prototype Interaction Patterns
The repository outlines three core interaction metaphors that surface the generative nature of AI without overwhelming users. Each pattern addresses different levels of user control and automation.
Prompt-and-Response Chat
The chat interface remains the most flexible pattern, supporting free-form text input with optional guided prompts. To maintain user agency, expose model parameters as tangible UI controls rather than hidden defaults.
The 06-text-generation-apps/python/aoai-chat-temp.py example demonstrates this by implementing a Flask application with a real-time temperature slider:
from flask import Flask, request, jsonify
import openai
app = Flask(__name__)
@app.route('/chat', methods=['POST'])
def chat():
user_message = request.form['message']
temperature = float(request.form['temperature']) # Controlled via UI slider
response = openai.ChatCompletion.create(
model="gpt-4",
messages=[{"role": "user", "content": user_message}],
temperature=temperature,
max_tokens=500
)
return jsonify({
"response": response.choices[0].message.content,
"temperature_used": temperature
})
if __name__ == '__main__':
app.run(debug=True)
This pattern allows users to tune creativity versus determinism on a per-interaction basis, making the generative process transparent and controllable.
Structured Forms
For domain-specific applications, translate user intent into optimized prompts through structured form fields rather than raw text entry. This reduces error rates and guides users toward valid inputs.
The React component in 06-text-generation-apps/typescript/recipe-app/src/PromptForm.tsx illustrates this approach by building a culinary prompt from discrete fields:
import React, { useState } from 'react';
const PromptForm: React.FC = () => {
const [dietaryRestriction, setDietaryRestriction] = useState('');
const [ingredients, setIngredients] = useState('');
const [mealType, setMealType] = useState('');
const constructPrompt = () => {
return `Create a ${dietaryRestriction} ${mealType} recipe using these ingredients: ${ingredients}. Include step-by-step instructions.`;
};
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
const prompt = constructPrompt();
// Send to API with constructed prompt
console.log('Generated prompt:', prompt);
};
return (
<form onSubmit={handleSubmit}>
<select value={dietaryRestriction} onChange={(e) => setDietaryRestriction(e.target.value)}>
<option value="">Select dietary restriction</option>
<option value="vegan">Vegan</option>
<option value="gluten-free">Gluten-Free</option>
</select>
<input
type="text"
value={ingredients}
onChange={(e) => setIngredients(e.target.value)}
placeholder="Enter ingredients (comma-separated)"
/>
<button type="submit">Generate Recipe</button>
</form>
);
};
export default PromptForm;
This abstraction layer shields users from prompt engineering complexities while ensuring the model receives properly formatted, context-rich instructions.
Function-Calling and Tool-Use
The function-calling pattern enables the model to invoke backend APIs on behalf of the user, creating a seamless "tool-use" experience where AI acts as an orchestrator rather than just a text generator.
As implemented in 11-integrating-with-function-calling/python/function-call-example.py, this pattern requires defining available tools and letting the model decide when to invoke them:
import openai
import json
def get_calendar_events(date):
"""Mock function to retrieve calendar events"""
return {"date": date, "events": ["Team Standup", "Client Review"]}
tools = [
{
"type": "function",
"function": {
"name": "get_calendar_events",
"description": "Get calendar events for a specific date",
"parameters": {
"type": "object",
"properties": {
"date": {"type": "string", "description": "Date in YYYY-MM-DD format"}
},
"required": ["date"]
}
}
}
]
response = openai.ChatCompletion.create(
model="gpt-4-0613",
messages=[{"role": "user", "content": "What meetings do I have tomorrow?"}],
tools=tools,
tool_choice="auto"
)
# Check if model wants to call a function
if response.choices[0].message.get("tool_calls"):
tool_call = response.choices[0].message["tool_calls"][0]
function_name = tool_call["function"]["name"]
arguments = json.loads(tool_call["function"]["arguments"])
# Execute the function
if function_name == "get_calendar_events":
result = get_calendar_events(**arguments)
print(f"Retrieved events: {result}")
This pattern bridges the gap between conversational AI and actionable workflows, allowing users to complete complex tasks through natural language while maintaining system security through controlled API access.
Iterate with Human-in-the-Loop Feedback
Deploying a minimal viable UI is only the beginning. The framework recommends implementing reinforcement loops that capture real-world interactions to refine both the interface and underlying prompts.
Key mechanisms include:
- Logging prompt/response pairs for error analysis and bias detection
- Clarifying questions triggered when model confidence falls below thresholds
- Undo and regenerate controls that let users correct undesired outputs without friction
The 06-text-generation-apps/javascript/web-ui-controls.html file demonstrates user correction workflows:
<!DOCTYPE html>
<html>
<head>
<title>AI Assistant with Controls</title>
</head>
<body>
<div id="chat-container"></div>
<button id="regenerate-btn" onclick="regenerateLastResponse()">Regenerate Response</button>
<button id="undo-btn" onclick="undoLastAction()">Undo</button>
<script>
let conversationHistory = [];
let lastResponse = null;
async function regenerateLastResponse() {
if (!lastResponse) return;
// Call API again with same context but different seed/temperature
const response = await fetch('/api/chat', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({
messages: conversationHistory,
regenerate: true, // Signal to backend to vary output
temperature: 0.9 // Increase variability
})
});
const data = await response.json();
updateUI(data.content);
}
function undoLastAction() {
if (conversationHistory.length > 0) {
conversationHistory.pop(); // Remove last exchange
lastResponse = conversationHistory[conversationHistory.length - 1]?.assistant;
renderConversation();
}
}
</script>
</body>
</html>
These controls preserve user autonomy when AI outputs miss the mark, transforming potential frustration into opportunities for iterative refinement.
Summary
Designing UX for generative AI applications requires balancing technical capabilities with human-centered design principles:
- Understand User Goals before writing code, mapping persona-driven scenarios and failure modes in
12-designing-ux-for-ai-applications/README.md - Choose Appropriate Patterns – chat interfaces for flexibility, structured forms for precision, and function-calling for action-oriented workflows
- Expose Model Parameters such as
temperatureandmax_tokensas intuitive UI controls rather than hidden configuration - Implement Feedback Loops with logging, clarifying questions, and undo/regenerate functionality to build trust through user control
Frequently Asked Questions
What are the three pillars of AI-centric UX?
According to the microsoft/generative-ai-for-beginners curriculum, successful generative AI interfaces balance creativity (allowing the model to generate novel outputs), control (giving users mechanisms to guide and constrain generation), and trust (through transparency, undo capabilities, and clear error handling). These pillars ensure users feel confident collaborating with AI systems.
How do I expose model parameters like temperature in the UI?
As demonstrated in 06-text-generation-apps/python/aoai-chat-temp.py, expose parameters as interactive sliders or dropdowns that map to the API call. For example, implement a temperature slider ranging from 0.0 to 1.0 where lower values produce deterministic outputs and higher values increase creativity. Always display the current setting to maintain transparency about the generation process.
What is the function-calling UX pattern?
Function-calling enables the AI to execute external tools or APIs on behalf of the user, as shown in 11-integrating-with-function-calling/python/function-call-example.py. The UI presents a natural language interface while the backend defines available functions (like calendar lookups or database queries). When the model detects a need for external data, it generates a structured function call rather than text, executes the action, and incorporates the results into its response.
How do I handle AI hallucinations in the user interface?
Implement failure-mode analysis during the design phase to anticipate hallucination scenarios, then build safeguards into the UI. Include clarifying question prompts when confidence is low, provide source attribution where possible, and add "regenerate" buttons (as seen in 06-text-generation-apps/javascript/web-ui-controls.html) so users can quickly obtain alternative outputs. Logging these incidents allows for prompt engineering improvements over time.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →