AIエージェントの検証の一環として、langgraphでPydanticを使用していましたので、今回は、Pydanticについてまとめていきます。
Pydanticは、Pythonの型ヒントを活用して、データのバリデーションや型変換を自動化する強力なライブラリです。
これにより、コードの信頼性と可読性が向上し、開発効率が飛躍的に高まります。
本記事では、Pydanticの基本的な使い方から、FastAPIとの統合によるAPI開発、環境変数の管理と設定の読み込みなど、実践的な応用方法まで解説します。
それでは、早速見ていきましょう。
Pydanticの概要
Pydanticとはどのようなライブラリか
Pydanticは、Pythonでデータのバリデーションや型の強制変換を簡単かつ効率的に行うためのライブラリになります。
具体的には、以下のような際に使用します。
- 入力データが正しい型や形式かどうかを簡単にチェックできる
- データの型を自動的に適切な形式に変換できる
- データの構造をわかりやすく定義できる
Pydanticは、Pythonの型ヒントを最大限に活用することで、コードの可読性や安全性を高めつつ、開発者が意図した通りのデータを扱えるようにしてくれます。
Pydanticの主な特徴と利点
Pydanticの特徴と利点は以下の通りです。
- 型のバリデーションと型変換の自動化を実施
- シンプルで直感的なモデル定義が可能
- エラーメッセージが詳細でわかりやすい
- FastAPIとの高い親和性
- カスタマイズ性の高さ
それでは、Pydanticの具体的な使い方や応用例について詳しく解説していきます。
Pydanticの基本的な使い方
Pydanticのインストール方法
Pydanticを使い始めるには、まずPython環境にPydanticをインストールする必要があります。
pip install pydantic
インストールが完了したら、以下のようにインポートして使う準備が整います。
from pydantic import BaseModel
基本的なモデルの定義方法
Pydanticでは、データ構造を表すために「モデル」を作成します。
以下は基本的なモデル定義の例です。
from pydantic import BaseModel
class User(BaseModel):
id: int
name: str
email: str
このコードでは、User
というモデルを定義しています。
このモデルには次の3つの属性があります。
- id:int型
- name:str型
- email:str型
Pydanticのモデルを作る際には、BaseModelを継承する必要があります。
定義したモデルは、次のようにインスタンス化して使用できます。
user = User(id=1, name="山田太郎", email="yamada_taro@example.com")
print(user)
以下のように出力されます。
出力: id=1 name='山田太郎' email='yamada_taro@example.com'
データのバリデーションと型変換の自動化
Pydanticの大きな特徴は、データのバリデーションと型変換を自動で行ってくれる点です。
モデルをインスタンス化する際に、入力データが定義した型と一致していない場合、適切に変換を試みたり、エラーを報告してくれます。
user = User(id="1", name="山田太郎", email="yamada_taro@example.com")
print(user)
この場合、id
に文字列("1"
)を渡していますが、Pydanticが自動的に整数(1
)に変換してくれます。
出力: id=1 name='山田太郎' email='yamada_taro@example.com'
一方で、変換できないデータが渡された場合はエラーを発生させます。
user = User(id="山田花子", name="山田太郎", email="yamada_taro@example.com")
エラーメッセージから、どのフィールドがどのように間違っているのかも簡単に理解できます。
出力:
pydantic_core._pydantic_core.ValidationError: 1 validation error for User
id
Input should be a valid integer, unable to parse string as an integer [type=int_parsing, input_value='山田花子', input_type=str]
このように、Pydanticの自動型変換とバリデーション機能を活用することでデータ処理の信頼性を向上させることができます。
高度な機能
ネストされたモデルの定義
Pydanticでは、モデルの中に別のモデルを含む「ネストされたモデル」を簡単に定義することができます。
以下は、ユーザー情報に住所情報を含むモデルを定義する場合になります。
from pydantic import BaseModel
class Address(BaseModel):
street: str
city: str
zipcode: str
class User(BaseModel):
id: int
name: str
email: str
address: Address
address_data = {"street": "123 Main Street", "city": "Tokyo", "zipcode": "100-1111"}
user_data = {"id": 1, "name": "山田太郎", "email": "yamada_taro@example.com", "address": address_data}
user = User(**user_data)
print(user)
以下、出力結果です。
出力:
id=1 name='山田太郎' email='yamada_taro@example.com' address=Address(street='123 Main Street', city='Tokyo', zipcode='100-1111')
このように、AddressモデルをUserモデルの中で利用することで、ユーザーの住所情報を含むデータを表現できます。
ネストされたモデルを使うと、複数のデータ構造を簡潔かつ明確に管理できるため、可読性が向上します。
カスタムバリデーションの実装方法
Pydanticでは、独自のバリデーションロジックを実装するためにカスタムバリデーションを追加することができます。
これには、@field_validatorデコレータを使用します。
例えば、ユーザーの名前が3文字以上でなければならないというルールを追加したい場合は以下のようにします。
from pydantic import BaseModel, field_validator
class User(BaseModel):
id: int
name: str
email: str
@field_validator('name')
def name_must_be_long_enough(cls, value: str) -> str:
if len(value) < 3:
raise ValueError("名前は3文字以上である必要があります。")
return value
# バリデーションエラー例
invalid_user = User(id=2, name="山田", email="yamada_hazime@example.com")
以下、出力結果です。
出力:
Value error, 名前は3文字以上である必要があります。 [type=value_error, input_value='山田', input_type=str]
カスタムバリデーションを利用することで、特定のビジネスルールに応じたデータチェックを簡単に追加できます。
デフォルト値とOptional型の活用
Pydanticのモデルでは、属性にデフォルト値を設定することができます。
デフォルト値を持つ属性は、データが渡されなかった場合に自動的にその値が使われます。
from pydantic import BaseModel
class User(BaseModel):
id: int
name: str
email: str
is_active: bool = True
user = User(id=1, name="山田太郎", email="yamada_taro@example.com")
print(user)
以下、出力結果です。
出力: id=1 name='山田太郎' email='yamada_taro@example.com' is_active=True
また、属性が必須ではない場合には、Optional型を使用します。
Optional型を使うことで、データが渡されなくてもエラーにならないようにできます。
from typing import Optional
from pydantic import BaseModel
class User(BaseModel):
id: int
name: str
email: Optional[str] = None # メールアドレスは必須ではない
user = User(id=1, name="山田太郎" )
print(user)
以下、出力結果です。
出力: id=1 name='山田太郎' email=None
Optional型とデフォルト値を組み合わせることで、柔軟なモデルを定義できます。
これらの機能は、データのすべての項目が必須ではない場合や、デフォルト設定を適用したい場合に使用します。
Pydanticの実践的な応用
FastAPIとの統合によるAPI開発
FastAPIは、Pythonで高速なAPIを構築するためのフレームワークであり、Pydanticと深く統合されています。
ユーザー情報を受け取るエンドポイントを作成する場合、以下のようにPydanticのBaseModelを継承したクラスを定義できます。
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class User(BaseModel):
id: int
name: str
email: str
@app.post("/users/")
async def create_user(user: User):
return user
このコードでは、Userモデルがリクエストボディとして受け取るデータの構造と型を定義しています。
FastAPIはこのモデルを利用して、受信したデータが期待された型と一致するかを自動的に検証します。
LanggraphにおいてPydanticモデルを用いたステート管理
LangGraphは、複雑なAIエージェントを視覚的に管理し、ノードとエッジを使って直感的に処理フローを設計できるツールです。
LangGraphについては、以下を参考にしてください。
このLangGraphで、Pydanticのモデルをステート管理に活用することで、データのバリデーションや型安全性を高めることができます。
例えば、以下のようにPydanticモデルを定義し、LangGraphで使用します。
from pydantic import BaseModel
from langgraph.graph import StateGraph, START, END
# Pydanticモデルの定義
class UserState(BaseModel):
name: str
age: int
is_adult: bool = False
# ノード関数の定義
def check_age(state: UserState):
if state.age >= 18:
state.is_adult = True
return state
def greet_user(state: UserState):
if state.is_adult:
print(f"Hello, {state.name}. You are an adult.")
else:
print(f"Hi, {state.name}. You are not an adult yet.")
return state
# グラフの構築
builder = StateGraph(UserState)
builder.add_node(check_age)
builder.add_node(greet_user)
builder.add_edge(START, "check_age")
builder.add_edge("check_age", "greet_user")
builder.add_edge("greet_user", END)
graph = builder.compile()
# グラフの実行
initial_state = UserState(name="Yamada", age=30)
graph.invoke(initial_state)
以下、出力結果です。
出力:Hello, Yamada. You are an adult.
上記コードでは、UserStateというPydanticモデルを定義し、ユーザーの名前、年齢、成人かどうかの情報を管理しています。
このように、Pydanticモデルをステートとして使用することで、データの構造を明確に定義し、各ノード間でのデータの受け渡しやバリデーションを容易に行うことができます。
環境変数の管理と設定の読み込み
Pydanticは、環境変数の管理や設定の読み込みを簡素化するための機能も提供しています。
pydantic-settingsパッケージを使用することで、環境変数や.envファイルから設定値を読み込み、データのバリデーションや型変換を自動的に行うことができます。
from pydantic_settings import BaseSettings, SettingsConfigDict
class Settings(BaseSettings):
model_config = SettingsConfigDict(env_file=".env", env_file_encoding="utf-8")
db_user: str
db_password: str
db_host: str = "localhost"
db_port: int = 5432
settings = Settings()
print(settings.db_user)
print(settings.db_password)
.envファイル
db_user=YAMADA_TARO
db_password=password123
以下、出力結果です。
YAMADA_TARO
password123
このコードでは、Settingsクラスが環境変数や.envファイルからデータベースの設定を読み込みます。
model_configで.envファイルのパスとエンコーディングを指定しています。
これにより、環境変数の管理が容易になり、設定値のバリデーションや型変換も自動的に行われます。
まとめ
本記事では、Pydanticの基本的な使い方から高度な機能、そして実践的な応用例までを解説しました。
Pydanticを活用することで、Pythonでのデータバリデーションや型安全性の確保が容易になり、コードの信頼性と可読性が向上します。
様々な場面で応用の利くライブラリと感じますので、今後も使用していきたいと思います。