---
title: "构建工具集成（API）"
description: "让您的智能体在对话过程中调用您的 API——搜索数据库、创建工单、查询订单。"
---

**工具集成**是智能体可在通话期间调用的可复用 HTTP 端点。您向 ThunderPhone 提供工具的 JSON Schema 描述和端点 URL；智能体会根据对话决定何时调用它，ThunderPhone 则会从其服务器发出出站 HTTP 请求，并将响应返回给智能体。

<Note>
  无需使用此 API，控制台即可满足大多数工具需求：**连接
  → 应用**只需几次 OAuth 点击即可连接 Slack、HubSpot、Salesforce、Google Calendar、
  Google Sheets 和 Cal.com；**连接 →
  API**可将任何 HTTP API 转换为智能体操作（粘贴 cURL 命令后，AI 向导会起草工具，并提供内置的测试请求）；**连接 → MCP**可添加 MCP 服务器。请参阅
  [连接](/zh/guides/concepts)。本指南介绍的是 API 功能界面底层的原始
  API。
</Note>

本指南将带您端到端构建一个天气查询工具。

## 工具的构成

包含两部分：

1. **Schema** —— OpenAI 风格的函数定义
   (`{type: "function", function: {name, description, parameters}}`)，
   用于告知 LLM 工具的功能及其接受的参数。
2. **端点** —— 当 LLM 决定使用该工具时，ThunderPhone 服务器调用的 URL。
   请求为 JSON POST，正文包含 LLM 选择的参数。

## 1. 选择编辑方式

<CardGroup cols={2}>
  <Card title="控制台" icon="window-maximize">
    打开 **连接 → API**，创建或编辑 API 连接，
    将参数编辑器切换为 **JSON**，然后在其中添加格式。
  </Card>
  <Card title="集成 API" icon="plug">
    使用 `POST /v1/integrations` 创建规范，或使用
    `PATCH /v1/integrations/{id}` 更新规范。
  </Card>
</CardGroup>

两种方式都会创建已保存的集成。保存后，将该集成附加到智能体。智能体 API 没有可写入的内联 `tools`
字段。本指南使用集成 API 方式。

## 2. 创建集成

```bash
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` 的请求头以保留已存储的值。

<Tip>
  请认真编写工具及其每个参数的 `description`。LLM 会在运行时使用这些字符串来决定是否调用工具以及如何调用。描述模糊 → 工具调用模糊。
</Tip>

### 在地址参数上声明 `format: "email"`

接收电子邮件地址的参数应在其架构中明确说明：

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

`format` 不只是提示。对于已解析的电子邮件架构，在调用您的端点前，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`，告知其与来电者确认拼写并重新发送字面量地址。

省略的可选电子邮件不会被修改。当属性为可选或可为空时，`null`、空字符串或仅包含空白字符的字符串同样不会被修改；对于必填且不可为空的电子邮件，这些值会被拒绝。

会检查本地架构引用，例如 `#/$defs/email` 和 `#/definitions/email`，以及 `anyOf`、`oneOf` 和 `allOf`，并施加循环和深度限制。非本地或无法解析的 `$ref` 是已知的执行限制，会原样传递；工具快照没有可用架构的调用也是如此。如果您需要应用此校验门控，请保持电子邮件架构为本地引用。

未强制执行电子邮件格式的参数会完全按照模型生成的内容原样传递。

支持的格式包括 `date-time`、`time`、`date`、`duration`、`email`、`hostname`、`ipv4`、`ipv6` 和 `uuid`；目前只有 `email` 会被规范化并强制执行。

## 3. 在沙盒中测试端点

在将集成关联到智能体之前，从 ThunderPhone 的服务器发送一个已签名请求以确认连接：

```bash
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" }
  }'
```

```json 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` 关联：

```bash
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 会向您的 `endpoint_url` 发送已签名的 POST 请求：

```
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：

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

LLM 会读取该响应，并向来电者口述自然语言摘要。

<Warning>
  签名使用与您的 webhook 端点相同的 `secret`，基于原始请求正文计算。**请验证签名**——工具端点面向互联网，面临与 webhook 相同的伪造风险。请参阅
  [验证 webhook 签名](/zh/guides/verify-webhook-signatures)。
</Warning>

## 6. 测试流程

针对该智能体运行一个[麦克风会话](/api-reference/mic-sessions)，并提出您的工具可处理的问题（“94110 的天气怎么样？”）。该通话的转录记录会显示完整往返流程：

```json
{
  "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`](/api-reference/calls#get-transcript) 获取此内容；
原始事件流（包含每条记录的时间信息和音频偏移量）位于
[`GET /v1/calls/{call_id}/history`](/api-reference/calls#get-history)。

在通话列表和分析界面中，ThunderPhone 现将测试通话与真实流量分开报告。浏览器通话测试和模拟可以复用真实电话号码，并且可能显示为呼入通话，但不会计入生产通话总数、评分或问题指标。

## 常见问题

<AccordionGroup>
  <Accordion title="智能体从不调用工具">
    LLM 会根据工具的描述作出决定。如果来电者的问题与描述不匹配，模型不会调用该工具。请完善描述（添加常见同义词和表达方式），或在智能体提示词中明确提及它（“当来电者询问天气时，使用 `get_weather`。”）。
  </Accordion>

  <Accordion title="工具返回的数据过多">
    超过 6 kB 的响应会在转录预览中被截断。仅返回 LLM 所需的字段——不要返回您的整行数据。
  </Accordion>

  <Accordion title="超时">
    工具端点的默认超时时间为 10 秒。如果您需要更长时间，请异步处理：返回 `{"status": "pending", "request_id": "..."}`
    并通过单独的工具调用提供结果。
  </Accordion>

  <Accordion title="版本控制">
    每次集成 `PATCH` 都会创建一个新修订版本。检查
    [`GET /v1/integrations/{id}/versions`](/api-reference/integrations#version-history)
    以查看谁更改了什么内容。如果您破坏了某个工具的架构，可以通过将旧快照 PATCH 回去来手动回滚。
  </Accordion>
</AccordionGroup>

---

## 后续步骤

<CardGroup cols={2}>
  <Card title="集成参考" icon="plug" href="/api-reference/integrations">
    CRUD、迁移、版本历史记录。
  </Card>
  <Card title="Function Tools 规范" icon="screwdriver-wrench" href="/zh/tools/overview">
    完整的 JSON Schema 语法和签名端点约定。
  </Card>
  <Card title="验证签名" icon="shield-check" href="/zh/guides/verify-webhook-signatures">
    将 webhook 签名模式应用于工具端点。
  </Card>
  <Card title="转写记录 + 历史记录 API" icon="phone" href="/api-reference/calls">
    查看一次工具调用的完整往返过程。
  </Card>
</CardGroup>
