Jev API 串接筆記:請求結構、後端呼叫流程與結果判斷

更新

摘要

用一個 HTTP 請求呼叫 Jev 模型以獲得各選項的機率,可於後端設計中配合機械規則來判斷資料的類型。以客服工單為例,是否要求退款、該分派給哪個團隊、緊急到什麼程度分別用不同題型提問,機率不夠高的結果則轉給人工處理。

文章目錄

Jev 是 TypeSafe 的決策模型,後端只要用一個 HTTP POST 請求送出要判斷的資料 state 與題目,就能取得各個答案的機率。模型只負責判斷,結果要自動執行還是交給人工由程式依門檻決定。

先前的 Jev 與 Laya 決策模型的文章推薦實測 只測了 Choice 一種題型。本文以客服工單為例,設計部分不限系統架構,最後一節是後端呼叫流程。範例數據來自 2026-10-10 以 jev-1.13.0 實際呼叫 API 的結果,7 張工單各送一次請求,只用來說明題型用途而不代表準確率。

請求與回應結構

依官方 API Reference 與 Models 頁,請求的基本資訊如下:

項目 內容
端點 POST https://api.typesafe.ai/v1/systemone
標頭 Authorization: Bearer <API_KEY>、Content-Type: application/json
model(必填) 固定的模型版本如 jev-1.13.0,或指向旗艦模型的 jev-latest
state(必填) 要判斷的資料,可以是字串、物件或陣列
questions(必填) 以自訂 ID 為鍵的題目集合,回應用相同 ID 對應每題答案,ID 只給程式用而不會送給模型
每題欄位 type(noul、choice、score)與 instructions 必填,criteria 在 Choice 與 Score 必填、在 Noul 選填
長度限制 每次請求最多 64k tokens,state 加上最長的一題最多 32k tokens,只接受文字

一個請求可以放多題:

  • 官方 Primitives 文件 說明各題在同一份 state 上獨立評估,題目之間不會互相參照。
  • 實測把下例的 refund 與 team 拆成兩次單題請求,答案與合併請求相同。拆開送這兩題的輸入 token 合計 682,比三題合併一次的 500 還多。

下例用一張工單同時問三題:

1{
2  "model": "jev-1.13.0",
3  "state": "我的訂單 A-104 被重複扣款兩次,請今天退一筆給我。",
4  "questions": {
5    "refund": { "type": "noul", "instructions": "客戶是否要求退款?" },
6    "team": {
7      "type": "choice",
8      "instructions": "這張工單應該由哪個團隊處理?",
9      "criteria": {
10        "billing": "付款、扣款或退款",
11        "technical": "程式錯誤、當機或服務中斷",
12        "other": "其他問題"
13      }
14    },
15    "urgency": {
16      "type": "score",
17      "instructions": "這張工單有多緊急?",
18      "criteria": [
19        "不需處理:問題已經解決",
20        "可以等:輕微不便,沒有實際影響",
21        "本週處理:確實有問題,但有替代做法",
22        "今天處理:正在損失金錢或服務中斷"
23      ]
24    }
25  }
26}

上面的請求實際回傳模型版本 model、以題目 ID 為鍵的答案 answers 與 token 用量 usage:

1{
2  "model": "jev-1.13.0",
3  "answers": {
4    "refund": { "type": "noul", "noul": 0.99 },
5    "team": {
6      "type": "choice",
7      "choice": "billing",
8      "confidence": 1.0,
9      "probabilities": { "billing": 1.0, "technical": 0.0, "other": 0.0 }
10    },
11    "urgency": {
12      "type": "score",
13      "score": 3.0,
14      "confidence": 1.0,
15      "legend": {
16        "0": "不需處理:問題已經解決",
17        "1": "可以等:輕微不便,沒有實際影響",
18        "2": "本週處理:確實有問題,但有替代做法",
19        "3": "今天處理:正在損失金錢或服務中斷"
20      },
21      "probabilities": { "0": 0.0, "1": 0.0, "2": 0.0, "3": 1.0 }
22    }
23  },
24  "usage": { "input_tokens": 500, "output_tokens": 69 }
25}

三種題型的用途

官方 Models 頁 表示英文是主要訓練語言且準確度最好,中文等其他語言也能處理但準確度不如英文,用中文出題時要先拿自己的資料測過再決定門檻。以下 7 張工單都用上面同一組中文題目提問:

工單 內容
T1 我的訂單 A-104 被重複扣款兩次,請今天退一筆給我。
T2 上個月申請的退款已經收到了,謝謝。
T3 發票頁面偶爾載入有點慢,不影響使用。
T4 結帳頁面完全無法付款,客戶一直在流失。
T5 我被重複扣款,而且一打開帳務頁 App 就閃退。
T6 這個月的方案費用好像比上個月高,想確認是不是算錯了。
T7 想詢問企業方案有沒有年繳優惠。

Noul:判斷某件事是否成立

Noul 適合偵測與過濾,例如是否要求退款、是否含個資、是否為垃圾訊息。題目寫成一個是非問句,一題只問一個條件,問法要讓高值代表「是」而不用「是否不含個資」這類否定句。界線不明確時可以在 criteria 用 {"true": …, "false": …} 分別描述成立與不成立的情況。本例判斷的是「客戶是否要求退款?」,回傳的 noul 是客戶在這張工單中要求退款的機率:

工單 要求退款的機率 noul
T1 要求今天退一筆 0.99
T2 退款已經收到 0.42
T3 發票頁偶爾慢 0.03
T4 結帳無法付款 0.18
T5 重複扣款且 App 閃退 0.43
T6 確認費用是否算錯 0.06
T7 詢問年繳優惠 0.02

程式在 noul 機率上設門檻,誤判成「是」代價高時(例如核發退款)把門檻調高,漏掉「是」代價高時(例如安全問題)則調低。官方範例以高於 0.8 當成「是」、0.2 以下當成「否」,介於兩者之間的交給人工。T2 與 T5 都提到退款或扣款卻沒有明確要求退款,機率落在 0.42 與 0.43,在這組門檻下會交給人工。

Choice:從沒有順序的類別中選一個

Choice 適合分類與路由,例如分派團隊、判斷意圖、選擇要呼叫的工具。criteria 以選項名稱對應描述,最多 255 個選項。選項名稱和描述都會送給模型,choice 回傳的也是這個名稱,名稱本身夠清楚時描述可以填 null。列出的選項就是模型能選的範圍,官方 Choice 文件 建議選項可能涵蓋不完時保留 other 這類選項。本例判斷的是「這張工單應該由哪個團隊處理?」,選項為 billing(付款、扣款或退款)、technical(程式錯誤、當機或服務中斷)與 other(其他問題)。Choice 回傳機率最高的 choice、各選項的 probabilities 與 confidence:

工單 billing technical other choice confidence
T1 要求今天退一筆 1.00 0.00 0.00 billing 1.00
T2 退款已經收到 0.88 0.00 0.12 billing 0.82
T3 發票頁偶爾慢 0.01 0.92 0.07 technical 0.88
T4 結帳無法付款 0.30 0.70 0.00 technical 0.55
T5 重複扣款且 App 閃退 0.39 0.46 0.15 technical 0.19
T6 確認費用是否算錯 1.00 0.00 0.00 billing 1.00
T7 詢問年繳優惠 0.88 0.00 0.12 billing 0.82

程式直接依 choice 分派,confidence 低時交給人工。T5 同時涉及扣款與閃退,機率分散在三個選項,confidence 只有 0.19。T7 詢問年繳優惠,other 只有 0.12,模型以 0.82 的 confidence 選了 billing,若以 0.5 為門檻會直接通過。confidence 只代表機率有多集中而不是正確率,模型很有把握卻選錯時門檻擋不住,要靠調整選項描述或拿已知答案的資料校正。

Score:評估有順序的程度

Score 適合分級與排序,例如緊急程度、嚴重度、相關性。criteria 是由低到高排列的 2 到 10 個等級描述,回傳的 score 是依機率加權的等級數值,legend 以索引對應等級描述。每一級要描述具體情境而不是「輕微」「嚴重」這類程度詞,模型看不到等級編號與相鄰等級,描述裡不依賴等級編號,也不寫「比上一級更嚴重」這類相對說法。一題只量一個面向,多面向的判斷拆成多題再由程式組合。本例判斷的是「這張工單有多緊急?」,四個等級由低到高為 0 不需處理、1 可以等、2 本週處理、3 今天處理:

工單 緊急程度 score confidence
T2 退款已經收到 0.00 1.00
T3 發票頁偶爾慢 1.00 1.00
T7 詢問年繳優惠 1.02 0.96
T6 確認費用是否算錯 1.73 0.61
T1 要求今天退一筆 3.00 1.00
T4 結帳無法付款 3.00 1.00
T5 重複扣款且 App 閃退 3.00 1.00

程式依 score 排序工單,或四捨五入成等級後套用不同的處理時限。不同的機率分布可能算出同一個 score,例如 1、2 級各 0.5 得到 1.5 且 confidence 是 0.5,0、3 級各 0.5 也得到 1.5,confidence 卻是 0。四捨五入前要先看 confidence,偏低時改看 probabilities 或交給人工。T6 的 1.73 介於「可以等」與「本週處理」之間且較接近「本週處理」,機率主要分在這兩級(0.33 與 0.61),另有 0.06 在「今天處理」,confidence 是 0.61。

結果判斷

confidence 介於 0 到 1,只有 Choice 與 Score 會回傳,Noul 直接在 noul 機率上設門檻。兩者的算法不同:

  • Choice 的 confidence 只看最高選項的機率,並已依選項數換算,數值和機率不相等。例如 3 個選項時 confidence 0.5 約等於最高機率 0.67,最高機率同為 0.5 時 2 個選項的 confidence 是 0 而 10 個選項是 0.44。選項數不同的題目可以用同一個刻度比較。
  • Score 的 confidence 會考慮等級順序,機率分在相鄰等級時降得少,分在相隔很遠的等級時降得多。

官方 Confidence 文件 沒有提供通用門檻,只以查詢餘額與核准轉帳為例示範:

條件 範例動作
confidence 低於 0.5 交給人工處理(route_to_human)
0.5 以上的低風險動作,例如查詢餘額 直接執行
0.5 以上、0.9 以下的高風險動作,例如核准轉帳 先向使用者核實(ask_user_to_confirm)
超過 0.9 的高風險動作 進入確認並執行的流程(confirm_then_execute)

以 0.5 為團隊分派的門檻時,T5 的 0.19 會交給人工,T4 的 0.55 則自動分派給 technical。

後端呼叫流程

範例以 Python 撰寫,換成其他語言時流程相同。API 金鑰在 TypeSafe 主控台的 API Keys 頁面 建立,和模型版本、端點一起放在 .env,模型固定在 jev-1.13.0 這種明確版本,判斷品質才不會隨模型更新而改變:

1JEV_API_KEY=你的金鑰
2JEV_MODEL=jev-1.13.0
3JEV_API_URL=https://api.typesafe.ai/v1/systemone

官方列出的錯誤狀態碼與範例的處理方式如下,其中 429 與 529 建議以指數退避重試,最後一列是範例另外處理的連線錯誤:

狀態碼 意義 後端處理
401 金鑰缺少或錯誤 不重試,直接回報錯誤
422 請求格式不符,回應會指出出錯欄位 不重試,直接回報錯誤
429 超過速率限制 官方上限為每秒 100K tokens 與 80 個請求並會動態調整。回應帶 retry-after 標頭時以它為準,沒有才用指數退避,範例最多重試 3 次並依序等待 1、2、4 秒,未實際觸發過
529 服務過載 同 429
逾時、連線失敗 不是 HTTP 狀態碼 範例不重試,直接回報錯誤

我們可建立一個函式 ask_jev 來負責送出請求。它用 .env 讀出的模型版本 MODEL 與端點 API_URL,把要判斷的資料 state 與題目 questions 組成請求本體送出,遇到 429 或 529 時依上表重試,其他錯誤直接回報:

1def ask_jev(client, state, questions, retries=3):
2    payload = {"model": MODEL, "state": state, "questions": questions}
3    for attempt in range(retries + 1):
4        response = client.post(API_URL, json=payload)
5        if response.status_code not in (429, 529) or attempt == retries:
6            response.raise_for_status()
7            return response.json()
8        time.sleep(2**attempt)

後端服務在啟動時建立一個 HTTP 用戶端,以 .env 讀出的金鑰 API_KEY 設定授權標頭並設定逾時,所有請求共用它來重用連線,服務關閉時再釋放。下例的 ticket 是工單文字,前面請求範例的三道題目分別存成常數 REFUND_QUESTION、TEAM_QUESTION 與 URGENCY_QUESTION 後送出:

1# 服務啟動時建立,關閉時釋放
2with httpx.Client(
3    headers={"Authorization": f"Bearer {API_KEY}"},
4    timeout=httpx.Timeout(30, connect=10),  # 範例值
5) as client:
6    result = ask_jev(
7        client,
8        ticket,
9        {"refund": REFUND_QUESTION, "team": TEAM_QUESTION, "urgency": URGENCY_QUESTION},
10    )

取得 result 後,以 0.5 為門檻決定自動分派或交給人工:

1team = result["answers"]["team"]
2action = f"route to {team['choice']}" if team["confidence"] >= 0.5 else "send to a human"

總結

後端用共用的 HTTP 用戶端與只對 429、529 重試的呼叫函式就能完成串接,重點在選對題型與設定門檻:

  • 是否成立用 Noul,例如是否要求退款,門檻設在機率上。
  • 分類與路由用 Choice,例如分派團隊,confidence 低的交給人工。
  • 分級與排序用 Score,例如緊急程度,回傳的數值可以直接排序。
下一篇Astro 靜態網站加入新文章推播通知:Cloudflare Worker、D1 與 Queue 的 Web Push 設計Workflow
Ted Liou

Ted Liou

Unity 現役工程師,Unity、AI 技術開發經驗分享與諮詢。