iT邦幫忙

2025 iThome 鐵人賽

DAY 8
0
自我挑戰組

從讀書筆記到可落地 AI:LangChain、LangSmith 與 Agent 工具 30 講系列 第 8

Day 8|Agent Design - Prompt Design - 結構化輸出(2/5)

  • 分享至 

  • xImage
  •  

目標先講清楚:
結構化輸出(Structured Output)在 Agent 開發裡相關的框架。


為什麼要結構化輸出?

當任務被拆成多個節點(或多個子代理)時,每一步都像「微服務」:

  • 需要清楚的輸入/輸出契約(schema);
  • 需要可驗證、可重試的邊界;
  • 需要能在觀測與回溯時快速定位錯誤(哪一步、哪個欄位)。

→ 沒有結構化,就容易在多步驟工作流裡「前一步講 A、下一步聽成 B」,最後 Debug 非常痛。


工具地圖(與情境挑選)

  • LangChain structured output:工程級、好整合、適合放在多步驟 workflow。
  • Pydantic + LLM(教學路線):輕量、好上手,適合單個 agent 驗證。
  • langextract:長文件抽取與來源證據對位(頁碼/字元範圍/高亮)友好,適合合約/行程稽核。(這個套件比較適合文件整理使用,對於llm輸出的結構化無關;但因為都是結構化,因此也放在這邊)

快速比較表

面向 LangChain structured output Pydantic + LLM(教學/輕量) langextract
定位 通用 LLM 能力(.with_structured_output() Pydantic 驗證 LLM 輸出 長文抽取 + 證據對位
最佳用例 企業 RFP 解析 → 報價管線;多步驟、多供應商 客服抄錄 → JSON;簡單 API/PoC 合約/行程/報價書批次抽取與稽核
Schema 驅動 Pydantic / TypedDict / JSON Schema;可 union/literal 以 Pydantic 為核心 以抽取 schema 定義欄位與關係
失敗回補 內建重試/結構約束(視模型支援) 需自行處理 長文可多 pass/分塊
長文與證據 長文靠分塊/RAG;無內建證據高亮 同左 可輸出 evidence offset(頁碼/範圍)
整合能力 強(RAG、代理、工具、LangGraph/LCEL) 輕(自己拼) 匹配抽取→JSONL→檢閱
學習曲線
適用規模 中~大 小~中 中~大

本篇實作主軸:LangChain structured output + Pydantic。最簡單的 baseline 其實就是「直接在 Prompt 指示格式」,但搭配 Pydantic能顯著提升穩定度與可驗證性。


我怎麼落地:用 Pydantic 定義「每個節點的輸出」

1) 先定義每個節點的資料模型(單一來源的真相

from pydantic import BaseModel, Field

# 對應「意圖判斷Node」的輸出
class Intent(BaseModel):
    """判斷使用者問題的意圖以及是否與主題相關"""
    intent: str = Field(description="使用者的意圖分類")
    related: bool = Field(description="問題是否與主要主題相關")

# 對應「圖片識別Node」和「問題回覆Node」的輸出
class Description(BaseModel):
    """針對輸入內容產生的描述"""
    desc: str = Field(description="一段描述性文字")

# 對應「是否回答問題Node」的輸出
class Summary(BaseModel):
    """總結所有資訊並判斷是否完成"""
    summary: str = Field(description="綜合所有描述後產生的最終總結")
    finish: bool = Field(description="是否已經可以結束對話")

2) 設計工作流狀態(能觀測、能回滾

from typing import Annotated, TypedDict
from langgraph.graph.message import add_messages

class AgentState(TypedDict):
    messages: Annotated[list, add_messages]  # 自動管理對話歷史
    intent: str
    related: bool
    image_desc: str
    question_desc: str
    summary: str
    finish: bool

重點是:狀態欄位對齊 Schema,未來排錯能快速對焦「哪個欄位沒被填、哪個節點回傳格式錯」。

3) 讓 LLM 直接輸出結構(把自然語言→函式呼叫

# 需使用支援 structured output 的模型(部分模型如 deepseek-r1 就不支援)
structured_llm_intent = llm.with_structured_output(Intent)
structured_llm_desc = llm.with_structured_output(Description)
structured_llm_summary = llm.with_structured_output(Summary)

4) 節點行為(每個節點單一職責

def intent_node(state: AgentState):
    last_message = state['messages'][-1].content
    prompt = f"分析以下使用者問題,判斷其意圖以及是否與 'AI Agents' 這個主題相關:'{last_message}'"
    result = structured_llm_intent.invoke(prompt)
    return {"intent": result.intent, "related": result.related}

def image_recognition_node(state: AgentState):
    prompt = "這是一張關於 LangGraph 流程的圖片,請產生一段簡短描述。"
    result = structured_llm_desc.invoke(prompt)
    return {"image_desc": result.desc}

def question_answering_node(state: AgentState):
    last_message = state['messages'][-1].content
    prompt = f"針對以下問題提供一個簡短的回覆:'{last_message}'"
    result = structured_llm_desc.invoke(prompt)
    return {"question_desc": result.desc}

def summarize_node(state: AgentState):
    combined = f"圖片描述:{state['image_desc']}\n\n問題回覆:{state['question_desc']}"
    prompt = f"請根據以下綜合資訊,產生一段最終總結,並判斷是否已完整回答問題:\n\n{combined}"
    result = structured_llm_summary.invoke(prompt)
    return {"summary": result.summary, "finish": result.finish}

5) 連成一張可維運的圖(條件路由 + 結束條件

from langgraph.graph import StateGraph, END

graph = StateGraph(AgentState)
graph.add_node("intent_node", intent_node)
graph.add_node("image_recognition_node", image_recognition_node)
graph.add_node("question_answering_node", question_answering_node)
graph.add_node("summarize_node", summarize_node)

graph.set_entry_point("intent_node")

# 依意圖/主題判斷決定是否繼續與路由
graph.add_conditional_edges(
    "intent_node",
    should_continue,  # 你可以根據 state.intent/related 自訂
    {
        "continue_nodes": ["image_recognition_node", "question_answering_node"],
        "end_node": END,
    },
)

graph.add_edge("image_recognition_node", "summarize_node")
graph.add_edge("question_answering_node", "summarize_node")
graph.add_edge("summarize_node", END)

當model的結構越複雜(ex.包含巢狀項目) ,輸出結果轉換的時間也會變長。

接下來要做什麼

除了定義pydantic model,Prompt也可以進行設計


上一篇
Day 7|Agent Design - Prompt Design - 多代理人設計的必要性(1/5)
下一篇
Day 9|Agent Design - Prompt Design - 結構化輸出(3/5)
系列文
從讀書筆記到可落地 AI:LangChain、LangSmith 與 Agent 工具 30 講11
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言