[!NOTE] Automatische vertaling Dit artikel is automatisch vertaald vanuit de oorspronkelijke Engelse versie.
Schema-gestuurde Reasoning op vLLM: Structured Outputs met xgrammar en Pydantic
Het opnieuw uitvoeren van een LLM-aanroep garandeert geen geldige JSON. Het volgende voorbeeld kan op dezelfde manier mislukken, en herhaalde aanroepen leiden tot meer latency en extra kosten.
Schema-gestuurde Reasoning (SGR) zorgt ervoor dat een schema wordt nageleefd, terwijl de model elke token genereert. Je definieert de vereiste velden met Pydantic, en de inference-engine blokkeert tokens-invoer die deze structuur schendt. Het resultaat is van nature syntactisch geldig, zonder dat er opnieuw geprobeerd hoeft te worden.
Kort samengevat. SGR maakt gebruik van constrained decoding om de uitvoer van een LLM vast te leggen aan een door jou beheerd Pydantic-schema. In combinatie met het xgrammar backend van vLLM krijg je op deze manier steeds geldige JSON-objecten, met een verwaarloosbaar latency-overhead.
Wat is Schema-Guided Reasoning?
Schema-gestuurde Reasoning is een techniek die Rinat Abdullin geschreven in 2024. In plaats van model toe te staan tekst vrijelijk te voltooien (wat onconsistent of ambigu kan zijn), geeft u het een strikt sjabloon dat het volgende definiert:
- Welke stappen het moet doorlopen, zodat de analyse niet kan worden overgeslagen
- De volgorde van deze stappen, zodat de reasoning van gegevens naar beslissing werkt
- Waar het zich moet richten op attention, zodat de diepte precies daar wordt toegepast waar het belangrijk is
Zie het maar als een cognitieve controlelijst die de model moet volgen.
Wat het schema beheerst
Velden zoals churn_analysis, margin_math, en max_discount_percent Zorg ervoor dat de verwachte tussentijdse uitvoer duidelijk wordt weergegeven. De model moet de vereiste structuur hebben gevuld voordat het een definitieve beslissing over de korting kan nemen.
Dat geeft u:
- reproduceerbare reasoning uitvoeringen bij herhaalde runs
- controleerbare uitvoerwaarden waarbij elke stap kan worden geïnspecteerd
- tussentijdse velden die kunnen worden beoordeeld tegen een test dataset
- kleinere models structuren die bruikbaar worden, aangezien het schema bepaalt wat de model anders zou moeten leren
- een gemelde verbetering van de nauwkeurigheid met 5–10% de oorspronkelijke productiewerklasten van de praktiserende ontwikkelaar, wat u zelf moet controleren aan de hand van uw eigen gegevens
SGR versus Chain of Thought versus prompt engineering
De drie benaderingen verschillen voornamelijk in de mate waarin ze model beperken.
| Functie | Prompt Engineering | Chain of Thought | Schema-gestuurde Reasoning |
|---|---|---|---|
| Uitvoerstructuur | Variabele tekst | Vrije tekstvorm | Stijve JSON/Pydantic |
| Beheermechanisme | Semantische overtuiging (“Geef alstublieft JSON uit”) | Heuristisch prompting (“Laten we stap voor stap nadenken”) | Constrained decoding (op grammatica gebaseerd) |
| Reasoning Stroom | Model bepaalt | Model bepaalt | De ontwikkelaar bepaalt de (schema-topologie). |
| Auditeerbaarheid | Laag (vereist parsing) | Laag (vereist lezen van de tekst) | Hoog (inspectie op veldniveau) |
| Integratie | Moeilijk (regex-parsing) | Moeilijk (variabele vorm) | Triviaal (deserialisatie van native objecten) |
| Foutkans | Hoog (variatie in format) | Modereren (hallucination van het formaat) | |
| Model Vereiste | Sterke instructievolgging | Sterke reasoning-functionaliteit | Werkt ook met kleinere models. |
Prompt engineering: semantische overtuiging
Please analyze the customer data and output your response as valid JSON
with the following structure: {"discount": <number>, "reason": <string>}
Be careful with the formatting!
Je hoopt dat het begrip van model voor “output JSON” sterker is dan zijn neiging om op een conversatieve manier te reageren. Een update van model, een verandering in de temperature‑waarde, of een ander paar-shot voorbeeld kan je parser beschadigen.
Denkketen: betere reasoning, dezelfde structuurprobleem
Let's think step by step:
1. First, I'll analyze the customer's churn risk...
2. Then I'll calculate the margin...
3. Therefore, I recommend a 15% discount.
CoT verbetert de nauwkeurigheid van reasoning, maar verslechtert daardoor de structuur. Het resultaat is onvoorspelbaar proza dat bijna onmogelijk betrouwbaar te parseren is. Meestal moet men een tweede aanroep van LLM uitvoeren om de gestructureerde gegevens te extraheren.
SGR: gestructureerde denkketen
SGR behoudt de intuïtie van CoT dat tussentijdse reasoning-verwerking de nauwkeurigheid verbetert. Het formaliseert slechts de stappen:
class PricingLogic(BaseModel):
# 1. Data Analysis (must complete before decision)
churn_analysis: str = Field(..., description="Analyze churn_probability")
financial_analysis: str = Field(..., description="Analyze cart_value and margin")
# 2. Math Enforcement (explicit calculation)
margin_math: str = Field(..., description="Calculate: 'Cart $X * Y% = $Z'")
# 3. Decision Constraint (bounded by prior analysis)
max_discount_percent: float = Field(..., description="Max allowed discount")
# 4. Final Output
offer_code: str
customer_message: str
De model kan geen uitvoer genereren. max_discount_percent totdat churn_analysis, financial_analysis, en margin_math Ze worden gevuld. Het schema handhaaft de volgorde van reasoning.
SGR patronen
SGR beschikt over drie kernpatronen die samen een groter workflows vormen.
1. Cascade: sequentiële reasoning stappen
Cascade zorgt voor een reasoning volgorde. Elk veld moet worden ingevuld voordat het volgende kan worden verwerkt.
from pydantic import BaseModel
from typing import Literal, Annotated
from annotated_types import Ge, Le
class CandidateEvaluation(BaseModel):
"""Evaluate a job candidate with enforced reasoning order."""
# Step 1: Summarize (forces context awareness)
brief_candidate_summary: str
# Step 2: Rate (bounded integer)
rate_skill_match: Annotated[int, Ge(1), Le(10)]
# Step 3: Decide (constrained choices)
final_recommendation: Literal["hire", "reject", "hold"]
Goede toepassingen: beoordeling van kandidaten, classificatie van documenten, analyse van conformiteit, medische diagnostiek.
De model moet schrijven brief_candidate_summary Voor het een beoordeling kan uitvoeren, moet het eerst beoordeeld worden, en voordat het kan aanbevelen, moet het eerst beoordeeld zijn. Er is geen snellere weg.
2. Routing: een semantische switch-instructie
Routing zorgt ervoor dat de model zich committ aan één specifieke route uit een reeks opties, waarbij dit wordt gerealiseerd met Union typen.
from pydantic import BaseModel
from typing import Literal, Union
class FeatureLookup(BaseModel):
"""Route to database lookup."""
rationale: str
tool_name: Literal["fetch_user_features"] = "fetch_user_features"
user_id: str
class GeneralResponse(BaseModel):
"""Standard response for non-pricing queries."""
tool_name: Literal["respond"] = "respond"
content: str
class RouterSchema(BaseModel):
"""The model must pick exactly ONE branch."""
action: Union[FeatureLookup, GeneralResponse]
Goede toepassingen: intentclassificatie, keuze van hulpmiddelen, triaging van ondersteuning, en dispatch van multi-agent.
De Literal discriminator (tool_name) zorgt ervoor dat de model één specifieke branch kiest en alleen de velden invult die door die branch vereist zijn.
3. Cyclus: herhaalde reasoning met lijsten
Cycle zorgt ertoe dat de model meerdere elementen genereert, waarbij er grenzen zijn aan het aantal.
from pydantic import BaseModel
from typing import List, Literal, Annotated
from annotated_types import MinLen, MaxLen
class RiskFactor(BaseModel):
explanation: str
severity: Literal["low", "medium", "high"]
class RiskAssessment(BaseModel):
"""Generate 2-4 risk factors."""
factors: Annotated[List[RiskFactor], MinLen(2), MaxLen(4)]
Goede toepassingen zijn: risicobeoordeling, probleemextractie, parallelle tool calls uitvoeringen, en meestappige planning processen.
De MinLen en MaxLen De grenzen vereisen ten minste 2 en ten hoogste 4 elementen. In combinatie met Routing is dit de manier om een batch met vaste breedte van tool calls te verzenden.
Het laten werken van SGR: constrained decoding
De bovenstaande patronen zijn slechts Pydantic-schemas. Wat ze bindend maakt, is constrained decoding (ook wel Structured Output genoemd).
Constrained decoding wijzigt de generatiestap van token. In plaats van het model-voorbeeld vrijelijk toegang te geven tot zijn woordenschat, past de engine een grammaticamasker toe dat tokens blokkeert die het schema zouden schenden. Dit vindt plaats in de inference engine, en niet in uw applicatiecode.
[!TIP] SGR heeft geen behoefte aan “reasoning models”-modellen zoals o1 of DeepSeek-R1. Het functioneert uitstekend met instructie-getunede models-modellen, en vooral goed met models-modellen die zijn geconcentreerd op basis van reasoning-modellen.
Cloudaanbieders die dit ondersteunen
De meeste moderne LLM-aanbieders bieden structured outputs via constrained decoding aan:
| Provider | Ondersteuning |
|---|---|
| OpenAI | Structured Outputs (inclusief Azure). GPT-5 maakt gebruik van JSON Schema via llguidance |
| Google/Gemini | JSON Schema Ondersteuning vanaf november 2025 (Pydantic en Zod) |
| Mistral | Aanpasbare Structured Output |
| Grok | Structured Outputs voor meerdere models |
| Fireworks AI | JSON Schema |
| Cerebras | Structured Outputs |
| OpenRouter | Hangt af van de downstream-provider en komt overeen met JSON Schema |
Inference-motoren die dit ondersteunen
Voor zelf gehoste models-systemen beschikken alle belangrijkste engines over een constrained decoding backend:
| Motor | Backend |
|---|---|
| vLLM | xgrammar of richtlijnen |
| SGLang | Overzichten, XGrammar, of llguidance |
| TensorRT-LLM | GeleideDecoding |
| Ollama | Structured Outputs |
Waarom dit artikel zich richt op vLLM en xgrammar
Enkele redenen:
- vLLM is de meest wijdverspreide open-source LLM inference engine, waardoor ontwikkelde oplossingen hier gemakkelijk kunnen worden geport.
- xgrammar is geschreven in C++ en veroorzaakt nauwelijks extra latency.
- De API van vLLM is compatibel met OpenAI, waardoor migraties naar andere cloudproviders relatief goedkoop blijven.
- xgrammar kan complexe, geneste schema’s, unions en recursieve structuren verwerken.
In het volgende gedeelte wordt uitgelegd hoe xgrammar op het niveau van token daadwerkelijk een schema oplegt.
Hoe xgrammar schema’s afdraagt
Het is belangrijk om dit onderdeel nauwkeurig te begrijpen, aangezien het de manier waarop je SGR workflows deployt en afstelt, verandert.
Waar het maskeren plaatsvindt
xgrammar wijzigt de uitvoerlogits na de forward pass van het model en vóór het samplen. Het verandert het model zelf niet; in plaats daarvan filtreert het welke tokens geselecteerd mogen worden.
Een standaard inference-lus ziet er als volgt uit:
1. Input tokens → GPU Forward Pass → Logits (probability scores for all ~128K tokens)
2. Logits → Sampling (temperature, top-p, etc.) → Next Token
3. Repeat until done
xgrammar glipt weg tussen stap 1 en stap 2:
1. Input tokens → GPU Forward Pass → Raw Logits
2. Raw Logits → xgrammar Logits Processor → Masked Logits
3. Masked Logits → Sampling → Next Token (guaranteed valid)
4. Repeat until done
De model berekent nog steeds zijn volledige waarschijnheidsverdeling op de GPU. Vervolgens wordt xgrammar uitgevoerd op de CPU en wordt er een bitmask toegepast op die logits voordat er wordt gesampled. Ongeldige tokens hebben hun logits ingesteld op -∞wat ervoor zorgt dat hun waarschijnlijkheid na softmax precies 0 wordt.
Twee fasen
xgrammar splitst het werk op in een compilatietijdfase en runtime, en dat is wat ervoor zorgt dat het snel werkt.
Fase 1: compilatie van de grammatica, één keer per schema
# This happens once per schema
tokenizer_info = xgr.TokenizerInfo.from_huggingface(tokenizer)
grammar_compiler = xgr.GrammarCompiler(tokenizer_info)
compiled_grammar = grammar_compiler.compile_json_schema(schema_json)
Tijdens de compilatie, xgrammar:
- Zet de JSON Schema om in een contextvrije grammatica.
- Maak een pushdownautomaat (PDA) aan, die een state machine is met een stapel zodat deze genestte structuren kan verwerken.
{"a": {"b": {"c": ...}}}. - Er wordt van tevoren berekend welke tokens geldig zijn op elke grammaticale positie. Het resultaat vormt de “adaptieve token maskercache”.
- tokens worden geclassificeerd als “contextonafhankelijk” (cachébaar) of “contextafhankelijk” (moeten op runtime worden gecontroleerd tegen de toestand van de stack).
[!NOTE] Ongeveer 99% van tokens blijkt contextonafhankelijk te zijn en wordt uiteindelijk opgeslagen in de cache.Artikel over XGrammar). De meeste validatiecontroles bij runtime zijn slechts cacheopvragen, waardoor xgrammar zo snel is.
Fase 2: generatie van de runtime-maskers, bij elke token
Bij elke generatiestap:
- De
GrammarMatcherHet houdt de huidige positie in de grammatica bij. - Het zoekt het van tevoren berekende masker op voor contextonafhankelijke tokens.
- Het voert de PDA uit om de overgebleven contextafhankelijke tokens te controleren.
- Het combineert deze resultaten tot een eindmasker en past dit toe op de logits.
Waarom pushdownautomaten en niet regex?
Vanwege het nesten. Een reguliere expressie (een eindige state machine) kan structuren als deze niet betrouwbaar matchen:
{ "user": { "profile": { "settings": { "theme": "dark" } } } }
Het moeilijke deel zijn de sluitende haakjes. }}}: Je moet onthouden hoeveel instellingen je hebt geopend. Een pushdownautomaat beschikt over een stapel die dit bijhoudt, zodat hij een willekeurige diepte van nesting kan verwerken. Dit is ook de reden waarom xgrammar Unietypen, genest objecten en recursieve schema’s kan doorvoeren, terwijl regex-gebaseerde aanpakken hier niet toe in staat zijn.
Een concreet voorbeeld: het genereren van een float-veld
Wanneer de model wordt gegenereerd "max_discount_percent":, xgrammar weet uit het schema dat een float Hier volgt de masker:
- staat dit toe (waarschijnlijkheid onveranderd):
0,1,2, …,9,.,- - blokken (waarschijnlijkheid ingesteld op 0):
",{,[,true,false,nullen de rest van het vocabulaire van meer dan 128K woorden
De forward pass heeft mogelijk een hoge waarschijnlijkheid toegewezen aan het woord. "fifteen"Na de maskering door xgrammar heeft dat token een waarschijnlijkheid van 0. model moet cijfers uitvoeren.
Waarom “bijna-nul overhead”
Drie redenen:
- Parallelle uitvoering. De berekening van het masker op de CPU overlapt met de volgende forward pass op de GPU. Terwijl de GPU logits voor token N+1 berekent, wordt het masker voor token N gegenereerd door de CPU.
- Caching. Het grootste deel van de validatiewerkzaamheden vindt tijdens de compilatie plaats. Runtime bestaat voornamelijk uit cacheopvragen.
- C++-implementatie. De belastingspath is in C++, en niet in Python, waarbij het masker rechtstreeks op de logits wordt toegepast.
Bij benchmarks levert xgrammar een verwaarloosbaar overhead op, en gestructureerde generatie kan af en toe sneller zijn dan onbeperkte generatie, omdat het beperkte woordenschat de sampling-kosten verlaagt.
Praktische implementatie met vLLM
De referentie is de sgr – discount-manager een project, een kleine demo die SGR gebruikt voor dynamische prijsbepaling.
Projectstructuur
sgr/
├── agent.py # Main orchestration
├── models/
│ └── schemas.py # Pydantic SGR schemas
├── prompts/
│ ├── routing.py # Phase 1 prompts
│ └── pricing.py # Phase 3 prompts
├── store/
│ └── hybrid_store.py # Hot/Cold data retrieval
└── utils/
└── llm_client.py # LLM client wrapper with xgrammar
Stap 1: definieer de schema’s
# sgr/models/schemas.py
from pydantic import BaseModel, Field
from typing import Literal, Union
# --- Phase 1: Routing (Union for branching) ---
class FeatureLookup(BaseModel):
"""Route to DB lookup if pricing context is needed."""
rationale: str
tool_name: Literal["fetch_user_features"] = "fetch_user_features"
user_id: str
class GeneralResponse(BaseModel):
"""Standard response for non-pricing queries."""
tool_name: Literal["respond"] = "respond"
content: str
class RouterSchema(BaseModel):
action: Union[FeatureLookup, GeneralResponse]
# --- Phase 2: Pricing Logic (Cascade for sequential reasoning) ---
class PricingLogic(BaseModel):
"""
Strict reasoning topology for dynamic pricing.
Fields are ordered to enforce the analysis→decision flow.
"""
# 1. Data Analysis (Reflection)
churn_analysis: str = Field(...,
description="Analyze churn_probability (High > 0.7).")
financial_analysis: str = Field(...,
description="Analyze cart_value and profit_margin.")
# 2. Hard Math Enforcement
margin_math: str = Field(...,
description="Calculate absolute profit: 'Cart $200 * 0.20 Margin = $40'.")
# 3. The Decision Constraint
max_discount_percent: float = Field(...,
description="Max allowed discount %. NEVER exceed margin.")
# 4. Final Output
offer_code: str = Field(..., description="Generated code (e.g. SAVE20).")
customer_message: str = Field(..., description="The final polite offer text.")
Stap 2: een LLM-client die xgrammar activeert
# sgr/utils/llm_client.py
from openai import OpenAI
from pydantic import BaseModel
from typing import TypeVar
import json
T = TypeVar("T", bound=BaseModel)
class LLMClient:
"""Wrapper for vLLM with xgrammar-enforced structured generation."""
def __init__(self, base_url: str = "http://localhost:8000/v1"):
self.client = OpenAI(base_url=base_url, api_key="EMPTY")
self.model = self._get_available_model()
def _get_available_model(self) -> str:
"""Auto-detect the model running on vLLM server."""
try:
models = self.client.models.list()
if models.data:
return models.data[0].id
except Exception:
pass
return "Qwen/Qwen2.5-7B-Instruct"
def run_sgr(self, messages: list[dict], schema_class: type[T]) -> T:
"""Run inference with Schema-Guided Response constraints.
Uses vLLM's guided_json with xgrammar backend to enforce
strict schema constraints at the token generation level.
"""
schema_dict = schema_class.model_json_schema()
# Enhance system message with schema for model guidance
enhanced_messages = messages.copy()
if enhanced_messages and enhanced_messages[0]["role"] == "system":
schema_json = json.dumps(schema_dict, indent=2)
enhanced_messages[0] = {
"role": "system",
"content": (
enhanced_messages[0]["content"]
+ f"\n\nRespond with JSON matching this schema:\n{schema_json}"
),
}
# vLLM's guided_json with the xgrammar backend
completion = self.client.chat.completions.create(
model=self.model,
messages=enhanced_messages,
temperature=0.1, # Low temp for deterministic reasoning
extra_body={
"guided_json": schema_dict, # Pydantic schema as dict
"guided_decoding_backend": "xgrammar", # Hardware-enforced
},
)
raw_response = completion.choices[0].message.content
return schema_class.model_validate_json(raw_response)
[!NOTE] >
guided_jsonAccepteert een JSON Schema-dict. Metguided_decoding_backend: "xgrammar"De LLM kan alleen tokens genereren die voldoen aan de geldende JSON-specificaties van uw schema.
Stap 3: orkestreren van de agent
# sgr/agent.py
from .models.schemas import PricingLogic, RouterSchema
from .prompts.routing import build_routing_prompt
from .prompts.pricing import build_pricing_context_prompt, ASSISTANT_FETCH_MESSAGE
from .store.hybrid_store import HybridFeatureStore
from .utils.llm_client import LLMClient
def pricing_agent(user_query: str, user_id: str) -> str:
"""Process a pricing query with three-phase SGR workflow."""
llm = LLMClient()
feature_store = HybridFeatureStore()
# Build conversation history
history = [
{"role": "system", "content": build_routing_prompt(user_id)},
{"role": "user", "content": user_query},
]
# --- Phase 1: Routing (Uses RouterSchema) ---
print(f"🤖 Processing: '{user_query}' for {user_id}")
decision = llm.run_sgr(history, RouterSchema)
print(f"📍 Routing decision: {decision.action.tool_name}")
if decision.action.tool_name == "respond":
return decision.action.content
# --- Phase 2: Context Retrieval ---
if decision.action.tool_name == "fetch_user_features":
print(f"🔍 Fetching features for {user_id}...")
context = feature_store.get_user_context(user_id)
if not context:
return "Error: User profile not found."
print(f" [Data] LTV: ${context.get('user_ltv')} | "
f"Margin: {context.get('cart_profit_margin', 0) * 100}%")
# Inject context into conversation
history.append({"role": "assistant", "content": ASSISTANT_FETCH_MESSAGE})
history.append({
"role": "user",
"content": build_pricing_context_prompt(
churn_prob=context.get("churn_probability", 0.5),
cart_val=context.get("current_cart_value", 100),
margin=context.get("cart_profit_margin", 0.2),
user_ltv=context.get("user_ltv", 0),
),
})
# --- Phase 3: SGR Logic Execution (Uses PricingLogic) ---
print("🧠 Calculating Offer (Schema Enforced)...")
offer = llm.run_sgr(history, PricingLogic)
# Audit log — the SGR benefit: explicit reasoning traces
print(f" [Audit] Math: {offer.margin_math}")
print(f" [Audit] Max Allowed: {offer.max_discount_percent}%")
return offer.customer_message
return "I'm sorry, I couldn't process your request."
if __name__ == "__main__":
response = pricing_agent("I want a discount or I'm leaving!", "user_102")
print(f"\n💬 Final Reply: {response}")
Stap 4: voer vLLM uit met xgrammar
# Start vLLM server with xgrammar backend (default in recent versions)
python -m vllm.entrypoints.openai.api_server \
--model Qwen/Qwen2.5-7B-Instruct \
--port 8000
# Run the agent
uv run python -m sgr.agent
Voorbeelduitvoer
🤖 Processing: 'I want a discount or I'm leaving!' for user_102
📍 Routing decision: fetch_user_features
🔍 Fetching features for user_102...
[Data] LTV: $1,500 | Margin: 20%
🧠 Calculating Offer (Schema Enforced)...
[Audit] Math: Cart $200 * 0.20 Margin = $40
[Audit] Max Allowed: 15.0%
💬 Final Reply: We value your loyalty! Here's a special 15% discount
with code SAVE15. This reflects our appreciation for your continued
business with us.
Het auditlog laat het daadwerkelijke werk van de model zien: het berekende de marge (40 dollar voor een winkelwagen ter waarde van 200 dollar bij een marge van 20%) en beperkte de korting zodat het aanbod binnen de winstvereisten bleef.
Beste praktijken
Schemaontwerp
- Sorteer de velden op basis van de reasoning-stroom. Analysevelden komen voor decisionvelden.
- Schrijf beschrijvende tekst.
FieldBeschrijvingen. Zij sturen de attention van de model evenzeer aan als de veldnaam dat doet. - Beperk met
LiteralenAnnotated. GebruikLiteral["a", "b"]voor enumeraties enAnnotated[int, Ge(1), Le(10)]voor de grenzen. - Houd de schema’s gefocust. Eén schema per reasoning fase, en combineer ze vervolgens met meerdere oproepen.
vLLM configuratie
- Gebruik een lage temperatuur (0,1–0,3) voor deterministische reasoning-verwerking.
- Laat xgrammar de structuur beheren; probeer deze niet te beïnvloeden met opmaak instructies in prompt.
- Houd de gebruiksintensiteit van token in de gaten. SGR maakt doorgaans minder tokens-instellingen nodig dan CoT, omdat er geen uitgebreide beschrijvingen aanwezig zijn.
Overwegingen voor productieomgevingen
- Versieer je schema’s op dezelfde manier als je APIs versieert.
- Zelfs met SGR moeten netwerk- en serverfouten nog steeds op een soepele manier worden afgehandeld.
- Logeer de ruwe uitvoer van SGR voor compliancedoeleinden en bij het oplossen van problemen.
- Test op uiterste gevallen zodat het schema ook aan de randvoorwaarden voldoet.
Conclusie
SGR is wat ervoor zorgt dat iets niet langer alleen in een demo omgeving functioneert, maar ook daadwerkelijk in de productieomgeving werkt. Je definieert de reasoning topologie in Pydantic, laat xgrammar deze op het juiste moment controleren via decode, en het resultaat is:
- Altijd geldig, zonder herhalingscycli of parsingfouten
- Auditbaar op het veldniveau
- Te gebruiken met kleinere models, omdat ze zich niet langer zelf hoeven te concentreren op het formaat
- Goedkoper in uitvoering, aangezien er minder tokens worden gebruikt, minder herhalingen nodig zijn en de models kleiner zijn
De sgr – kortingbeheerder Demo Wire verbindt elk codevoorbeeld uit deze post met een echte vLLM-server. Cloneer deze server en begin de schema’s aan te passen aan uw eigen workflow.
Belangrijkste conclusies
- Schema-gestuurde Reasoning maakt de reasoning-topologie expliciet, in plaats van te verwachten dat de model de beschreven instructies opvolgt.
- Constrained decoding voorkomt ongeldige JSON-waarden al tijdens het genereren, wat efficiënter is dan deze later nog te valideren en opnieuw te proberen.
- Plaats analysevelden vóór beslissingsvelden wanneer het schema vereist dat reasoning eerst wordt uitgevoerd voordat er output wordt gegenereerd.
- Gebruik SGR wanneer downstream-code afhankelijk is van een specifieke structuur, en niet wanneer het eindresultaat bestaat uit vrije tekst.
Referenties
SGR Framework
- Schema-gestuurde Reasoning (SGR) — Het originele framework van Rinat Abdullin
- SGR Patronen — Cascade, Routing, cyclische patronen
xgrammar
- XGrammar: Een flexibele en efficiënte engine voor gestructureerde generatie van grote taalmodellen Models — Yixin Dong e.a., arXiv:2411.15100 (technisch artikel met benchmarks) xgrammar GitHub — Snelle, flexibele bibliotheek voor gestructureerde generatie
- xgrammar Documentatie — Officiële documentatie met een snelstartgids xgrammar Snelle start — Een introductie in xgrammar
- Efficiënte gestructureerde generatie bereiken met XGrammar — MLC-blogpost over de interne werking van xgrammar
vLLM
- vLLM Structured Outputs — Officiële documentatie
Demo-project
- sgr – kortingbeheerder — Werkende demo met alle codevoorbeelden uit deze post