ThunderPhone 2.0 уже доступен.Самостоятельный запуск — от 2 ¢/мин.Читать анонс

Developer cookbook

Создание интеграции с инструментом (API)

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

Интеграция инструмента — это повторно используемая HTTP-конечная точка, которую агент может вызывать во время звонка. Вы передаёте ThunderPhone описание инструмента в формате JSON Schema и URL конечной точки; агент решает, когда вызывать её, на основе разговора, а ThunderPhone выполняет исходящий HTTP-запрос со своих серверов и возвращает ответ агенту.

В этом руководстве пошагово рассматривается создание инструмента для получения прогноза погоды.

Устройство инструмента

Две части:

  1. Схема — определение функции в стиле OpenAI ({type: "function", function: {name, description, parameters}}), которое сообщает LLM, что делает инструмент и какие аргументы он принимает.
  2. Конечная точка — URL, который серверы ThunderPhone вызывают, когда LLM решает использовать инструмент. Запрос — это JSON POST с выбранными LLM аргументами в теле.

1. Выберите редактор

Панель управления

Откройте Подключения → API, создайте или отредактируйте API-подключение, переключите редактор параметров на JSON и добавьте туда формат.

API интеграций

Создайте спецификацию с помощью POST /v1/integrations или обновите её с помощью PATCH /v1/integrations/{id}.

Оба пути создают сохранённую интеграцию. После сохранения подключите эту интеграцию к агенту. В API агентов нет доступного для записи встроенного поля tools. В этом руководстве используется путь через API интеграций.

2. Создайте интеграцию

curl -X POST https://api.thunderphone.com/v1/integrations \
  -H "Authorization: Bearer sk_live_YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "display_name": "Weather API",
    "spec": {
      "type": "function",
      "function": {
        "name": "get_weather",
        "description": "Return the current weather for a zip code.",
        "parameters": {
          "type": "object",
          "properties": {
            "zip": { "type": "string", "description": "5-digit US ZIP code" }
          },
          "required": ["zip"]
        }
      }
    },
    "endpoint_url":    "https://api.example.com/weather",
    "endpoint_method": "GET",
    "headers": [
      { "key": "X-Api-Key", "value": "your-provider-key" }
    ]
  }'

Сохраните возвращённый id (UUID). Значения заголовков доступны только для записи: в ответах перечисляются только имена заголовков, а последующий PATCH может отправить заголовок без value, чтобы сохранить ранее сохранённое значение.

Укажите format: "email" для параметров адреса

Параметр, принимающий адрес электронной почты, должен быть обозначен в своей схеме:

"email": { "type": "string", "format": "email", "description": "The caller's email address" }

format — это больше, чем подсказка. Для разрешённой схемы email перед вызовом вашего эндпоинта ThunderPhone удаляет пробелы в начале и конце значения, приводит домен к нижнему регистру, преобразует отдельные английские слова at, dot, underscore, dash и hyphen в соответствующие символы и удаляет пробелы непосредственно вокруг @, ., _ и -. Эти слова имеют одинаковое значение независимо от того, содержит ли расшифровка уже буквальный символ @: "john dot smith at gmail dot com" преобразуется в john.smith@gmail.com.

Любые другие внутренние пробелы отклоняются, а не объединяются без уведомления. Слова, произносимые вместо разделителей, поддерживаются только на английском; неанглийские или нераспознанные формы с пробелами отклоняются. Допустимые интернационализированные домены и локальные части SMTPUTF8 принимаются. Введённый Punycode остаётся Punycode, а домен в Unicode остаётся Unicode после нормализации парсером, поэтому ваш API получает обычное представление, указанное звонящим. Если итоговое значение недопустимо, инструмент не вызывается. Агент получает invalid_email_argument, указывающий ему подтвердить написание со звонящим и повторно отправить буквальный адрес.

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

Локальные ссылки схемы, такие как #/$defs/email и #/definitions/email, а также anyOf, oneOf и allOf, проверяются с ограничениями циклов и глубины. Нелокальный или неразрешимый $ref — это известное ограничение проверки, и он передаётся без изменений, как и вызов, снимок инструмента которого не содержит пригодной схемы. Используйте локальные схемы email, если необходимо применить проверку.

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

Поддерживаемые форматы: date-time, time, date, duration, email, hostname, ipv4, ipv6 и uuid; сегодня нормализуется и принудительно проверяется только email.

3. Протестируйте эндпоинт в песочнице

Перед привязкой интеграции к агенту отправьте подписанный запрос с серверов ThunderPhone, чтобы подтвердить подключение:

curl -X POST https://api.thunderphone.com/v1/integrations/test-request \
  -H "Authorization: Bearer sk_live_YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "url":    "https://api.example.com/weather?zip=94110",
    "method": "GET",
    "headers": { "X-Api-Key": "your-provider-key" }
  }'
Response
{
  "ok": true,
  "status": 200,
  "elapsed_ms": 187,
  "response_headers": { "content-type": "application/json" },
  "response_preview": "{\"temperature_f\": 64, ...}"
}

Этот тест также усиливает защиту ThunderPhone от SSRF — запросы к localhost или диапазонам частных IP возвращают 400 code=url_not_allowed.

4. Свяжите интеграцию с агентом

Подключите через integration_ids при создании или обновлении агента:

curl -X PATCH https://api.thunderphone.com/v1/agents/12 \
  -H "Authorization: Bearer sk_live_YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "integration_ids": ["f9b5a1a4-..."]
  }'

К одному агенту можно подключить несколько интеграций. В промпте агента на них можно ссылаться по имени — «используй get_weather, когда звонящий спрашивает о погодных условиях» — либо агент может неявно обнаружить их по описаниям схем.

5. Реализуйте конечную точку

Когда агент вызывает инструмент, ThunderPhone отправляет подписанный POST-запрос на ваш endpoint_url:

POST /weather HTTP/1.1
Host: api.example.com
X-Api-Key: your-provider-key
X-ThunderPhone-Signature-V2: t=<unix seconds>,v1=<HMAC-SHA256 hex>
X-ThunderPhone-Signature: <HMAC-SHA256 hex>
X-ThunderPhone-Call-ID: 987654321
Content-Type: application/json

{"zip": "94110"}

Ваш сервер отвечает JSON-объектом, который возвращается LLM:

{"temperature_f": 64, "condition": "Partly cloudy", "wind_mph": 8}

LLM обрабатывает этот ответ и озвучивает звонящему понятное человеку резюме.

6. Протестируйте цикл

Запустите микрофонную сессию с агентом и задайте вопрос, который обрабатывает ваш инструмент («Какая погода в 94110?»). Расшифровка звонка показывает полный цикл:

{
  "call_id": 987654321,
  "transcripts": [
    { "role": "user",
      "content": "What's the weather in 94110?" },
    { "role": "tool_call",
      "content": "{\"tool_call\": \"get_weather\", \"arguments\": {\"zip\": \"94110\"}}" },
    { "role": "tool_response",
      "content": "{\"tool_name\": \"get_weather\", \"response\": {\"temperature_f\": 64, \"condition\": \"Partly cloudy\"}}" },
    { "role": "agent",
      "content": "It's 64 degrees and partly cloudy." }
  ]
}

Получить её можно через GET /v1/calls/{call_id}/transcript; поток необработанных событий (с таймингом каждой записи и смещениями аудио) доступен по адресу GET /v1/calls/{call_id}/history.

В интерфейсах списка звонков и аналитики ThunderPhone теперь показывает тестовые звонки отдельно от реального трафика. Тесты и симуляции Browser Talk могут повторно использовать реальный номер телефона и выглядеть как входящие, но они не учитываются в общем количестве звонков в продакшене, оценках или показателях проблем.

Распространённые ошибки

Агент никогда не вызывает инструмент

LLM принимает решение на основе описания инструмента. Если вопрос звонящего не соответствует описанию, модель не вызовет инструмент. Уточните описание (добавьте распространённые синонимы и формулировки) или явно укажите это в промпте агента («Когда звонящий спрашивает о погоде, используй get_weather.»).

Инструмент возвращает слишком много данных

Ответы размером более 6 кБ обрезаются в предпросмотре расшифровки. Возвращайте только поля, необходимые LLM, — а не всю запись.

Тайм-ауты

Для конечных точек инструментов установлен тайм-аут по умолчанию 10 секунд. Если нужно больше времени, обрабатывайте запрос асинхронно: верните {"status": "pending", "request_id": "..."} и предоставьте результат через отдельный вызов инструмента.

Версионирование

Каждый PATCH интеграции создаёт новую редакцию. Просмотрите GET /v1/integrations/{id}/versions, чтобы узнать, кто и что изменил. Если вы нарушите схему инструмента, можно вручную откатить изменения, отправив PATCH с более старым снимком.


Следующие шаги