DSPy 與 Omar Khattab:停止寫 Prompt,開始寫程式

DSPy 與 Omar Khattab:停止寫 Prompt,開始寫程式

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


你花了三個小時調 Prompt,模型終於給出了你想要的結果。

然後你換了一個模型,或者換了一批資料,或者需求改了一點點——你的 Prompt 壞掉了,你得重新調。

這就是 Prompt Engineering 的本質困境:它是一門手藝,不是一門工程。 它依賴直覺、難以測試、無法自動化。

2022 年,一位 Stanford 的博士生 Omar Khattab 決定從根本上解決這個問題。

他的答案是 DSPy(Declarative Self-improving Python)——不要手工寫 Prompt,讓程式自動找出最好的 Prompt。

DSPy 的核心主張:Prompt 不是藝術品,是可以被優化的參數。就像神經網路的權重一樣,你給定目標,讓 Optimizer 去找最佳解。


為什麼你需要認識 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 年財報...")
Loading Diagram...

比較:手工 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-shotDSPy 優化後提升
GSM8K(數學推理)約 50%約 80%++30pp
多跳 RAG 任務基準線+49pp顯著
MMLU(小模型)基準線接近 SOTA縮短差距

DSPy 的適用場景 vs 不適合場景

場景DSPy 合適?理由
複雜的多步驟 Agent pipelineOptimizer 能找到最好的 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)

官方資源

學術論文

延伸閱讀

推薦影片