AI Autocomplete Implementation: From Concept to Polished UX
תארו לעצמכם: משתמש מקליד תשובה במסנג׳ר, והאפליקציה מציעה לסיים את המשפט ב־"תודה על האימייל שלך, אשקול את ההצעה שלך." אם ההצעה מופיעה באיחור של 2 שניות או מהבהבת בכל תו — חווית המשתמש נשברת. פתרנו את הבעיה הזו עבור אפליקציית פינטק עם יותר מ־500 אלף משתמשים. התוצאה: 30% מהמשתמשים משתמשים בהשלמה האוטומטית מדי יום, וזמן ההקלדה ירד ב־40%. הפער בין הרעיון ליישום בפועל טמון בפרטי חווית המשתמש והביצועים.
מתי להציג את ההצעה
החלק הכי מוערך בחסר הוא הטריגר. ההצעה לא צריכה להופיע על כל תו. היוריסטיקה שעובדת: אנחנו מספקים השלמה אוטומטית אם המשתמש הקליד לפחות 3 מילים בשורה הנוכחית ועצר ליותר מ־600 אלפיות השנייה, או לחץ על רווח בסוף משפט לא שלם. להלן השוואה של אסטרטגיות טריגר נפוצות.
| היוריסטיקה | השהיה | דיוק | תרחיש לדוגמה |
|---|---|---|---|
| כל תו | 0 אלפיות השנייה | נמוך (הרבה חיוביים כוזבים) | הקלדת כל תו |
| השהיה מעל 600 אלפיות השנייה + מינימום 3 מילים | ~600 אלפיות השנייה | גבוה (95% הצלחה) | המשתמש עוצר כדי לחשוב |
| רווח אחרי סוף משפט | 0 אלפיות השנייה | בינוני (קונטקסטואלי בלבד) | "אני חושב ש. " (רווח) |
מידע נוסף על טריגרים
בפועל, אנחנו משלבים שתי היוריסטיקות: השהיה של יותר מ־600 אלפיות השנייה לאחר הזנת לפחות 15 תווים, ולחיצה על רווח אחרי נקודה, סימן שאלה או סימן קריאה. זה מכסה 95% מהתרחישים שבהם המשתמש מצפה להצעה.
שלבי היישום
כדי ליישם השלמה אוטומטית מבוססת AI, בצעו את השלבים הבאים:
- בחרו מודל (API שרת כמו gpt-4o-mini או על־המכשיר כמו Apple Intelligence/Gemini Nano).
- הגדירו היוריסטיקות טריגר (השהיה, מספר מילים, פיסוק).
- יישמו debounce (600 אלפיות השנייה) ובטלו משימות קודמות.
- שלבו את המודל באמצעות מצב השלמה עם stop tokens וטמפרטורה נמוכה.
- בנו ממשק משתמש מובנה שמציג טקסט אפור אחרי הסמן.
- טפלו במחיקה ובהבהוב (השוו אורך הצעה חדשה).
- בדקו על מספר מכשירים לדיוק וזמן השהיה.
// iOS - триггер автодополнения
private var autocompleteTask: Task<Void, Never>?
func textDidChange(_ textView: UITextView) {
autocompleteTask?.cancel()
let text = textView.text ?? ""
let cursorPosition = textView.selectedRange.location
let textBeforeCursor = String(text.prefix(cursorPosition))
// Не предлагаем в середине слова
guard textBeforeCursor.last == " " || textBeforeCursor.last == "\n" else {
hideAutocomplete()
return
}
// Минимум 15 символов контекста
guard textBeforeCursor.trimmingCharacters(in: .whitespaces).count > 15 else {
return
}
autocompleteTask = Task {
try? await Task.sleep(nanoseconds: 600_000_000) // 600ms debounce
guard !Task.isCancelled else { return }
await fetchAutocomplete(context: textBeforeCursor)
}
} בקשה למודל וניתוח התשובה
אנחנו משתמשים במצב השלמה, לא בצ׳אט. // iOS - триггер автодополнения private var autocompleteTask: Task<Void, Never>? func textDidChange(_ textView: UITextView) { autocompleteTask?.cancel() let text = textView.text ?? "" let cursorPosition = textView.selectedRange.location let textBeforeCursor = String(text.prefix(cursorPosition)) // Не предлагаем в середине слова guard textBeforeCursor.last == " " || textBeforeCursor.last == "\n" else { hideAutocomplete() return } // Минимум 15 символов контекста guard textBeforeCursor.trimmingCharacters(in: .whitespaces).count > 15 else { return } autocompleteTask = Task { try? await Task.sleep(nanoseconds: 600_000_000) // 600ms debounce guard !Task.isCancelled else { return } await fetchAutocomplete(context: textBeforeCursor) } } עם gpt-4o-mini ו־max_tokens: 30 — מהיר וצפוי. עלות API שרת היא בערך $0.01 לכל 1000 טוקנים, כך שכל הצעה עולה בערך $0.0003 — זניח עבור רוב האפליקציות.
struct AutocompleteRequest: Encodable {
let model = "gpt-4o-mini"
let messages: [ChatMessage]
let maxTokens = 30
let temperature = 0.3
let stop = ["\n", "."] // останавливаемся на конце предложения
}
func buildPrompt(context: String) -> [ChatMessage] {
[
ChatMessage(role: "system", content: "Complete the text naturally. Continue from where it ends. Output only the continuation, no commentary."),
ChatMessage(role: "user", content: context)
]
}stop tokens temperature: 0.3 ו־struct AutocompleteRequest: Encodable { let model = "gpt-4o-mini" let messages: [ChatMessage] let maxTokens = 30 let temperature = 0.3 let stop = ["\n", "."] // останавливаемся на конце предложения } func buildPrompt(context: String) -> [ChatMessage] { [ ChatMessage(role: "system", content: "Complete the text naturally. Continue from where it ends. Output only the continuation, no commentary."), ChatMessage(role: "user", content: context) ] } חשובים. בלעדיהם, המודל היה יוצר משפטים מרובים, אבל אנחנו צריכים המשך יחיד.
למה מודלים על־המכשיר לא תמיד מתאימים?
חלופה על־המכשיר — \n — לא עובדת; אנחנו צריכים מודל גנרטיבי. ב־iOS 18+ יש את המסגרת . עם LLM על־המכשיר (Apple Intelligence). באנדרואיד — Gemini Nano דרך CreateML Text Classifier. עם זאת, Gemini Nano זמין ב־Pixel 8+ ובחלק ממכשירי סמסונג — לא פתרון אוניברסלי. לפי Apple Foundation Models, LLM על־המכשיר דורש A17 Pro או M1+. עבור קהל רחב, נדרש גיבוי שרת.
// Android - Gemini Nano on-device (требует поддержки устройства)
val generativeModel = GenerativeModel(
modelName = "gemini-nano",
generationConfig = generationConfig {
maxOutputTokens = 30
temperature = 0.3f
stopSequences = listOf(".", "\n")
}
)
val response = generativeModel.generateContent(
content {
text("Complete naturally: $contextText")
}
)
val completion = response.text?.trim() ?: ""הטבלה שלהלן משווה בין הגישות:
| פרמטר | API שרת (gpt-4o-mini) | על־המכשיר (Apple Intelligence/Gemini Nano) |
|---|---|---|
| זמן השהיה | ~300-800 אלפיות השנייה (תלוי ברשת) | <100 אלפיות השנייה (ללא רשת) |
| אחוז הצלחה | 95% השלמות מדויקות | 85% השלמות מדויקות |
| זמינות | כל מכשיר עם אינטרנט | רק מכשירי דגל |
| פרטיות | נתונים נשלחים לשרת | פרטיות מלאה על־המכשיר |
| עלות | ~$0.0003 לכל הצעה | חינם למפתח |
| תמיכה במצב לא מקוון | לא | כן |
גישה היברידית — על־המכשיר עם גיבוי שרת — נותנת את הטוב משני העולמות.
איך להימנע מהבהוב?
ההצעה מהבהבת. קורה כשבקשה חדשה חוזרת תוך פחות מ־200 אלפיות השנייה ומיד מחליפה את הקודמת. פתרון — הצג רק אם ההצעה החדשה שונה מהנוכחית ביותר מ־3 תווים.
המודל ממשיך טקסט שנמחק. אם המשתמש מחק טקסט — ההקשר להנחיה חייב להיות הגרסה הנוכחית, לא הקודמת. שמרו על Foundation Models מסונכרן עם מצב Google AI Edge SDK בפועל.
Tab נחטף על ידי המערכת. באנדרואיד, // Android - Gemini Nano on-device (требует поддержки устройства) val generativeModel = GenerativeModel( modelName = "gemini-nano", generationConfig = generationConfig { maxOutputTokens = 30 temperature = 0.3f stopSequences = listOf(".", "\n") } ) val response = generativeModel.generateContent( content { text("Complete naturally: $contextText") } ) val completion = response.text?.trim() ?: "" במקלדת הרכה אינו זמין. השתמשו במקש מובנה מותאם אישית או במחוות החלקה ימינה דרך textBeforeCursor.
הצגת ההצעה
תבנית סטנדרטית: טקסט אפור מובנה אחרי הסמן. המשתמש לוחץ Tab או מחליק ימינה — ההצעה מתקבלת. כל קלט אחר מסתיר אותה.
// Android Compose - inline suggestion
@Composable
fun TextFieldWithSuggestion(
value: String,
suggestion: String,
onValueChange: (String) -> Unit,
onAcceptSuggestion: () -> Unit
) {
val annotatedText = buildAnnotatedString {
append(value)
withStyle(SpanStyle(color = Color.Gray.copy(alpha = 0.6f))) {
append(suggestion)
}
}
BasicTextField(
value = TextFieldValue(
annotatedString = annotatedText,
selection = TextRange(value.length) // курсор — после реального текста
),
onValueChange = { tfv ->
val newText = tfv.text.take(value.length + suggestion.length)
if (newText.startsWith(value + suggestion)) {
onAcceptSuggestion()
} else {
onValueChange(tfv.text.take(value.length))
}
},
keyboardActions = KeyboardActions(
onDone = { onAcceptSuggestion() }
)
)
}ב־iOS, הצעה מובנית דרך TextStorage + Tab או פשוט יותר דרך תווית overlay הממוקמת באמצעות GestureDetector.
מה כלול
כשאתם מזמינים יישום השלמה אוטומטית מבוססת AI, אתם מקבלים:
- תיעוד ארכיטקטוני: בחירת גישה (שרת / על־המכשיר / היברידי), סכמת אינטגרציה.
- יישום טריגר ו־debounce תוך התחשבות בחווית משתמש.
- אינטגרציה עם API נבחר או SDK על־המכשיר.
- רכיבי ממשק משתמש להצעה מובנית ב־iOS ואנדרואיד.
- בדיקות על מכשירים אמיתיים: דיוק הצעות, ללא הבהוב, התנהגות נכונה במחיקה.
- הוראות תפעול והמלצות לכיוונון עדין של המודל.
עם ניסיון של יותר מ־5 שנים ויותר מ־20 פרויקטי AI, אנחנו מבטיחים פעולה יציבה של ההצעות. צרו קשר כדי להעריך את הפרויקט שלכם — נקבע את הפתרון האופטימלי תוך 1–2 ימים.
הערכות לוחות זמנים
השלמה אוטומטית בסיסית עם API שרת + ממשק מובנה — 5–8 ימים. על־המכשיר דרך Apple Intelligence / Gemini Nano עם גיבוי שרת — 2–3 שבועות. לוחות זמנים מדויקים תלויים במספר הפלטפורמות, דרישות העיצוב וצרכי התמיכה במצב לא מקוון. קבלו ייעוץ — נחשב את לוח הזמנים לפרויקט שלכם.
בעיות נפוצות והפתרונות שלהן
טעויות יישום נפוצות
- התעלמות ממחיקת טקסט: ההקשר הופך למיושן → המודל משלים תווים שנמחקו. פתרון: סנכרון
// Android Compose - inline suggestion @Composable fun TextFieldWithSuggestion( value: String, suggestion: String, onValueChange: (String) -> Unit, onAcceptSuggestion: () -> Unit ) { val annotatedText = buildAnnotatedString { append(value) withStyle(SpanStyle(color = Color.Gray.copy(alpha = 0.6f))) { append(suggestion) } } BasicTextField( value = TextFieldValue( annotatedString = annotatedText, selection = TextRange(value.length) // курсор — после реального текста ), onValueChange = { tfv -> val newText = tfv.text.take(value.length + suggestion.length) if (newText.startsWith(value + suggestion)) { onAcceptSuggestion() } else { onValueChange(tfv.text.take(value.length)) } }, keyboardActions = KeyboardActions( onDone = { onAcceptSuggestion() } ) ) }אחרי כל שינוי. - ללא debounce: כל תו → בקשת API → יותר מ־500 בקשות בדקה → עומס ועלות טוקנים גבוהה. פתרון: סף של 600 אלפיות השנייה וביטול משימה קודמת.
- התעלמות מ־stop tokens: המודל יוצר משפטים מרובים → ההצעה תופסת חצי מסך. פתרון:
UITextInputו־drawText(in:).







