文章目錄

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 個選項時confidence0.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,例如緊急程度,回傳的數值可以直接排序。