Building AI Copilots: A Developer's Guide
How to build AI copilots that integrate seamlessly into your product.
AI copilots are embedded assistants that help users accomplish tasks within your application. Unlike chatbots, copilots understand your product's context, can take actions, and proactively suggest next steps. This guide covers how to build them.
What Makes a Copilot Different
| Feature | Chatbot | Copilot |
|---|---|---|
| Context | Conversation only | App state + user data |
| Actions | Can only talk | Can modify the app |
| Initiation | User-initiated | Proactive suggestions |
| Integration | Standalone | Embedded in product |
| Personalization | None | Knows user preferences |
Copilot Architecture
βββββββββββββββββββββββββββββββββββββββββββ
β Your Application β
β βββββββββββββββββββββββββββββββββββ β
β β User Interface β β
β β ββββββββββββββββββββββββββββ β β
β β β Copilot Panel β β β
β β β ββββββββββββββββββββββ β β β
β β β β Chat Interface β β β β
β β β ββββββββββββββββββββββ β β β
β β β ββββββββββββββββββββββ β β β
β β β β Suggested Actions β β β β
β β β ββββββββββββββββββββββ β β β
β β ββββββββββββββββββββββββββββ β β
β βββββββββββββββββββββββββββββββββββ β
β βββββββββββββββββββββββββββββββββββ β
β β Copilot Backend β β
β β ββββββββββββ¬βββββββββββ β β
β β β Context β Action β β β
β β β Provider β Router β β β
β β ββββββββββββ΄βββββββββββ β β
β β ββββββββββββ¬βββββββββββ β β
β β β LLM β Tool β β β
β β β Engine β Executor β β β
β β ββββββββββββ΄βββββββββββ β β
β βββββββββββββββββββββββββββββββββββ β
βββββββββββββββββββββββββββββββββββββββββββ
Building the Context Provider
class CopilotContext {
// Gather context from the application state
async buildContext(userId: string, currentPage: string): Promise<Context> {
return {
// User context
user: await this.getUser(userId),
// Current page context
page: {
route: currentPage,
title: this.getPageTitle(currentPage),
elements: await this.getVisibleElements(currentPage),
},
// Recent actions
recentActions: await this.getRecentActions(userId, limit=10),
// User preferences
preferences: await this.getUserPreferences(userId),
// App-specific data
data: await this.getRelevantData(userId, currentPage),
};
}
async getRelevantData(userId, page) {
// Return data relevant to what user is looking at
switch (page) {
case "/dashboard":
return { metrics: await getDashboardMetrics(userId) };
case "/projects":
return { projects: await getProjects(userId) };
case "/settings":
return { settings: await getSettings(userId) };
}
}
}
Building the Action Router
class CopilotActionRouter {
// Define actions the copilot can take
actions = {
// Navigation actions
"navigate": async (params) => {
window.location.href = params.url;
},
// Data actions
"create_item": async (params) => {
return await api.create(params.resource, params.data);
},
"update_item": async (params) => {
return await api.update(params.resource, params.id, params.data);
},
"delete_item": async (params) => {
return await api.delete(params.resource, params.id);
},
// UI actions
"open_modal": async (params) => {
ui.openModal(params.modal, params.data);
},
"show_notification": async (params) => {
ui.notify(params.message, params.type);
},
"highlight_element": async (params) => {
ui.highlight(params.selector, params.duration);
},
// Help actions
"show_help": async (params) => {
ui.openHelp(params.topic);
},
"start_tutorial": async (params) => {
ui.startTutorial(params.tutorialId);
},
};
async execute(actionName: string, params: any) {
const action = this.actions[actionName];
if (!action) throw new Error(`Unknown action: ${actionName}`);
// Log for audit
await this.logAction(actionName, params);
// Execute
return await action(params);
}
}
The Copilot Agent
class Copilot {
constructor(
private context: CopilotContext,
private actions: CopilotActionRouter,
) {}
async process(userMessage: string, userId: string, page: string): Promise<string> {
// 1. Build context
const ctx = await this.context.buildContext(userId, page);
// 2. Create system prompt with context
const systemPrompt = this.buildPrompt(ctx);
// 3. Get available actions for this page
const availableActions = this.getAvailableActions(page);
// 4. Call LLM
const response = await this.llm.complete({
model: "gpt-4o",
system: systemPrompt,
messages: [{ role: "user", content: userMessage }],
tools: availableActions,
});
// 5. Execute any actions
if (response.toolCalls) {
for (const call of response.toolCalls) {
await this.actions.execute(call.name, call.arguments);
}
}
return response.text;
}
buildPrompt(ctx: Context): string {
return `You are a copilot for ${ctx.app.name}.
User: ${ctx.user.name} (${ctx.user.role})
Current page: ${ctx.page.title} (${ctx.page.route})
Recent actions: ${ctx.recentActions.map(a => a.description).join(", ")}
Available data on this page:
${JSON.stringify(ctx.data, null, 2)}
User preferences: ${JSON.stringify(ctx.preferences)}
You can help the user by:
- Answering questions about their data
- Suggesting actions
- Creating, updating, or deleting items
- Navigating to different pages
- Providing tutorials and help
Be concise, helpful, and proactive. If you notice the user struggling,
offer specific suggestions.`;
}
}
Proactive Suggestions
class ProactiveCopilot:
"""Copilot that suggests actions before the user asks."""
async def check_for_suggestions(self, user_id, current_page):
context = await self.get_context(user_id, current_page)
suggestions = []
# Detect confusion patterns
if await self.is_user_stuck(context):
suggestions.append({
"type": "help",
"message": "It looks like you might be stuck. Want a quick tour?",
"action": "start_tutorial",
})
# Detect incomplete setup
if await self.has_incomplete_setup(context):
suggestions.append({
"type": "setup",
"message": "Your profile is 60% complete. Want to finish it?",
"action": "navigate_to_settings",
})
# Detect optimization opportunities
if await self.can_optimize(context):
suggestions.append({
"type": "optimization",
"message": "I noticed you could save 3 hours/week by automating reports.",
"action": "show_automation_guide",
})
return suggestions
async def is_user_stuck(self, context):
"""Detect if user seems confused."""
# Patterns: repeated visits to help, slow progress, back-and-forth
return (
context.recent_actions.count("visit_help") > 2 or
context.time_on_page > 120 and context.actions_on_page == 0
)
Copilot UI Patterns
Side Panel
ββββββββββββββββββββββββββββββ
β Main Application β Copilot β
β β β
β [App Content] β Chat β
β β history β
β β β
β β βββββββ β
β β βInputβ β
β β βββββββ β
ββββββββββββββββββββββββββββββ΄ββββββββββ
Inline Suggestions
βββββββββββββββββββββββββββββββ
β Email Composer β
β βββββββββββββββββββββββββββ β
β β Subject: [ ] β¨β β
β β β "Quarterly Review?" β β
β βββββββββββββββββββββββββββ β
βββββββββββββββββββββββββββββββ
Action Bar
βββββββββββββββββββββββββββββββ
β Copilot: "I can help with:" β
β [Schedule] [Analyze] [Share]β
βββββββββββββββββββββββββββββββ
Security for Copilots
class SecureCopilot extends Copilot {
// Verify user can perform the action
async execute(actionName: string, params: any, userId: string) {
// Check permissions
const hasPermission = await this.rbac.check(
userId,
actionName,
params.resource,
);
if (!hasPermission) {
return "You don't have permission to do that.";
}
// Sanitize params
const sanitized = this.sanitize(params);
// Execute
return await super.execute(actionName, sanitized);
}
}
Measuring Copilot Success
| Metric | Target | How to Measure |
|---|---|---|
| Usage rate | > 40% of users | Active copilot users / total users |
| Tasks completed via copilot | > 15% | Copilot-triggered actions / total |
| User satisfaction | > 4/5 | Post-interaction rating |
| Time saved | Measurable | Compare task time with/without copilot |
| Deflection rate | > 30% | Support tickets avoided via copilot |
Publishing Copilot Skills
Build copilot functionality as MCP skills that others can use:
# A copilot skill for any application
@mcp_tool(
name="form-filler",
description="Intelligently fill forms based on user data and context",
)
async def fill_form(form_schema, user_data):
filled = await ai.fill_form(form_schema, user_data)
return {"filled_form": filled, "confidence": 0.95}
Publish on SkillExchange so other applications can integrate your copilot skill.
Conclusion
AI copilots represent the next evolution of user interfaces β from passive tools to active assistants that understand context, take actions, and proactively help users succeed.
By building copilots with strong context awareness, action capabilities, and proactive suggestions, you can dramatically improve user experience and productivity in your application.
Learn More
- Building AI Agents with TypeScript
- Custom AI Assistant Development
- Skill-Based AI Architecture
- AI Agent Integration Patterns
Build copilots with skills from SkillExchange.