Pydantic AI Capabilities 與 Hooks

在開發大型且複雜的 AI 應用程式時,我們常常需要跨越多個不同的代理人 (Agent) 來重複使用相同的工具、系統指令或設定檔,或者需要在程式執行的特定階段插入自訂邏輯。為了提升程式碼的可維護性與重用性,Pydantic AI 提供了 Capabilities(能力套件)Hooks(生命週期掛鉤) 的強大機制。

Capabilities:模組化的能力套件

在 Pydantic AI 中,Capability (能力套件) 是一個可重複使用、可組合的代理人行為單元。與其將指令 (instructions)、模型設定 (model settings)、工具 (tools) 散落在各處,你可以將相關的行為打包成一個 Capability,並透過 Agentcapabilities 參數傳入。

Capabilities 可以包含:

  • 工具 (Tools)
  • 生命週期掛鉤 (Lifecycle hooks)
  • 動態或靜態的系統指令 (Instructions)
  • 模型設定 (Model settings)

內建的 Capabilities

Pydantic AI 提供了許多實用的內建 Capabilities,涵蓋了常見的應用需求:

  • Thinking:啟用模型的思考與推理能力(例如 Claude 3.7 Sonnet 的 thinking mode)。
  • WebSearch / WebFetch:提供網頁搜尋與抓取內容的能力(支援內建工具或本地降級處理)。
  • ImageGeneration:提供圖片生成的能力。
  • MCP:連接 MCP (Model Context Protocol) 伺服器,擴充外部資源存取。
  • Hooks:提供基於裝飾器 (decorator) 的生命週期掛鉤註冊。

使用內建 Capabilities 範例

你可以像堆積木一樣,把不同的能力組合在一起,賦予給 Agent。以下範例展示了如何同時賦予 Agent「深度思考」與「網路搜尋」的能力:

from pydantic_ai import Agent
from pydantic_ai.capabilities import Thinking, WebSearch

# 建立一個具備「深度思考」與「網路搜尋」能力的 Agent
agent = Agent(
    'anthropic:claude-3-7-sonnet-latest',
    instructions='你是一個專業的研究助理。請詳細思考並附上資料來源。',
    capabilities=[
        # 啟用高強度的思考模式
        Thinking(effort='high'),
        # 賦予網路搜尋能力
        WebSearch(),
    ],
)

# 執行 Agent 進行查詢
result = agent.run_sync('請分析 2026 年最新的 AI 發展趨勢?')
print(result.output)

透過這種設計,你可以將與核心業務邏輯無關的監控、日誌記錄或是特定領域的工具,抽離成獨立的套件供團隊內部共用。

Hooks:生命週期攔截器

在軟體工程中,Hooks 是一種常見的模式,允許開發者在程式執行的特定階段介入。Pydantic AI 提供了專門的 Hooks Capability,讓你可以攔截並修改 Agent 在各個階段的行為(例如:發送請求給模型前、執行工具前、處理串流事件時等),而不需要繼承複雜的類別。

攔截模型請求

你可以使用 @hooks.on.* 裝飾器來註冊生命週期事件。例如,我們可以在發送請求給模型之前,攔截請求並印出日誌,這對於除錯與監控非常有用:

from pydantic_ai import Agent, ModelRequestContext, RunContext
from pydantic_ai.capabilities import Hooks

# 建立 Hooks 實例
hooks = Hooks()

# 註冊 Hook:在發送請求給模型之前觸發
@hooks.on.before_model_request
async def log_request(ctx: RunContext[None], request_context: ModelRequestContext) -> ModelRequestContext:
    # 印出即將發送的訊息數量
    print(f'準備發送 {len(request_context.messages)} 則訊息給模型...')
    
    # 必須回傳 request_context,你也可以在這裡修改它
    return request_context

# 將 hooks 作為 capability 傳入 Agent
agent = Agent('openai:gpt-4o', capabilities=[hooks])
result = agent.run_sync('你好!')

結果驗證與重試機制

Hooks 也可以用來驗證模型的輸出。如果不符合商業邏輯,可以拋出 ModelRetry 異常,Pydantic AI 會自動帶著錯誤訊息要求模型重新思考並修正錯誤:

from pydantic_ai import Agent, RunContext
from pydantic_ai.capabilities import Hooks
from pydantic_ai.exceptions import ModelRetry
from pydantic_ai.messages import ModelResponse
from pydantic_ai.models import ModelRequestContext

hooks = Hooks()

# 註冊 Hook:在模型回傳結果之後觸發
@hooks.on.after_model_request
async def check_response(
    ctx: RunContext[None],
    *,
    request_context: ModelRequestContext,
    response: ModelResponse,
) -> ModelResponse:
    # 檢查模型的回覆內容是否包含敏感字眼
    if "密碼" in str(response.parts) or "password" in str(response.parts).lower():
        # 拋出 ModelRetry,要求模型重試
        raise ModelRetry('請注意安全規範,絕對不要在回覆中包含任何密碼資訊。')
        
    # 驗證通過,回傳原始 response
    return response

agent = Agent('gemini-1.5-flash', capabilities=[hooks])

Agent 層級的快捷裝飾器

除了使用 Hooks Capability 來進行深度的生命週期攔截外,Pydantic AI 也直接在 Agent 實例上提供了幾個常用的快捷裝飾器,適合用於簡單的動態提示或最終結果驗證。

動態系統提示

我們可以在模型開始生成回答「之前」,根據當下的執行環境或時間,動態地調整系統提示。這透過 @agent.system_prompt 裝飾器來達成:

from pydantic_ai import Agent, RunContext
import datetime

agent = Agent('gemini-1.5-flash')

# 每次執行前都會觸發這個 Hook,將回傳的字串加入到系統提示中
@agent.system_prompt
def inject_current_time(ctx: RunContext) -> str:
    # 取得當前日期
    now = datetime.datetime.now().strftime("%Y-%m-%d")
    return f"今天的日期是 {now}。在回答結尾請加上一句鼓勵的話。"

最終結果驗證

如果你只需要針對 Agent 的「最終輸出結果」進行驗證(特別是當你有定義結構化輸出 result_type 時),使用 @agent.result_validator 會比 Hooks Capability 更簡單直覺:

from pydantic_ai import Agent, RunContext, ModelRetry

agent = Agent('gemini-1.5-flash')

# 模型生成最終結果後會觸發
@agent.result_validator
def validate_no_secrets(ctx: RunContext, result: str) -> str:
    # 檢查最終結果字串
    if "密碼" in result:
        # 觸發重試機制
        raise ModelRetry('請注意安全規範,不要在回覆中包含任何密碼資訊。')
    return result

透過巧妙運用 Capabilities 與 Hooks,你能大幅提升 Agent 程式碼的可維護性,並建立出具備強大防錯機制的企業級 AI 應用。