Optimizarea este scumpă, iar rezultatul ei este un artefact portabil. Fluxul din documentație este: compilezi o dată pentru o versiune a programului, salvezi, iar în producție doar încarci. Sunt două moduri. Doar starea: program.save("program.json") scrie, pentru fiecare predictor, demonstrațiile, instrucțiunile și descrierile câmpurilor, plus versiunile Python, DSPy și cloudpickle. Fișierul JSON este mic, lizibil și ușor de comparat în git. Pentru a-l folosi, construiești programul la fel ca la salvare și apelezi program.load("program.json").
Programul întreg: program.save("dir/", save_program=True) scrie în director program.pkl (cloudpickle) și metadata.json; se încarcă cu dspy.load("dir/", allow_pickle=True), fără să ai clasa programului în cod. Fără allow_pickle=True, dspy.load ridică ValueError, pentru că încărcarea unui pickle poate executa cod arbitrar. Tot pickle (.pkl în loc de .json) se folosește când starea conține obiecte care nu se pot scrie în JSON, ca dspy.Image.
Cheile API nu se salvează niciodată. Un predictor salvează configurația modelului doar dacă are un LM propriu; la încărcare, api_base, base_url și model_list sunt eliminate, cu excepția cazului allow_unsafe_lm_state=True. Configurează deci modelul la pornirea aplicației. O diferență de versiune DSPy între salvare și încărcare produce doar un avertisment.
Concepte cheie și caracteristici
| Concept / Parametru | Descriere practică |
|---|---|
save(path, save_program=False, modules_to_serialize=None) |
.json sau .pkl: doar starea. Cu save_program=True, path este un director. |
load(path, allow_pickle=False, allow_unsafe_lm_state=False) |
Încarcă starea într-un program existent; pentru .pkl cere allow_pickle=True. |
dspy.load(path, allow_pickle=False) |
Încarcă un program salvat cu save_program=True; cere allow_pickle=True. |
modules_to_serialize |
Module Python ale căror clase se includ prin valoare în pickle, când procesul care încarcă nu le poate importa. |
allow_unsafe_lm_state |
Păstrează api_base, base_url și model_list din starea salvată. |
dump_state() / load_state() |
Dicționarul de stare din spatele save / load. |
Exemple practice și scenarii reale
Exemplul 1: Salvarea după optimizare
optimizat = optimizator.compile(program, trainset=train)
optimizat.save("artefacte/asistent_suport_v1.json")
Exemplul 2: Încărcarea într-o aplicație FastAPI
După tutorialul de deployment: programul se încarcă o singură dată, la pornire, iar dspy.asyncify îl rulează în fire separate.
from fastapi import FastAPI
from pydantic import BaseModel
import dspy
dspy.configure(lm=dspy.LM("openai/gpt-6-luna"), async_max_workers=4)
asistent = dspy.ChainOfThought("intrebare_client -> solutie_tehnica")
asistent.load("artefacte/asistent_suport_v1.json")
asistent_async = dspy.asyncify(asistent)
app = FastAPI()
class Cerere(BaseModel):
intrebare: str
@app.post("/asistenta")
async def raspunde(cerere: Cerere):
rez = await asistent_async(intrebare_client=cerere.intrebare)
return {"raspuns": rez.solutie_tehnica}
Exemplul 3: Programul întreg, fără clasa lui în codul care încarcă
optimizat.save("artefacte/asistent_v1/", save_program=True)
# în alt proces
import dspy
dspy.configure(lm=dspy.LM("openai/gpt-6-luna"))
asistent = dspy.load("artefacte/asistent_v1/", allow_pickle=True)
Greșeli frecvente și capcane
- Încarci un JSON într-un program construit altfel (alte module sau alte câmpuri decât la salvare). Construiește programul exact ca înainte de
save. - Încarci
.pklsau un directorsave_program=Truedintr-o sursă necunoscută. Pickle poate executa cod; preferă JSON-ul. - Te bazezi pe modelul salvat în fișier. Cheile API nu se salvează, iar adresele de server sunt eliminate implicit; configurează LM-ul la pornire.
- Compilezi programul la fiecare cerere HTTP sau la fiecare pornire. Compilarea se face offline; serverul doar încarcă fișierul.