English translation is not available yet. Showing Russian content.

Как вы проверяете, что модель действительно использует структуру представления, а не игнорирует ее?

Краткий тезис

В Agentic RAG модель часто получает структурированные данные (JSON, XML, таблицы) и должна опираться на них при генерации ответа или вызове инструментов. Если модель игнорирует структуру и работает с данными как с плоским текстом, теряется точность и предсказуемость. Проверка использования структуры ведётся тремя основными методами: probing (анализ attention patterns), perturbation (намеренное нарушение структуры) и ablation (замена структурированного представления на text|plain text). Комбинация этих подходов позволяет количественно оценить, насколько модель действительно «видит» и использует заданную структуру.


1. Термин: Структура представления (structured representation) в Agentic RAG

Структура представления — это способ организации информации, передаваемой модели в контексте, с явными синтаксическими или семантическими маркерами. В RAG|Agentic RAG такими структурами могут быть:

  • JSON-схемы для вызова функций (function calling);
  • XML-теги для разделения контекста, инструкций и примеров;
  • Markdown-таблицы для табличных данных;
  • YAML-блоки для конфигураций;
  • Специализированные токены (например, <|tool_call|>, <|context|>).

Модель должна не просто прочитать эти данные, а интерпретировать их синтаксис и извлекать семантику в соответствии с заданной структурой. Например, при вызове функции модель должна корректно заполнить поля JSON, а не просто скопировать текст.


2. Почему важно проверять использование структуры

Если модель игнорирует структуру, возникают проблемы:

  • Ошибки в вызове инструментов — модель генерирует некорректный JSON, пропускает обязательные поля.
  • Потеря контекста — модель не различает, где инструкция, а где данные, и смешивает их.
  • Снижение точности — структурированные данные (например, таблицы) обрабатываются как обычный текст, теряются связи между ячейками.
  • Небезопасность — модель может выполнить вредоносную команду, если не распознаёт границы структуры.

Поэтому на этапе тестирования и валидации Agentic RAG-системы необходимо убедиться, что модель действительно использует структуру, а не просто «пропускает её мимо ушей».


3. Метод 1: Probing (анализ attention patterns)

Probing — это метод, при котором мы анализируем внутренние представления модели (обычно attention weights или hidden states), чтобы понять, на какие части входных данных модель «обращает внимание».

Как применить к проверке структуры

  1. Подаём на вход структурированный пример (например, JSON с полями name, age).
  2. Извлекаем attention scores между токенами, относящимися к структуре (например, ключи "name", "age", скобки {, }).
  3. Сравниваем attention patterns на структурированных vs неструктурированных частях (например, plain text с теми же значениями).

Ожидаемый результат Если модель использует структуру, attention weights на синтаксических маркерах (скобки, кавычки, ключи) будут выше, чем на аналогичных токенах в plain text. Можно построить heatmap attention и визуально оценить.

Инструменты библиотеки transformers (Hugging Face), bertviz, captum.

Пример кода (упрощённый):

from transformers import AutoModelForCausalLM, AutoTokenizer
import torch

model_name = "microsoft/phi-2"
tokenizer = AutoTokenizer.from_pretrained(model_name)
model = AutoModelForCausalLM.from_pretrained(model_name, output_attentions=True)

structured_input = '{"name": "Alice", "age": 30}'
plain_input = "Alice is 30 years old."

inputs_struct = tokenizer(structured_input, return_tensors="pt")
inputs_plain = tokenizer(plain_input, return_tensors="pt")

with torch.no_grad():
    outputs_struct = model(**inputs_struct, output_attentions=True)
    outputs_plain = model(**inputs_plain, output_attentions=True)

# Сравниваем attention на последнем слое для токена "name"
# (упрощённо: берём среднее по головам)
attn_struct = outputs_struct.attentions[-1].mean(dim=1).squeeze()
attn_plain = outputs_plain.attentions[-1].mean(dim=1).squeeze()

print("Attention on structured tokens:", attn_struct[0, :].mean().item())
print("Attention on plain tokens:", attn_plain[0, :].mean().item())

Ограничения требует доступа к внутренним состояниям модели, не все API предоставляют attention. Кроме того, высокий attention на структуре не гарантирует, что модель её использует — может просто «смотреть», но не интерпретировать.


4. Метод 2: Perturbation (нарушение структуры)

Perturbation — это метод, при котором мы намеренно «ломаем» структуру представления и наблюдаем за деградацией качества ответа модели.

Как применить

  1. Берём корректный структурированный пример (например, валидный JSON).
  2. Создаём несколько вариантов с нарушениями:
    • Удаляем закрывающую скобку }.
    • Меняем ключ на невалидный (например, "name""nme").
    • Добавляем лишние символы внутри структуры.
    • Превращаем JSON в невалидный (например, дублируем запятую).
  3. Подаём оба варианта (корректный и нарушенный) модели и сравниваем ответы.

Ожидаемый результат Если модель действительно использует структуру, то при нарушении структуры ответ должен существенно ухудшиться (например, модель не сможет корректно вызвать функцию, выдаст ошибку или сгенерирует бессмысленный ответ). Если модель игнорирует структуру, то ответ останется почти неизменным (она просто прочитает данные как текст).

Метрики деградации

  • Accuracy вызова функции (правильность JSON).
  • ROUGE-L / BLEU между ответами на корректном и нарушенном входах.
  • Perplexity модели на нарушенном входе (если структура важна, perplexity вырастет).

Пример кода

import json

def test_perturbation(model, tokenizer, correct_json, broken_json):
    correct_text = f"Extract the name from: {correct_json}"
    broken_text = f"Extract the name from: {broken_json}"
    
    inputs_correct = tokenizer(correct_text, return_tensors="pt")
    inputs_broken = tokenizer(broken_text, return_tensors="pt")
    
    with torch.no_grad():
        out_correct = model.generate(**inputs_correct, max_new_tokens=10)
        out_broken = model.generate(**inputs_broken, max_new_tokens=10)
    
    return tokenizer.decode(out_correct[0]), tokenizer.decode(out_broken[0])

correct = '{"name": "Alice", "age": 30}'
broken = '{"name": "Alice", "age": 30'  # нет закрывающей скобки

resp_correct, resp_broken = test_perturbation(model, tokenizer, correct, broken)
print("Correct:", resp_correct)
print("Broken:", resp_broken)

Преимущества не требует доступа к внутренностям модели, подходит для black-box тестирования.


5. Метод 3: Ablation (замена на plain text)

Ablation — это метод, при котором мы полностью удаляем структуру и заменяем её эквивалентным по содержанию, но неструктурированным текстом. Затем сравниваем поведение модели.

Как применить

  1. Берём структурированное представление (например, таблицу в Markdown).
  2. Создаём plain text-версию с теми же данными, но без форматирования (например, просто список через запятую).
  3. Подаём оба варианта модели и сравниваем ответы на одинаковые запросы.

Ожидаемый результат Если модель использует структуру, то ответы на структурированном и plain text вариантах будут различаться (структурированный даст более точный/полный ответ). Если модель игнорирует структуру, ответы будут идентичны.

Пример:
Структурированный:

| Name  | Age |
|-------|-----|
| Alice | [[30. Как вы проверяете, что fine-tuned модель не сломала базовые способности\|30]]  |

Plain text:

Name: Alice, Age: 30

Запрос: «Сколько лет Алисе?»

  • Если модель использует таблицу, она может точнее извлечь значение.
  • Если игнорирует — оба ответа будут одинаковыми.

Метрики: точность ответа, F1-score извлечения сущностей, время генерации.


6. Дополнительные методы

6.1 Counterfactual evaluation (контрфактическая оценка)

Создаём пары входов, где структура изменена, но содержание остаётся тем же (например, меняем порядок полей в JSON). Если модель чувствительна к структуре, ответ может измениться (что нежелательно, если структура не должна влиять на семантику). И наоборот, если структура должна влиять (например, порядок важен), проверяем, что модель реагирует.

6.2 Consistency checks (проверка согласованности)

Подаём один и тот же структурированный контент несколько раз с разными формулировками запроса. Если модель действительно использует структуру, ответы должны быть согласованы (например, всегда извлекать одно и то же значение из одного поля). Если модель игнорирует структуру, ответы могут быть случайными.

6.3 Logit analysis (анализ логитов)

Сравниваем распределение вероятностей на токенах, соответствующих структурным элементам (например, вероятность токена } после открывающей скобки). Если модель «понимает» структуру, вероятность закрывающей скобки должна быть высокой в правильном контексте и низкой при нарушении.


7. Метрики для оценки использования структуры

МетрикаОписаниеПрименение
Structural FidelityДоля ответов, в которых модель корректно воспроизводит структуру (например, валидный JSON)Perturbation, Ablation
Attention GapРазница в attention weights между структурированными и неструктурированными токенамиProbing
Degradation ScoreОтносительное ухудшение качества (accuracy, ROUGE) при нарушении структурыPerturbation
Consistency ScoreДоля согласованных ответов при разных формулировках запросаConsistency checks
Perplexity RatioPerplexity на нарушенном входе / perplexity на корректномPerturbation

8. Пример кода: комплексный тест с perturbation и ablation

import json
import random

def test_structure_usage(model, tokenizer, structured_input, plain_input, query):
    """
    Сравнивает ответы модели на структурированном и plain text входах,
    а также на нарушенной структуре.
    """
    def get_response(text):
        inputs = tokenizer(text, return_tensors="pt")
        out = model.generate(**inputs, max_new_tokens=50)
        return tokenizer.decode(out[0], skip_special_tokens=True)
    
    # Ablation: структурированный vs plain
    resp_struct = get_response(f"{query}\n{structured_input}")
    resp_plain = get_response(f"{query}\n{plain_input}")
    
    # Perturbation: ломаем структуру (удаляем закрывающую скобку)
    broken = structured_input.rstrip('}') if structured_input.endswith('}') else structured_input + '}'
    resp_broken = get_response(f"{query}\n{broken}")
    
    return {
        "structured": resp_struct,
        "plain": resp_plain,
        "broken": resp_broken
    }

# Пример использования
structured = '{"name": "Alice", "age": 30}'
plain = "Alice is 30 years old."
query = "What is Alice's age?"

results = test_structure_usage(model, tokenizer, structured, plain, query)
print(results)

9. Сравнение методов

МетодДоступ к моделиСложность реализацииНадёжностьЧто измеряет
ProbingТребуется white-boxВысокаяСредняяВнимание к структуре
PerturbationBlack-boxНизкаяВысокаяЧувствительность к нарушениям
AblationBlack-boxНизкаяВысокаяВлияние структуры на ответ
ConsistencyBlack-boxСредняяСредняяСтабильность использования
Logit analysisТребуется white-boxСредняяВысокаяВероятность структурных токенов

Рекомендация начинать с perturbation и ablation как с самых простых и информативных. Probing и logit analysis — для глубокого анализа, если есть доступ к модели.


10. Ограничения и подводные камни

  • Probing может вводить в заблуждение модель может «смотреть» на структуру, но не использовать её для принятия решений (например, attention на скобках может быть высоким из-за синтаксической близости, а не семантической важности).
  • Perturbation не всегда однозначен если модель обучена на неструктурированных данных, она может быть устойчива к нарушениям (например, игнорировать ошибки JSON). Тогда деградация будет слабой, хотя структура не используется.
  • Ablation требует эквивалентности plain text должен содержать те же данные, иначе сравнение некорректно.
  • Зависимость от токенизации структура может быть «сломана» уже на уровне токенов (например, если JSON разбит на subword токены неоптимально).
  • Разные модели по-разному обрабатывают структуру некоторые fine-tuned модели (например, для function calling) специально обучены на структурированных данных, и тесты должны это учитывать.

Пет-проект для закрепления

Задача Разработать набор тестов для проверки использования структуры в Agentic RAG-системе на основе открытой LLM (например, Llama 3 или Mistral).

Инструменты Python, Hugging Face Transformers, библиотека json, pandas для таблиц, matplotlib для визуализации attention.

Шаги:

  1. Выберите модель, поддерживающую function calling (например, NousResearch/Hermes-2-Pro-Mistral-7B).
  2. Подготовьте датасет из 50 структурированных примеров (JSON-схемы вызова функций, XML-инструкции, Markdown-таблицы).
  3. Для каждого примера создайте:
    • Корректную структуру.
    • Нарушенную структуру (удаление скобок, замена ключей).
    • Plain text-эквивалент.
  4. Реализуйте функции для probing (извлечение attention weights на последнем слое) и perturbation (генерация ответов).
  5. Посчитайте метрики: Structural Fidelity (доля валидных JSON в ответах), Degradation Score (среднее падение accuracy при нарушении), Attention Gap.
  6. Визуализируйте attention heatmap для нескольких примеров.
  7. Сделайте вывод: использует ли модель структуру или игнорирует?

Ожидаемый результат Отчёт с таблицами метрик и графиками, показывающими, что при нарушении структуры качество ответов падает (если модель её использует) или остаётся стабильным (если игнорирует). Дополнительно — рекомендации по улучшению промптов для принудительного использования структуры.


Связь с другими вопросами

ВопросТема
190Как спроектировать Agentic RAG-систему?
191Какие типы агентов бывают в RAG?
192Как модель принимает решение о вызове инструмента?
193Как обрабатывать ошибки при вызове инструментов?
194Как обеспечить безопасность Agentic RAG?
196Как тестировать Agentic RAG end-to-end?

Навигация