Adaptorul este stratul dintre semnătură și model. Ciclul lui are cinci pași: pregătire (folosește funcții native ale modelului, ca apelul de unelte), formatare (mesajul de sistem cu descrierea câmpurilor și instrucțiunile, apoi exemplele și intrările), apelul la model, post-procesare și parsare. Parsarea convertește fiecare câmp în tipul declarat cu Pydantic, deci ieșirile pot fi bool, float, Literal, list[str], modele Pydantic sau tipuri DSPy.
ChatAdapter delimitează câmpurile cu marcaje [[ ## nume ## ]] și, implicit, repetă cererea prin JSONAdapter dacă răspunsul nu poate fi parsat. JSONAdapter cere un obiect JSON și folosește, când modelul îl acceptă, modul de ieșire structurată response_format cu schemă JSON. XMLAdapter folosește etichete <camp>...</camp>. TwoStepAdapter lasă modelul principal să răspundă liber și folosește un al doilea model pentru extragerea câmpurilor.
Adaptorul implicit diferă între cod și documentație: în dspy 3.4.1 (PyPI), Predict folosește ChatAdapter dacă nu ai configurat altul, iar documentația de pe dspy.ai, construită din ramura principală, descrie deja XMLAdapter(use_native_function_calling=True) ca implicit. Dacă formatul contează, setează adaptorul explicit cu dspy.configure(adapter=...) sau dspy.context(adapter=...). DSPy nu alege automat adaptorul după model.
Concepte cheie și caracteristici
| Concept / Parametru | Descriere practică |
|---|---|
dspy.ChatAdapter(use_native_function_calling=False, use_json_adapter_fallback=True) |
Marcaje [[ ## camp ## ]]; la eroare de parsare reîncearcă o dată cu JSONAdapter. |
dspy.JSONAdapter(use_native_function_calling=True) |
Ieșire JSON; folosește ieșirea structurată a furnizorului când e disponibilă. |
dspy.XMLAdapter(...) |
Etichete XML pentru câmpuri, <item> pentru liste. |
dspy.TwoStepAdapter(extraction_model) |
Două apeluri: răspuns liber, apoi extragerea câmpurilor cu extraction_model. |
dspy.Image |
Intrare de tip imagine: URL, data URI, bytes sau PIL. Fișierele locale se citesc doar cu dspy.Image.from_path(), iar descărcarea se face cu from_url(). |
dspy.History |
Istoricul conversației, ca listă de dicționare cu cheile câmpurilor din semnătură; adaptorul îl transformă în mesaje separate. |
dspy.Reasoning |
Câmp de raționament care folosește modul nativ de raționament al modelului, dacă există. |
Exemple practice și scenarii reale
Exemplul 1: Ieșire structurată cu un model Pydantic
import dspy
from pydantic import BaseModel
class Factura(BaseModel):
furnizor: str
total: float
moneda: str
class ExtrageFactura(dspy.Signature):
"""Extrage datele facturii din text."""
text: str = dspy.InputField()
factura: Factura = dspy.OutputField()
dspy.configure(lm=dspy.LM("openai/gpt-6-luna"), adapter=dspy.JSONAdapter())
extrage = dspy.Predict(ExtrageFactura)
rez = extrage(text="Factura nr. 381 de la Alfa SRL, total de plată 1.250,40 lei.")
print(rez.factura.total)
Exemplul 2: Alt adaptor doar pentru un bloc și verificarea promptului
with dspy.context(adapter=dspy.XMLAdapter()):
rez = extrage(text="...")
dspy.inspect_history(n=1) # mesajele exacte trimise modelului
Exemplul 3: Imagine ca intrare
descrie = dspy.Predict("imagine: dspy.Image -> descriere")
rez = descrie(imagine=dspy.Image.from_path("bon.jpg"))
print(rez.descriere)
Exemplul 4: Conversație cu dspy.History
După tutorialul oficial: fiecare tură adaugă în istoric intrările și ieșirile ei.
class QA(dspy.Signature):
question: str = dspy.InputField()
history: dspy.History = dspy.InputField()
answer: str = dspy.OutputField()
predict = dspy.Predict(QA)
history = dspy.History(messages=[])
for question in ["Ce este TCP?", "Și prin ce diferă de UDP?"]:
outputs = predict(question=question, history=history)
history.messages.append({"question": question, **outputs})
Greșeli frecvente și capcane
- Scrii
dspy.Image("poza.jpg")ca în codul vechi. Din DSPy 3.3, constructorul nu mai citește fișiere și nu mai descarcă URL-uri; foloseșteImage.from_path()sauImage.from_url(). - Nu observi că o eroare de parsare dublează apelurile.
ChatAdapterreîncearcă implicit cu JSONAdapter; în teste, seteazăuse_json_adapter_fallback=Falseca să vezi eroarea. - Presupui un anumit adaptor implicit. Versiunea publicată și documentația curentă diferă; setează adaptorul explicit.
- Folosești
TwoStepAdapterfără să socotești costul: fiecare predicție face două apeluri.