Сценарий · Inquir Compute

Бэкенд AI-агента на serverless-функциях

Инструменты модели становятся HTTP-маршрутами на API-шлюзе: закрывайте их API-ключами, подставляйте секреты на уровне функции, выносите долгие шаги в задачи или пайплайны и пишите обработчики на Node.js, Python или Go с той же наблюдаемостью, что и у остального стека.

Обновлено: 2026-06-28

Суть ответа

Бэкенд AI-агента на serverless-функциях. Функция на инструмент локализует зависимости и риск деплоя; история выполнений в консоли совпадает с вызовами конкретного инструмента — дежурить проще, чем с общим монолитом.

Когда подходит

  • Многошаговые агенты
  • Доступ инструментов к закрытым данным

На что обратить внимание

  • Привилегированные инструменты на устройствах пользователей ломаются, как только данные попадают под регулирование или пользователь работает с заблокированного ноутбука.
  • Один большой сервер со всеми инструментами превращает каждый деплой в рискованное изменение, а строки логов невозможно привязать к конкретной возможности агента.

Что агентам нужно на самом деле

Ответ модели — только верхушка. В продакшене агенты ходят в закрытые данные, пишут в учётные системы, эскалируют вопросы людям и соблюдают ограничения — и у каждого из этих шагов должна быть понятная семантика сбоев и ретраев.

Без настоящего бэкенда побочные эффекты уезжают в текст промпта или в браузер пользователя — там их почти невозможно проаудировать или аккуратно отозвать.

Паттерны, которые ломаются в продакшене

Привилегированные инструменты на устройствах пользователей ломаются, как только данные попадают под регулирование или пользователь работает с заблокированного ноутбука.

Один большой сервер со всеми инструментами превращает каждый деплой в рискованное изменение, а строки логов невозможно привязать к конкретной возможности агента.

Компонуемые serverless-инструменты для AI-агентов

Функция на инструмент локализует зависимости и риск деплоя; история выполнений в консоли совпадает с вызовами конкретного инструмента — дежурить проще, чем с общим монолитом.

Обработчики используют те же рантаймы Node.js, Python и Go, что и вся платформа. Опциональные тёплые пулы срезают накладные расходы холодного старта, когда модель вызывает инструменты в плотном цикле, — замеряйте под реальной нагрузкой.

Правила реализации инструментов AI-агента

Одна функция — один инструмент

Разделяйте функции, если зависимости не связаны жёстко. Один инструмент на функцию — риск деплоя минимален, логи привязаны к конкретной возможности.

Валидируйте вход, возвращайте структурированный JSON

Опишите обязательные поля и отклоняйте невалидные payload как можно раньше. Возвращайте стабильную JSON-схему, которую оркестратор разберёт без особых случаев.

Секреты в переменных окружения, не в промптах

Выдавайте каждой функции-инструменту свои API-ключи в секретах воркспейса. Ротируйте ключи независимо от версий модели, не трогая шаблоны промптов, — секреты не попадают ни в логи, ни в контекстные окна.

Авторизация на шлюзе до обработчика

Каждый маршрут инструмента требует API-ключ, и проверяет его шлюз. Функции получают только уже авторизованные запросы — без самописной проверки в обработчике и без случайно открытых эндпоинтов.

Длинную работу — в задачи или пайплайны

Когда шаг инструмента превышает таймаут шлюза, сразу верните ID задачи, а обогащение или побочные эффекты продолжите в фоновом пайплайне. Оркестратор опрашивает статус или получает вебхук по завершении пайплайна.

Тёплые контейнеры для плотных циклов вызовов

Когда модель вызывает инструменты подряд, задержка холодного старта накапливается. Включите тёплые пулы для функций-инструментов с устойчивым трафиком — замерьте p95/p99 до и после, чтобы подтвердить выигрыш.

Отслеживайте сбои по каждому эндпоинту инструмента

Настройте алерты по доле ошибок на маршрут инструмента, а не только на сессию чата. Один упавший инструмент должен всплыть в наблюдаемости раньше, чем тихо ухудшит качество агента.

Как строить инструменты AI-агента на Inquir Compute

1

Описать контракт входных данных инструмента

Задокументируйте обязательные поля, поведение валидации и форму ошибки, которую должен обработать оркестратор.

2

Описать схему вывода и модель авторизации

Держите форму ответа стабильной между версиями. Используйте авторизацию по API-ключу на уровне маршрута, чтобы инструменты случайно не оказались открытыми для публичного трафика.

3

Описать ретраи, идемпотентность и передачу по таймауту

Решите, когда ретраить на месте, когда возвращать ID задачи и продолжать через пайплайн, и какие стабильные ID использовать как ключ идемпотентности записей, чтобы ретраи не создавали дубликатов.

Обработчики инструментов: Node.js, Python, Go и асинхронная передача

Синхронные инструменты используют один контракт события шлюза: body приходит строкой, возвращайте {statusCode, body}. Языки можно смешивать по инструментам: Python — ML-инференс, Node.js — вызовы API, Go — быстрые поиски с высокой пропускной способностью. Когда шаг не укладывается в таймаут шлюза, верните 202 с ID задачи и продолжите в пайплайне.

tools/lookup.mjs (Node.js 22)
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 }) };
}
tools/classify.py (Python 3.12)
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})}
tools/lookup.go (Go 1.22)
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
}
tools/enrich-async.mjs (асинхронная передача)
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-функциями?

В продакшене — да: функция на инструмент (или на тесную группу) изолирует зависимости, снижает риск деплоя и делает логи атрибутируемыми — проще, чем монолит, где смешаны пользовательские сессии и ввод-вывод инструментов.

Где хранить секреты для вызовов инструментов?

В секретах воркспейса Inquir с подстановкой в окружение — тогда API-ключи не живут ни в промптах, ни в клиентских бандлах; ротируйте их независимо от версий модели.

Стриминг ответов пользователю?

Стриминг к конечному пользователю — забота API-шлюза; многие стеки с вызовом инструментов по-прежнему обмениваются обычным JSON запрос/ответ между оркестратором и каждым инструментом — так проще ретраи и идемпотентность.

Как сделать вызовы инструментов идемпотентными при ретраях модели?

Используйте стабильные ID из payload инструмента (ID клиента, заказа, внешней записи) как ключ идемпотентности записей. При повторе возвращайте ту же JSON-схему, чтобы оркестратор считал дубликат вызова безопасным no-op.