AIエージェントの出力を型安全に保証する設計原則 ―契約による設計がAI時代に必須な理由
出典: 伊武 成昭 (naruaki ibu)

AIエージェントはデフォルトでテキストを返しますが、後続処理で構造化データが必要な場面は多々あります。Microsoft Agent Frameworkを題材に、出力形式を保証する設計原則と「契約による設計」の重要性を解説します。
AIエージェントの出力形式が重要になった背景
AIエージェントの活用が広がる中、単なるテキスト応答では不十分な場面が増えています。スコアリング、データ集計、他システムとの連携など、構造化されたデータとしての出力が求められるケースは日常的です。
伊武成昭氏が「AIエージェント設計原則シリーズ」の第3回で取り上げているのは、まさにこの課題です。Microsoft Agent Frameworkを題材に、AIエージェントの出力形式をどう保証するかという、実践的でありながら本質的な問題に切り込んでいます。
出力形式の保証がなぜ必要なのか
AIエージェントがテキストを返すのはデフォルト動作です。しかし実際の開発現場では、次のような要求に直面します:
型や構造が明示されていない場合、パース処理は脆弱になります。LLMの出力は確率的であり、微妙な表現の揺れが発生します。これを毎回正規表現や文字列処理で対応するのは、メンテナンス性を著しく損ないます。
伊武氏が言及している「**契約による設計(Design by Contract)**」は、まさにこの問題への解決策です。関数やメソッドが満たすべき事前条件・事後条件・不変条件を明示することで、コードの信頼性を高める設計手法ですが、これはAIエージェントの出力にも適用できます。
Microsoft Agent Frameworkでの実装アプローチ
Microsoft Agent Frameworkは、エージェントの出力形式を型として定義し、それを保証する仕組みを提供しています。具体的には:
Pydanticモデルによる型定義
PythonのPydanticライブラリを使い、出力のスキーマを明示的に定義します。例えば:
from pydantic import BaseModel, Field
class SentimentScore(BaseModel):
sentiment: str = Field(..., pattern="^(positive|negative|neutral)$")
confidence: float = Field(..., ge=0.0, le=1.0)
reason: strこのモデルは、エージェントが返すべきデータの「契約」を表現しています。`sentiment`は3つの値のいずれか、`confidence`は0から1の範囲、といった制約が型レベルで保証されます。
Structured Outputの活用
OpenAI APIやAzure OpenAI Serviceには、Structured Output機能があります。これをAgent Frameworkと組み合わせることで、LLMに対して「この型に従った出力を生成せよ」と指示できます。
agent = Agent(
model=ChatCompletionClient(...),
output_schema=SentimentScore
)
result = await agent.run("この製品レビューを分析してください")
assert isinstance(result.output, SentimentScore)出力が自動的にバリデーションされ、型安全性が保証されます。パースエラーやデータ不整合のリスクが大幅に減少します。
編集部の視点
従来手法との比較
従来、LLMの出力を構造化する方法としては以下がありました:
**1. プロンプトエンジニアリング頼み**
「JSON形式で出力してください」とプロンプトに書く方法です。簡単ですが、出力の保証はゼロです。LLMが指示を無視したり、微妙に形式を間違えたりするリスクが常にあります。
**2. 後処理でのパース**
正規表現やJSON.parseで出力を解析する方法です。柔軟性はありますが、エラーハンドリングが複雑化し、コードが脆くなります。
**3. Few-shot学習**
例示を与えて出力形式を学習させる方法です。効果的ですが、トークン数を消費し、完全な保証にはなりません。
**契約による設計アプローチ**は、これらの問題を根本から解決します。型システムとバリデーションを組み合わせることで、**コンパイル時・実行時の両方で安全性を担保**できます。
メリットと注意点
**メリット:**
**注意点:**
適用範囲の考察
このアプローチが特に有効なのは:
一方、探索的な対話や創造的な文章生成など、自由度が重要な用途には向きません。出力形式の保証と表現の自由度は、トレードオフの関係にあります。
今日から試せるアクション
1. 既存のエージェント出力を型定義してみる
現在テキストで受け取っているエージェントの出力を、Pydanticモデルとして書き出してみましょう。これだけで、暗黙の仕様が明示化されます。
# Before: 曖昧なテキスト処理
result = agent.run("分析してください")
score = float(result.split("スコア:")[1].split()[0])
# After: 型安全な処理
class AnalysisResult(BaseModel):
score: float = Field(..., ge=0, le=100)
summary: str
result = agent.run("分析してください", output_schema=AnalysisResult)
print(result.score) # 型安全にアクセス可能2. Structured Outputを試す
OpenAI APIを使っている場合、`response_format`パラメータでJSON Schemaを指定できます。まずは簡単な構造から試してみましょう。
from openai import OpenAI
client = OpenAI()
completion = client.chat.completions.create(
model="gpt-4o",
messages=[{"role": "user", "content": "東京の天気を教えて"}],
response_format={
"type": "json_schema",
"json_schema": {
"name": "weather_response",
"schema": {
"type": "object",
"properties": {
"weather": {"type": "string"},
"temperature": {"type": "number"}
},
"required": ["weather", "temperature"]
}
}
}
)3. バリデーションエラーのハンドリング戦略を立てる
型定義を導入すると、バリデーションエラーが可視化されます。これをどう処理するか、戦略を決めましょう:
from pydantic import ValidationError
for attempt in range(3):
try:
result = await agent.run(query, output_schema=MySchema)
break
except ValidationError as e:
if attempt == 2:
raise
# エラー情報を次の試行に活用
query += f"\n\n前回のエラー: {e.errors()}"まとめ
AIエージェントの出力形式を保証する設計原則は、AIシステムの信頼性とメンテナンス性を大きく向上させます。契約による設計の考え方を取り入れることで、AIの確率的な性質と、エンジニアリングの決定論的な要求を両立できます。
Microsoft Agent Frameworkのような現代的なフレームワークは、この考え方を実装レベルでサポートしています。まずは小さな範囲から型定義を導入し、その効果を実感してみてください。
この情報は @伊武 成昭 (naruaki ibu) さんの投稿を参考にしています。


