Antigravity のエージェントを使ったシステムで「エージェントの出力が毎回微妙に違う形式になってしまう」「バリデーションエラーで後続処理が止まる」「出力の品質が安定しない」といった問題に悩んだことはないでしょうか。
AI エージェントの出力は確率的であり、常に期待通りの形式や品質を保証するわけではありません。ここではAntigravity エージェントの出力検証エラーに体系的に対処するための実装パターンをご紹介します。
エージェント出力検証エラーの分類
出力検証エラーは大きく3つのカテゴリに分類できます。
形式エラー(Format Errors):JSON が不正、必須フィールドが欠落、データ型が違う、など。後続システムがパースできないケースです。
品質エラー(Quality Errors):形式は正しいが内容が期待を満たしていません。回答が短すぎる、情報が不正確、タスクの要件を満たしていない、などが該当します。
整合性エラー(Consistency Errors):複数回実行した際に結果が大きく異なります。マルチエージェントシステムで各エージェントの出力が矛盾する、などです。
形式エラーへの対処:構造化出力の強制
プロンプトによる形式指定
エージェントへの指示でJSON形式を明示し、スキーマを提示することで形式エラーを減らせます。
def create_structured_prompt(task: str, schema: dict) -> str:
"""構造化出力を促すプロンプトを生成"""
schema_str = json.dumps(schema, ensure_ascii=False, indent=2)
return f"""
以下のタスクを実行し、必ず下記のJSONスキーマに従って出力してください。
タスク: {task}
出力スキーマ:
```json
{schema_str}重要な注意事項:
- 必ずJSONのみを出力してください(説明文は不要です)
- すべての必須フィールドを含めてください
- nullは使用せず、空の場合は空文字列または空配列を使用してください
JSON出力: """
使用例
schema = { "title": "string(必須)", "summary": "string(必須、100文字以内)", "key_points": ["string(必須、3〜5個)"], "confidence_score": "number(必須、0.0〜1.0)" }
prompt = create_structured_prompt("最新のAI動向を分析してください", schema)
### パースエラーのハンドリングと自動修復
```python
import json
import re
from typing import Optional, Any
def parse_agent_output(raw_output: str, schema: dict) -> Optional[dict]:
"""
エージェントの出力をパースし、形式エラーを自動修復する
"""
# 1. まずそのままパースを試みる
try:
return json.loads(raw_output)
except json.JSONDecodeError:
pass
# 2. マークダウンコードブロックを除去してパース
code_block_pattern = r'```(?:json)?\s*([\s\S]*?)\s*```'
match = re.search(code_block_pattern, raw_output)
if match:
try:
return json.loads(match.group(1))
except json.JSONDecodeError:
pass
# 3. JSON部分を抽出してパース(前後の余分なテキストを除去)
json_pattern = r'\{[\s\S]*\}'
match = re.search(json_pattern, raw_output)
if match:
try:
return json.loads(match.group())
except json.JSONDecodeError:
pass
# 4. パース失敗 - None を返して上位でハンドリング
return None
def validate_output(parsed: dict, schema: dict) -> tuple[bool, list[str]]:
"""
パース済み出力のスキーマ検証
Returns: (is_valid, list of error messages)
"""
errors = []
for field, field_type in schema.items():
if field not in parsed:
errors.append(f"必須フィールドが欠落: {field}")
continue
value = parsed[field]
if "string" in str(field_type) and not isinstance(value, str):
errors.append(f"型エラー: {field} は文字列である必要があります(実際: {type(value).__name__})")
elif "number" in str(field_type) and not isinstance(value, (int, float)):
errors.append(f"型エラー: {field} は数値である必要があります")
elif "array" in str(field_type) or isinstance(field_type, list):
if not isinstance(value, list):
errors.append(f"型エラー: {field} はリストである必要があります")
return len(errors) == 0, errors
リトライによる自動修復
from antigravity import Agent # 仮のAntigravity SDK
async def get_validated_output(
agent: Agent,
task: str,
schema: dict,
max_retries: int = 3
) -> Optional[dict]:
"""
バリデーションが通るまで自動リトライする
"""
for attempt in range(max_retries):
prompt = create_structured_prompt(task, schema)
raw_output = await agent.run(prompt)
# パース試行
parsed = parse_agent_output(raw_output)
if parsed is None:
if attempt < max_retries - 1:
# リトライ用プロンプトで修正を依頼
task = f"""
前回の出力がJSONとして正しくパースできませんでした。
前回の出力: {raw_output[:500]}
修正してください: {task}
"""
continue
return None
# スキーマ検証
is_valid, errors = validate_output(parsed, schema)
if is_valid:
return parsed
if attempt < max_retries - 1:
# エラーを伝えて修正を依頼
error_list = "\n".join(f"- {e}" for e in errors)
task = f"""
前回の出力に以下の問題がありました:
{error_list}
修正してください: {task}
"""
else:
print(f"バリデーション失敗 (最終試行): {errors}")
return None
return None品質エラーへの対処:セルフチェックパターン
AI エージェントに自己評価をさせることで、品質エラーを事前にフィルタリングできます。
QUALITY_CHECK_PROMPT = """
以下のエージェント出力を品質評価してください。
タスク: {task}
出力: {output}
以下の基準で評価し、JSONで返してください:
{{
"meets_requirements": true/false,
"quality_score": 0.0〜1.0,
"issues": ["問題点のリスト"],
"needs_revision": true/false,
"revision_guidance": "修正が必要な場合の指示"
}}
評価基準:
1. タスクの要件をすべて満たしているか
2. 情報が具体的で実用的か
3. 内容に矛盾や誤りはないか
4. 必要な情報量が含まれているか
"""
async def get_quality_checked_output(
primary_agent: Agent,
checker_agent: Agent,
task: str,
schema: dict,
min_quality_score: float = 0.7
) -> Optional[dict]:
"""セルフチェック付きの出力生成"""
for attempt in range(3):
# プライマリエージェントで出力生成
output = await get_validated_output(primary_agent, task, schema)
if output is None:
continue
# チェッカーエージェントで品質評価
check_prompt = QUALITY_CHECK_PROMPT.format(
task=task,
output=json.dumps(output, ensure_ascii=False)
)
check_raw = await checker_agent.run(check_prompt)
check_result = parse_agent_output(check_raw, {})
if check_result is None:
# チェック自体が失敗した場合は出力をそのまま使用
return output
quality_score = check_result.get("quality_score", 0)
needs_revision = check_result.get("needs_revision", False)
if not needs_revision and quality_score >= min_quality_score:
return output
if attempt < 2:
# 修正指示を追加してリトライ
guidance = check_result.get("revision_guidance", "品質を改善してください")
issues = check_result.get("issues", [])
task = f"{task}\n\n改善指示:\n{guidance}\n問題点: {', '.join(issues)}"
else:
# 最終試行でも品質不足の場合は低品質フラグを付けて返す
output["_quality_warning"] = {
"score": quality_score,
"issues": check_result.get("issues", [])
}
return output
return None整合性エラーへの対処:アグリゲーションパターン
複数回の実行や複数エージェントの出力が矛盾する場合、投票・アグリゲーションで整合性を確保します。
from collections import Counter
from typing import List
async def get_consensus_output(
agent: Agent,
task: str,
num_samples: int = 3,
schema: dict = None
) -> Optional[dict]:
"""
複数回実行して多数決で最終出力を決定する
(確率的な揺れを抑制する)
"""
outputs = []
for _ in range(num_samples):
output = await get_validated_output(agent, task, schema or {})
if output:
outputs.append(output)
if not outputs:
return None
if len(outputs) == 1:
return outputs[0]
# 文字列フィールドは最も多く出現した値を採用
consensus = {}
all_keys = set().union(*[o.keys() for o in outputs])
for key in all_keys:
if key.startswith("_"): # 内部フィールドはスキップ
continue
values = [str(o.get(key, "")) for o in outputs if key in o]
if not values:
continue
if isinstance(outputs[0].get(key), (int, float)):
# 数値は平均値を使用
numeric_values = [o[key] for o in outputs if key in o and isinstance(o[key], (int, float))]
consensus[key] = sum(numeric_values) / len(numeric_values)
elif isinstance(outputs[0].get(key), list):
# リストは出現頻度の高いアイテムを優先
all_items = [item for o in outputs for item in o.get(key, [])]
item_counts = Counter(all_items)
consensus[key] = [item for item, _ in item_counts.most_common()]
else:
# 文字列は最頻値を使用
consensus[key] = Counter(values).most_common(1)[0][0]
return consensus出力検証のモニタリング
本番環境では、バリデーションエラーの発生率を継続的に監視する点が肝心です。
import time
from dataclasses import dataclass, field
from collections import defaultdict
@dataclass
class ValidationMetrics:
total_attempts: int = 0
format_errors: int = 0
quality_errors: int = 0
success_count: int = 0
retry_counts: list = field(default_factory=list)
error_details: list = field(default_factory=list)
@property
def success_rate(self) -> float:
return self.success_count / self.total_attempts if self.total_attempts > 0 else 0
@property
def avg_retries(self) -> float:
return sum(self.retry_counts) / len(self.retry_counts) if self.retry_counts else 0
def alert_if_needed(self):
if self.success_rate < 0.8:
print(f"⚠️ 出力検証成功率が低下: {self.success_rate:.1%}")
if self.avg_retries > 1.5:
print(f"⚠️ 平均リトライ回数が高い: {self.avg_retries:.1f}回")
metrics = ValidationMetrics()全体を振り返って
Antigravity エージェントの出力検証エラーへの対処は、形式エラー(パースエラーの自動修復・リトライ)、品質エラー(セルフチェックパターン)、整合性エラー(複数実行でのアグリゲーション)の3層で考えるとわかりやすくなります。完璧な出力を毎回保証することは難しいですが、適切な検証と回復戦略を組み合わせることで、実用的なレベルの品質と安定性を実現できます。困っている方のお役に立てれば幸いです。
個人開発12年の現場で実感したこと
線引きするときの3つの判断軸
- 失敗時の影響が金銭やユーザー体験にどれだけ波及するか
- 復旧オペレーションが明文化されているか
- 観測ログから人間が再現できる粒度に整っているか