Главная / Статьи / 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, ограничивает правило сделками с высокой стоимостью; тот же процент имеет другое значение для контракта стоимостью 50 тыс.
  • Включение {?capRatio} в текст сообщения позволяет рецензентам видеть точный процент вместо расплывчатого предупреждения.
  • Порог в 30% является организационной политикой — измените это значение, если юридический отдел требует 40%. Сам файл и является документом, фиксирующим эту политику.

Шаг 3 — Связывание нарушений с понятными пунктами

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

"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 превращалась в полную историю аудита. При изменении пороговых значений версионируйте строку сообщения и добавляйте тесты на границы, чтобы старые значения не могли возвращаться незаметно. Рассматривайте карты «ключевое слово → пункт» как интерфейс API: сохраняйте результаты демонстрации в CI, чтобы при рефакторинге нельзя было удалить EXECUTIVE из списка токенов ответственности без сбоя задачи. Предпочитайте создание новых структур вместо редактирования общих SPARQL-объектов; независимые структуры позволяют чисто восстанавливать состояние при неудаче эксперимента с политикой. Наконец, публикуйте измеренные коэффициенты во всех предупреждениях, предназначенных для людей — рецензенты больше доверяют цифрам, которые они могут пересчитать из RDF, чем общим сообщениям вроде «требуется одобрение».