Бэкенд AI-агента на serverless-функциях
Инструменты модели становятся HTTP-маршрутами на API-шлюзе: закрывайте их API-ключами, подставляйте секреты на уровне функции, выносите долгие шаги в задачи или пайплайны и пишите хендлеры на Node.js, Python или Go с той же наблюдаемостью, что и у остального стека.
Обновлено: 2026-06-28
Кратко
Суть ответа
Бэкенд AI-агента на serverless-функциях. Функция на инструмент локализует зависимости и риск деплоя; история выполнений в консоли совпадает с вызовами конкретного инструмента — дежурить проще, чем с общим монолитом.
Когда подходит и когда нет
- Многошаговые агенты
- Доступ инструментов к закрытым данным
На что обратить внимание
- Привилегированные инструменты на устройстве пользователя перестают работать, как только данные становятся регулируемыми, а ноутбук — жёстко заблокированным.
- Один большой сервер со всеми инструментами превращает каждый деплой в рискованное изменение, а строки логов невозможно привязать к конкретной возможности агента.
Ситуация: нагрузка и где обычно ломается
Что агенту на самом деле нужно в продакшене
Ответ модели — только верхушка. В продакшене агенты ходят в закрытые данные, пишут в учётные системы, эскалируют вопросы людям и соблюдают ограничения — и у каждого из этих шагов должны быть понятные ретраи и обработка сбоев.
Без нормального бэкенда побочные эффекты уезжают в промпт или браузер — там их почти нельзя ни проконтролировать, ни отозвать.
Когда простых рецептов недостаточно
Паттерны, которые ломаются в продакшене
Привилегированные инструменты на устройстве пользователя перестают работать, как только данные становятся регулируемыми, а ноутбук — жёстко заблокированным.
Один большой сервер со всеми инструментами превращает каждый деплой в рискованное изменение, а строки логов невозможно привязать к конкретной возможности агента.
Как Inquir помогает в этом сценарии
Инструменты агента как отдельные serverless-функции
Функция на инструмент локализует зависимости и риск деплоя; история выполнений в консоли совпадает с вызовами конкретного инструмента — дежурить проще, чем с общим монолитом.
Хендлеры используют те же рантаймы Node.js, Python и Go, что и вся платформа. Опциональные тёплые пулы срезают накладные расходы холодного старта, когда модель вызывает инструменты в плотном цикле, — замеряйте под реальной нагрузкой.
Что вы получаете на платформе
Правила реализации инструментов AI-агента
Одна функция — один инструмент
Разделяйте функции, если зависимости не связаны жёстко. Один инструмент на функцию — риск деплоя минимален, логи привязаны к конкретной возможности.
Проверяйте входные данные, возвращайте структурированный JSON
Отклоняйте невалидные запросы как можно раньше. Возвращайте стабильную JSON-схему, которую оркестратор сможет разобрать без исключений.
Секреты в переменных окружения, не в промптах
Выдавайте каждой функции-инструменту свои API-ключи в секретах рабочего пространства. Ротируйте ключи независимо от версий модели, не трогая шаблоны промптов, — секреты не попадают ни в логи, ни в контекстные окна.
Модель безопасности: аутентификация на шлюзе до хендлера
Каждый маршрут инструмента требует API-ключ на уровне шлюза. Функции получают только уже аутентифицированные запросы — без самописной проверки в хендлере и без случайно открытых эндпоинтов.
Длинную работу — в задачи или пайплайны
Когда шаг инструмента превышает таймаут шлюза, сразу верните ID задачи, а обогащение или побочные эффекты продолжите в фоновом пайплайне. Оркестратор опрашивает статус или получает вебхук по завершении.
Тёплые контейнеры для частых циклов вызовов инструментов
Когда модель вызывает инструменты подряд, холодный старт накапливается. Включите тёплые пулы для функций с устойчивым трафиком — замерьте p95/p99 до и после, чтобы оценить выигрыш.
Отслеживайте сбои по каждому эндпоинту инструмента
Настройте алерты по ошибкам на маршрут инструмента, а не только на сессию чата. Один упавший инструмент должен всплыть в наблюдаемости раньше, чем тихо ухудшит качество агента.
Что сделать дальше, по шагам
Как строить инструменты AI-агента на Inquir Compute
Описать контракт входных данных инструмента
Задокументируйте обязательные поля, поведение валидации и форму ошибки, которую должен обработать оркестратор.
Описать схему вывода и модель доступа
Держите форму ответа стабильной между версиями. Используйте API-ключ на уровне маршрута, чтобы инструменты случайно не оказались открытыми.
Описать повторы, идемпотентность и передачу по таймауту
Решите, когда повторять на месте, когда возвращать ID задачи и продолжать через пайплайн, и как ключевать записи, чтобы повторы не создавали дубликатов.
Пример кода
Паттерны хендлера инструмента — Node.js, Python, Go и async handoff
Синхронные инструменты используют один контракт шлюза: body приходит строкой, возвращайте { statusCode, body }. Языки можно смешивать: Python — ML-классификация, Node.js — поиск по справочникам и вызовы API, Go 1.22 — быстрые поисковые запросы. Когда шаг не укладывается в таймаут шлюза, верните 202 с ID задачи и продолжите в пайплайне (четвёртый блок).
export async function handler(event) { const { id } = JSON.parse(event.body || '{}'); if (!id) return { statusCode: 400, body: JSON.stringify({ error: 'id required' }) }; // API key auth is enforced at the gateway route — handler assumes authenticated caller const row = await db.findById(id); if (!row) return { statusCode: 404, body: JSON.stringify({ error: 'not found' }) }; return { statusCode: 200, body: JSON.stringify({ row }) }; }
import json, os from openai import OpenAI client = OpenAI(api_key=os.environ["OPENAI_API_KEY"]) # injected from workspace secrets def handler(event, context): body = json.loads(event.get("body") or "{}") text = body.get("text") if not text: return {"statusCode": 400, "body": json.dumps({"error": "text required"})} r = client.chat.completions.create( model="gpt-4o-mini", messages=[{"role": "user", "content": f"Classify intent of: {text}"}], ) return {"statusCode": 200, "body": json.dumps({"intent": r.choices[0].message.content})}
package main import ( "encoding/json" ) func parsePayload(event map[string]interface{}) map[string]interface{} { if s, ok := event["body"].(string); ok && s != "" { var out map[string]interface{} if err := json.Unmarshal([]byte(s), &out); err != nil || out == nil { return map[string]interface{}{} } return out } return event } // Handler — API key auth enforced at the gateway route func Handler(event map[string]interface{}, ctx map[string]interface{}) (interface{}, error) { payload := parsePayload(event) id, _ := payload["id"].(string) if id == "" { b, _ := json.Marshal(map[string]string{"error": "id required"}) return map[string]interface{}{"statusCode": 400, "body": string(b)}, nil } row, err := db.FindByID(id) if err != nil { b, _ := json.Marshal(map[string]string{"error": "not found"}) return map[string]interface{}{"statusCode": 404, "body": string(b)}, nil } b, _ := json.Marshal(map[string]interface{}{"row": row}) return map[string]interface{}{"statusCode": 200, "body": string(b)}, nil }
export async function handler(event) { const { customerId } = JSON.parse(event.body || '{}'); if (!customerId) return { statusCode: 400, body: JSON.stringify({ error: 'customerId required' }) }; // Return fast; continue in pipeline — orchestrator polls /jobs/:jobId or receives webhook const { instanceId: jobId } = await global.durable.startNew('enrich-customer', undefined, { customerId }); return { statusCode: 202, body: JSON.stringify({ jobId }) }; }
Когда подходит и когда нет
Когда выносить инструменты агента в отдельные функции
Когда это уместно
- Многошаговые агенты
- Доступ инструментов к закрытым данным
Когда лучше выбрать другое
- Разовые ответы модели без побочных эффектов
Вопросы и ответы
Вопросы и ответы
Инструменты должны быть отдельными HTTP-функциями?
В продакшене обычно да — проще изоляция, меньше риск при публикации новой версии и понятнее логи, чем у монолита.
Где хранить секреты?
В секретах рабочего пространства с подстановкой в окружение — ключи не в промптах и не в клиентском бандле.
Стриминг ответов пользователю?
Стриминг к конечному пользователю — забота API-шлюза; между оркестратором и инструментами чаще остаётся обычный JSON request/response — так проще ретраи и идемпотентность.
Как сделать вызовы инструментов идемпотентными при повторных попытках модели?
Ключуйте записи стабильными ID из payload инструмента (ID клиента, заказа, внешней записи). Возвращайте ту же JSON-схему при повторе, чтобы оркестратор мог считать дубликат безопасным no-op.