SHACL TDD dla agentów GraphRAG: jedna zasada ograniczająca działania, która zapobiega nieodpowiednim zachowaniom
Zakoduj politykę zatwierdzania przez kierownictwo z ograniczeniem odpowiedzialności na 30% w formacie SHACL, udowodnij ją za pomocą pytest, a następnie obserwuj, jak firewall ontologiczny zatrzymuje agenta podczas demonstracji kontraktu o wartości 2,3 miliona dolarów.
Czytanie notatek architektonicznych nie jest tym samym co wdrożenie zasady zarządzania. Ten tekst kontynuuje trójkierową porównanie – zwykłego RAG, GraphRAG oraz GraphRAG w połączeniu z OWL/SHACL/policy – na przykładzie umowy o wartości 2,3 miliona dolarów i koncentruje się na umiejętności przenoszalnej: kodowaniu jednej polityki biznesowej w postaci struktury, jej weryfikacji za pomocą automatycznego sprawdzenia oraz obserwowaniu, jak agent odmawia dalszej pracy.
Repozytorium Ontology RAG Firewall zawiera słownictwo cont:, pliki strukturalne oraz demo offline użyte w tym przykładzie. Sklonuj je, upewnij się, że zestaw działa poprawnie na gałęzi main, a następnie opcjonalnie odtwórz starszy commit, aby osobiście doświadczyć cyklu czerwono-zielonego.
Upewnij się co do stanu bazowego na main
git clone https://github.com/cloudbadal007/ontology-rag-firewall
cd ontology-rag-firewall
pip install -e ".[dev]"
pytest -q # 18 passed (full suite)
python examples/demo_offline.py
Przytwierdzenie do wersji 6318929 jest opcjonalne, jeśli ostatnia zweryfikowana wersja artykułu ma znaczenie; wersja z main może być już nowsza.
Zdrowy wynik pokazuje 18 przetestowanych. Dema offline powinno już wyświetlać ostrzeżenie skierowane do kierownictwa w sekcji dotyczącej odszkodowań, mniej więcej w takim tonie:
Safe to act: 🚫 NO
- Flagged: 5
...
⚠️ EXECUTIVE APPROVAL: Liability cap is below 30% of contract value. Cap ratio: 25.00%. Agent action requires executive sign-off.
...
AGENT ACTION: HALTED. Routed to human review queue.
Total value protected: $2,300,000
To właśnie takie ostrzeżenie zostało wprowadzone dzięki nowej strukturze. Pozostała część opisuje, w jaki sposób zostało ono stworzone poprzez podejście rozwijania oparte na testach.
Polityka, która jest kodowana
Jedenastu typów węzłów znajduje się już w pliku contract_domain_shacl.ttl i obejmuje warunki płatności, okresy wypowiedzenia umowy, SLA dotyczące czasu dostępności, wydobywanie informacji o niskiej pewności, luki w środkach naprawczych, automatyczne odnowienie umowy, brak zapisów dotyczących odszkodowań, przegląd bezpośrednich szkód, stosunek limitu do wartości na poziomie 10%, przypadek wysokiej wartości przy niskim bezwzględnym limicie oraz stosunek kierowniczy, na który zwraca uwagę ten przewodnik.
W przypadku umowy demonstracyjnej górna granica w wysokości 575 tys. dolarów stanowi 25% od 2,3 miliona dolarów — co jest wyżej niż dolna granica na poziomie 10% — więc starszy model stosunku pozostaje niewykorzystany. Model kierowniczy eliminuje tę lukę w przypadku drogich umów.
Dodatkowy wymóg działu zakupów, w prostych słowach:
Zawsze, gdy wartość umowy wynosi co najmniej 500 tys. dolarów, a górna granica odszkodowania jest poniżej 30% tej wartości, agent musi uzyskać zatwierdzenie kierownictwa przed podjęciem działań.
To zdanie przekształca się w ExecutiveCapRatioShape wraz z dwoma przypadkami testowymi typu pytest.
Krok 1 — Najpierw nieudana próba
Zawsze twórz przekazanie przed TTL.
W obecnym main te sprawdzenia już przepuszczają testy. Aby doświadczyć niepowodzenia, przejdź do wersji bbeb15e (przed użyciem modelu), wstaw testy, zauważ czerwony kolor, dodaj model z Kroku 2, a następnie wróć do main.
Dodaj lub porównaj ten przypadek w pliku tests/test_shacl_constraints.py:
def test_liability_cap_below_30_percent_on_high_value_contract() -> None:
"""
25% cap on a $2.3M contract must trigger ExecutiveCapRatioShape.
Existing shapes (10% ratio, $100K absolute) do not catch 575K / 2.3M.
"""
clause = ExtractedClause(
"test-cap-ratio",
"LiabilityClause",
"text",
{"liabilityCap": 575_000, "liabilityScope": "DirectDamagesOnly"},
0.9,
1,
)
graph = ClauseRDFBuilder().build(clause, 2_300_000)
conforms, violations, _ = SHACLContractValidator().validate(graph)
assert not conforms
assert any(
"30%" in v or "executive" in v.lower() for v in violations
), violations
def test_liability_cap_at_32_percent_no_executive_flag() -> None:
"""32.6% cap on $2.3M should not trigger the 30% executive rule."""
clause = ExtractedClause(
"test-cap-ratio-ok",
"LiabilityClause",
"text",
{"liabilityCap": 750_000, "liabilityScope": "FullDamages"},
0.9,
1,
)
graph = ClauseRDFBuilder().build(clause, 2_300_000)
_, violations, _ = SHACLContractValidator().validate(graph)
cap_ratio_hits = [
v for v in violations if "30%" in v or "executive" in v.lower()
]
assert len(cap_ratio_hits) == 0, cap_ratio_hits
Wykonaj:
pytest tests/test_shacl_constraints.py::test_liability_cap_below_30_percent_on_high_value_contract -v
Jak wygląda czerwień
Zanim kształt istnieje (na przykład w bbeb15e):
FAILED tests/test_shacl_constraints.py::test_liability_cap_below_30_percent_on_high_value_contract
AssertionError: ... executive ...
W obecnym main identyczne wezwanie ma kolor zielony. Następnie przychodzi sam TTL – już połączony w głównym kodzie, odtworzony tak, aby wzorzec mógł być ponownie wykorzystany.
Krok 2 — Stworzenie autora kształtu
Dodaj treść do pliku ontologies/contract_domain_shacl.ttl. Upewnij się, że cont: wskazuje na przestrzeń nazw OWL za pomocą surowego IRI z GitHuba (unikaj tworzenia ścieżki /contract#, która nie jest rozwiązywalna):
https://raw.githubusercontent.com/cloudbadal007/ontology-rag-firewall/main/ontologies/contract_domain_owl.ttl#
Budowniczowie instancji tworzą URI pod tą samą bazą (…#instance/).
Czy chcesz odtworzyć proces przy bbeb15e? Wykorzystaj już istniejący URI prefiksu z pliku SHACL tej wersji. W wersji main lepiej użyć surowego IRI, aby ontologia, struktury i testy były spójne.
cont:ExecutiveCapRatioShape a sh:NodeShape ;
sh:targetClass cont:LiabilityClause ;
sh:severity sh:Warning ;
sh:message "⚠️ EXECUTIVE APPROVAL: Liability cap is below 30% of contract value. Cap ratio: {?capRatio}%. Agent action requires executive sign-off." ;
sh:sparql [
a sh:SPARQLConstraint ;
sh:select """
PREFIX cont: <https://raw.githubusercontent.com/cloudbadal007/ontology-rag-firewall/main/ontologies/contract_domain_owl.ttl#>
PREFIX xsd: <http://www.w3.org/2001/XMLSchema#>
SELECT $this ?capRatio WHERE {
?contract cont:hasLiabilityClause $this ;
cont:contractValue ?v .
$this cont:liabilityCap ?cap .
BIND((xsd:decimal(?cap) / xsd:decimal(?v) * 100) AS ?capRatio)
FILTER (xsd:decimal(?v) >= 500000)
FILTER (?capRatio < 30)
}
""" ;
] .
Trzy wybrane rozwiązania są celowe:
FILTERwymagający wartości>= 500000ogranicza zasadę do transakcji o wysokiej wartości; ta sama procentowa wartość ma inne znaczenie w przypadku umowy o wartości 50 tys. dolarów.- Włączenie
{?capRatio}do wiadomości skierowanej do ludzi pozwala recenzentom uzyskać dokładny procent zamiast niejasnego ostrzeżenia. - Progiem 30% jest polityka organizacji — edytuj tę wartość, jeśli dział prawny wymaga 40%. Sam plik jest dokumentem reprezentującym tę politykę.
Krok 3 — Przyporządkowanie naruszeń do czytelnych klauzul
Kształty wytwarzają naruszenia związane z maszynami; firewall przekształca trafienia kluczowych słów w linie raportu na poziomie klauzul. W pliku main, EXECUTIVE jest już wymieniony wśród tokenów odpowiedzialności w firewall.py:
"LiabilityClause": ["LIABILITY", "LOW CONFIDENCE", "LEGAL REVIEW", "HIGH-VALUE", "EXECUTIVE"],
Podczas ponownego uruchamiania bbeb15e należy dodać ten token obok kształtu – w przeciwnym razie demo może obliczyć naruszenie bez powiązania go z klauzulą odszkodowawczą.
Krok 4 — Ponowne uruchomienie zestawu i demo
pytest -q # 18 passed (entire repo)
pytest tests/test_shacl_constraints.py -v # 8 passed (this file)
python examples/demo_offline.py
Oczekuj pełnego zestawu w ciągu zaledwie kilku sekund. Raport demo zawiera dedykowaną linię dotyczącą klauzuli odszkodowawczej (liczba flag może pozostać bez zmian, gdy kilka naruszeń dotyczy tej samej klauzuli):
⚠️ EXECUTIVE APPROVAL: Liability cap is below 30% of contract value. Cap ratio: 25.00%. Agent action requires executive sign-off.
AGENT ACTION: HALTED. Routed to human review queue.
Total value protected: $2,300,000
Jedna polityka – zakodowana, sprawdzona i widoczna. To właśnie ten cykl umożliwia skalowanie do kolejnych reguł domeny.
Dlaczego nie „po prostu to zapytać”?
Wprowadzanie tych samych wytycznych na poziomie 30% do promptu systemowego zawodzi, gdy prawnik przeformułowuje klauzulę, gdy instrukcja ginie w długim kontekście, gdy ktoś edytuje prompty bez uwzględnienia kontekstu zgodności, lub gdy audytorzy pytają, która wersja zastosowała jaką zasadę w którym dniu.
Kształt jest deterministyczny w przypadku typowanego RDF, znajduje się pod kontrolą wersji, zawiera test regresji, generuje ustrukturyzowane dowody włączając mierzony stosunek, i nie może zniknąć tylko dlatego, że ktoś dążył do większej płynności w innym miejscu.
Rządy dotyczące systemów agentowych wymagają formalnych ograniczeń oprócz funkcji wyszukiwania i generowania. Polityka znajduje się w kształcie; dowód – w teście; tekst naruszenia stanowi artefakt audytu.
Sposób powtarzalnego rozszerzania
docs/extending.md szczegółowo to opisuje; forma skrócona brzmi:
- Sformułuj zasadę w języku zrozumiałym dla osoby odpowiedzialnej za zgodność.
Docelowy stan: każda struktura ma test; każdy test odpowiada konsekwencji biznesowej. Edycje prawne mają określony czas trwania; CI przeprowadza weryfikacje; stan systemu pozostaje poddawalny recenzji.
Plan rozwoju poza pojedynczą umową
Dzisiejszy firewall wykorzystuje ścieżki oparte na jednej umowie. Pozostają dwa braki w produkcji: podsumowania partialne dla całego portfela (examples/demo_batch_processing.py jest przykładem) oraz kontekst wieloetapowy dostawcy (przechowujący dane, incydenty) po stworzeniu powiązanego grafu właściwości — a nie tylko adres URL.
Dopóki to nie nastąpi, ćwicz ten cykl: napisz strukturę, sprawdź ją, obserwuj wynik, a następnie zastosuj ten sam schemat w kolejnej dziedzinie.
Zachowuj osobny rejestr dla każdego kształtu – właściciela, daty wejścia w życie, identyfikatora notatki źródłowej oraz identyfikatora węzła pytest – aby linia z informacją o winowajcy w git blame mogła zostać rozszerzona na ślad audytowy. Gdy zmieniają się progi, zaktualizuj ciąg znaków z wiadomością oraz dodaj testy graniczne, aby stare wartości nie mogły powrócić bez śladu. Traktuj mapy słów kluczowych→klauzul jako interfejs API: zrób zrzut ekranu wyników demonstracji w CI, aby modyfikacje nie mogły usunąć EXECUTIVE z listy tokenów ochronnych bez powodowania nieudanej pracy. Wolij kształty dodawane od edycji wspólnych bloków SPARQL; niezależne kształty pozwalają na czyste przywrócenie stanu w przypadku niepowodzenia eksperymentu polityki. Wreszcie publikuj zmierzoną wartość stosunku we wszystkich ostrzeżeniach skierowanych do ludzi – recenzenci bardziej ufają liczbom, które mogą ponownie obliczyć na podstawie RDF, niż ogólnym komunikatom typu „wymaga zatwierdzenia”.