iT邦幫忙

2025 iThome 鐵人賽

DAY 16
0
DevOps

30 天帶你實戰 LLMOps:從 RAG 到觀測與部署系列 第 16

Day16 - LangChain × Guidance:打造可組合、可控的 Prompt 工作流

  • 分享至 

  • xImage
  •  

🔹 前言

昨天我們談到 Prompt 系統化:包括模板化、版本管理、測試與整合。這些設計,讓我們能像管理程式碼一樣管理 Prompt。

但在真實應用中,單一 Prompt 常常不夠,我們需要更靈活的方式,來設計、組合與控制 Prompt:

  • 如何把多個 Prompt 串接成一條工作流程?
  • 如何根據上下文自動生成 Prompt?
  • 如何確保輸出的格式正確?

這就是今天(Day16)的主題:Prompt Template 與 Prompt Chain
我們會進一步介紹兩個常見的工具:LangChainGuidance

https://ithelp.ithome.com.tw/upload/images/20250930/20120069M0nLZHaNP6.png

⚠️ 提醒:本篇介紹的工具偏進階,如果你剛接觸 Prompt,建議先熟悉 Day15 的基礎(模板化、版本管理、測試),再回來閱讀這一篇。

⚠️ 版本相容性提醒
本文撰寫與測試版本如下,若遇到 API mismatch,請先檢查套件版本是否不同:
> LangChain 0.2.11
> LangChain-Core 0.2.21
> LangChain-OpenAI 0.1.7
> Guidance 0.1.14


今天的 Demo Code 僅會擷取部分片段,完整程式碼放在 GitHub Repo

🔹 LangChain:Prompt + Chain 的工作流程框架

LangChain 是一個在 2022 年後迅速爆紅的 LLM 應用開發框架,它的定位是「讓開發者更容易把大語言模型整合進應用程式」。 它的設計哲學是:把 LLM 當成一個元件,並透過 Chain 與 Agent 來編排多步驟流程

https://ithelp.ithome.com.tw/upload/images/20250930/20120069dNoyMGuFhX.png
LangChain:Prompt + Chain + Agent(Optional) 流程圖

優勢

  • 社群大、資源多,幾乎是 LLM 開發的「入門框架」。
  • 適合做 原型開發複雜工作流組合

缺點

  • 功能龐雜,初學者容易迷失。
  • 在生產環境裡可能需要裁切,只用其中一部分(如 PromptTemplate + Chain)。

LangChain 的核心概念有三個:

➡️ 1. Prompt Template:把 Prompt 參數化

首先,把 Prompt 抽象化,用 {} 插入動態變數,可以統一管理不同場景下的 Prompt(FAQ、摘要、推理)。再來,將 Day 15 的 prompt template - prompts_v1.yaml 載入後,轉成 LangChain 需要的 {} 風格,把「文件內容」與「使用者問題」參數化,再建立 Prompt:

# 讀 YAML(Day15 的 Prompt Registry)
YAML_PATH = "prompts/prompts_v1.yaml"
if not os.path.exists(YAML_PATH):
    raise FileNotFoundError(f"找不到 {YAML_PATH},請確認路徑與工作目錄。")

with open(YAML_PATH, "r", encoding="utf-8") as f:
    yml = yaml.safe_load(f) or {}

# 取出模板字串,並轉成 LangChain 可用格式
try:
    raw_summary_template = yml["prompts"]["summary"]["template"]
    raw_faq_template = yml["prompts"]["faq"]["template"]
except KeyError as e:
    raise KeyError(
        f"YAML 結構不符,缺少 {e}. 期待結構為 prompts.summary.template / prompts.faq.template"
    )

summary_template = normalize_template(raw_summary_template)   # 需要 {context}
faq_template = normalize_template(raw_faq_template)           # 需要 {context}、{question}

# Step 1️⃣: 摘要 Prompt(吃 {context})
summary_prompt = PromptTemplate(
    input_variables=["context"],
    template=summary_template
)
# dict(context=...) → Prompt → LLM → str
summary_chain = summary_prompt | llm | StrOutputParser()

# Step 2️⃣: FAQ Prompt(吃 {context} 與 {question})
faq_prompt = PromptTemplate(
    input_variables=["context", "question"],
    template=faq_template
)

# --- 用法 A:直接 QA(context + question → faq) ---
# [IN]  {"text": <原始內容>, "question": <問題>}
# [OUT] str(答案)
qa_chain = (
    {
        # 將原始輸入映射到 Prompt 需要的鍵
        "context": RunnableLambda(lambda x: x["text"]),
        "question": RunnableLambda(lambda x: x["question"]),
    }
    | faq_prompt          # dict → Prompt 字串
    | llm                 # Prompt → 模型輸出
    | StrOutputParser()   # → 純文字
)

執行結果:

❯ python langchain_chain_demo.py
=== 用法 A:直接 QA(context + question → faq) ===
- 步驟 1:安裝軟體
- 步驟 2:設定帳號
- 步驟 3:連線
...

➡️ 2. Prompt Chain: 把多個步驟組合起來

有時候我們的需求不只是一個 Prompt,而是需要「多步驟推理」。官方定義的常見流程模式包括 Sequential(依序執行)、Router(依條件分流)、Map-Reduce(並行處理後彙總)。

類型 流程 適用情境 真實案例
Sequential Chain Prompt1 → Prompt2 → … 需要逐步推理,前一步的結果是後一步的輸入 文件先摘要,再交給 LLM 生成 FAQ(例如內部知識庫建置)
Router Chain 根據條件分派到不同 Prompt 問題類型不同需走不同流程 公司內部 Chatbot:若問題包含「價格」→ 進 Pricing Prompt;若是技術問題 → 進 FAQ Prompt
Map-Reduce Chain 多個 Prompt 分塊處理 → 彙整 大文件、多人分工 法律文件分析:先把 200 頁契約切成 20 份 → 每份做摘要(Map)→ 再彙整成一份重點摘要(Reduce)

💡 完整的三種模式程式碼實作也放在 GitHub Repo - langchain_chain_router_demo.py, 有興趣的讀者可以自行研究看看。

最常見的模式就是「先做 A,再做 B」。
在我們的 Demo Code (langchain_chain_demo.py) 中,用法 B 就是典型的 Sequential Chain:

  • 第一步:先用 Summary Prompt 把文件摘要。
  • 第二步:把摘要再丟給 FAQ Prompt,產生常見問題與回答。
...
...
# --- 用法 B:先摘要再 FAQ(summary → faq) ---
# Step 0️⃣: 定義一個「自動問題產生器」
# 在這裡我們固定輸出一段文字:「請依據以上摘要產生三個常見問題並回答,條列式。」
# 實際應用中,你也可以改成根據情境動態產生不同問題。
# [IN]  {"text": <原始內容>}
# [OUT] str(條列 FAQ)
auto_question = RunnableLambda(
    lambda _: "請依據以上摘要產生三個常見問題並回答,條列式。"
)
summary_to_faq_chain = (
    {
        # Step 1: 從輸入資料結構中取出 text,包裝成 {context}
        # 並先丟給 summary_chain,產生「文件摘要」
        "context": RunnableLambda(lambda x: {"context": x["text"]}) | summary_chain,
        "question": auto_question, # Step 2: 生成問題(固定輸出三個常見問題的要求)
    }
    | faq_prompt            # Step 3: 把 {context, question} 套用到 faq_prompt,組裝完整的 Prompt
    | llm                   # Step 4: 把組裝好的 Prompt 丟給 LLM,產生回答
    | StrOutputParser()     # Step 5: 解析 LLM 輸出,只留下純文字(去掉多餘 metadata)
)

if __name__ == "__main__":
    doc = "VPN 設定文件:步驟 1 安裝軟體,步驟 2 設定帳號,步驟 3 連線。"

    print("=== 用法 A:直接 QA(context + question → faq) ===")
    out_qa = qa_chain.invoke({"text": doc, "question": "如何設定公司 VPN?"})
    print(out_qa)

    print("\n=== 用法 B:先摘要再 FAQ(summary → faq) ===")
    out_summary_faq = summary_to_faq_chain.invoke({"text": doc})
    print(out_summary_faq)

執行結果:

...
=== 用法 B:先摘要再 FAQ(summary → faq) ===
1. **如何安裝 VPN 軟體?**  
   - 請參考文件中的第一步驟,安裝 VPN 軟體。

2. **我該如何設定帳號資訊?**  
   - 請依據文件中的第二步驟,設定帳號資訊。

3. **如何進行連線操作?**  
   - 請根據文件中的第三步驟,進行連線操作。

這種方式適合 需要逐步推理 的場景,例如「先做摘要,再問答」或「先翻譯,再潤稿」。很多初學者看到這段程式碼時,會覺得像「黑箱」:到底哪一段是處理輸入的?哪一段是組裝 Prompt?哪一段才是整條 Chain?

👉 我們可以把 langchain_chain_demo.py 拆解成三個角色來理解:

元件 功能定位 程式碼裡的例子
RunnableLambda 小型轉接器:負責從輸入中取值或轉換資料,傳給下一步 lambda x: x["text"]lambda x: x["question"]
PromptTemplate 模板生成器:把變數代入 Prompt 模板,產生完整輸入給 LLM summary_promptfaq_prompt
Chain 流水線:把多個元件串起來,形成一個可執行的流程 qa_chainsummary_to_faq_chain

有了對照表,就會發現整條 Chain 是由小元件逐步拼起來的流水線。

➡️ 3. Agent(延伸功能)

除了 Template 與 Chain,LangChain 還有 Agent 概念,讓 LLM 可以根據指令「自己決定」要不要查資料庫、呼叫 API。

這個功能比較進階,我們會在之後的章節再深入介紹。


🔹 Guidance:專注於嚴格控制輸出

Guidance 是由 Microsoft Research 團隊開源的 Prompt 程式設計框架。與強調「組合流程」的 LangChain 不同,Guidance 的核心理念是 在生成過程中就施加約束,確保輸出結果 一定符合指定格式(如 JSON、SQL、Markdown 表格),而不是事後再去解析與清洗。

https://ithelp.ithome.com.tw/upload/images/20250930/20120069QSfy8droVJ.png

🚨 為什麼需要 Guidance?

在真實應用中,LLM 輸出「多了一句廢話」就可能讓整個流程掛掉:

  • 你要 JSON,結果模型回:好的,以下是 JSON: {"answer": "VPN 設定步驟…"}
    ----->>>>> Parser 直接報錯。

Guidance 的解法是 強制 LLM 在生成時就遵守 Schema,不會多一個字,輸出一定可解析。

📝 特色

功能面向 說明
Prompt 內建邏輯 支援 iffor、變數等控制結構,讓 Prompt 更像「程式碼」
格式輸出保證 可指定輸出必須是 JSON、Markdown 表格,甚至 SQL 語句;適合需要下游 API 消化的情境
多模型支援 不侷限 OpenAI,也能在 HuggingFace 模型上使用

⚖️ 優缺點對比

優點 缺點
Guidance 適合 需要結構化輸出(例如 API 回傳 JSON、生成 SQL)。 生態較小、文件不如 LangChain 完善
Prompt + 邏輯更緊密,開發者能掌控每一個 token 的生成過程。 偏向工程師導向,入門曲線比 LangChain 陡。

➡️ Guidance 範例 1:強制輸出 JSON

# ---------- Guidance 程式(要求只輸出 JSON) ----------
@guidance
def faq_json_program(lm, draft):
    lm += f"""
你會收到一段 FAQ 草稿,請轉成 JSON 陣列,每個元素必須包含 "q" 與 "a" 欄位。
嚴格要求:只輸出合法 JSON,不要前後多任何文字、說明或標記。

FAQ 草稿:
{draft}

輸出:
{{{{gen 'json' temperature=0 max_tokens=800}}}}
"""
    return lm

執行結果:

❯ python guidance_faq_json_demo.py
=== FAQ JSON ===
[{'q': '公司 VPN 怎麼設定?', 'a': '請先安裝軟體,接著輸入帳號密碼,最後按下連線。'}]

Guidance 也允許在 Prompt 中內嵌程式邏輯,確保輸出更符合需求。

舉例來說,用 regex 約束生成格式(例如「一定是數字」):

import os
from dotenv import load_dotenv
import guidance
from guidance import models, gen

load_dotenv()
llm = models.OpenAI(model="gpt-4o-mini", api_key=os.getenv("OPENAI_API_KEY"))

@guidance
def ask_age(lm):
    lm += "You are a teenager.\nHow old are you?\nAge: "
    lm += gen("age", regex=r"\d{1,2}")  # 只允許 1~2 位數字
    return lm

out = ask_age(model=llm)
print("age =", out["age"])  # 例如 "15"

🔹 LangChain + Guidance:組合拳

https://ithelp.ithome.com.tw/upload/images/20250930/201200695OGbz3MbBH.png

  • Prompt Template:一切的起點,把需求參數化。 (請參考 Day15)
  • Prompt Chain(LangChain):專注於 工作流設計,能把多步驟邏輯(例如:摘要 → FAQ → 翻譯)編排成一條完整流程。
  • Guidance:專注於 結構控制,確保輸出嚴格符合 JSON、Markdown、SQL 等格式,方便下游系統解析。

很多團隊會把這兩者搭配起來用:

  • LangChain 負責流程編排 :摘要 → FAQ 草稿。
  • Guidance 負責最後收斂:強制把 FAQ 草稿轉成 JSON,方便前端或 API 消費。

➡️ Demo:整合範例

程式碼請參照 GitHub Repo 的 combined_demo.py

  • LangChain 產生 FAQ 草稿(文字)
  • Guidance 把草稿 收斂為 JSON(必要時 fallback 為官方 SDK,確保一定有合法 JSON)。

輸出結果(示意)

❯ python combined_demo.py
=== LangChain → FAQ 草稿(文字) ===
1. **如何安裝VPN軟體?**  
   - 請參考文件中的第一步驟:安裝VPN軟體。

2. **如何設定使用者帳號?**  
   - 請參考文件中的第二步驟:設定使用者帳號。

3. **如何進行連線?**  
   - 請參考文件中的第三步驟:進行連線。

=== Guidance → FAQ JSON ===
[{'q': '如何安裝VPN軟體?', 'a': '請參考文件中的第一步驟:安裝VPN軟體。'}, {'q': '如何設定使用者帳號?', 'a': '請參考文件中的第二步驟:設定使用者帳號。'}, {'q': '如何進行連線?', 'a': '請參考文件中的第三步驟:進行連線。'}]

🔹 比較表

特性 LangChain Guidance LangChain + Guidance
定位 LLM 應用框架,偏向工作流編排 Prompt + 程式邏輯整合,偏向嚴格輸出 先編排流程,再收斂格式,兩者互補
核心功能 PromptTemplate、Chain、Agent 嵌入 if/for/變數,格式控制(JSON、Markdown 等) 流程用 LangChain 串接,輸出用 Guidance 保證結構
優勢 社群大、模組多、容易做快速原型 控制力強,能確保輸出格式正確 快速開發 + 穩定上線,降低出錯率
缺點 功能龐雜,生產環境常需裁切 生態小,入門門檻較高 複雜度提高,需要同時維護兩層
適合場景 多步驟推理、原型開發、複雜工作流 API 輸出需要結構化(SQL、JSON、報表) 內部 FAQ Bot → 前端需要 JSON:LangChain 串流程,Guidance 收斂成 JSON
失敗 fallback 策略 若 Guidance 輸出失敗 → 重試;仍失敗 → 回退正則清洗,避免下游爆炸
比喻 「管弦樂指揮」:協調多步驟任務 「演奏家」:保證輸出符合規格 「合作演出」:指揮協調全場演奏家,演奏者把每一個音節演奏完美

🔹 今天的收穫

  • LangChain = 幫你設計與管理 Prompt 工作流,適合快速開發。
  • Guidance = 幫你保證輸出格式正確,適合生產落地。
  • 組合起來:先用 LangChain 拆解步驟,再用 Guidance 收斂格式。

今天學到的 Prompt Workflow 工具,讓我們能建立穩健的 Prompt 輸出系統。 明天我們會聊聊不同規模的 LLM 應用專案應該選擇哪種部署策略。

🤔 延伸問題:如果你只有一天時間學,怎麼選?

你的目標 建議工具 原因
⚡ 想快速做 Demo / Hackathon LangChain 提供現成的 PromptTemplate / Chain,能快速把多個 Prompt 串成可用流程。
🛡️ 想要上線 API,需要穩定輸出 Guidance 能嚴格控制輸出格式(JSON、Markdown、SQL),避免下游系統爆炸。

📚 引用 / 延伸閱讀

LangChain 官方文件

Guidance 相關資源

其他


上一篇
Day15 - Prompt Generation:用模板和版本管理 Prompt,規範 LLM 的回應
下一篇
Day17 - 成本、隱私、維運怎麼取捨?LLM 應用部署策略解析
系列文
30 天帶你實戰 LLMOps:從 RAG 到觀測與部署17
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言