Skip to content

對話 API:同步呼叫與逐字串流

對話 API 是一問一答的同步 API:送一段對話過去,直接在回應裡拿到 AI 的回覆; 加上 stream: true 就會以 Server-Sent Events 一個字一個字吐回來。只給 Skill agent 用。

跟通用 Webhook 怎麼選 ​

對話 API通用 Webhook
回覆方式同一個請求直接回(可逐字串流)先回 200,回覆稍後 POST 到你的 callback
對話歷史你每次帶完整對話你每次帶完整對話
適合網站聊天視窗、App 內即時對話客服系統、訊息平台這類非同步的收發
Agent 類型SkillSkill、Channel(含表單、轉真人、主動追問)

用 Channel agent 的金鑰打對話 API 會回 400:

json
{"error": "客服 AI(Channel agent)不支援對話 API,請改用通用 Webhook 串接"}

Channel agent 的表單、情境、轉真人都要跨好幾則訊息保存狀態,一問一答的 API 撐不起來。

認證 ​

到 Skill agent 的串接分頁,對話 API 卡片上有API 金鑰,每個 agent 一把。 放在 header:

Authorization: Bearer <API 金鑰>

API 金鑰就是憑證,拿到的人可以用這個 agent 對話、消耗你的用量,所以:

  • 只放在你的後端,不要寫進網頁或 App 的前端程式碼裡——任何人打開瀏覽器開發者工具都看得到。 網站聊天視窗請由你的後端轉呼叫。
  • 外流了按重新產生金鑰,舊的立刻失效。

沒帶或金鑰錯誤會回 401:

json
{"detail": "UNAUTHORIZED", "status_code": 401}

請求 ​

http
POST https://api.oakxgen.ai/agent/api/v2/conversation/
Authorization: Bearer <API 金鑰>
Content-Type: application/json

{
  "prompt": [
    {"role": "user", "content": "你們有賣帆布包嗎?"},
    {"role": "assistant", "content": "有的,目前有經典款與大容量款。"},
    {"role": "user", "content": "大容量款多少錢?"}
  ],
  "stream": false
}
欄位必填說明
prompt✅完整對話,照時間順序。role 是 user(客人)或 assistant(AI),最後一則必須是 user,也就是這次要 AI 回答的那句。
streamtrue 逐字串流,預設 false。
webhook_url給了就改成非同步:立刻回 200,回覆 POST 到這個網址。同時帶 stream: true 的話以串流為準,webhook_url 會被忽略。
webhook_secret搭配 webhook_url,我們用它簽回呼的內容,見下方「非同步回呼」。
json_schema給了就改成回結構化 JSON,見下方「結構化輸出」。
prompt_vars代換 agent 提示詞裡的 {變數名},見下方「提示詞變數」。
allowed_tools限制 AI 能用的工具,[] 代表完全不用工具(不查知識庫、不打 API)。不給就是 agent 設定的全部工具。

OakXgen 不保存對話內容,每次都要帶完整的 prompt。要控制成本的話,可以只帶最近幾輪。

最後一則不是 user 會回 400:

json
{"error": "prompt 最後一則必須是 user 訊息"}

同步回應(stream: false) ​

json
{
  "stream": false,
  "messages": [
    {"type": "text", "text": "大容量款是 NT$ 1,290。"},
    {"type": "card_carousel", "cards": [{"title": "大容量帆布包", "description": "NT$ 1,290", "image_url": "https://..."}]},
    {"type": "quick_replies", "replies": [{"label": "加入購物車", "data": "加入購物車"}]}
  ],
  "references": [],
  "usage": {"input_tokens": 1834, "output_tokens": 52}
}

messages 是一串區塊,照順序顯示:text 文字、card_carousel 卡片、quick_replies 快速回覆。 references 只有 agent 開了顯示參考資料時才會出現,列出 AI 引用了知識庫的哪些段落。 usage 是這次請求消耗的 token(含 AI 查工具時的多次呼叫),也會記進後台的 AI 用量頁面。

結構化輸出(json_schema) ​

要拿 AI 的判斷結果去跑自己的程式(意圖分類、欄位抽取、從清單裡挑項目),帶一個 JSON Schema,回應會多一個 parsed,保證符合這個格式:

json
{
  "prompt": [{"role": "user", "content": "明天台北空氣好嗎"}],
  "allowed_tools": [],
  "json_schema": {
    "title": "intention",
    "type": "object",
    "properties": {"intention": {"type": "string"}},
    "required": ["intention"]
  }
}
json
{
  "messages": [{"type": "text", "text": "{\"intention\": \"空氣品質查詢\"}"}],
  "parsed": {"intention": "空氣品質查詢"},
  "usage": {"input_tokens": 229, "output_tokens": 19}
}
  • agent 的提示詞與模型照用,只問一次、不會用工具,通常搭配 allowed_tools: []。
  • Python 用 pydantic 的話,直接傳 Model.model_json_schema()。
  • 只支援 OpenAI、Anthropic 引擎的 agent;其他引擎,或同時帶 stream: true,會回 400。
  • 模型回的不是合法 JSON 時回 502(極少見)。

提示詞變數(prompt_vars) ​

agent 的提示詞可以留 {變數名},每次呼叫時再填:

json
{
  "prompt": [{"role": "user", "content": "生小孩有補助嗎"}],
  "prompt_vars": {"chatbot_information": "[{\"id\": 1, \"template_name\": \"育兒津貼說明\"}]"}
}

只換 prompt_vars 裡有的名稱,其他花括號(例如提示詞裡寫的 JSON 範例)原樣保留。 值不是字串的話會先轉成 JSON 文字。prompt_vars 與 json_schema 不會出現在回應裡。

逐字串流(stream: true) ​

回應的 Content-Type 是 text/event-stream,每個事件是一行 data: <JSON>,事件之間空一行, 最後一行是 data: [DONE]:

data: {"type": "thinking"}

data: {"type": "tool-input-available", "toolCallId": "call_1", "toolName": "search_knowledge", "label": "查詢知識庫", "input": {...}}

data: {"type": "tool-output-available", "toolCallId": "call_1"}

data: {"type": "text-start", "id": "t1"}

data: {"type": "text-delta", "id": "t1", "delta": "大容量款"}

data: {"type": "text-delta", "id": "t1", "delta": "是 NT$ 1,290。"}

data: {"type": "text-end", "id": "t1"}

data: {"type": "complete", "session_id": null, "session_name": null, "references": []}

data: {"type": "usage", "input_tokens": 1834, "output_tokens": 52}

data: [DONE]
type意思
thinkingAI 開始處理,可以顯示「思考中」。
tool-input-available / tool-output-availableAI 正在用工具(查知識庫、打 API…),label 是給人看的說明。
text-start / text-delta / text-end文字回覆。把 delta 依序接起來就是完整回覆。
rich-content卡片或快速回覆,欄位是 cards 與 quick_replies,格式同上面的同步回應。
complete回覆結束,references 是引用的知識庫段落。
usage這次請求消耗的 token,緊接在 [DONE] 之前。
error處理失敗,message 是原因。這之後連線直接結束,不會有 [DONE],讀取迴圈要能處理串流自然結束。

瀏覽器內建的 EventSource 只能發 GET、也不能帶 Authorization header,所以用不了。 請在後端用 fetch 或任何支援串流讀取的 HTTP 客戶端,逐行讀 data:。

Node.js:

js
const res = await fetch('https://api.oakxgen.ai/agent/api/v2/conversation/', {
  method: 'POST',
  headers: { Authorization: `Bearer ${API_KEY}`, 'Content-Type': 'application/json' },
  body: JSON.stringify({ prompt, stream: true })
})

const decoder = new TextDecoder()
let buffer = ''
for await (const chunk of res.body) {
  buffer += decoder.decode(chunk, { stream: true })
  const events = buffer.split('\n\n')
  buffer = events.pop()
  for (const event of events) {
    const data = event.replace(/^data: /, '')
    if (data === '[DONE]') break
    const payload = JSON.parse(data)
    if (payload.type === 'text-delta') process.stdout.write(payload.delta)
  }
}

Python:

python
import json, requests

with requests.post(
    "https://api.oakxgen.ai/agent/api/v2/conversation/",
    headers={"Authorization": f"Bearer {API_KEY}"},
    json={"prompt": prompt, "stream": True},
    stream=True,
) as res:
    for line in res.iter_lines(decode_unicode=True):
        if not line.startswith("data: "):
            continue
        data = line[len("data: "):]
        if data == "[DONE]":
            break
        payload = json.loads(data)
        if payload["type"] == "text-delta":
            print(payload["delta"], end="", flush=True)

中間如果經過你自己的反向代理(nginx 等),記得關掉 response buffering,不然會整段收完才一次吐出來。

非同步回呼(webhook_url) ​

帶了 webhook_url 會立刻回 {"status": "ok"},AI 回覆好之後 POST 同步回應那一包 JSON 到 webhook_url。送一次、不重試。

有帶 webhook_secret 的話,回呼會帶簽章,算法跟通用 Webhook 的簽章相同:

X-OakXgen-Timestamp: <unix 秒數>
X-OakXgen-Signature: base64(HMAC-SHA256(webhook_secret, "<timestamp>." + raw body))

請拿收到的 raw body 驗簽,並拒收時間差超過 5 分鐘的回呼。舊的 X-Webhook-Secret header (直接放 webhook_secret 明文)目前還會帶,但只是為了相容;它擋不了內容被竄改或重送,新接的請改驗簽章。

webhook_url 必須是對外公開的 https:// 網址,http://、內網 IP、localhost、雲端 metadata 位址會回 400, 錯誤訊息以「webhook_url 不允許」開頭。這個檢查在 AI 開始回覆之前就做,網址不合格不會消耗額度。

需要真人接手、主動追問這些功能的話,請改用通用 Webhook。

Agent 被關閉時 ​

agent 被關閉時回 200 與空物件 {},不會有回覆。