Home Gallery AISPA Paper GitHub Follow

customer-service system prompt

Category: Extracted prompts. Audited against the AISPA standard.

2 Prompts on record
0 Flagged instructions
AI audit Audit source
D1 · Identity Transparency D2 · Truthfulness & Information Integrity D3 · Privacy & Data Protection D4 · Tool/Action Safety D5 · User Agency & Manipulation Prevention D6 · Unsafe Request Handling D7 · Harm Prevention & User Safety D8 · Fairness, Inclusion & Neutrality

customer-service - customer service / owly / engine

8747 characters

import OpenAI from "openai"; import { prisma } from "@/lib/prisma"; import { owlyTools, executeToolCall } from "./tools"; import { emitNewMessage } from "@/lib/realtime"; import { analyzeSentiment, detectIntent, estimateConfidence, requiresHumanApproval } from "./guardrails"; import type { AIMessage, AIConfig, ConversationContext, KnowledgeItem, } from "./types"; function buildSystemPrompt(context: ConversationContext): string { const toneGuide: Record<string, string> = { friendly: "Be warm, approachable, and conversational. Use a casual but professional tone.", professional: "Be polished and business-like. Maintain a confident, competent tone while remaining personable.", formal: "Be professional, polished, and courteous. Use formal language and proper grammar.", technical: "Be precise and detailed. Use technical terminology when appropriate and provide thorough explanations.", }; const knowledgeSection = context.knowledgeBase.length > 0 ? context.knowledgeBase .sort((a, b) => b.priority - a.priority) .map( (k) => `[${k.category}] ${k.title}:\n${k.content}` ) .join("\n\n---\n\n") : "No specific knowledge base entries available. Answer based on general knowledge about the business."; return `You are Owly, the AI customer support assistant for ${context.businessName}. ${context.businessDesc ? `About the business: ${context.businessDesc}` : ""} ## Communication Style ${toneGuide[context.tone] || toneGuide.friendly} ${context.language !== "auto" ? `Always respond in: ${context.language}` : "Respond in the same language the customer uses."} ## Your Knowledge Base Use the following information to answer customer questions accurately: ${knowledgeSection} ## Important Guidelines - Always be helpful and try to resolve the customer's issue - If you cannot answer a question from the knowledge base, honestly say so and offer to connect them with a team member - Use the create_ticket tool when a customer reports a problem that needs human intervention - Use send_internal_email to notify relevant team members about urgent issues - Use get_customer_history to check if the customer has contacted before - Never make up information that isn't in your knowledge base - Keep responses concise but thorough - The customer is contacting via: ${context.channel} ${context.customerName !== "Unknown" ? `- Customer name: ${context.customerName}` : ""} ## Customer History ${context.customerHistory.length > 0 ? context.customerHistory.join("\n") : "This is the customer's first interaction."}`; } async function getKnowledgeBase(): Promise<KnowledgeItem[]> { const entries = await prisma.knowledgeEntry.findMany({ where: { isActive: true }, include: { category: true }, orderBy: { priority: "desc" }, }); return entries.map((e: { category: { name: string }; title: string; content: string; priority: number }) => ({ category: e.category.name, title: e.title, content: e.content, priority: e.priority, })); } async function getAIConfig(): Promise<AIConfig & ConversationContext> { let settings = await prisma.settings.findFirst(); if (!settings) { settings = await prisma.settings.create({ data: { id: "default" } }); } return { provider: settings.aiProvider, model: settings.aiModel, apiKey: settings.aiApiKey, maxTokens: settings.maxTokens, temperature: settings.temperature, businessName: settings.businessName, businessDesc: settings.businessDesc, welcomeMessage: settings.welcomeMessage, tone: settings.tone, language: settings.language, knowledgeBase: [], customerName: "", customerHistory: [], channel: "", }; } export async function chat( conversationId: string, userMessage: string ): Promise<string> { const config = await getAIConfig(); if (!config.apiKey) { return "AI is not configured. Please add your API key in Settings > AI Configuration."; } const conversation = await prisma.conversation.findUnique({ where: { id: conversationId }, include: { messages: { orderBy: { createdAt: "asc" }, take: 50 }, }, }); if (!conversation) { return "Conversation not found."; } const knowledgeBase = await getKnowledgeBase(); const context: ConversationContext = { ...config, knowledgeBase, customerName: conversation.customerName, channel: conversation.channel, customerHistory: [], }; // Build message history const messages: AIMessage[] = [ { role: "system", content: buildSystemPrompt(context) }, ]; for (const msg of conversation.messages) { if (msg.role === "customer") { messages.push({ role: "user", content: msg.content }); } else if (msg.role === "assistant") { messages.push({ role: "assistant", content: msg.content }); } } messages.push({ role: "user", content: userMessage }); // Guardrails: check if human approval needed const approval = requiresHumanApproval(userMessage); if (approval.required) { const sentiment = analyzeSentiment(userMessage); const intent = detectIntent(userMessage); // Store metadata for dashboard visibility await prisma.conversation.update({ where: { id: conversationId }, data: { metadata: { escalationReason: approval.reason, sentiment: sentiment.sentiment, intent: intent.intent, }, }, }); } // Save user message await prisma.message.create({ data: { conversationId, role: "customer", content: userMessage, }, }); // Call AI const response = await callAI(config, messages, conversationId); // Save assistant message const savedMessage = await prisma.message.create({ data: { conversationId, role: "assistant", content: response, }, }); // Update conversation timestamp await prisma.conversation.update({ where: { id: conversationId }, data: { updatedAt: new Date() }, }); // Confidence scoring const confidence = estimateConfidence(response, knowledgeBase.length, false); if (confidence.shouldEscalate) { await prisma.conversation.update({ where: { id: conversationId }, data: { status: "escalated" }, }); } emitNewMessage(conversationId, { id: savedMessage.id, role: "assistant", content: response }); return response; } async function callAI( config: AIConfig, messages: AIMessage[], conversationId: string, depth = 0 ): Promise<string> { if (depth > 5) { return "I apologize, but I'm having trouble processing your request. Let me connect you with a team member."; } const openai = new OpenAI({ apiKey: config.apiKey }); let response; try { response = await openai.chat.completions.create({ model: config.model, messages: messages as OpenAI.ChatCompletionMessageParam[], tools: owlyTools as OpenAI.ChatCompletionTool[], max_tokens: config.maxTokens, temperature: config.temperature, }); } catch { return "I'm temporarily unable to process your request. Please try again in a moment, or I can connect you with a team member."; } const choice = response.choices[0]; if ( choice.finish_reason === "tool_calls" && choice.message.tool_calls?.length ) { // Process tool calls const toolCalls = choice.message.tool_calls as Array<{ id: string; type: string; function: { name: string; arguments: string }; }>; messages.push({ role: "assistant", content: choice.message.content || "", tool_calls: toolCalls.map((tc) => ({ id: tc.id, type: "function" as const, function: { name: tc.function.name, arguments: tc.function.arguments, }, })), }); for (const toolCall of toolCalls) { const args = JSON.parse(toolCall.function.arguments); const result = await executeToolCall( toolCall.function.name, args, conversationId ); messages.push({ role: "tool", content: result, tool_call_id: toolCall.id, }); } // Continue the conversation with tool results return callAI(config, messages, conversationId, depth + 1); } return choice.message.content || "I apologize, I could not generate a response."; } export async function createNewConversation( channel: string, customerName: string, customerContact: string, customerId?: string ) { return prisma.conversation.create({ data: { channel, customerName, customerContact, ...(customerId && { customerId }), }, }); }

customer-service - customer service / owly / guardrails

7276 characters

/** * AI Guardrails - Controls what the AI can and cannot do. */ export interface GuardrailConfig { blockedTopics: string[]; maxResponseLength: number; requireHumanApproval: string[]; confidenceThreshold: number; } const DEFAULT_GUARDRAILS: GuardrailConfig = { blockedTopics: [ "legal advice", "medical advice", "investment advice", "price commitments", "competitor comparisons", ], maxResponseLength: 2000, requireHumanApproval: [ "refund", "cancellation", "discount", "compensation", "legal", ], confidenceThreshold: 0.6, }; /** * Check if a message contains blocked topics. */ export function checkBlockedTopics( message: string, config: GuardrailConfig = DEFAULT_GUARDRAILS ): { blocked: boolean; topic?: string } { const lower = message.toLowerCase(); for (const topic of config.blockedTopics) { if (lower.includes(topic.toLowerCase())) { return { blocked: true, topic }; } } return { blocked: false }; } /** * Check if a message requires human approval before AI responds. */ export function requiresHumanApproval( message: string, config: GuardrailConfig = DEFAULT_GUARDRAILS ): { required: boolean; reason?: string } { const lower = message.toLowerCase(); for (const keyword of config.requireHumanApproval) { if (lower.includes(keyword.toLowerCase())) { return { required: true, reason: keyword }; } } return { required: false }; } /** * Truncate AI response to maximum allowed length. */ export function enforceResponseLength( response: string, config: GuardrailConfig = DEFAULT_GUARDRAILS ): string { if (response.length <= config.maxResponseLength) return response; return response.substring(0, config.maxResponseLength).trimEnd() + "..."; } /** * Analyze sentiment of a message. * Returns: positive, negative, neutral, or frustrated */ export function analyzeSentiment(message: string): { sentiment: "positive" | "negative" | "neutral" | "frustrated"; score: number; } { const lower = message.toLowerCase(); const negativePatterns = [ "angry", "furious", "terrible", "worst", "hate", "awful", "disgusting", "unacceptable", "ridiculous", "horrible", "scam", "fraud", "sue", "lawyer", "complaint", ]; const frustratedPatterns = [ "again", "still", "waiting", "how long", "not working", "broken", "frustrated", "annoyed", "disappointed", "already told", "third time", "keep asking", ]; const positivePatterns = [ "thank", "great", "excellent", "awesome", "love", "perfect", "amazing", "wonderful", "fantastic", "helpful", "appreciate", "satisfied", "happy", ]; let score = 0; for (const p of negativePatterns) { if (lower.includes(p)) score -= 2; } for (const p of frustratedPatterns) { if (lower.includes(p)) score -= 1; } for (const p of positivePatterns) { if (lower.includes(p)) score += 2; } if (score <= -3) return { sentiment: "negative", score: Math.max(-1, score / 10) }; if (score <= -1) return { sentiment: "frustrated", score: score / 10 }; if (score >= 2) return { sentiment: "positive", score: Math.min(1, score / 10) }; return { sentiment: "neutral", score: 0 }; } /** * Detect customer intent from message. */ export function detectIntent(message: string): { intent: string; confidence: number; } { const lower = message.toLowerCase(); const intents: { intent: string; keywords: string[]; weight: number }[] = [ { intent: "support", keywords: ["help", "issue", "problem", "not working", "broken", "error", "bug", "fix"], weight: 1 }, { intent: "billing", keywords: ["invoice", "bill", "charge", "payment", "refund", "price", "cost", "subscription", "plan"], weight: 1 }, { intent: "sales", keywords: ["buy", "purchase", "pricing", "demo", "trial", "interested", "quote", "proposal"], weight: 1 }, { intent: "complaint", keywords: ["complaint", "unhappy", "dissatisfied", "terrible", "worst", "unacceptable", "sue"], weight: 1.5 }, { intent: "information", keywords: ["how", "what", "when", "where", "can i", "do you", "is it", "tell me"], weight: 0.5 }, { intent: "cancellation", keywords: ["cancel", "unsubscribe", "stop", "close account", "terminate", "end service"], weight: 1.5 }, { intent: "feedback", keywords: ["suggest", "feedback", "improve", "feature request", "would be nice", "wish"], weight: 1 }, { intent: "greeting", keywords: ["hello", "hi", "hey", "good morning", "good afternoon"], weight: 0.3 }, ]; let bestMatch = { intent: "general", confidence: 0 }; for (const { intent, keywords, weight } of intents) { let matchCount = 0; for (const kw of keywords) { if (lower.includes(kw)) matchCount++; } const confidence = Math.min(1, (matchCount * weight) / 3); if (confidence > bestMatch.confidence) { bestMatch = { intent, confidence }; } } return bestMatch; } /** * Generate a conversation summary from messages. */ export function generateSummaryPrompt( messages: { role: string; content: string }[] ): string { const transcript = messages .slice(-20) .map((m) => `${m.role}: ${m.content.substring(0, 300)}`) .join("\n"); return `Summarize this customer support conversation in 2-3 sentences. Focus on: what the customer needed, what was done, and the outcome. Conversation: ${transcript} Summary:`; } /** * Calculate AI confidence score based on response characteristics. */ export function estimateConfidence( response: string, knowledgeBaseSize: number, hasToolCalls: boolean ): { score: number; shouldEscalate: boolean } { let score = 0.5; // Knowledge base coverage if (knowledgeBaseSize > 20) score += 0.1; if (knowledgeBaseSize > 50) score += 0.1; // Response quality indicators if (response.length > 50 && response.length < 1500) score += 0.1; if (hasToolCalls) score += 0.1; // Uncertainty indicators const uncertainPhrases = [ "i'm not sure", "i don't know", "i cannot", "i apologize", "unfortunately", "i'm unable", "beyond my knowledge", "connect you with", "team member", ]; const lower = response.toLowerCase(); for (const phrase of uncertainPhrases) { if (lower.includes(phrase)) { score -= 0.15; break; } } score = Math.max(0, Math.min(1, score)); return { score: Math.round(score * 100) / 100, shouldEscalate: score < DEFAULT_GUARDRAILS.confidenceThreshold, }; } /** * Generate suggested replies for an agent based on conversation context. */ export function generateSuggestedRepliesPrompt( messages: { role: string; content: string }[], cannedResponses: { title: string; content: string }[] ): string { const lastMessages = messages.slice(-5); const transcript = lastMessages .map((m) => `${m.role}: ${m.content.substring(0, 200)}`) .join("\n"); const canned = cannedResponses .slice(0, 5) .map((c) => `- ${c.title}: ${c.content.substring(0, 100)}`) .join("\n"); return `Based on this conversation, suggest 3 short professional replies the support agent could send. Each reply should be 1-2 sentences. Recent messages: ${transcript} ${canned ? `Available templates:\n${canned}\n` : ""} Return exactly 3 suggestions as a JSON array of strings.`; }

All prompts here were collected from publicly available sources and are reproduced for transparency research. Browse the extracted prompts category, the full gallery of 400+ products, or read the paper behind the AISPA standard.