Разработка AI-агента для мобильного приложения с интеграцией внешних API
Мы создаём AI-агентов, которые сами решают, какие внешние API вызвать и в какой последовательности. Пользователь пишет «забронируй мне перелёт в Берлин на следующую пятницу, отель рядом с центром до 100 евро» — агент, используя function calling, ищет рейсы, выбирает оптимальный, ищет отели по критериям, сравнивает варианты и предлагает подтвердить. Несколько API, несколько шагов, минимум вмешательства. Наш опыт показывает: правильно спроектированный агент сокращает время пользователя на 70% по сравнению с ручным поиском. Наша команда имеет 5+ лет опыта в мобильной разработке и AI-интеграциях, реализовала более 30 проектов с агентной архитектурой. Мы уже внедряли такие решения для туризма, логистики и финансов — клиенты экономят до 40% бюджета на интеграции благодаря переиспользованию инструментов.
Как работает orchestration loop?
Сердце агента — цикл: LLM → tool_calls → execute → LLM → ... На мобильном этот цикл живёт либо на клиенте, либо на сервере (рекомендуем второй вариант для сложных агентов). Клиентский цикл уместен для 2–4 инструментов без длинных цепочек зависимостей.
// Android — агентный цикл
suspend fun runAgent(userMessage: String): String {
val messages = mutableListOf(Message(role = "user", content = userMessage))
repeat(MAX_ITERATIONS) {
val response = llmClient.complete(messages, tools = availableTools)
if (response.finishReason == "stop") return response.content ?: ""
if (response.finishReason == "tool_calls") {
messages.add(response.toAssistantMessage())
response.toolCalls.map { call ->
async { toolDispatcher.dispatch(call.name, call.arguments) }
}.awaitAll().forEachIndexed { i, result ->
messages.add(Message(role = "tool", toolCallId = response.toolCalls[i].id, content = result))
}
}
}
return "Агент не смог завершить задачу за $MAX_ITERATIONS шагов"
}
MAX_ITERATIONS — критически важная защита. Без неё агент может зациклиться и выжечь бюджет токенов. Для большинства задач 10 итераций достаточно.
Какие проблемы возникают при доступе к внешним API?
Аутентификация и безопасность токенов. Агент вызывает внешние API от имени пользователя — нужны токены (OAuth, API key). Никогда не храните чужие API-ключи в мобильном приложении открытым текстом. Правильная схема: мобильный клиент → ваш бэкенд (с валидацией) → внешний API. Бэкенд хранит токены, проксирует запросы, логирует вызовы. Иначе ключ Google Maps или Booking API утечёт через декомпиляцию APK. Мы используем шифрование на стороне сервера и короткоживущие токены.
Rate limiting и таймауты. Внешние API ограничивают запросы. Если агент делает 5 запросов к одному сервису за 2 секунды, получает 429 Too Many Requests. Нужен retry с exponential backoff: первый retry через 1с, второй через 2с, третий через 4с. OkHttp Interceptor позволяет реализовать это прозрачно для всех вызовов. В наших проектах мы добавляем до 3 ретраев и пул соединений.
Непредсказуемые ответы API. Внешние API возвращают данные в разных форматах, с разными кодами ошибок, иногда возвращают HTML вместо JSON при ошибках инфраструктуры. Каждый инструмент должен возвращать агенту понятный текст ошибки, а не бросать исключение. Агент умеет работать с ошибками — если ему передать {"error": "Авиакомпания недоступна, попробуй другую"}, он переключится на альтернативу.
Почему описание инструментов решает всё?
Архитектурно каждый внешний API — это один или несколько инструментов с чётким описанием. Пример для API бронирования авиабилетов:
{
"name": "search_flights",
"description": "Ищет доступные рейсы. Используй ТОЛЬКО когда пользователь хочет найти или забронировать перелёт. Не используй для отелей или трансфера.",
"parameters": {
"origin": {"type": "string", "description": "IATA-код аэропорта отправления, например MSQ, SVO"},
"destination": {"type": "string", "description": "IATA-код аэропорта назначения"},
"date": {"type": "string", "description": "Дата в формате YYYY-MM-DD"},
"passengers": {"type": "integer", "default": 1}
}
}
Слово «ТОЛЬКО» в описании важно — без явных ограничений модель может вызвать инструмент в неподходящем контексте.
Серверный или клиентский агент: что выбрать?
| Характеристика | Клиентский агент | Серверный агент |
|---|---|---|
| Количество инструментов | 2–4 | 5+ |
| Безопасность токенов | Низкая (ключи на клиенте) | Высокая (ключи на сервере) |
| Продолжение при сворачивании | Нет | Да (через WebSocket) |
| Кеширование | Ограниченное | Полное |
| Сложность реализации | 1–2 недели | 4–7 недель |
Серверный агент в 3 раза безопаснее клиентского при 5+ API, так как токены не покидают сервер. Для агентов с доступом к 5+ API, долгими цепочками вызовов или чувствительными данными рекомендуем серверную оркестрацию. Клиент отправляет задачу, получает обновления через WebSocket или long polling, рендерит прогресс. Это позволяет:
- Продолжать работу агента при сворачивании приложения
- Кешировать промежуточные результаты
- Логировать каждый шаг для дебага
- Не раскрывать ключи внешних API на клиенте
На мобильном остаётся только UI: прогресс-индикатор шагов агента, возможность отмены, итоговая карточка с результатом и подтверждением действия.
Примерные сроки разработки AI-агента
| Сложность | Количество API | Тип | Сроки |
|---|---|---|---|
| Базовая | 1–2 | Клиентский | 2–3 недели |
| Средняя | 3–5 | Серверный | 4–7 недель |
| Сложная | 6+ | Серверный с микросервисами | 8–12 недель |
Пример кода для iOS (Swift)
// iOS - агентный цикл
func runAgent(userMessage: String) async -> String {
var messages = [Message(role: "user", content: userMessage)]
for _ in 0..<MAX_ITERATIONS {
let response = await llmClient.complete(messages, tools: availableTools)
if response.finishReason == "stop" { return response.content ?? "" }
if response.finishReason == "toolCalls" {
messages.append(response.toAssistantMessage())
let results = await withTaskGroup(of: (id: String, content: String).self) { group in
for call in response.toolCalls {
group.addTask { await (call.id, toolDispatcher.dispatch(call.name, call.arguments)) }
}
var dict = [String: String]()
for await result in group { dict[result.id] = result.content }
return dict
}
for call in response.toolCalls {
messages.append(Message(role: "tool", toolCallId: call.id, content: results[call.id] ?? ""))
}
}
}
return "Агент не смог завершить задачу за \(MAX_ITERATIONS) шагов"
}
Что входит в работу
- Архитектурная схема взаимодействия мобильного приложения, бэкенда и внешних API
- Реализация агентного цикла с защитой от зацикливания (MAX_ITERATIONS, дефолтные обработчики ошибок)
- Интеграция с выбранными внешними API (аутентификация, rate limiting, ретраи)
- Бэкенд-прокси для безопасного хранения токенов и логирования запросов
- UI-компоненты для отображения прогресса агента (шаги, статусы, отмена)
- Документация инструментов и примеры использования
- Инструкция по деплою и мониторингу
Этапы работы
- Аудит внешних API и их аутентификации
- Проектирование инструментов и схем
- Реализация бэкенд-прокси (если нужен)
- Агентный цикл с защитой от зацикливания
- Обработка rate limit и таймаутов
- UX прогресс агента на клиенте
- Тестирование сценариев с ошибками API
- Мониторинг и алерты
Сроки: агент с 3–5 внешними API, серверная оркестрация — 4–7 недель. Клиентский агент для 2–3 простых API — 2–3 недели.
Закажите консультацию нашего инженера по AI-агентам. Свяжитесь с нами, чтобы обсудить ваш проект.







