Головна / Статті / SHACL TDD для агентів GraphRAG: одне правило Executive Cap, яке зупиняє погані дії

SHACL TDD для агентів GraphRAG: одне правило Executive Cap, яке зупиняє погані дії

Закодуйте політику схвалення керівництва з обмеженням відповідальності на рівні 30% у форматі SHACL, доведіть її за допомогою pytest та спостерігайте, як брандмауер онтології зупиняє агента під час демонстрації контракту на суму 2,3 млн доларів.

1411 слів

Читання архітектурних нотаток — це не те саме, що впровадження правила управління. Ця стаття є продовженням тристороннього порівняння — звичайного RAG, GraphRAG та GraphRAG у поєднанні з OWL/SHACL/policy — на зразку угоди вартістю 2,3 млн доларів, і присвятована переносимій навичці: кодувати одну бізнес-політику у вигляді форми, підтверджувати її за допомогою автоматизованої перевірки та спостерігати, як агент відмовляється продовжувати роботу.

У репозиторії Ontology RAG Firewall зберігаються словник cont:, файли форм та офлайн-демонстрація, використані тут. Клонуйте його, переконайтеся, що всі компоненти працюють коректно у ветці main, а потім за бажанням перегляньте старішу версію, щоб особисто побачити процес зміни статусу з червоного на зелений.

Підтвердіть базовий стан у ветці 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

Фіксація версії на 6318929 є необов’язковою, якщо остання перевірена порада у статті має значення; порада з ветки main може вже бути новішою.

Здоровий результат виконання показує 18 пройдено. Офлайн-демо вже має відображати попередження, орієнтоване на керівництво, у розділі про компенсацію, приблизно таке:

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

Саме таке попередження було введено завдяки новій структурі. Решта описує, як вона була розроблена за принципом тестування на першому етапі.

Політика, яка кодується

Вже одинадцять форматів вузлів існують у файлі contract_domain_shacl.ttl та охоплюють умови оплати, терміни попередження, SLA щодо часу роботи, видобуток даних з низькою достовірністю, прогалини у заходах виправлення, автоматичне поновлення, відсутність формулювань про компенсацію, перевірку прямих збитків, співвідношення ліміту до вартості 10%, випадки високої вартості з низьким абсолютним лімітом, а також формат співвідношення для керівництва, на який звертається увага в цьому огляді.

У демонстраційній угоді верхня межа у розмірі 575 тис. доларів становить 25% від 2,3 млн доларів — що перевищує нижню межу у 10% — тому стара формула співвідношень залишається недійсною. Формула для керівництва усуває цю проблему для дорогих угод.

Додаткова вимога від відділу закупівель простою мовою:

Кожного разу, коли вартість угоди становить принаймні 500 тис. доларів, а верхня межа відшкодувань становить менше 30% від цієї суми, агент повинен отримати затвердження керівництва перед тим, як діяти.

Це речення перетворюється на ExecutiveCapRatioShape разом із парою тестів pytest.

Крок 1 — Спочатку спричинити помилку

Завжди створюйте твердження перед TTL.

У сучасному варіанті main ці перевірки вже пройшли. Щоб відчути помилку, перейдіть до версії bbeb15e (попередня версія), додайте тести, побачите червоний колір, додайте формулу з Кроку 2, а потім поверніться до main.

Додайте або порівняйте цей випадок у 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

Виконайте:

pytest tests/test_shacl_constraints.py::test_liability_cap_below_30_percent_on_high_value_contract -v

Як виглядає червоний колір

До створення форми (наприклад, у bbeb15e):

FAILED tests/test_shacl_constraints.py::test_liability_cap_below_30_percent_on_high_value_contract
AssertionError: ... executive ...

У поточному main ідентичне викликання має зелений колір. Далі йде сам TTL — він вже об’єднаний у верхньому рівні, відтворений для того, щоб шаблон можна було використовувати знову.

Крок 2 — Створення авторства форми

Додайте це до ontologies/contract_domain_shacl.ttl. Зберігайте, щоб cont: вказував на простір імен OWL через сирі IRI з GitHub (уникайте створення шляху /contract#, який не резолюється):

https://raw.githubusercontent.com/cloudbadal007/ontology-rag-firewall/main/ontologies/contract_domain_owl.ttl#

Будівельники інстанцій створюють URI під тим самим базовим шляхом (…#instance/).

Повторна обробка за ідентифікатором bbeb15e? Використайте вже наявний URI-префікс із файлу SHACL цієї ревізії. Для версії main краще використовувати сирі IRI, щоб онтологія, форми та тести були узгоджені.

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)
}
""" ;
    ] .

Три вибори дизайну зроблені навмисно:

  • FILTER, який вимагає значення >= 500000, обмежує правило справами з високою вартістю; та сама відсоткова норма має інше значення для замовлення на $50K.
  • Вбудування {?capRatio} у людське повідомлення дає рецензентам конкретний відсоток замість розмитого попередження.
  • Поріг у 30% — це політика організації; якщо юридичний відділ хоче 40%, потрібно змінити це значення. Сам файл є проявом цієї політики.

Крок 3 — Перенесення порушень на зрозумілі пункти

Форми генерують порушення правил машини; брандмауер перетворює знахідки ключових слів на рядки звіту на рівні клозу. У файлі main значення EXECUTIVE вже включено до списку токенів відповідальності у firewall.py:

"LiabilityClause": ["LIABILITY", "LOW CONFIDENCE", "LEGAL REVIEW", "HIGH-VALUE", "EXECUTIVE"],

Під час повторної обробки bbeb15e додайте цей токен поруч із формою — інакше демонстраційна версія може обчислити порушення, не пов’язавши його зі статтею про компенсацію.

Крок 4 — Перезапустити набір тестів та демонстрацію

pytest -q                         # 18 passed (entire repo)
pytest tests/test_shacl_constraints.py -v   # 8 passed (this file)
python examples/demo_offline.py

Очікуйте завершення всього набору тестів протягом кількох секунд. У звіті демонстрації з’явиться окремий рядок щодо статті про компенсацію (кількість прапорців може залишатися незмінною, якщо кілька порушень стосуються однієї клози):

⚠️ 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

Одна політика — зашифрована, перевірена та видима. Саме цей цикл дозволяє масштабуватися до наступних правил домену.

Чому не «просто запитати його»?

Використання того самого керівного принципу на рівні 30% у системних запитах зазнає невдачі, коли юрист перефразує умову, коли інструкція загублюється в довгому контексті, коли хтось редагує запити без урахування контексту відповідності, або коли аудитори запитують, яка версія застосувала яке правило у який день.

Форма є детермінованою у типовому RDF, знаходиться під контролем версій, має тест на регресію, генерує структуровані докази, включаючи виміряне співвідношення, і не може зникнути через те, що хтось прагне більшої плавності в інших частинах.

Управління агентними системами потребує формальних обмежень окрім функцій пошуку та генерації. Політика знаходиться у формі; докази — у тестах; текст про порушення є результатом аудиту.

Схема створення розширень, яку можна повторювати

docs/extending.md детально описує це; скорочена форма така:

  1. Сформулюйте правило мовою, зрозумілою для відповідальної особи з питань відповідності.
  • Розширюйте OWL лише тоді, коли потрібні нові типи чи властивості.
  • Додавайте одну структуру на кожне правило — невеликі та окремі елементи кращі за моноліт.
  • Забезпечте, щоб pytest був червоним до виконання структури та зеленим після неї.
  • Бажаний стан: кожна структура має тест; кожен тест відповідає певним бізнес-наслідкам. Зміни перевіряються за TTL; CI виконує перевірки; стан системи залишається під контролем.

    План розвитку після укладення однієї угоди

    Сьогодні брандмауер використовує шляхи однієї угоди. Залишаються дві проблеми в продакшені: підсумки пакетної обробки для всього портфеля (examples/demo_batch_processing.py є прикладом) та контекст багатоетапної взаємодії з постачальниками (зберігання даних, інциденти) після створення взаємопов’язаної графіки властивостей — а не просто статичної URL.

    До того часу практикуйте цей цикл: напишіть структуру, перевірте її, спостерігайте за результатом, а потім застосуйте ту саму схему до наступної області.

    Тримайте окремий журнал для кожної форми — власника, дату набрання чинності, ідентифікатор записки джерела та ідентифікатор вузла pytest — щоб рядок git blame перетворювався на історію аудиту. Коли змінюються порогові значення, версіонуйте рядок повідомлення та додавайте тести на межі, щоб старі значення не могли приховано повернутися. Розглядайте мапи ключових слів→клаузул як інтерфейс API: зберігайте результати демонстрації у CI, щоб під час рефакторингу не можна було видалити EXECUTIVE зі списку токенів компенсації без провалу завдання. Віддавайте перевагу додатковим формам замість редагування спільних блоків SPARQL; незалежні форми чисто повертаються до початкового стану при провалі експерименту з політикою. Нарешті, публікуйте виміряне співвідношення у кожному попередженні, орієнтованому на людей — рецензенти довіряють цифрам, які можна перерахувати з RDF, більше, ніж загальним написам типу «треба схвалення».