---
title: "Инструменти за функции"
description: "Предоставете на своите AI агенти инструменти за функции, които извикват външни API по време на разговор — извличат данни за клиенти, записват часове, актуализират записи — с типизирани параметри."
---

Функционалните инструменти позволяват на вашите AI агенти да извикват външни API по време на телефонни разговори. Използвайте ги, за да търсите данни за клиенти, да проверявате наличност, да записвате часове или да извършвате всяко действие, което вашият бекенд поддържа.

## Как работи

1. Дефинирате инструменти със схема (какви аргументи приема инструментът)
2. Предоставяте конфигурация за `endpoint` (къде ThunderPhone извиква вашето API) — или я пропускате, за да получавате извикванията на инструменти в уебхука на вашата организация
3. По време на разговор AI решава кога да използва инструмент въз основа на разговора
4. ThunderPhone извиква вашия endpoint с аргументите на инструмента
5. Отговорът на вашето API се подава обратно към AI, за да продължи разговорът

| Възможност | Къде се изпълнява | Настройка |
| --- | --- | --- |
| [Вградени инструменти](/bg/guides/built-in-tools) | ThunderPhone | Инструкции в промпта; някои инструменти изискват и настройка на агент |
| [Връзки с приложения](/bg/guides/connect-apps) | ThunderPhone и свързаният доставчик | Свържете акаунта и прикачете одобрените действия |
| [API връзки](/bg/guides/api-connections) и функционални инструменти | Вашето HTTP API | Дефинирайте endpoint и схема или получавайте функционални извиквания чрез уебхук |
| [MCP сървъри](/bg/guides/mcp-servers) | Отдалечен MCP сървър | Добавете сървъра, открийте неговите инструменти и го прикачете към агента |

---

## Схема на инструмента

Всеки инструмент следва тази структура:

```json
{
  "type": "function",
  "function": {
    "name": "search_appointments",
    "description": "Find available appointment slots for a given date",
    "parameters": {
      "type": "object",
      "properties": {
        "date": {
          "type": "string",
          "description": "Date in YYYY-MM-DD format"
        },
        "service": {
          "type": "string",
          "description": "Type of service (e.g., 'consultation', 'follow-up')"
        }
      },
      "required": ["date"]
    }
  },
  "endpoint": {
    "url": "https://api.example.com/appointments/search",
    "method": "POST",
    "headers": {
      "X-Api-Key": "your-api-key"
    }
  },
  "timeout": 120
}
```

### Конфигурация на инструмента

| Поле | Тип | Задължително | Описание |
|-------|------|----------|-------------|
| `timeout` | число | Не | Максимално време за изпълнение в секунди (по подразбиране: `20`, максимум: `180`) |

### Дефиниция на функцията

| Поле | Тип | Задължително | Описание |
|-------|------|----------|-------------|
| `name` | низ | Да | Уникален идентификатор за инструмента |
| `description` | низ | Да | Обяснява на AI кога да използва този инструмент |
| `parameters` | обект | Да | JSON Schema за аргументите на инструмента |

### Конфигурация на endpoint

| Поле | Тип | Задължително | Описание |
|-------|------|----------|-------------|
| `url` | низ | Да | URL адресът на endpoint на вашето API |
| `method` | низ | Не | HTTP метод (по подразбиране: `POST`) |
| `headers` | обект | Не | Персонализирани заглавки за включване |

<Note>
  Конфигурацията на `endpoint` **не** се изпраща до AI модела — тя се използва само от ThunderPhone за изпълнение на извикването на инструмента.
</Note>

---

## Два пътя за извикване

Заявката, която вашият сървър получава, зависи от това дали инструментът има
`endpoint`:

| | Инструмент **с** `endpoint` | Инструмент **без** `endpoint` |
|---|---|---|
| Къде се изпраща заявката | Директно към `endpoint.url` | [URL адресът на наследения уебхук](/api-reference/organizations#legacy-single-url-webhook) на вашата организация |
| Тяло | **Само аргументите на инструмента** | Обвивка `telephony.tool` / `web.tool` |
| Заглавки | Вашите `endpoint.headers` + `X-ThunderPhone-Call-ID` + `X-ThunderPhone-Signature` | `Content-Type` + `X-ThunderPhone-Signature` |
| Ключ за подписване | Тайната на уебхука на организацията | Тайната на уебхука на организацията |

И двата пътя са **блокиращи** — AI изчаква резултата по средата на изречение. Времето за изчакване по подразбиране е **20 s**; задайте `timeout` на най-горното ниво на инструмента, за да позволите по-дълго изпълнение, до максимума на платформата от **180 s**. Поддържайте обработчиците бързи. Възможна е комбинация:
при разговор, чиято организация има URL адрес на уебхук, инструментите с `endpoint` се
извикват директно, а останалите използват уебхука.

## Директни извиквания на крайни точки

Когато AI извика инструмент, който има `endpoint`, ThunderPhone изпраща
заявка към вашия URL адрес:

### Заглавки на заявката

```http
POST /appointments/search HTTP/1.1
Host: api.example.com
Content-Type: application/json
X-ThunderPhone-Signature: abc123...
X-ThunderPhone-Call-ID: 987654321
X-Api-Key: your-api-key
```

Персонализираните заглавки от вашите `endpoint.headers` винаги се включват
дословно, плюс две заглавки в пространството от имена на ThunderPhone:

- `X-ThunderPhone-Signature` — HMAC-SHA256 на точните байтове на тялото на
  заявката, с ключ вашата **тайна за webhook на организацията**
- `X-ThunderPhone-Call-ID` — Идентификаторът на текущото обаждане

`Content-Type: application/json` се задава, освен ако вашите `endpoint.headers`
не го заменят — персонализиран `Content-Type` има предимство.

<Warning>
  Подписът използва като ключ тайната за webhook на ниво организация от
  [`GET /v1/webhook`](/api-reference/organizations#legacy-single-url-webhook).
  Ако организацията ви никога не е конфигурирала наследения webhook, няма
  тайна и извикванията на инструменти съдържат **само** `X-ThunderPhone-Call-ID` —
  обработчик, който прекратява изпълнението при липсващ подпис, би ги отхвърлил.
  Или конфигурирайте наследения webhook, за да получите тайна, или поставете
  собствена споделена тайна в `endpoint.headers`.
</Warning>

### Тяло на заявката

За `POST` / `PUT` / `PATCH` тялото съдържа **само** аргументите на инструмента
(без обвивка), сериализирани канонично (сортирани ключове, компактни
разделители):

```json
{"date":"2025-01-02","service":"consultation"}
```

За `GET` / `DELETE` аргументите се изпращат като **параметри на заявката**
и тялото е празно — тогава подписът се изчислява върху празния
низ от байтове. Вижте
[Проверка на подписи за webhook](/bg/guides/verify-webhook-signatures).

### Отговор

Върнете JSON отговор с резултата от инструмента:

```json
{
  "available_slots": ["9:00 AM", "2:00 PM", "4:30 PM"],
  "timezone": "America/Los_Angeles"
}
```

Отговорът се форматира и предоставя на AI, за да продължи
разговора. Отговорите, които не са JSON, се обвиват като `{"data": "<text>"}`;
изчакванията и неуспешните връзки се съобщават на AI като грешки, така че
агентът да може да се извини и да продължи, вместо да блокира.

## Диспечиране в режим webhook

Инструментите **без** `endpoint` се диспечират към наследения URL адрес за
webhook на вашата организация като подписана заявка `telephony.tool` (телефонни
обаждания) или `web.tool` (уеб обаждания). За разлика от [известията за одит](/bg/webhooks/events),
доставяни до крайни точки за webhook след изпълнение, тази заявка **е**
изпълнението — вашият HTTP отговор е резултатът от инструмента.

```json
{
  "type": "telephony.tool",
  "data": {
    "call_id": 987654321,
    "tool_name": "search_appointments",
    "arguments": { "date": "2026-04-21" },
    "from_number": "+14155550199",
    "to_number": "+15551234567"
  }
}
```

`web.tool` съдържа `origin_domain` вместо `from_number` /
`to_number`. Отговорете с резултата от инструмента като JSON — същия договор за
отговор като при директните извиквания на крайни точки. Заявката се подписва с
тайната за webhook на организацията върху необработеното тяло, както всеки друг webhook.

<Note>
  Абонираните [крайни точки за webhook](/bg/webhooks/endpoints) допълнително
  получават неблокиращо **известие след** `telephony.tool` / `web.tool`
  след изпълнението на всеки инструмент (по който и път да е изпълнен), включително
  отговора на инструмента — полезно за одитни следи. Вижте
  [каталога на събитията](/bg/webhooks/events).
</Note>

---

## Проверка на подписа

Директните извиквания на инструменти се подписват по същия начин като уебхуковете:

- HMAC-SHA256 върху точните байтове на тялото на заявката (каноничният JSON —
  сортирани ключове, без допълнителни интервали)
- С ключ, който е вашата тайна за уебхукове на организацията
- Инструментите `GET` / `DELETE` подписват празния низ от байтове

<CodeGroup>
```python Python
import hmac
import hashlib

def verify_tool_call(body: bytes, signature: str, secret: str) -> bool:
    expected = hmac.new(secret.encode(), body, hashlib.sha256).hexdigest()
    return hmac.compare_digest(expected, signature)

@app.post("/appointments/search")
async def search_appointments(request: Request):
    body = await request.body()
    signature = request.headers.get("X-ThunderPhone-Signature", "")

    if not verify_tool_call(body, signature, WEBHOOK_SECRET):
        raise HTTPException(status_code=401)

    data = json.loads(body)
    date = data["date"]

    # Look up availability
    slots = await get_available_slots(date)

    return {"available_slots": slots}
```

```javascript Node.js
app.post('/appointments/search', express.raw({type: 'application/json'}), (req, res) => {
  const signature = req.headers['x-thunderphone-signature'] || '';
  const expected = crypto
    .createHmac('sha256', WEBHOOK_SECRET)
    .update(req.body)
    .digest('hex');

  if (!signature ||
      signature.length !== expected.length ||
      !crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(signature))) {
    return res.status(401).send('Invalid signature');
  }

  const { date, service } = JSON.parse(req.body);

  // Look up availability
  const slots = getAvailableSlots(date, service);

  res.json({ available_slots: slots });
});
```
</CodeGroup>

Пълните примери — включително случая с празно тяло и уточнението при липса на тайна —
са в [Проверка на подписи на уебхукове](/bg/guides/verify-webhook-signatures).

---

## Пример: Пълен процес за записване на час

Ето набор от инструменти за цялостна система за записване на часове:

```json
{
  "tools": [
    {
      "type": "function",
      "function": {
        "name": "search_appointments",
        "description": "Find available appointment slots",
        "parameters": {
          "type": "object",
          "properties": {
            "date": { "type": "string", "description": "YYYY-MM-DD" },
            "service": { "type": "string" }
          },
          "required": ["date"]
        }
      },
      "endpoint": {
        "url": "https://api.example.com/appointments/search",
        "method": "POST",
        "headers": { "X-Api-Key": "key" }
      }
    },
    {
      "type": "function",
      "function": {
        "name": "book_appointment",
        "description": "Book an appointment at a specific time",
        "parameters": {
          "type": "object",
          "properties": {
            "date": { "type": "string", "description": "YYYY-MM-DD" },
            "time": { "type": "string", "description": "HH:MM format" },
            "customer_name": { "type": "string" },
            "customer_phone": { "type": "string" }
          },
          "required": ["date", "time", "customer_name"]
        }
      },
      "endpoint": {
        "url": "https://api.example.com/appointments/book",
        "method": "POST",
        "headers": { "X-Api-Key": "key" }
      }
    },
    {
      "type": "function",
      "function": {
        "name": "cancel_appointment",
        "description": "Cancel an existing appointment",
        "parameters": {
          "type": "object",
          "properties": {
            "confirmation_number": { "type": "string" }
          },
          "required": ["confirmation_number"]
        }
      },
      "endpoint": {
        "url": "https://api.example.com/appointments/cancel",
        "method": "POST",
        "headers": { "X-Api-Key": "key" }
      }
    }
  ]
}
```

---

## Най-добри практики

<AccordionGroup>
  <Accordion title="Пишете ясни описания">
    Полето `description` помага на AI да разбере **кога** да използва инструмента. Посочете конкретно какво прави и кога е подходящо да се използва.
  </Accordion>

  <Accordion title="Обработвайте грешките коректно">
    Връщайте съобщения за грешка, които AI може да разбере: `{"error": "No slots available for that date"}`, вместо общи грешки 500.
  </Accordion>

  <Accordion title="Поддържайте отговорите кратки">
    Връщайте само това, от което AI се нуждае, за да продължи разговора. Големите полезни товари забавят времето за отговор.
  </Accordion>

  <Accordion title="Използвайте задължителните полета разумно">
    Маркирайте полетата като `required` само когато това е наистина необходимо. AI ще поиска от потребителя задължителната информация, преди да извика инструмента.
  </Accordion>
</AccordionGroup>

---

## Свързани

<CardGroup cols={2}>
  <Card title="Вградени инструменти" icon="wrench" href="/bg/guides/built-in-tools">
    Подканвайте управлявани от платформата действия при обаждания, без да дефинирате крайна точка.
  </Card>
  <Card title="Връзки с приложения" icon="plug" href="/bg/guides/connect-apps">
    Управлявани от платформата инструменти за HubSpot, Salesforce, Slack, Google
    Calendar, Google Sheets и Cal.com — не е необходима крайна точка.
  </Card>
  <Card title="MCP сървъри" icon="server" href="/bg/guides/mcp-servers">
    Свържете MCP сървър и позволете на агента да извиква неговите инструменти.
  </Card>
  <Card title="API връзки" icon="code" href="/bg/guides/api-connections">
    Многократно използваеми REST интеграции, които можете да свързвате с агенти.
  </Card>
  <Card title="Проверка на подписите на webhook заявки" icon="shield-check" href="/bg/guides/verify-webhook-signatures">
    Един помощен инструмент за проверка на webhook заявки и извиквания на инструменти.
  </Card>
</CardGroup>
