# 開放 API

> 一個與 OpenAI 相容、用來呼叫你的角色的端點。

Source: https://reverie.im/zh-TW/docs/integrations/open-api
Language: zh-TW

Reverie 提供一個與 OpenAI 相容的 Chat Completions 端點。把任何 OpenAI 用戶端指向它，帶上一組 Reverie API 金鑰，再用 `model` 欄位指定**角色或場景**，而不是某個大型語言模型。對話、記憶和摘要都由 Reverie 在它那一端保存，所以每次請求只需要附上你的新訊息。

任何已登入的帳號都能使用。不需要任何訂閱方案，也沒有候補名單——你只需要一組 API 金鑰，就這樣。[建立金鑰 →](https://reverie.im/zh-TW/docs/integrations/api-keys)

## 基礎網址與驗證 \[#base-url-and-auth]

```
https://reverie.im/api/device/v1
Authorization: Bearer rk_...
```

**設定 → 更多 → API 金鑰**中的 **API 端點**卡片會顯示確切的基礎網址，旁邊還有複製按鈕。

> **這是伺服器端 API。** 跨來源的瀏覽器請求會被封鎖，所以無論金鑰是否正確，從你自己的網頁發出的 fetch 都會失敗。請從後端呼叫，並且不要把金鑰放進用戶端程式碼。

## 快速開始 \[#quick-start]

**cURL**

```bash
curl --no-buffer https://reverie.im/api/device/v1/chat/completions \
  -H "Authorization: Bearer $REVERIE_API_KEY" \
  -H "Content-Type: application/json" \
  --data-binary '{
  "model": "Luna [abc12345]",
  "messages": [
    {
      "role": "user",
      "content": "Hi"
    }
  ],
  "stream": true,
  "temperature": 0.8
}'
```

**Python**

```python
import os
from openai import OpenAI

client = OpenAI(
    api_key=os.environ["REVERIE_API_KEY"],
    base_url="https://reverie.im/api/device/v1",
)

stream = client.chat.completions.create(
    model="Luna [abc12345]",   # an id from GET /models, a full character or scenario ID, or omit entirely
    messages=[{"role": "user", "content": "Hi"}],
    stream=True,
    temperature=0.8,
)

for chunk in stream:
    print(chunk.choices[0].delta.content or "", end="")
```

**TypeScript**

```ts
import OpenAI from "openai";

const client = new OpenAI({
	apiKey: process.env.REVERIE_API_KEY,
	baseURL: "https://reverie.im/api/device/v1",
});

const stream = await client.chat.completions.create({
	model: "Luna [abc12345]",
	messages: [{ role: "user", content: "Hi" }],
	stream: true,
	temperature: 0.8,
});

for await (const chunk of stream) {
	process.stdout.write(chunk.choices[0]?.delta?.content ?? "");
}
```

**設定 → 更多 → API 金鑰**也有一個請求產生器，會用你真實的某組金鑰和某個真實的角色，產生上面這三段程式碼。

## `model` 欄位指定的是角色 \[#the-model-field-names-a-character]

> **只寫角色名稱是行不通的。** `model: "Luna"` 不會去查找名叫 Luna 的角色。它什麼都比對不到，於是改用金鑰的預設角色或你最近的一場聊天，你會在沒有任何錯誤的情況下收到另一個角色的回覆。請一律使用 `GET /models` 回傳的 `Name [shortid]` 格式。

| `model` 的值                            | 由誰回答                           |
| ------------------------------------- | ------------------------------ |
| `Luna [abc12345]`                     | ID 以這 8 個字元結尾的角色或場景            |
| 完整的角色或場景 ID                           | 該角色或場景                         |
| Reverie 的模型 ID，例如 `deepseek-v4-flash` | 選擇的是**大型語言模型**；角色則來自金鑰或你上一次的聊天 |
| 省略                                    | 金鑰綁定的角色，若沒有則為你最近的一場聊天          |

決定簡短 ID 能否解析的細節：

* `[...]` 必須位於字串的**結尾**。
* 長度在 8 個字元以內的後綴，只會比對**你已經聊過天**的角色，無論是在網頁上還是透過 API。你從未開啟過的角色，其簡短 ID 會在沒有任何提示的情況下比對失敗。
* 只有在金鑰綁定了角色時，才會查找場景。使用全域金鑰時，簡短的場景 ID 永遠無法解析。
* 超過 8 個字元會被視為完整 ID。

### 列出你可以對話的對象 \[#listing-what-you-can-talk-to]

```bash
curl https://reverie.im/api/device/v1/models \
  -H "Authorization: Bearer $REVERIE_API_KEY"
```

**全域**金鑰會回傳你最近聊過的角色。**綁定角色**的金鑰則會回傳該角色的場景；如果它沒有任何場景，則回傳角色本身。每一筆項目看起來像這樣：

```json
{
	"id": "Luna [abc12345]",
	"object": "model",
	"created": 1759200000,
	"owned_by": "character",
	"permission": [],
	"root": "<full character id>",
	"parent": null
}
```

把 `id` 直接複製到 `model` 即可。這個端點的錯誤使用簡單的錯誤格式，而不是 OpenAI 的錯誤外層結構。

## 請求內容會讀取哪些欄位 \[#what-the-request-body-reads]

只會讀取四個欄位。其他欄位都會被接受，然後在沒有任何提示的情況下被捨棄。

| 欄位            | 型別           | 預設值               | 說明                       |
| ------------- | ------------ | ----------------- | ------------------------ |
| `messages`    | array        | 必填                | 見下方——只會用到其中一部分           |
| `model`       | string       | 選填                | 角色、場景或大型語言模型，如上所述        |
| `stream`      | boolean      | **`true`**        | 預設就是串流，這點與 OpenAI API 不同 |
| `temperature` | 0–2 的 number | 角色本身的設定，否則為 `0.8` | 超出範圍的值會被拒絕               |

會被忽略且不會發出警告的欄位：`max_tokens`、`top_p`、`n`、`stop`、`presence_penalty`、`frequency_penalty`、`logit_bias`、`seed`、`user`、`response_format`、`tools`、`tool_choice`、`logprobs`、`stream_options`。

`messages` 的處理方式：

* **`system` 訊息會被捨棄。** 人設和行為來自角色本身的欄位，而不是請求。[提示詞欄位 →](https://reverie.im/zh-TW/docs/characters/prompt-fields)
* **不支援視覺輸入。** 內容可以是字串或分段陣列，但只會讀取 `text` 分段；圖片分段會被忽略。
* 只有**最後一則 `user` 訊息**會加入對話。較早的使用者訊息都會被忽略。
* 助理訊息只用於同步紀錄（見下方）。
* 如果最後一則使用者訊息是空的或只有空白，會回傳 `missing_user_message`。

## 回應 \[#responses]

非串流回應是一般的 `chat.completion` 物件。`finish_reason` 一律是 `stop`——內部的工具呼叫永遠不會對外公開——而且 `usage` 計數全都是零，所以請從 API 金鑰頁面上的使用統計來計算 Token，而不是從回應中取得。

串流回應的格式是 `text/event-stream`：

1. 內容區塊：帶有 `delta.content` 的 `chat.completion.chunk` 物件。\*&#x2A;不會傳送開頭的 `delta: {"role": "assistant"}` 區塊。*\*
2. 一個結束區塊，`finish_reason` 為 `stop`、`length` 或 `content_filter`。因工具呼叫而結束的情況會回報為 `stop`。
3. 一個 `choices` 陣列為空的用量區塊。
4. `data: [DONE]`。

如果生成在串流途中失敗，串流會送出 `data: {"error": {...}}`，接著是 `[DONE]`。

## 對話狀態 \[#conversation-state]

對話由 Reverie 保存。每一組「金鑰＋角色」對應一場聊天，它會和你的其他聊天一起出現在網頁應用程式中；每一輪都由以下內容組成：角色的系統提示詞和場景、世界書設定、長期記憶、滾動摘要、角色的敘述風格、回覆長度和 NSFW 設定、依你帳號語系而定的語言鎖定，以及模型上下文視窗所能容納的近期紀錄。當模型支援工具時，記憶工具就會執行。

由於紀錄保存在伺服器端，你不需要重新傳送整段紀錄。

> **傳送助理訊息可能會刪除紀錄。** 如果你的 `messages` 陣列中含有助理訊息，Reverie 會取出最後一則，在最近 20 則已儲存的訊息中尋找它；一旦找到，就會永久刪除在它之後建立的所有訊息，以及那個時間點之後建立的記憶。如果找不到，就不會刪除任何內容。陣列的長度並不重要。若要完全避免這種情況，請只傳送新的使用者訊息。

## 由哪個大型語言模型回答 \[#which-llm-answers]

由角色決定，而不是由請求決定——除非你指定了某個大型語言模型。優先順序由低到高為：預設聊天模型，接著是角色本身的模型設定，然後是作為 `model` 傳入的 Reverie 模型 ID，最後是你在網頁應用程式中為該聊天設定的模型偏好，它的優先順序高於前面所有設定。[模型 →](https://reverie.im/zh-TW/docs/reference/models)

## 錯誤 \[#errors]

`/chat/completions` 的錯誤使用 OpenAI 的錯誤外層結構：`{"error": {"message", "type", "param": null, "code"}}`。

| HTTP | `code`                 | `type`                  | 原因                                     |
| ---- | ---------------------- | ----------------------- | -------------------------------------- |
| 401  | `invalid_api_key`      | `invalid_request_error` | 缺少 `Authorization` 或格式錯誤、金鑰不存在，或金鑰已停用  |
| 401  | `api_key_expired`      | `invalid_request_error` | 金鑰已超過有效期限                              |
| 404  | `scenario_not_found`   | `invalid_request_error` | 場景 ID 已解析，但該場景已不存在                     |
| 404  | `character_not_found`  | `invalid_request_error` | 角色不存在或已被刪除                             |
| 404  | `no_chat_history`      | `invalid_request_error` | 沒有 `model`、沒有綁定角色，也沒有可以改用的先前聊天         |
| 400  | `missing_user_message` | `invalid_request_error` | 沒有非空白的 `user` 訊息                       |
| 429  | `rate_limit_exceeded`  | `rate_limit_exceeded`   | 免費模型的時段額度已用完。`Retry-After` 會告訴你需要等待的秒數 |
| 429  | `insufficient_quota`   | `insufficient_quota`    | 點數不足以支付這次請求                            |
| 502  | `MODEL_EMPTY_RESPONSE` | `server_error`          | 模型正常結束，但沒有產出任何文字                       |
| 不一定  | 供應商的錯誤代碼               | `server_error`          | 上游生成失敗                                 |

有兩種情況不使用這個外層結構：`GET /models` 的錯誤，以及未通過驗證而被拒絕的請求內容（例如超出範圍的 `temperature`）——這兩者都會回傳簡單的錯誤格式。

## 速率限制與配額 \[#rate-limits-and-quotas]

這個 API **沒有每組金鑰的速率限制、沒有並行上限，也沒有請求配額**。限制你用量的只有兩件事：

| 限制     | 數值                                                                 |
| ------ | ------------------------------------------------------------------ |
| 點數餘額   | 每次請求前都會檢查；不足時回傳 `insufficient_quota`                               |
| 免費模型時段 | 每個帳號每 3 小時 15 則訊息——Pro 為 ×3（45）、Premium 為 ×4（60）、Ultimate 為 ×5（75） |

免費模型的時段額度與網頁聊天、故事、小說和機器人共用——每個帳號只有一份額度，而不是每個使用管道各一份。[免費模型 →](https://reverie.im/zh-TW/docs/reference/free-models)

Reverie 不會對外傳送 webhook；沒有可以訂閱的生成完成事件。

## 匯入角色 \[#importing-characters]

第二個端點 `POST /api/v1/characters/import` 可以從角色卡建立角色。它需要一組具備匯入權限範圍的金鑰，而設定頁面建立的金鑰並不是這種。[API 金鑰 →](https://reverie.im/zh-TW/docs/integrations/api-keys)與[匯入角色 →](https://reverie.im/zh-TW/docs/characters/import-characters)

## 相關內容 \[#related]

* [API 金鑰](https://reverie.im/zh-TW/docs/integrations/api-keys)——建立、綁定、設定到期並監控金鑰
* [模型](https://reverie.im/zh-TW/docs/reference/models)——模型 ID 和點數倍率
* [限制](https://reverie.im/zh-TW/docs/reference/limits)——產品中的其他上限
* [點數](https://reverie.im/zh-TW/docs/billing/credits)——請求如何計價
