外觀
對話 API 是一問一答的同步 API:送一段對話過去,直接在回應裡拿到 AI 的回覆; 加上 stream: true 就會以 Server-Sent Events 一個字一個字吐回來。只給 Skill agent 用。
跟通用 Webhook 怎麼選
| 對話 API | 通用 Webhook | |
|---|---|---|
| 回覆方式 | 同一個請求直接回(可逐字串流) | 先回 200,回覆稍後 POST 到你的 callback |
| 對話歷史 | 你每次帶完整對話 | 你每次帶完整對話 |
| 適合 | 網站聊天視窗、App 內即時對話 | 客服系統、訊息平台這類非同步的收發 |
| Agent 類型 | Skill | Skill、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 回答的那句。 |
stream | true 逐字串流,預設 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 | 意思 |
|---|---|
thinking | AI 開始處理,可以顯示「思考中」。 |
tool-input-available / tool-output-available | AI 正在用工具(查知識庫、打 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 與空物件 {},不會有回覆。