Jak sprawić, by sieć neuronowa generowała poprawny JSON bez obejść i ponownych prób
Każdy, kto próbował połączyć model językowy z prawdziwym backendem, napotkał ten problem. Piszesz szczegółowy prompt, prosisz model o zwrócenie ściśle określonego JSON-a z wymaganą strukturą, testujesz na dziesięciu przykładach — wszystko działa świetnie. Ale przy setnym żądaniu sieć neuronowa nagle zapomina zamknąć cudzysłów, dodaje na początku odpowiedzi frazę "Oto twoja odpowiedź!" lub wymyśla nieistniejące pole. W rezultacie Pydantic zgłasza błąd walidacji, a twój serwis pada.
Zazwyczaj programiści rozwiązują ten problem za pomocą ponownych prób wysłania żądania do API, złożonych wyrażeń regularnych lub prób "naprawienia" uszkodzonej odpowiedzi po jej wygenerowaniu. Twórcy z zespołu .txt poszli inną drogą i stworzyli Outlines — bibliotekę, która kieruje procesem generowania tekstu na poziomie pojedynczych tokenów.

Jak działa kontrolowana generacja
Większość frameworków traktuje LLM-y jako czarną skrzynkę: wysyłają tekst i czekają na gotowy ciąg znaków. Outlines przejmuje kontrolę podczas próbkowania. Na każdym etapie generacji biblioteka sprawdza, które tokeny ze słownika modelu są zgodne z określonym schematem, a które naruszają reguły.
Jeśli poprosisz o liczbę, biblioteka po prostu zeruje prawdopodobieństwo próbkowania dla wszystkich tokenów zawierających litery lub znaki interpunkcyjne. Model fizycznie nie może wybrać nieprawidłowego symbolu. Rezultatem nie jest nadzieja na poprawny JSON, lecz gwarantowana poprawna struktura od pierwszej próby.
To podejście oszczędza tokeny i czas. Nie musisz już prosić modelu w prompcie "nie dodawać dodatkowego tekstu" ani uruchamiać regeneracji, gdy parsowanie się nie powiedzie.
Co potrafi biblioteka
Interfejs Outlines stara się naśladować znajomą składnię typów Pythona. Po prostu przekazujesz żądany typ danych wraz z promptem.
Naprawianie opcji odpowiedzi
Jeśli potrzebujesz wyboru z ograniczonego zbioru wartości, zdefiniuj go przez Literal lub Enum. Model nie będzie pisać długich rozważań — natychmiast wyświetli jedną z określonych wartości.
import outlines
from typing import Literal
from transformers import AutoTokenizer, AutoModelForCausalLM
model_name = "microsoft/Phi-3-mini-4k-instruct"
model = outlines.from_transformers(
AutoModelForCausalLM.from_pretrained(model_name, device_map="auto"),
AutoTokenizer.from_pretrained(model_name)
)
# Модель вернет строго одно из трех слов
sentiment = model(
"Оцени тональность ответа: 'Сервис работает отлично, спасибо!'",
Literal["Positive", "Negative", "Neutral"]
)
print(sentiment) # Positive
Generacja na podstawie schematów Pydantic
W przypadku złożonych obiektów możesz użyć standardowych modeli Pydantic. Outlines buduje gramatykę na podstawie schematu i zapewnia, że struktura odpowiedzi w pełni jej odpowiada.
from pydantic import BaseModel
from enum import Enum
class TicketPriority(str, Enum):
low = "low"
medium = "medium"
high = "high"
urgent = "urgent"
class ServiceTicket(BaseModel):
priority: TicketPriority
category: str
requires_manager: bool
summary: str
prompt = """
Проанализируй обращение:
Срочно! Не могу войти в личный кабинет после оплаты. Через час презентация клиенту!
"""
# Модель сгенерирует JSON, который точно совпадает со структурой ServiceTicket
ticket_json = model(prompt, ServiceTicket, max_new_tokens=200)
ticket = ServiceTicket.model_validate_json(ticket_json)
print(ticket.priority) # TicketPriority.urgent
print(ticket.requires_manager) # True
Wyrażenia regularne i gramatyki
Jeśli modele Pydantic są zbyt rozbudowane dla twojego zadania, możesz zdefiniować ścisłe wyrażenie regularne. Jest to wygodne do wyodrębniania numerów telefonów, dat, kodów pocztowych lub tworzenia wewnętrznych DSL-i.
Z jakimi modelami to działa
Outlines dostosowuje się do twojego stacku. Biblioteka obsługuje wiele trybów:
- Lokalne wnioskowanie przez
transformersillama.cpp - Rozwiązania serwerowe oparte na vLLM i Ollama
- Zewnętrzne API, takie jak OpenAI i Gemini
Największe korzyści z biblioteki osiąga się podczas pracy z własnymi lokalnymi modelami lub serwerami wnioskowania on-premises. Tam bezpośrednia kontrola maskowania tokenów zapewnia 100% gwarancję struktury i przyspiesza działanie.
Przypadki użycia w produkcji
W praktyce biblioteka rozwiązuje jednocześnie kilka typowych zadań programistycznych:
- Automatyczne sortowanie zgłoszeń. Wyodrębnianie kategorii, pilności i tagów z przychodzących e-maili klientów bez ryzyka otrzymania uszkodzonego obiektu.
- Ekstrakcja encji z obsługą niekompletnych danych. Dzięki typom
Unionmożesz pozwolić modelowi zwrócić albo wypełniony obiekt, albo jawny ciąg znaków z informacją o brakujących danych. - Wywoływanie funkcji. Możesz przekazać zwykłą funkcję Pythona do modelu, a Outlines automatycznie wyodrębni typy z jej argumentów, aby utworzyć poprawne parametry wywołania.
- Kategoryzacja katalogu produktów. Szybkie parsowanie opisów produktów na kategorie, marki i kluczowe cechy.
Ograniczenia i niuanse
Przy wszystkich zaletach ważne jest, aby zrozumieć specyfikę technologii. Obliczanie masek dla tokenów wymaga dodatkowych zasobów. Jeśli twój schemat Pydantic składa się z dziesiątek zagnieżdżonych obiektów i złożonych wyrażeń regularnych, przygotowanie gramatyki przed rozpoczęciem generacji może zająć trochę czasu.
Ponadto, jeśli pracujesz wyłącznie przez API OpenAI, biblioteka będzie korzystać z wewnętrznych mechanizmów dostawcy (Structured Outputs / JSON Mode). W tym przypadku Outlines działa jako wygodny ujednolicony interfejs, ale sam proces próbkowania jest kontrolowany przez serwer OpenAI.
Outlines rozwiązuje realny problem inżynieryjny, z którym boryka się każdy zespół przy wdrażaniu LLM-ów do produkcji. Projekt usuwa niestabilność z generacji i pozwala pracować z sieciami neuronowymi jak ze zwykłymi typowanymi funkcjami. Jeśli budujesz usługi backendowe lub autonomiczne agenty oparte na otwartych modelach, to narzędzie zdecydowanie zasługuje na miejsce w twoim zestawie narzędzi.
Powiązane projekty