נציג תמיכה עונה לכרטיס ה-80 של היום. התשובה סטנדרטית — "הבקשה שלך התקבלה, אנחנו מטפלים בה" — אבל בכל פעם הוא צריך להקליד אותה או לחפש בתבניות. לפי הסטטיסטיקה, נציג מבזבז עד 30% מזמנו על כתיבת תשובות חוזרות. יצירת תשובות באמצעות AI אינה מחליפה את הנציג; היא מסירה עבודה מכנית: טיוטת תשובה מוכנה תוך שניות, הנציג עורך ושולח אותה. עם זאת, הטמעת מערכת כזו באפליקציה הניידת של הנציג (לא של הלקוח) מציבה אתגרים טכניים: עורך מהיר עם השלמת טקסט, סטרימינג של תגובת ה-LLM, וסנכרון עם היסטוריית השיחה. הניסיון שלנו — למעלה מ-5 שנים בפיתוח מובייל — מראה שארכיטקטורה נכונה מקצרת את זמן התגובה ב-40–60% כבר בשבוע הראשון. כפי שהניסיון שלנו מראה, זמן התגובה יורד ב-55%. החיסכון לכל נציג מגיע עד $400–580 לחודש (כ-$500 USD), וזמן ההחזר הוא 2–3 חודשים. עבור צוות של 10 נציגים, החיסכון השנתי עולה על $60,000 USD.
יצירה קונטקסטואלית והקשר הכרטיס
הטעות המרכזית היא להעביר ל-LLM רק את ההודעה האחרונה של המשתמש. תשובה טובה דורשת הקשר: שיחות קודמות, סטטוס הזמנה, תוכנית הלקוח. אנחנו בונים בקשה ל-OpenAI עם הקשר מלא:
// iOS struct ResponseGenerationRequest: Encodable {
let model = "gpt-4o-mini"
let stream = true
let messages: [ChatMessage]
}
func buildMessages(ticket: Ticket, history: [Message], agentKnowledgeBase: String) -> [ChatMessage] {
var messages = [ChatMessage]()
messages.append(ChatMessage(
role: "system",
content: """
Ты — оператор поддержки \(companyName). Пиши кратко, по делу, без воды. База знаний:\n\(agentKnowledgeBase) Статус заказа клиента: \(ticket.orderStatus ?? "нет данных")
"""
))
history.suffix(6).forEach { msg in
messages.append(ChatMessage(role: msg.role, content: msg.text))
}
messages.append(ChatMessage(role: "user", content: ticket.latestMessage))
return messages
}// iOS struct ResponseGenerationRequest: Encodable { let model = "gpt-4o-mini" let stream = true let messages: [ChatMessage] } func buildMessages(ticket: Ticket, history: [Message], agentKnowledgeBase: String) -> [ChatMessage] { var messages = [ChatMessage]() messages.append(ChatMessage( role: "system", content: """ Ты — оператор поддержки \(companyName). Пиши кратко, по делу, без воды. База знаний:\n\(agentKnowledgeBase) Статус заказа клиента: \(ticket.orderStatus ?? "нет данных") """ )) history.suffix(6).forEach { msg in messages.append(ChatMessage(role: msg.role, content: msg.text)) } messages.append(ChatMessage(role: "user", content: ticket.latestMessage)) return messages } — אנחנו לוקחים את 6 ההודעות האחרונות, לא את כל ההיסטוריה. הקשר ארוך מגדיל עלות וזמן תגובה, ולרוב הכרטיסים 3–4 הודעות אחרונות מספיקות. במידת הצורך, אנחנו מחברים RAG לחיפוש במאגר הידע.
סטרימינג: מהיר פי 10 מיצירה ללא סטרימינג עבור נציגים במובייל
ללא סטרימינג, הנציג ממתין 2–5 שניות ל-LLM שייצור את התשובה המלאה. עם suffix(6), המילים הראשונות מופיעות תוך 300–500 אלפיות השנייה. זה קריטי לחוויית המשתמש בממשק הנציג הנייד — הנציג לא צריך לשבת מול אינדיקטור טעינה. סטרימינג מנצח יצירה ללא סטרימינג פי 10 במהירות התחלתית: 300 אלפיות השנייה לעומת 3 שניות.
// Парсим SSE-поток
func streamResponse(for request: URLRequest) -> AsyncStream<String> {
AsyncStream { continuation in
let task = URLSession.shared.dataTask(with: request) { data, response, error in
// не подходит для стриминга
}
// Используем URLSession.bytes для SSE
Task {
let (bytes, _) = try await URLSession.shared.bytes(for: request)
for try await line in bytes.lines {
guard line.hasPrefix("data: "),
let json = line.dropFirst(6).data(using: .utf8),
let chunk = try? JSONDecoder().decode(StreamChunk.self, from: json),
let text = chunk.choices.first?.delta.content else { continue }
continuation.yield(text)
}
continuation.finish()
}
}
}באנדרואיד אנחנו משתמשים ב-stream: true עם // Парсим SSE-поток func streamResponse(for request: URLRequest) -> AsyncStream<String> { AsyncStream { continuation in let task = URLSession.shared.dataTask(with: request) { data, response, error in // не подходит для стриминга } // Используем URLSession.bytes для SSE Task { let (bytes, _) = try await URLSession.shared.bytes(for: request) for try await line in bytes.lines { guard line.hasPrefix("data: "), let json = line.dropFirst(6).data(using: .utf8), let chunk = try? JSONDecoder().decode(StreamChunk.self, from: json), let text = chunk.choices.first?.delta.content else { continue } continuation.yield(text) } continuation.finish() } } } מ-OkHttp או מנתחים EventSourceListener שורה אחר שורה.
| פרמטר | ללא סטרימינג | עם סטרימינג |
|---|---|---|
| זמן עד למילה הראשונה | 2–5 שניות | 300–500 אלפיות השנייה |
| חוויית משתמש | הנציג ממתין | הטקסט מופיע בהדרגה |
| עומס רשת | כל התשובה בבת אחת | נתחים תוך כדי יצירה |
עורך טיוטות עם ניתוח עריכות
הטקסט שנוצר הוא טיוטה, לא תשובה סופית. הממשק חייב לכלול:
- שדה העריכה נפתח ישירות עם הטקסט — הנציג רואה שהוא יכול לערוך
- כפתור "צור מחדש" לגרסה חדשה באותו נושא
- "התאם טון": רשמי יותר / ניטרלי / אמפתי — תוספת סיומת להנחיה
- מונה שינויים ביחס למקור — כדי לעקוב איך נציגים עורכים את ה-AI (ניתוח עריכות)
// Android Compose @Composable fun ResponseEditor(
aiDraft: String,
onSend: (String) -> Unit,
onRegenerate: () -> Unit
) {
var editedText by remember { mutableStateOf(aiDraft) }
val editDistance = remember(editedText, aiDraft) {
levenshteinDistance(aiDraft, editedText) // кастомная утилита
}
Column {
OutlinedTextField(
value = editedText,
onValueChange = { editedText = it },
modifier = Modifier.fillMaxWidth().heightIn(min = 120.dp)
)
Row {
Text("Правок: $editDistance символов", style = MaterialTheme.typography.labelSmall)
Spacer(Modifier.weight(1f))
TextButton(onClick = onRegenerate) {
Text("Переписать")
}
Button(onClick = { onSend(editedText) }) {
Text("Отправить")
}
}
}
}מונה השינויים אינו רק קישוט ממשק. הוא מתועד בניתוח: אם נציגים עורכים יותר מ-50% מהטקסט, המודל לא מכוון היטב למאגר הידע. בפרויקטים שלנו, אנחנו מבטיחים ≤30% עריכות לאחר כיול.
מאגר ידע ושילוב RAG
לשאלות ספציפיות על מוצר, ה-LLM ממציא תשובות ללא הקשר. אנחנו מחברים RAG (Retrieval-Augmented Generation): לפני יצירת תשובה, אנחנו מבצעים חיפוש וקטורי בתיעוד הפנימי ומכניסים קטעים רלוונטיים להנחיית המערכת. בשרת: Pinecone, Weaviate, או pgvector (אם PostgreSQL כבר קיים). לקוח הנייד לא משתתף — הוא פשוט מקבל את הנחיית המערכת המוכנה מהשרת.
מידע נוסף על הגדרת RAG
- אינדוקס מסמכים במסד נתונים וקטורי.
- יצירת embeddings דרך OpenAI Embeddings API.
- הגדרת רלוונטיות (top-k = 3–5).
- שילוב בצינור היצירה.
שלבי יישום ולוח זמנים
השירות המקצה-לקצה שלנו עוקב אחר תהליך מובנה:
- גילוי (1–2 ימים): הערכת מערכת הכרטיסים הנוכחית שלך, שילוב עם OpenAI API, ותכנון הגדרת סטרימינג.
- פיתוח ליבה (1.5–2 שבועות): הטמעת סטרימינג של LLM עם עורך טיוטות ב-iOS (Swift) ואנדרואיד (Kotlin), כולל ניתוח עריכות והתאמת טון.
- RAG בשרת (1–2 שבועות): הקמת מסד נתונים וקטורי, צינור embeddings, וחיבור למאגר הידע שלך.
- בדיקות וכיול (3–5 ימים): הפחתת שיעור העריכות ל-≤30% על ידי כוונון הנחיות ופרמטרי RAG.
- פריסה והדרכה (2–3 ימים): שחרור לנציגים, מתן תיעוד, והעברת מפגשי הדרכה.
| שלב | לוח זמנים |
|---|---|
| יצירה בסיסית ללא סטרימינג | 2–3 ימים |
| עורך עם סטרימינג + התאמת טון | 1.5–2 שבועות |
| שילוב RAG בשרת | 1–2 שבועות |
| מחזור מקצה-לקצה מלא | 3–4 שבועות |
הניסיון שלנו — למעלה מ-5 שנים בפיתוח מובייל ו-10+ פרויקטים עם שילוב AI. צרו קשר כדי לדון בפרטים.







