Zum Inhalt

Structured Outputs

Beherrschen Sie die Evaluierung von programmatisch formatierten LLM-Antworten, die für agentische Anwendungen essentiell sind

Structured Outputs ermöglichen Entwicklern, sicherzustellen, dass das LLM Antworten in einem programmatisch deterministischen Format erzeugt. Agentische Programme nutzen diese Funktion intensiv, um Interoperabilität zwischen Code-Pfaden und LLM-Antworten zu ermöglichen. Dies macht die Evaluierung structured Outputs zu einem wesentlichen Bestandteil der Evaluierung von Agenten.

Grundlegende Verwendung

Ein Beispiel, das die Verwendung von Pydantic-Modellen für die Generierung und Evaluierung von structured Outputs zeigt:

"""v1.0 API: Structured Outputs Example

Demonstrates how to use Pydantic models as response_format for structured
LLM outputs. The model analyzes product reviews and extracts structured
sentiment analysis data.

v1.0 API changes:
- client.prompt_templates.aget_or_create() -> client.get_or_create_prompt_template()
- client.collections.aget_or_create() -> client.get_or_create_collection()
- client.criteria.aadd_many() -> criterion_set.add_criteria()
- client.experiments.aget_or_create() -> client.run_experiment()
- Sync-first approach (no asyncio.run needed)

INSIGHT: Structured outputs use Pydantic models to enforce response schemas.
The LLM is instructed to return JSON matching the schema, and the response
is validated against it. This is powerful for data extraction tasks.
"""

from dotenv import load_dotenv
from elluminate import Client
from elluminate.schemas import RatingMode
from pydantic import BaseModel, Field

load_dotenv(override=True)


class ProductReviewSentimentAnalysis(BaseModel):
    """Schema for structured sentiment analysis of reviews."""

    stars: int = Field(description="Number of stars of the review", ge=1, le=5)
    sentiment: str = Field(
        description="Overall sentiment: positive, negative, or neutral",
        pattern="^(positive|negative|neutral)$",
    )
    confidence: float = Field(
        description="Confidence score of the sentiment analysis between 0 and 1",
        ge=0,
        le=1,
    )


def main():
    client = Client()

    print("v1.0: Structured Outputs Example")
    print("=" * 50)

    # v1.0: get_or_create_prompt_template with response_format
    template, created = client.get_or_create_prompt_template(
        name="Product Review Analysis v1",
        messages="""Analyze this product review and extract key information:

Review: {{review_text}}

Provide review stars, sentiment and confidence score.""",
        response_format=ProductReviewSentimentAnalysis,
    )
    print(f"Template: {'Created' if created else 'Found existing'}")

    # v1.0: get_or_create_collection with variables at creation time
    collection, created = client.get_or_create_collection(
        name="Product Review Data v1",
    )
    if created:
        collection.add_many(
            variables=[
                {
                    "review_text": "Stars: **** Great wireless headphones! Audio quality is fantastic and noise cancellation works perfectly. Battery could be better but overall very satisfied."
                },
                {
                    "review_text": "Stars: ** Poor laptop experience. Screen flickered after 2 weeks, customer service was helpful, but would not recommend this product."
                },
            ]
        )
    print(f"Collection: {'Created' if created else 'Found existing'}")

    # v1.0: get_or_create_criterion_set with add_criteria
    criterion_set, created = client.get_or_create_criterion_set(name="Review Analysis Criteria v1")
    if created:
        criterion_set.add_criteria(
            [
                "In the 'stars' field, is the counted number of stars correct?",
                "Does the 'sentiment' field accurately reflect the review's tone?",
                "Is the 'confidence' score appropriate for the certainty of the sentiment?",
            ]
        )
    print(f"Criterion set: {'Created' if created else 'Found existing'}")

    # v1.0: run_experiment - creates and runs in one call
    print("\nRunning experiment...")
    experiment = client.run_experiment(
        name="Review Analysis Experiment v1",
        prompt_template=template,
        collection=collection,
        criterion_set=criterion_set,
        rating_mode=RatingMode.FAST,
        n_epochs=1,
    )

    print(f"\nExperiment completed: {experiment.name}")
    print(f"Total responses: {len(experiment.rated_responses)}")

    # Display results
    for i, response in enumerate(experiment.responses(), 1):
        print(f"\n--- Example {i} ---")
        review_text = response.prompt.template_variables.input_values["review_text"]
        print(f"Review: {review_text[:80]}...")
        print("Analysis:")
        for message in response.messages:
            if message.role == "assistant":
                print(f"  {message.content}")

    # Show aggregated results
    if experiment.result:
        print("\n--- Results ---")
        print(f"Pass rate: {experiment.result.mean_all_ratings.yes:.2%}")


# =========================================================================
# Migration Insights
# =========================================================================
#
# 1. RESPONSE FORMAT
#    Both versions: Pass Pydantic model class as response_format
#    v1.0: Pass as keyword argument (part of template identity)
#    The SDK automatically converts to JSON schema for the API
#
# 2. CRITERIA SETUP
#    v0.x: client.criteria.aadd_many([...], template, delete_existing=True)
#    v1.0: criterion_set.add_criteria([...])
#    Note: v1.0 links criteria to criterion_set, not directly to template
#    The criterion_set is then linked to the template via experiment
#
# 3. DELETE_EXISTING PATTERN
#    v0.x: delete_existing=True to replace criteria
#    v1.0: Create new criterion_set or check if created before adding
#    This is a cleaner pattern that doesn't destroy existing data
#
# 4. PYDANTIC FIELD CONSTRAINTS
#    Both versions support Field() with:
#    - ge/le for numeric ranges
#    - pattern for string regex validation
#    - description for LLM guidance
#    These constraints help the LLM generate valid responses
#
# 5. SDK ENHANCEMENT OPPORTUNITY
#    The v0.x delete_existing=True pattern is useful for iteration.
#    Consider adding criterion_set.clear() or criterion_set.replace_criteria()
#    for easier iteration during development.
#


if __name__ == "__main__":
    main()

  1. Schema definieren: Erstellen Sie ein Pydantic-Modell mit Feldbeschreibungen und grundlegenden Constraints, um die exakte JSON-Struktur zu definieren, die das LLM zurückgeben soll

  2. Template erstellen: Verwenden Sie den Parameter response_format beim Erstellen eines Prompt-Templates, um anzugeben, dass Antworten Ihrer Pydantic-Modellstruktur folgen sollen

  3. Kriterien hinzufügen: Definieren Sie Evaluierungskriterien, die sich auf spezifische Schema-Felder beziehen - Kriterien können auch wie gewohnt automatisch generiert werden

  4. Experiment ausführen: Erstellen und führen Sie Experimente normal aus - das strukturierte Ausgabeformat wird automatisch für alle Antwortgenerierungen angewendet

  5. Auf Antworten zugreifen: Die structured Outputs finden sich im content-Feld der Assistant-Nachricht als JSON-String

Schema-Definitionsmethoden

Pydantic-Modelle

Pydantic-Modelle bieten die intuitivste und empfohlene Art, Schemas für structured Outputs zu definieren. Setzen Sie einfach response_format auf die Pydantic-Klassendefinition, und elluminate übernimmt den Rest.

OpenAI JSON Schema Format

Zusätzlich zu Pydantic-Modellen können Sie auch das response_format direkt mit einer OpenAI JSON Schema-Definition setzen:

schema = {
    "type": "json_schema",
    "json_schema": {
        "name": "sentiment",
        "schema": {
            "type": "object",
            "properties": {
                "stars": {
                    "type": "integer",
                    "description": "Anzahl der Sterne der Bewertung",
                    "minimum": 1,
                    "maximum": 5
                },
                "sentiment": {
                    "type": "string",
                    "description": "Die Stimmungsausgabe, könnte positiv, negativ oder neutral sein.",
                    "enum": [
                        "positive",
                        "negative",
                        "neutral"
                    ]
                },
                "confidence": {
                    "type": "number",
                    "description": "Konfidenzwert der Stimmungsanalyse zwischen 0 und 1",
                    "minimum": 0,
                    "maximum": 1
                }
            },
            "required": [
                "stars",
                "sentiment",
                "confidence"
            ],
            "additionalProperties": False
        }
    }
}

KI-gestützte Schema-Generierung

Schema-Generierungsdialog

Das Frontend bietet einen KI-gestützten Schema-Generator, der JSON-Schemas aus natürlichsprachlichen Beschreibungen erstellt. Beschreiben Sie einfach, was Sie extrahieren möchten, und elluminate generiert ein entsprechendes Schema.

Evaluierung von structured Outputs

Das Rating-Modell hat Zugriff auf alle Feldbeschreibungen aus Ihrem Schema für structured Outputs und bietet dadurch wertvollen Kontext darüber, was jedes Feld enthalten sollte und wie es interpretiert werden sollte. Um structured Outputs zu evaluieren, erstellen Sie einfach wie gewohnt Kriterien und führen ein Experiment aus.

Verwendung von Feldnamen in Kriterien

Es kann vorteilhaft sein, Feldnamen aus Ihrem Schema in den Kriterien zu verwenden. Dies hilft dem Rating-Modell zu verstehen, auf welchen Teil der JSON-Struktur es sich genau konzentrieren soll. Zum Beispiel ist "Ist das 'sentiment'-Feld..." präziser als "Ist die Stimmung korrekt?"