15 min給工具使用者

Prompt 不是魔法:Structured Output、Tool Calling 與非確定性

LLM 為什麼同一個 Prompt 會產生不同答案?從 Prompt constraints、Structured Output、Tool Calling、Temperature、Top-p 到 nondeterministic testing,理解 AI Application Engineer 如何從 Prompt Thinking 走向 System Thinking。

Aaron Huang系統、產品與 AI 實作

Prompt 不是魔法:Structured Output、Tool Calling 與非確定性

前一篇談到 Transformer、Attention 與 Embedding。

理解這些機制之後,下一個實務問題通常是:

既然 LLM 本質上是在預測下一個 Token,我們到底要怎麼讓它穩定地完成工作?

很多人第一個答案是:

把 Prompt 寫得更好。

這沒有錯,但只說對了一部分。

Prompt 可以影響模型的行為,卻不能把一個機率模型變成傳統程式。

當 AI 從聊天工具進入真正的應用系統後,我們真正需要處理的其實是幾個不同層次的問題:

  • 怎麼把任務與限制說清楚?
  • 怎麼讓輸出具有可被程式使用的結構?
  • 模型要怎麼和外部工具互動?
  • 為什麼同一個問題可能得到不同答案?
  • 如果輸出本來就可能變動,我們又該怎麼測試?

這也是從「會用 ChatGPT」走向「會做 AI Application」的一個重要分界。

Prompt Thinking 到 System Thinking 系統圖


一、Prompt 的真正作用:不是下咒語,而是縮小模型的選擇空間

Prompt Engineering 很容易被講得像一套神秘技巧:

只要找到某個神奇句型,模型就會突然變聰明。

但從模型的角度來看,事情其實沒那麼神秘。

LLM 每一步都在根據目前 Context,估計下一個 Token 的可能分布。

所以 Prompt 真正做的事情,是提供更多條件,讓某些輸出變得更合理、某些輸出變得比較不合理。

換句話說:

Prompt 的本質,是約束模型的行為空間。

例如只說:

「幫我分析這個產品。」

模型必須自己猜:

  • 要分析市場嗎?
  • 要分析產品功能嗎?
  • 要分析競爭者嗎?
  • 要多長?
  • 給誰看?
  • 要不要提供建議?

但如果改成:

「你是一名 B2B Product Marketing Analyst。請根據以下產品資料,從 Target Customer、Pain Point、Value Proposition、Competitive Differentiation 四個面向分析,每個面向最多三點;如果資料不足,標記為 insufficient information,不要自行補充。」

模型可選擇的空間就小很多。

因此好的 Prompt 通常不是靠「寫得很長」,而是把幾件事情說清楚:

1. Task

到底要模型做什麼。

例如:

  • 摘要
  • 分類
  • 擷取
  • 比較
  • 推理
  • 改寫
  • 產生內容

2. Context

模型完成任務需要知道什麼。

包含:

  • 背景
  • 使用者需求
  • 文件內容
  • 前一步結果
  • 外部系統提供的資料

3. Constraints

哪些事情不能做,或必須符合什麼限制。

例如:

  • 只能根據提供資料回答
  • 不知道就標記 unknown
  • 不超過 300 字
  • 必須使用繁體中文
  • 必須輸出指定欄位

4. Output Contract

結果要長什麼樣子。

這一點尤其重要。

因為當 LLM 只是給人閱讀時,「大致正確」可能就夠了。

但如果下一個接收者不是人,而是另一段程式碼,那麼:

「意思差不多」

通常不夠。


二、Structured Output:從「文字」變成「系統可以處理的資料」

假設你要模型判斷一張客服工單。

你期待得到:

類別:退款
緊急程度:高
需要人工處理:是

如果只是給人看,這完全沒問題。

但如果下一步要讓程式自動讀取結果,它可能期待的是:

{
  "category": "refund",
  "priority": "high",
  "requires_human": true
}

兩者對人來說意思差不多。

對程式來說卻完全不同。

這就是 Structured Output 要解決的問題。

自然語言是一個很差的 API Interface

LLM 非常擅長自然語言。

但自然語言本身充滿變化。

例如模型可能回答:

Priority: High

也可能:

This request appears to be high priority.

甚至:

{
  "priority": "urgent"
}

如果你的 downstream system 只接受:

"priority": "high"

那麼意思再接近都可能造成程式錯誤。

因此 AI Application 常常需要把模型輸出限制成某種 schema。

例如:

{
  "category": "billing | technical | account",
  "priority": "low | medium | high",
  "requires_human": true
}

這相當於建立一個:

Model Output Contract

模型不再只是回答問題。

它是在產生另一個系統可以消費的資料。


三、Structured Output 不等於「叫它輸出 JSON」

這裡有一個常見誤區。

很多人會在 Prompt 裡寫:

Please output JSON.

然後看到模型真的輸出 JSON,就認為問題解決了。

但這只是第一層。

真正的系統通常至少還要考慮:

Syntax Validation

它是不是合法 JSON?

Schema Validation

欄位是否存在?

型別是否正確?

例如:

"requires_human": "yes"

可能是合法 JSON,但如果系統期待 Boolean:

"requires_human": true

它仍然是不合法的 application output。

Semantic Validation

就算格式完全正確,內容仍可能不合理。

例如:

{
  "priority": "low",
  "requires_human": false
}

格式沒有任何問題。

但如果原始使用者說:

我的帳號遭到盜用,而且信用卡正在被扣款。

那問題就不在 JSON,而在模型的判斷。

所以:

格式正確 ≠ 結果正確。

Structured Output 解決的是「介面穩定性」的一部分,而不是替代內容驗證。


四、Tool Calling:LLM 不需要自己做所有事情

假設使用者問:

我今天下午三點有沒有會議?

LLM 本身不知道你的 Calendar。

如果直接回答,它只能:

  • 使用 Context 裡已有的資訊
  • 或猜

更合理的系統設計,是讓模型判斷:

我需要查 Calendar。

然後產生類似:

{
  "tool": "get_calendar_events",
  "arguments": {
    "date": "2026-09-21",
    "time": "15:00"
  }
}

Application 收到這個結果後,才真正去執行 Calendar API。

取得資料後,再把結果送回模型。

模型最後回答:

你下午三點有一場 Project Review。

這就是 Tool Calling 的核心概念。


五、LLM 通常不是 Tool Executor,而是 Tool Decision Maker

這個區別非常重要。

很多架構圖會畫成:

LLM → Tool

看起來像是模型自己呼叫 API。

但實際應用通常更接近:

User
 ↓
Application
 ↓
LLM
 ↓
Tool Request
 ↓
Application
 ↓
External Tool / API
 ↓
Result
 ↓
Application
 ↓
LLM
 ↓
Final Response

也就是:

模型提出它想使用什麼工具,以及需要哪些參數。

真正執行工具的是外部 Application Runtime。

因此 Tool Calling 本質上不是模型突然獲得了超能力。

而是:

我們讓模型可以參與軟體系統的控制流程。

這也是 LLM Application 和單純聊天機器人開始產生巨大差異的地方。


六、Tool Calling 最重要的不是「有工具」,而是 Interface

假設模型有一個搜尋工具:

search(query)

這個 interface 非常寬。

模型必須自己猜:

  • query 要多長?
  • 用什麼語言?
  • 要不要放日期?
  • 搜尋的是網頁還是公司資料?
  • 結果有幾筆?

如果改成:

search_documents(
  query: string,
  source: "internal" | "web",
  max_results: integer,
  date_after: date | null
)

模型可以做出的選擇就清楚很多。

這和 Prompt constraints 是同一個核心概念:

不要期待模型自己猜對所有事情,而是透過 interface design 縮小錯誤空間。

因此 Tool Calling 做得好不好,不只是模型能力問題。

Tool schema、本身的 description、argument definition,同樣會直接影響成功率。


七、Sampling:為什麼同一個 Prompt 不一定得到同一個答案?

這時候就會碰到另一個 LLM 和傳統軟體很不一樣的地方:

它的輸出具有機率性。

模型在每個 Token 上,都會產生一組候選 Token 的 probability distribution。

概念上可能像:

AI       0.38
model    0.21
system   0.17
agent    0.09
...

接下來系統必須決定:

要選哪一個?

最簡單的方法,是永遠選最高機率的 Token。

但實際生成系統也可能從多個候選中抽樣。

因此模型可能在不同執行中走上不同的文字路徑。

八、Temperature:不是「創意按鈕」,而是調整機率分布

Temperature 經常被簡化成:

Temperature 高 = 有創意
Temperature 低 = 準確

這種說法方便理解,但不夠精確。

Temperature 更接近:

調整候選 Token probability distribution 的集中程度。

Temperature 較低時:

高機率 Token 會更突出。

模型傾向選擇較高機率的結果。

Temperature 較高時:

機率分布會比較平坦。

原本機率較低的 Token 也更可能被選到。

所以更精確的理解是:

Lower temperature
→ narrower output distribution

Higher temperature
→ wider output distribution

這確實可能表現在「更保守」或「更多樣化」上。

但 Temperature 本身不是:

  • factuality knob
  • intelligence knob
  • creativity detector

它只是 sampling configuration 的其中一部分。


九、Top-p:限制模型從多大的候選集合裡抽樣

另一個常見參數是 Top-p。

它的基本概念是:

不要從整個 vocabulary 中抽樣,而是先找出一組累積機率達到指定門檻的 Token,再從這組 Token 中選擇。

例如某一步:

A  0.50
B  0.25
C  0.15
D  0.06
E  0.04

如果 Top-p 設定為 0.9,系統可能只保留:

A + B + C = 0.90

後面的 Token 不參與這次抽樣。

因此:

  • Temperature 改變 probability distribution 的形狀
  • Top-p 改變 sampling candidate set

兩者都會影響模型輸出的多樣性與穩定程度。


十、為什麼 AI Application 很難用傳統 Unit Test 思維測試?

傳統程式通常可以測:

assert add(2, 2) == 4

輸入一樣。

輸出應該一樣。

但如果測試 LLM:

Input:
Summarize this customer complaint.

第一次可能輸出:

Customer requests a refund because the product arrived damaged.

第二次:

The customer received a damaged product and is asking for a refund.

兩個答案其實都正確。

如果我們測:

assert output == expected_output

第二個答案就會被判定失敗。

這就是 LLM Application 測試很重要的一個思維轉換:

很多時候我們不能測 exact output,而要測 expected behavior。


十一、不要問「答案是不是完全一樣」,而要問「它有沒有違反 Contract」

例如客服分類系統,我們可以測:

Schema

是不是符合:

{
  "category": "...",
  "priority": "...",
  "requires_human": true
}

Allowed Values

category 是否只能是:

billing
technical
account

Business Rules

如果使用者描述帳號被盜:

requires_human == true

Grounding

如果來源資料沒有訂單編號,模型不能自己生成訂單編號。

Tool Behavior

需要最新訂單狀態時,有沒有使用 order lookup tool?

這些通常比檢查整段文字是否完全一樣更有意義。


十二、AI 測試的核心:從 Exact Match 轉成 Invariant

可以把這個觀念理解成:

傳統程式:

Input
↓
Expected Exact Output

LLM Application:

Input
↓
Range of Acceptable Outputs
↓
Required Invariants

Invariant 指的是:

不管模型怎麼表達,都不應該被破壞的條件。

例如:

不能捏造資料
必須輸出合法 schema
重要事件必須 escalated
不能呼叫不存在的 tool
工具參數必須符合 schema

這些才是系統真正關心的事情。


十三、所以 Prompt Engineering 其實只是其中一層

把整篇文章串起來,就會發現:

一個可靠的 LLM Application 不會把所有希望都壓在 Prompt 上。

比較完整的結構反而是:

Prompt
↓
Constraints
↓
Structured Output
↓
Schema Validation
↓
Tool Interface
↓
Application Logic
↓
Evaluation

Prompt 負責告訴模型:

你應該怎麼做。

Structured Output 負責定義:

你的回答必須長什麼樣子。

Tool Calling 負責:

你可以向外部世界取得什麼能力。

Sampling 告訴我們:

為什麼模型結果天然可能變動。

Testing 則回答:

在輸出可能變動的情況下,我們怎麼判斷系統仍然正常。


十四、真正的轉變:從 Prompt Thinking 走向 System Thinking

這也是 AI Application Engineer 很重要的一個能力分界。

初學者遇到模型答錯,第一個反應通常是:

Prompt 再改一下。

這有時候有效。

但真正進入 Production 之後,更應該問:

這是 Prompt 問題?
還是 Context 不夠?

是輸出格式不穩?
還是 Schema 沒有限制?

是模型沒有資料?
還是應該呼叫 Tool?

是模型正常的 Sampling Variation?
還是真的 Regression?

是模型回答不好?
還是整個 System Design 有問題?

這時我們已經不再只是在「使用一個模型」。

而是在設計:

一個包含機率模型的軟體系統。

而這也會帶出下一個更大的問題。

即使 Prompt 寫得很好、Structured Output 正確、Tool Calling 成功,每個單點看起來都正常:

整個 AI 系統真的就可靠了嗎?

答案通常沒有這麼簡單。

因為真正上線之後,我們還要面對:

  • model failure
  • retrieval failure
  • tool failure
  • validation
  • fallback
  • observability
  • cost
  • latency
  • evaluation

這些問題已經不只是 Prompt Engineering。

而是 Production AI Engineering

這就是下一篇要處理的問題。