iT邦幫忙

2026 iThome 鐵人賽

DAY 16
0
AI Engineering

從 4,343 筆職缺到 AI Engineer:MLOps × GenAI Engineering 雙主軸實戰系列 第 16

Day 16:從 Prompt 到 Context Engineering — 輸入視窗是一份預算

  • 分享至 

  • xImage
  •  

今天進入主軸二:生成式 AI。前八天我們在解決關於 MLOps 的「模型會不會掛」,接下來八天解決「模型會不會智商下線」。

主軸一的讀者會發現,這邊的問題同樣需要工程紀律,只是換了一種形式。

今天要解決的問題

繼續一個在模型上線後的經典情境:

第 1 週:工程師寫了一句 prompt,效果不錯,上線。
第 3 週:發現某些情況答得不好,加了幾句規則。
第 8 週:prompt 長到 300 行,裡面有 27 條「注意:如果……就……」的規則,改一句話會讓另外三個情境壞掉,而且沒有人敢動它

這就是「prompt 工程」做到極限的樣子。問題不在技巧不夠,在於把所有事情都塞進一段文字裡這個做法本身有上限。

今天要換一個框架來看這件事。

📊 職缺訊號

「Prompt 與脈絡設計」出現在 32.9% 的生成式 AI 職缺中。但要注意:幾乎沒有職缺是「只做 prompt」的——它總是與 RAG(48.1%)、Agent(47.6%)綁在一起。這說明市場已經過了「prompt engineer」這個職稱的階段,提示設計是一項基本功,不是一個職務


一、現象:模型看到的不只是你寫的那段話

先建立一個基本認知:當你呼叫一次 LLM,模型實際收到的輸入是這樣組成的:

image

圖 16-1:模型輸入的六個來源(示意架構)。「prompt engineering」通常只處理第一項,而輸出品質由六項共同決定。

這就是 Context Engineering 與 Prompt Engineering 的差別:

  • Prompt Engineering:怎麼把指令寫好。
  • Context Engineering在有限的 token 預算內,決定放什麼、放多少、放在哪個位置

當你的系統開始有 RAG 與工具時,第二個問題的重要性會遠超過第一個。

二、原理:位置很重要——Lost in the Middle

如果脈絡是預算,那放在哪裡放什麼一樣重要:

Lost in the Middle:答對率隨關鍵文件位置呈 U 形

圖 16-2:關鍵資訊在脈絡中的位置對答對率的影響(示意曲線,趨勢依 Liu et al. 2023 的公開發現繪製)。放在開頭或結尾的資訊被使用得最好,放在中段的最容易被忽略。

這個現象有兩個直接的工程結論:

  1. 不要為了「多給資訊」而把脈絡塞滿。 塞進去的第 15 份文件,可能反而稀釋了第 1 份文件的效果。這是 Day 19 要做重排序(reranking)的原因——與其給 20 份,不如給最相關的 3 份
  2. 重要的東西放前面或後面。 實務上的常見安排:系統提示與規則放最前(也利於 prompt caching,Day 26)、檢索到的文件放中段但依相關度排序、使用者的問題與最關鍵的指令放最後

三、動手:四個立即可用的技巧

3.1 弱 prompt 與強 prompt 的對照

提示技巧的效果對照

圖 16-3:常用提示技巧與適用情境(示意對照)。技巧的效果高度依賴任務類型,沒有一招通用。

直接看實例。任務是「從客服對話中擷取退貨資訊」:

❌ 弱 prompt:
請幫我看這段對話有沒有要退貨,有的話告訴我。

問題:沒有定義「退貨資訊」包含什麼、沒有指定輸出格式、沒有處理「不確定」的情況。

✅ 強 prompt:
你是客服對話分析助理。請從以下對話中擷取退貨相關資訊。

擷取欄位:
- order_id:訂單編號(格式為 A 開頭加 5 碼數字)
- reason:退貨原因,只能是 ["瑕疵", "不符描述", "不喜歡", "重複下單", "其他"] 之一
- urgency:緊急程度 1-3(3 為最急,客戶明確表達不滿或提到期限時為 3)

規則:
- 只擷取對話中明確提到的資訊,不要推測。
- 若某欄位無法從對話中確定,填入 null。
- 若整段對話與退貨無關,回傳 {"is_return_request": false}。

以 JSON 格式輸出,不要有其他文字。

<對話>
{{ conversation }}
</對話>

四個關鍵改進:定義了欄位與型別、用列舉限制了自由度、明確處理「不確定」與「不適用」、用標籤包住不可信的輸入(這也是 Day 25 防注入的基礎)。

3.2 讓輸出可解析:結構化輸出 + 驗證 + 重試

光在提示裡說「請輸出 JSON」是不夠的,生產環境要三件套:

"""結構化輸出的正確做法:約束 → 驗證 → 帶錯誤重試。"""
from pydantic import BaseModel, Field, ValidationError
from typing import Literal, Optional
import json

class ReturnRequest(BaseModel):
    is_return_request: bool
    order_id: Optional[str] = Field(None, pattern=r"^A\d{5}$")
    reason: Optional[Literal["瑕疵", "不符描述", "不喜歡", "重複下單", "其他"]] = None
    urgency: Optional[int] = Field(None, ge=1, le=3)

def extract(client, conversation, max_retries=2):
    messages = [{"role": "user", "content": PROMPT.replace("{{ conversation }}", conversation)}]
    for attempt in range(max_retries + 1):
        resp = client.chat.completions.create(
            model="...", messages=messages,
            response_format={"type": "json_object"},   # 1) 用 API 層的約束
        )
        raw = resp.choices[0].message.content
        try:
            return ReturnRequest.model_validate_json(raw)   # 2) 應用層驗證
        except ValidationError as e:
            if attempt == max_retries:
                raise
            # 3) 把錯誤訊息回饋給模型,讓它自己修
            messages += [
                {"role": "assistant", "content": raw},
                {"role": "user", "content": f"輸出格式錯誤:{e}\n請依 schema 重新輸出。"},
            ]

第三步是關鍵:把驗證錯誤原文丟回去,模型的修正成功率相當高。這比自己寫正規表達式硬拆字串穩健得多。

3.3 把提示當程式碼管理

prompts/
├── customer_service/
│   ├── v1.jinja2          # 用模板引擎,變數與文字分離
│   ├── v2.jinja2          # 每個版本一個檔案,可 diff、可回滾
│   └── CHANGELOG.md       # 改了什麼、為什麼改、評測分數變化
└── tests/
    └── test_customer_service.py   # 每個版本都要跑評測集(Day 24)

📌 實務建議

提示的每次修改,都必須跑一次評測集。 因為提示的修改是「全域性」的——你為了修好情境 A 而加的一句話,很可能讓情境 B、C 壞掉,而你不會知道。這就是 Day 11 說的品質門檻在生成式 AI 的對應物。

四、取捨:什麼時候該停止調 prompt

這是本篇最重要的一段。提示工程有明確的天花板,撞到以下情況時,該換工具了:

症狀 代表什麼 該換的工具
需要模型知道它不知道的事實 知識問題 RAG(Day 18–20)
需要模型執行動作(查資料庫、寄信) 能力問題 工具呼叫(Day 17、21)
提示裡塞了大量同類型範例(>10 個) 格式/風格問題 微調(Day 23)
提示長到沒人敢改、規則互相衝突 複雜度問題 拆成多步驟工作流(Day 21)
每次都要在提示裡叮嚀「不要洩漏內部資訊」 安全問題 Guardrails(Day 25)

4.1 脈絡預算怎麼編

既然說「輸入視窗是一份預算」,就該真的編一份。以 32K 的視窗為例,一個企業知識助理的合理配置:

項目 預算 說明
系統提示與規則 800–1,500 token 放最前面,且逐字固定以利快取(Day 26)
few-shot 範例 500–2,000 token 3–5 個代表性範例即可,多了效益遞減
工具定義 300–1,000 token 工具越多越貴,這是「工具不要太多」的成本理由
檢索脈絡 2,000–4,000 token 不是越多越好,見圖 16-2
對話歷史 1,000–2,000 token 超過就摘要(Day 17)
使用者輸入 視情況
保留給輸出 1,000–2,000 token 最常被忘記的一項

留白很重要。 我看過團隊把脈絡塞到 30K,然後發現模型生成到一半就被截斷——因為輸入加輸出超過了視窗上限。編預算時要把輸出算進去。

另一個常被忽略的點:預算不是拿來用滿的。上表的總和刻意遠低於 32K,因為多數情況下,精簡的脈絡表現優於塞滿的脈絡。視窗大小是上限,不是目標。

💡 一個判斷標準

當你發現自己在 prompt 裡寫「如果……就……」超過五次時,你其實是在用自然語言寫程式。自然語言不適合寫控制流——把那些 if 拆出來變成真正的程式邏輯,讓模型只做它擅長的:理解與生成。


今日小結

  • Prompt Engineering 問「怎麼寫指令」,Context Engineering 問「有限預算內放什麼、放多少、放哪裡」。有 RAG 與工具之後,後者更重要。
  • Lost in the Middle:關鍵資訊放中段最容易被忽略。與其塞 20 份文件,不如給最相關的 3 份(這是 Day 19 重排序的動機)。
  • 強 prompt 的四個要素:定義欄位與型別、用列舉限制自由度、明確處理不確定與不適用、用標籤包住不可信輸入。
  • 結構化輸出要三件套:API 層約束 → 應用層驗證 → 帶著錯誤訊息重試
  • 提示是資產,要版本化、要 CHANGELOG、每次修改都要跑評測集
  • 提示工程有天花板:知識問題找 RAG、能力問題找工具、風格問題找微調、複雜度問題找工作流、安全問題找 Guardrails。

明天預告

有了好的提示,明天把它變成一個真正的服務:串流輸出、函式呼叫、對話狀態、與企業內部系統對接。

明天也是這個系列與 2020 年《用 LINE 串起多媒體系統》 的對話——從六年前的 LINE Bot 到今天的 LLM 應用,介面變了、串接的思維沒變,但多了一件根本性的新事情。

延伸閱讀


上一篇
Day 15:AI 平臺與成本治理——損益平衡點與 GPU 利用率
下一篇
Day 17:LLM 應用骨架 — 從 LINE Bot 到會自己決定的系統
系列文
從 4,343 筆職缺到 AI Engineer:MLOps × GenAI Engineering 雙主軸實戰18
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言