• בלוג
  • יום 13 - פלט מובנה

יום 13 - פלט מובנה

12/10/2025

ראינו אתמול איך מודלים מאומנים להבין איך לעבוד עם כלים, לבקש הפעלת כלים ולקרוא תשובות מכלים. סוג נוסף של אימון שמודלים עוברים הוא אימון ליצירת פלט מובנה כלומר פלט שיגיע אלינו בתור אוביקט JSON ולא ידרוש פיענוח טקסטואלי. היום נראה איך זה עובד ומתי מומלץ להשתמש במנגנון זה.

1. איך זה עובד

המודלים של OpenAI ומודלים מסוימים נוספים מאומנים כדי להבין הודעות בפורמט מיוחד שמבקש מהמודל פלט מובנה. כשאנחנו כותבים הודעה למודל שמאומן לכך אנחנו יכולים לבקש מבנה מסוים של פלט ואז המודל יתאמץ לכתוב לנו את הפלט בפורמט שביקשנו.

דוגמה ראשונה מהתיעוד תראה לנו איך להפוך טקסט לאוביקט פייתון:

import asyncio

from pydantic import BaseModel
from agents import Agent, Runner

class CalendarEvent(BaseModel):
    name: str
    date: str
    participants: list[str]

agent = Agent(
    name="Calendar extractor",
    instructions="Extract calendar events from text",
    output_type=CalendarEvent,
)

async def main():
    result = await Runner.run(agent, "We're having a party this Saturday night, 9:00pm with Mark and Dana")
    print(result.final_output.__class__)
    print(result.final_output)

if __name__ == "__main__":
    asyncio.run(main())

הגדרתי קלאס שיורש מ BaseModel של pydantic, שזו ספריה להגדרת טיפוסים בפייתון. את הקלאס העברתי בתור output_type לסוכן ועכשיו כשאני מפעיל את run אני מקבל בחזרה אוביקט מהקלאס הזה.

נשים לב שלא כל המודלים מאומנים על הפעלת כלים או על פורמט הפעלת כלים שתואם את OpenAI ולכן אותה תוכנית עם מודל אחר לדוגמה DeepSeek-R1 תחזיר את השגיאה:

openai.UnprocessableEntityError: Error code: 422 - {'error': {'code': 'Invalid input', 'status': 422, 'message': 'invalid input error', 'details': [{'type': 'value_error', 'loc': ['body', 'response_format', 'type'], 'msg': "Value error, Response format was json_schema but must be either 'text' or 'json_object'.", 'input': 'json_schema', 'ctx': {'error': {}}}]}}

2. מתי נשתמש

כשאני ראיתי את היכולת של Structured Output בפעם הראשונה אני מודה שמאוד התלהבתי וניסיתי להשתמש בה כמה שיותר. לאורך הזמן בעבודה עם מודלים למדתי על החסרונות:

  1. קשה להחליף למודלים אחרים כי לא כולם מאומנים על פלט מובנה באותו אופן.

  2. כשהמודל לא יודע מה לענות הוא יחזיר שגיאה או None במקום הסבר מה קרה. לדוגמה כשאני מריץ את הפרומפט:

result = await Runner.run(agent, "Hello")

אני מקבל אוביקט אירוע בלוח שנה ששם האירוע הוא Hello אבל אין לו תאריך ואין לו רשימת מוזמנים. בעצם אני מכריח את המודל לענות לי בפורמט מסוים גם כשזה לא אפשרי ולכן מקבל תשובה שגויה.

דרך אחרת לכתוב את קוד הדוגמה היא:

import asyncio

from pydantic import BaseModel
from agents import Agent, Runner
from agents.extensions.models.litellm_model import LitellmModel
import os
import json

agent = Agent(
    name="Calendar extractor",
    model=LitellmModel(model="github/gpt-4.1", api_key=os.environ["GITHUB_TOKEN"]),
    instructions="""
    Extract calendar events from text. Return JSON object of format:
    {
      name: str,
      date: str,
      participants: list[str]
    }    
    """,
)

async def main():
    result = await Runner.run(agent, "We're having a party this Saturday night, 9:00pm with Mark and Dana")
    data = json.loads(result.final_output)
    print(data)

if __name__ == "__main__":
    asyncio.run(main())

ועכשיו אני מקבל את התוצאה בתור JSON ויכול לטעון אותו למילון בפייתון, וגם אם אני מחליף למודל DeepSeek הקוד עדיין עובד. כמובן שגם פה יש חסרונות כי יש יותר סיכוי לקבל מהמודל תוצאה חלקית או לא נכונה.

3. עכשיו אתם

  1. קראו על pydantic בתיעוד שלהם: https://docs.pydantic.dev/latest/#pydantic-examples

  2. נסו את תוכנית הדוגמה עם מספר מודלים ובדקו איזה מודלים יודעים להחזיר פלט מובנה ואיזה יעדיפו להחזיר לכם מילון בפורמט JSON.