本文 AI 產出,尚未審核

LangChain 教學:Output Parsers(輸出解析)— 結構化輸出(JSON、Pydantic)


簡介

在 LLM(大型語言模型)與應用程式之間,文字是唯一的橋樑。然而,純文字結果往往難以直接供程式碼使用,尤其在需要後續計算、資料庫寫入或 API 呼叫時更是如此。
為了讓 LLM 的回應能直接被程式處理,我們必須把「自由文字」轉換成結構化資料(如 JSON、Pydantic 物件),這就是 LangChain 中的 Output Parsers 所扮演的角色。

本單元將說明:

  1. 為什麼要使用結構化輸出。
  2. LangChain 內建的 JsonOutputParserPydanticOutputParser 如何運作。
  3. 實作範例、常見陷阱與最佳實踐,最後帶出幾個真實的應用情境。

掌握輸出解析,等於掌握了 LLM 與程式碼之間的「雙向翻譯」能力,讓你的 AI 應用從「聊天」升級為「自動化」!


核心概念

1. 輸出解析的基本流程

  1. Prompt 設計:在提示中明確告訴模型要回傳的結構(例如 JSON schema 或 Pydantic 類別)。
  2. LLM 呼叫:模型根據提示產生文字回應。
  3. Parser 解析:使用對應的 OutputParser 把文字轉成程式可操作的物件。
  4. 後續處理:將解析後的資料傳遞給下游流程(資料庫、API、業務邏輯等)。

關鍵:Prompt 與 Parser 必須「說一致」——Prompt 中描述的結構必須與 Parser 能解析的格式相符,否則會產生解析錯誤。


2. JsonOutputParser

JsonOutputParser 是 LangChain 最簡單的結構化輸出工具,只需要 JSON Schema(或簡易說明)即可。它會在模型回傳文字後,嘗試以 json.loads 解析,若失敗會拋出 OutputParserException

為什麼使用 JSON?

  • 跨語言友好:幾乎所有程式語言都支援 JSON。
  • 易於驗證:可以使用 JSON Schema 進行結構驗證。
  • 可視化:在除錯或日誌中直接閱讀。

3. PydanticOutputParser

PydanticOutputParser 進一步結合了 Pydantic(Python 的資料模型驗證庫),讓我們可以:

  • 定義型別(int、float、datetime、Enum 等)。
  • 自動驗證:解析失敗時會回傳具體的錯誤訊息。
  • 直接得到 Python 物件,省去手動 dict 轉換。

適用情境:當你的應用需要嚴格的型別檢查、或是要把資料直接塞入 ORM、FastAPI 請求模型時,Pydantic 會是最佳選擇。


程式碼範例

以下範例均以 Python 為例,搭配 LangChain v0.0.XXX(請自行替換為最新版本)。

範例 1:最簡單的 JSON 輸出解析

from langchain import PromptTemplate, LLMChain
from langchain.llms import OpenAI
from langchain.output_parsers import JsonOutputParser

# 1️⃣ Prompt,要求回傳簡單的 JSON
prompt = PromptTemplate(
    template="""
    請以 JSON 格式回傳以下資訊:
    - 使用者名稱 (string)
    - 年齡 (integer)
    - 是否為會員 (boolean)
    輸出範例: {{example}}
    """,
    input_variables=[],
    partial_variables={"example": '{"name":"Alice","age":30,"is_member":true}'}
)

# 2️⃣ LLM
llm = OpenAI(model_name="gpt-3.5-turbo", temperature=0)

# 3️⃣ 建立 Chain
chain = LLMChain(prompt=prompt, llm=llm, output_parser=JsonOutputParser())

# 4️⃣ 呼叫
result = chain.run()
print(result)  # => {'name': 'Alice', 'age': 30, 'is_member': True}

說明

  • JsonOutputParser() 會自動把模型回傳的文字轉成 dict
  • temperature=0 確保模型產生的 JSON 格式更一致。

範例 2:使用 JSON Schema 進行嚴格驗證

from langchain.output_parsers import JsonOutputParser
from langchain.schema import OutputParserException

# 定義 JSON Schema
schema = {
    "type": "object",
    "properties": {
        "product_id": {"type": "string"},
        "price": {"type": "number"},
        "tags": {
            "type": "array",
            "items": {"type": "string"}
        }
    },
    "required": ["product_id", "price"]
}

parser = JsonOutputParser(schema=schema)

raw_output = """{
    "product_id": "P12345",
    "price": 199.99,
    "tags": ["electronics", "sale"]
}"""

try:
    data = parser.parse(raw_output)
    print(data)  # 成功解析
except OutputParserException as e:
    print("解析失敗:", e)

重點:若模型回傳的 JSON 缺少 price 欄位,或 price 為字串而非數字,parse 會拋出例外,讓你在程式層面即時捕獲錯誤。


範例 3:Pydantic 模型定義

from pydantic import BaseModel, Field, ValidationError
from langchain.output_parsers import PydanticOutputParser

class OrderInfo(BaseModel):
    order_id: str = Field(..., description="訂單編號")
    amount: float = Field(..., gt=0, description="金額,必須大於 0")
    currency: str = Field(..., regex="^[A-Z]{3}$", description="三位大寫貨幣代碼")
    items: list[str] = Field(..., description="商品名稱列表")

parser = PydanticOutputParser(pydantic_object=OrderInfo)

raw = """{
    "order_id": "ORD-2025-001",
    "amount": 1500.5,
    "currency": "TWD",
    "items": ["筆記型電腦", "滑鼠"]
}"""

try:
    order = parser.parse(raw)
    print(order)               # <OrderInfo ...>
    print(order.dict())        # 轉成 dict 方便後續處理
except ValidationError as ve:
    print("驗證失敗:", ve)

說明

  • gt=0regex 等限制會在解析階段自動檢查。
  • 若 LLM 回傳 "currency": "usd"(小寫),將觸發 ValidationError,讓你即時知道資料不符合規範。

範例 4:結合 Prompt、LLM 與 PydanticOutputParser

from langchain import PromptTemplate, LLMChain
from langchain.llms import OpenAI

prompt = PromptTemplate(
    template="""
    你是一位客服機器人,請根據以下對話產生 JSON 回報,欄位如下:
    - ticket_id (string)
    - priority (enum: "low","medium","high")
    - summary (string)
    - resolved (boolean)

    對話內容:
    {conversation}
    """,
    input_variables=["conversation"]
)

# 使用 Pydantic 定義回報結構
class TicketReport(BaseModel):
    ticket_id: str
    priority: str
    summary: str
    resolved: bool

parser = PydanticOutputParser(pydantic_object=TicketReport)

chain = LLMChain(
    llm=OpenAI(model_name="gpt-4o-mini", temperature=0),
    prompt=prompt,
    output_parser=parser
)

conversation = """
客戶: 我的訂單遲遲未送達,已經超過預計時間兩天。
客服: 很抱歉造成不便,我會立即幫您查詢。請問您的訂單編號是?
客戶: ORD-2025-009
"""

report = chain.run(conversation=conversation)
print(report)  # TicketReport 物件

實務觀點:此範例展示了 完整的端到端流程:從自然語言對話 → LLM 回傳結構化 JSON → Pydantic 直接得到驗證過的 Python 物件,最後可直接寫入資料庫或觸發自動化流程。


範例 5:自訂錯誤回饋(Retry 機制)

from langchain.output_parsers import JsonOutputParser, OutputParserException

parser = JsonOutputParser()

def safe_parse(text: str, max_retry: int = 2) -> dict:
    attempt = 0
    while attempt <= max_retry:
        try:
            return parser.parse(text)
        except OutputParserException as e:
            attempt += 1
            # 簡單的重試策略:請 LLM 再次產生符合 JSON 的回應
            print(f"第 {attempt} 次解析失敗,嘗試重新產生…")
            # 這裡假設有一個 `llm` 物件可呼叫
            text = llm.invoke(f"請把以下文字重新整理成正確的 JSON:\n{text}")
    raise RuntimeError("多次嘗試仍未成功解析")

技巧:結合 RetryLLM 再次生成,可以大幅降低因模型偶爾產生不完整 JSON 而導致的失敗率。


常見陷阱與最佳實踐

陷阱 說明 解決方式
模型產生非 JSON 文字 LLM 有時會在回應前後加上說明文字或程式碼區塊 (```json) 在 Prompt 中加入「只回傳 JSON,別加任何說明」;或在 Parser 前使用正則把前後雜訊去掉
欄位順序或缺漏 JSON Schema 要求的 required 欄位若缺失會拋錯 在 Prompt 中給予完整範例,或使用 Pydanticdefault_factory 提供預設值
型別不符合 例如 "price": "199"(字串)而非數字 使用 Pydantic 的型別檢查,並在 Prompt 中明確說「price 必須是數字」
Enum/Enum 值不一致 priority 只接受 `low medium
過長的 JSON 超過 token 限制 大量資料時,模型回傳會被截斷 考慮分批產生、或改用 list of objects + page token 機制

最佳實踐

  1. Prompt 先寫範例:提供一個完整、正確的 JSON 範例,讓模型學習格式。
  2. 固定輸出格式:使用 output_parser 前,先在 Prompt 中加上「請直接回傳 JSON,不要加任何文字」的指示。
  3. 驗證層:永遠在程式碼層面做一次結構驗證(JSON Schema 或 Pydantic),即使模型看起來正確。
  4. 錯誤回饋機制:結合 try/exceptRetry,自動請模型重新產生,提升穩定性。
  5. 單元測試:把 Prompt、Parser、LLM 呼叫寫成測試,確保未來模型升級或 Prompt 變更不會破壞解析。

實際應用場景

場景 為何需要結構化輸出 典型實作
客服票務系統 從聊天記錄自動抽取 Ticket ID、優先權、解決狀態 使用 PydanticOutputParser 建立 TicketReport,直接寫入 Ticket DB
金融報表產生 LLM 生成的財務指標必須精確數值與單位 使用 JsonOutputParser 搭配 JSON Schema,確保 floatcurrency 正確
商品推薦引擎 輸出多個商品的 id、name、price 給前端 產生 JSON 陣列,前端直接解析渲染 UI
自動化測試腳本 從自然語言需求產生測試案例的 title、steps、expected Pydantic 讓每個欄位都有型別與說明,減少手動校正
資料清洗管線 LLM 讀取非結構化文字(如 PDF)並轉成結構化紀錄 先用 JsonOutputParser 把文字轉 dict,再交給 Pandas 處理

總結

  • Output Parsers 是讓 LLM 與程式碼「說同一種語言」的關鍵橋樑。
  • JSON 提供跨語言、輕量的結構化方式;Pydantic 則在 Python 生態系中提供型別安全、驗證完整的模型。
  • 正確的 Prompt 設計Schema/Pydantic 定義、以及 錯誤回饋機制,是避免解析失敗的三大法寶。
  • 透過本篇提供的範例與實務建議,你可以迅速將 LLM 的自然語言輸出轉成可直接使用的資料,從而建構更可靠、可維護的 AI 應用。

下一步:挑選一個你目前正在開發的功能,試著把回傳的文字改寫為 JSON 或 Pydantic 物件,觀察開發效率與錯誤率的變化。祝你在 LangChain 的旅程中玩得開心、寫得更好! 🚀