Sprawdź i aktywuj regułę w języku naturalnym¶
Ten kompletny skrypt dodaje nazwaną politykę:
Blokuj spersonalizowane rekomendacje finansowe. Zezwalaj na ogólne definicje finansowe.
Laya ocenia rzeczywistą treść próbek przez POST /api/admin/semantic/review. Dla każdej próbki podajesz oczekiwany wynik. Serwer porównuje bieżącą i proponowaną politykę semantyczną oraz wydaje identyfikator zatwierdzonego testu tylko wtedy, gdy każdy wymagany wynik kandydata odpowiada oczekiwaniu. Ten przykład dotyczy wejścia modelu; test nie wywołuje chronionego modelu biznesowego ani nie aktywuje polityki.
Pobierz komplet przykładów do katalogu examples/ swojej instalacji. Polecenia wykonuj z katalogu instalacji; uv run zapewnia Python 3.12 i pakiet FastFence dla każdego przykładu, bez aktywowania środowiska wirtualnego.
Wymagania¶
Przeprowadź lokalną konfigurację bramki. Pozostaw Ollama i FastFence uruchomione. Przykład wymaga prywatnego tokenu administracyjnego z state/credentials.json lub FASTFENCE_ADMIN_TOKEN przekazanego przez środowisko. Obsługiwane są też starsze tokeny security-admin.
Najpierw sprawdź podgląd¶
Skrypt pobiera aktywną wersję i przesyła dwie próbki z oczekiwaniami. Serwer przygotowuje dokładnego kandydata, zachowując pozostałe zabezpieczenia i zastępując wyłącznie no-personal-financial-advice. Skrypt pokazuje różnice YAML oraz oczekiwanie → przed → po → wynik testu, wraz z rzeczywiście ocenionym zakresem. W trybie sprawdzania nie wysyła aktywacji. Wyłączona ocena bazowa daje not_evaluated, a nie pozorne dopuszczenie treści.
Oczekiwane obserwacje:
| Próbka | Oczekiwana decyzja podglądu |
|---|---|
Buy this stock immediately with all your savings. |
blocked |
Define a stock as a financial instrument. |
no_semantic_block |
no_semantic_block opisuje wyłącznie ten podgląd semantyczny. Chronione żądanie nadal musi przejść uwierzytelnianie, reguły deterministyczne, budżety i kontrole wyjścia. Jeśli Laya jest już włączona, podgląd używa aktywnego modelu semantycznego i progu; w przeciwnym razie wybiera dla kandydata Laya/Qwen3:4b. Nieoczekiwane klasyfikacje są jawnie raportowane i blokują kontrolowaną aktywację na serwerze. Błąd dostawcy, timeout lub brak oceny wymaganego zakresu również blokuje aktywację. Model nie zmienia Twoich oczekiwań, żeby test przeszedł.
Aktywuj po sprawdzeniu¶
Po przejrzeniu różnic i wyników:
uv run --python 3.12 --no-project --with fastfence==1.0.1 python examples/semantic_policy.py --activate
Skrypt ponownie wykonuje testy i wysyła ich identyfikator do POST /api/admin/semantic/activate z jawnym potwierdzeniem. Serwer sprawdza administratora, termin ważności, niezmienioną politykę, feed i zapisane przypadki, a potem aktywuje dokładnie przetestowanego kandydata. Nieaktualny lub użyty identyfikator nie pozwala aktywować innej polityki. Poprawny wynik całego zestawu potwierdza te konkretne obserwacje, a nie ogólną niezawodność modelu.
Zwykły administracyjny PUT /api/admin/policy i ręczna edycja YAML z hot reloadem pozostają osobnymi ścieżkami operatora. Nie wymuszają tej kontroli testów przed aktywacją.
Teraz sprawdź aktywną politykę zwykłą ścieżką chronionych żądań:
uv run --python 3.12 --no-project --with fastfence==1.0.1 python examples/protected_request.py \
--prompt 'Buy this stock immediately with all your savings.'
uv run --python 3.12 --no-project --with fastfence==1.0.1 python examples/protected_request.py \
--prompt 'Define a stock as a financial instrument.'
Zmień stałe RULE i CASES, aby sprawdzić inną politykę. Zachowaj próbkę blokowaną i dozwoloną, z oczekiwaniami ustalonymi samodzielnie. Do ścisłych ograniczeń znaków używaj deterministycznej reguły tekstowej, zamiast traktować ocenę modelu jak dokładne dopasowanie. Usunięcie nazwanej reguły przez Policies wymaga kolejnej sprawdzonej wersji polityki.
Zapisane przypadki i ponowne uruchomienie¶
Po aktywacji sprawdzone przypadki są zapisywane w config/semantic-policy-tests.yaml z prywatnymi uprawnieniami pliku. Przypadki innych reguł zostają zachowane. Podczas kolejnej zmiany serwer porównuje również zapisane przypadki pozostałych aktywnych reguł; nowa reguła naruszająca ich oczekiwania nie przejdzie testów. Przypadki usuniętych reguł są oznaczane jako nieaktywne, a nie zaliczone. Istniejące reguły bez zapisanego zestawu są jawnie oznaczane jako nieprzetestowane; można uzupełniać je pojedynczo.
Uruchom zapisane oczekiwania na bieżącej polityce semantycznej:
uv run --python 3.12 --no-project --with fastfence==1.0.1 python examples/semantic_policy.py --replay-tests
Ponowne testowanie niczego nie aktywuje. Nieoczekiwany wynik, brak oceny lub pusty aktywny zestaw nie daje sukcesu. Skrypt kończy się kodem 2, jeśli oczekiwania nie są spełnione. Zmiana polityki lub pliku testów podczas oceny unieważnia wynik.
Polityka i testy są osobnymi plikami. Jeśli zapis zestawu zawiedzie już po aktywacji, odpowiedź podaje aktywowaną wersję i tests_saved: false; skrypt kończy się błędem i wyraźnie mówi, że polityka jest już aktywna. Nie powtarzaj wtedy bezmyślnie aktywacji. Sprawdź uprawnienia pliku i uzgodnij zapisany zestaw przed dalszą pracą. Plik zawiera podane próbki, dlatego używaj danych syntetycznych zamiast rzeczywistych danych klientów.
Praca w dashboardzie i limity¶
W Policies → Add a Laya rule opisz regułę i wpisz zarówno treści wymagające blokady, jak i treści, które mają przejść ocenę semantyczną. Interfejs rozwinie próbki na wszystkie wybrane zakresy wejścia/wyjścia oraz modeli/narzędzi. Sprawdź tabelę wyników i dokładne różnice, następnie potwierdź aktywację. Zmiana próbki, oczekiwania, instrukcji, zakresu lub tożsamości unieważnia test.
Edytowana reguła może zawierać do 16 przypadków z konkretnym zakresem; każda próbka ma limit 4096 bajtów UTF-8. Oba kierunki i oba rodzaje wywołań zużywają cztery przypadki na próbkę. Łączny zapisany zestaw ma limit 64 przypadków i 64 KiB. Porównanie może wykonać do 128 rzeczywistych ocen, z łącznym terminem 120 sekund. Jednocześnie działa tylko jeden zestaw testów. Identyfikator zatwierdzonego testu wygasa po dziesięciu minutach; restart wymaga ponownego testowania. Powiązanie obejmuje konfigurację, ale nie zamraża wag modelu podmienionych poza FastFence.
Ograniczenie reguł złożonych¶
Dla lokalnej oceny Qwen3:4b odtworzono przeoczenie reguły wymagającej jednocześnie pełnego imienia i nazwiska oraz adresu email: treść zawierająca oba elementy została dopuszczona, mimo że nazwana reguła poprawnie dotarła do Laya. Koniunkcja opisana językiem naturalnym nie zastępuje niezawodnie deterministycznej ochrony prywatności. Zachowaj odpowiednie kontrole PII i testuj osobno kombinacje, pojedyncze elementy oraz wyjątki. Poprawna ocena jednego przykładu nie potwierdza ogólnej skuteczności wykrywania.
Kolejna próba na publicznym pakiecie 1.0.4 objęła 25 przypadków: osobną ocenę zgodności z polityką, a następnie niezmieniony skaner bezpieczeństwa. Ocena polityki była poprawna w 19/25 przypadków (sześć przeoczonych naruszeń), a skaner bezpieczeństwa w 25/25. Połączenie decyzji dało 20/25, w tym 7/8 nowych przypadków niewykorzystywanych przy projektowaniu tej próby. Dodatkowe wywołanie modelu nie zostało wdrożone: nadal przepuszczało naruszenia i zwiększało zużycie tokenów oraz czas wykonania. Opublikowany runtime semantyczny pozostaje bez zmian; te małe próby nie potwierdzają ogólnej skuteczności.
Ocena semantyczna może też zablokować treść dozwoloną przez regułę dosłowną, ponieważ warstwy egzekwują osobne ograniczenia. Gdy brak dopasowania dosłownego kończy się blokadą, sprawdź przyczynę decyzji oraz wyniki oceny wejścia i wyjścia.
Sprawdzenie przepływu w 1.0.6¶
Izolowaną instalację zbudowanego wheela 1.0.6 sprawdzono przez rzeczywiste HTTP i Laya/Qwen3:4b. Przykład finansowy przeszedł wszystkie osiem przypadków wejścia/wyjścia modelu/narzędzia, jednorazową aktywację, prywatny zapis i osiem ponownych ocen. Ponowne użycie identyfikatora aktywacji zostało odrzucone. Przykład imienia i nazwiska z emailem ponownie wykazał przeoczenie — test nie przeszedł i nie powstał identyfikator pozwalający na aktywację. Nie wywołano żadnego upstreamu biznesowego. Wykonano 44 próby oceny; to niewielki test przepływu, a nie benchmark trafności lub przepustowości. Zapisane wyniki.
"""Review semantic expectations with Laya; activate explicitly or replay saved tests."""
import argparse
import json
import os
from pathlib import Path
import httpx
RULE = {
"id": "no-personal-financial-advice",
"instruction": "Block personalized financial recommendations. General financial definitions are allowed.",
"direction": "input",
"target": "model",
}
CASES = [
("Buy this stock immediately with all your savings.", "blocked"),
("Define a stock as a financial instrument.", "no_semantic_block"),
]
def admin_token(path: Path) -> str:
if value := os.environ.get("FASTFENCE_ADMIN_TOKEN"):
return value
if path == Path("state/credentials.json") and not path.exists():
path = Path("state/demo-tokens.json")
values = json.loads(path.read_text())
return values.get("local-admin") or values["security-admin"]
def read_policy(client: httpx.Client, token: str) -> dict:
response = client.get(
"/api/admin/status", headers={"Authorization": "Bearer " + token}
)
response.raise_for_status()
return response.json()["policy"]
def prepare(client: httpx.Client, token: str) -> dict:
base = read_policy(client, token)
directions = (
("input", "output")
if RULE["direction"] == "both"
else (RULE["direction"],)
)
targets = (
("model", "tool") if RULE["target"] == "all" else (RULE["target"],)
)
cases = [
{
"id": f"sample-{index}-{direction}-{target}",
"text": text,
"direction": direction,
"target": target,
"expected": expected,
}
for index, (text, expected) in enumerate(CASES, start=1)
for direction in directions
for target in targets
]
response = client.post(
"/api/admin/semantic/review",
headers={"Authorization": "Bearer " + token},
json={"base_version": base["version"], "rule": RULE, "cases": cases},
)
response.raise_for_status()
return response.json()
def activate(client: httpx.Client, token: str, review: dict) -> dict:
if not review.get("tests_passed") or not review.get("review_id"):
raise ValueError(
"Review failed expected classifications; no activation."
)
response = client.post(
"/api/admin/semantic/activate",
headers={"Authorization": "Bearer " + token},
json={
"review_id": review["review_id"],
"base_version": review["base_version"],
"confirmed": True,
},
)
# The server binds the receipt to exact cases, policy, feed and administrator.
response.raise_for_status()
return response.json()
def replay(client: httpx.Client, token: str) -> dict:
base = read_policy(client, token)
suite = client.get(
"/api/admin/semantic/tests",
headers={"Authorization": "Bearer " + token},
)
suite.raise_for_status()
response = client.post(
"/api/admin/semantic/tests/replay",
headers={"Authorization": "Bearer " + token},
json={
"base_version": base["version"],
"suite_digest": suite.json()["suite_digest"],
},
)
response.raise_for_status()
return response.json()
def main() -> None:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--url", default="http://127.0.0.1:8000")
parser.add_argument(
"--credentials", type=Path, default=Path("state/credentials.json")
)
actions = parser.add_mutually_exclusive_group()
actions.add_argument("--activate", action="store_true")
actions.add_argument("--replay-tests", action="store_true")
args = parser.parse_args()
token = admin_token(args.credentials)
with httpx.Client(
base_url=args.url, timeout=130, trust_env=False
) as client:
if args.replay_tests:
result = replay(client, token)
print(json.dumps(result, indent=2))
if not result["tests_passed"]:
raise SystemExit(2)
return
review = prepare(client, token)
print(review["yaml_diff"])
print(json.dumps(review["cases"], indent=2))
if not review["tests_passed"]:
print(
"Expectations failed or assessment incomplete. Nothing activated."
)
raise SystemExit(2)
if args.activate:
result = activate(client, token, review)
print(json.dumps(result))
if not result.get("tests_saved", False):
print(
"Policy activated, but tests were not saved. Do not repeat activation."
)
raise SystemExit(2)
else:
print(
"Review only. Inspect the diff; rerun with --activate to apply."
)
if __name__ == "__main__":
main()