
DSPy 與 Omar Khattab:停止寫 Prompt,開始寫程式
AI 系列-深入介紹 Stanford 研究員 Omar Khattab 打造的 DSPy 框架,從 Signature、Module、Optimizer 三大核心,到為什麼「讓程式自動調 Prompt」比人工調優更可靠 - 一次看懂這套顛覆 Prompt Engineering 思維的框架。
WRITTEN BY

- Name
- Harry Chang
你花了三個小時調 Prompt,模型終於給出了你想要的結果。
然後你換了一個模型,或者換了一批資料,或者需求改了一點點——你的 Prompt 壞掉了,你得重新調。
這就是 Prompt Engineering 的本質困境:它是一門手藝,不是一門工程。 它依賴直覺、難以測試、無法自動化。
2022 年,一位 Stanford 的博士生 Omar Khattab 決定從根本上解決這個問題。
他的答案是 DSPy(Declarative Self-improving Python)——不要手工寫 Prompt,讓程式自動找出最好的 Prompt。
DSPy 的核心主張:Prompt 不是藝術品,是可以被優化的參數。就像神經網路的權重一樣,你給定目標,讓 Optimizer 去找最佳解。
- 為什麼你需要認識 Omar Khattab?
- Omar Khattab 是誰?
- Prompt Engineering 的三個根本問題
- DSPy 的三個核心概念
- 比較:手工 Prompt vs DSPy
- DSPy 的適用場景 vs 不適合場景
- 實用建議:三個起步行動
- 我的反思
- 參考資料 (References)
為什麼你需要認識 Omar Khattab?
在這個 AI Agent 系列裡,我們看過各種「怎麼讓 Agent 做更多事」的框架。DSPy 站在一個不同的位置:它問的是一個更底層的問題——
「我們現在寫 LLM 應用的方式,從根本上就錯了嗎?」
Omar Khattab 的答案是:是的。把 Prompt 字串硬編碼進程式,和把模型的 Weight 硬編碼進程式一樣荒謬。
他這個主張聽起來激進,但邏輯非常清晰:我們訓練神經網路時,從來不會手動設定每一個 Weight;我們設定目標函數(Loss),讓 Optimizer 自動找到最好的 Weight。
那 Prompt 為什麼不能這樣做?
Omar Khattab 是誰?
Stanford NLP 的頂尖研究員
Omar Khattab 的學術背景紮實到令人望塵莫及:
- Stanford 大學博士,指導教授是 Matei Zaharia(Apache Spark 共同創始人)和 Christopher Potts(Stanford NLP 教授)
- Apple Scholars in AI/ML Fellowship 得主——Apple 每年只頒給全球最頂尖的 AI 博士生
- 2024 年加入 MIT,擔任 EECS 助理教授
- 博士期間在 Databricks 擔任研究科學家
ColBERT:在 DSPy 之前,他已經改變了資訊檢索
在 DSPy 之前,Omar Khattab 最有名的貢獻是 ColBERT——一個把深度學習帶進文件搜尋的模型。
ColBERT 的洞見是「Late Interaction」:不要把整份文件壓縮成一個向量再比較,而是把文件每個 Token 都保留,在查詢時才做精細比對。這讓它在精準度上超越傳統搜尋,同時又比全量 BERT 計算便宜數百倍。
這個背景很重要: 他不只是一個寫框架的工程師,他是一個對「如何讓複雜系統更有效率」有深刻研究的學者。DSPy 的設計,帶著同樣的學術嚴謹性。
Prompt Engineering 的三個根本問題
在介紹 DSPy 之前,先釐清 Prompt Engineering 為什麼有問題:
# 傳統做法:硬編碼 Prompt 字串
prompt = """
你是一位專業的財務分析師。
請根據以下資訊分析這家公司的投資價值。
請使用結構化格式,包含:優勢、風險、結論。
思考過程要清楚,先列出關鍵指標...
(以下省略 500 字的精心設計的 Prompt)
資訊:{company_info}
"""
這段 Prompt 有三個問題:
| 問題 | 說明 |
|---|---|
| 脆弱性 | 換一個模型(從 GPT-4o 換到 Claude),效果可能大幅下降 |
| 不可測試 | 你怎麼知道這個 Prompt 是「最好的」而不只是「夠用的」? |
| 不可維護 | 需求改了,你要手動找出哪幾行 Prompt 需要改,然後再調一遍 |
DSPy 的三個核心概念
1. Signature:定義「做什麼」,不定義「怎麼說」
import dspy
# 傳統:你要告訴 LLM 怎麼說
# DSPy:你只需要告訴 LLM 要做什麼
# 簡單 Signature(字串形式)
class FinancialAnalysis(dspy.Signature):
"""分析公司的投資價值並給出建議"""
company_info: str = dspy.InputField(desc="公司的財務資訊與業務描述")
analysis: str = dspy.OutputField(desc="包含優勢、風險、投資建議的分析報告")
confidence: float = dspy.OutputField(desc="0到1之間的信心分數")
Signature 只定義輸入輸出的規格,不定義任何具體的 Prompt 字串。DSPy 會根據這個規格,自動生成並優化對應的 Prompt。
這就像 Python 的 type hints——你描述合約,不描述實作。
2. Module:可組合的 LLM 操作單元
import dspy
lm = dspy.LM('openai/gpt-4o-mini')
dspy.configure(lm=lm)
# 最簡單的 Module:直接預測
predict = dspy.Predict(FinancialAnalysis)
# 進階 Module:強迫 LLM 先思考再回答(Chain of Thought)
cot = dspy.ChainOfThought(FinancialAnalysis)
# 使用
result = cot(company_info="台積電 2024 年營收 2.89 兆新台幣,毛利率 53%...")
print(result.analysis) # → 詳細的投資分析
print(result.confidence) # → 0.87
print(result.reasoning) # → CoT 自動加上的思考過程(ChainOfThought 獨有)
DSPy 內建的 Module 包括:
| Module | 功能 |
|---|---|
dspy.Predict | 直接預測,最基本 |
dspy.ChainOfThought | 先思考再回答(自動加入 reasoning 欄位) |
dspy.ReAct | 結合工具呼叫的推理行動循環 |
dspy.ProgramOfThought | 生成並執行程式碼來解題 |
3. Optimizer(Compiler):讓程式自動找最好的 Prompt
這是 DSPy 最核心、最革命性的部分:
from dspy.teleprompt import BootstrapFewShot, MIPROv2
# 你的訓練資料
trainset = [
dspy.Example(
company_info="台積電...",
analysis="台積電具備強大護城河...",
confidence=0.9
).with_inputs('company_info'),
# ... 更多範例
]
# 你的評估指標
def quality_metric(example, pred, trace=None):
# 回答是否有包含必要元素?信心分數是否合理?
has_risks = "風險" in pred.analysis
has_recommendation = "建議" in pred.analysis
confidence_valid = 0 <= pred.confidence <= 1
return has_risks and has_recommendation and confidence_valid
# 讓 Optimizer 自動找最好的 Prompt 與 Few-shot 範例
optimizer = BootstrapFewShot(metric=quality_metric, max_bootstrapped_demos=4)
compiled_program = optimizer.compile(cot, trainset=trainset)
# compiled_program 現在有自動優化過的 Prompt——你不需要知道它長什麼樣子
result = compiled_program(company_info="Apple 2024 年財報...")
比較:手工 Prompt vs DSPy
# ❌ 傳統方式:手工調,容易壞,不可測
prompt = "你是財務分析師,請分析以下公司...(300字的精心設計)"
# 問題:換模型就壞;沒有系統性測試;下次需求改了又要重調
# ✅ DSPy 方式:宣告式,可測試,自動優化
class MyAnalyzer(dspy.Module):
def __init__(self):
self.analyze = dspy.ChainOfThought("company_info -> analysis, risk_score")
def forward(self, company_info):
return self.analyze(company_info=company_info)
# 定義好 metric,編譯,done
optimizer = MIPROv2(metric=my_metric, auto="medium")
compiled = optimizer.compile(MyAnalyzer(), trainset=trainset)
DSPy 在真實 Benchmark 上的表現:
| 任務 | 傳統 Few-shot | DSPy 優化後 | 提升 |
|---|---|---|---|
| GSM8K(數學推理) | 約 50% | 約 80%+ | +30pp |
| 多跳 RAG 任務 | 基準線 | +49pp | 顯著 |
| MMLU(小模型) | 基準線 | 接近 SOTA | 縮短差距 |
DSPy 的適用場景 vs 不適合場景
| 場景 | DSPy 合適? | 理由 |
|---|---|---|
| 複雜的多步驟 Agent pipeline | ✅ | Optimizer 能找到最好的 Chain 設計 |
| 需要跨模型移植(從 GPT 換 Claude) | ✅ | 重新 compile 就好,不用改 Prompt |
| 有明確評估指標的任務 | ✅ | Optimizer 依賴 Metric 優化 |
| 簡單的一次性對話 | ❌ | 殺雞用牛刀,手動 Prompt 更快 |
| 沒有任何訓練資料 | ❌ | Optimizer 需要資料才能工作 |
實用建議:三個起步行動
步驟 1:先用 dspy.Predict 替換你的 Prompt 字串
pip install dspy-ai
import dspy
lm = dspy.LM('openai/gpt-4o-mini')
dspy.configure(lm=lm)
# 把你最常用的一個 Prompt,用 Signature 重寫
predict = dspy.Predict("question -> answer")
result = predict(question="什麼是 RAG?")
print(result.answer)
這一步不需要 Optimizer,只是讓你感受「宣告式 LLM 程式設計」的感覺——你說清楚輸入輸出,不再管 Prompt 怎麼說。
步驟 2:加入 ChainOfThought,觀察 reasoning 欄位
把 dspy.Predict 換成 dspy.ChainOfThought,然後印出 result.reasoning。你會看到模型自動產生的思考過程——DSPy 自動把「思考再回答」的邏輯插入了 Prompt,你完全不需要手動寫「Let's think step by step」。
步驟 3:收集 20 個你的任務範例,跑一次 BootstrapFewShot
有了 Signature + Module 之後,收集你任務的 20 個輸入輸出範例,定義一個評分函數,跑 BootstrapFewShot。觀察優化前後的結果差異——這個體驗會徹底改變你對 Prompt Engineering 的看法。
我的反思
每隔幾年,軟體工程就會發生一次「從手動到自動」的典範轉移:
- 組合語言 → 高階語言:程式員不再手動管理記憶體位址
- 手動 ML 特徵工程 → 深度學習:模型自動學習特徵表示
- 手動 Prompt Engineering → DSPy:程式自動優化語言模型的使用方式
Omar Khattab 做的事,不是讓 AI 更聰明——他讓我們使用 AI 的方式更聰明。
這個系列走到現在,我們談了很多「AI 能做什麼」:Agent 架構、工具連接、Multi-Agent 協作。但 DSPy 提醒我們一件事:如果我們和 AI 溝通的方式本身就是脆弱的、不可靠的,那再好的架構也是建在沙上的。
把 Prompt 從「藝術品」變成「工程問題」,這才是 AI 應用從實驗室走向工廠的關鍵那一步。
參考資料 (References)
官方資源
學術論文
延伸閱讀
推薦影片