0. TL;DR
Что это и зачем
Исследование предлагает Agent-Event-Coder (AEC) — мультиагентный фреймворк, который превращает извлечение событий из текста в задачу генерации кода. Вместо прямого промптинга LLM, система использует 4 специализированных агента, которые последовательно анализируют текст, планируют извлечение, генерируют код и проверяют его на соответствие схеме. Результат: +5-10% точности по сравнению с базовыми методами на всех протестированных моделях.
Какую проблему решает
Метод решает две ключевые проблемы zero-shot извлечения событий: контекстную неоднозначность (слово “strike” может означать забастовку или удар) и структурную точность (LLM часто нарушают схему данных, добавляя несуществующие поля или неверные типы). AEC даёт +7.8% на идентификации триггеров и +6.0% на классификации событий (ACE 2005, Llama3-8B).
Барьер входа
Средний: Нужна адаптация под свою задачу. Требуется:
- Определить схему событий в виде Python dataclass
- Настроить промпты для 4 агентов
- Реализовать логику проверки (или использовать готовую из репозитория)
- Доступ к LLM API (GPT-4, Claude, Llama)
Готового “скопируй-вставь” промпта нет — это архитектурный паттерн, требующий интеграции.
Ключевой концепт
Извлечение событий = компиляция кода. LLM обучены на огромных объёмах кода и умеют работать со структурированными данными лучше, чем с абстрактными инструкциями. Представляя схему события как Python класс, мы получаем:
- Детерминированную валидацию — код либо компилируется, либо нет
- Точную диагностику — ошибки типов, отсутствующие поля, нарушения схемы
- Итеративное исправление — как отладка программы, а не угадывание правильного формата
Вместо “извлеки событие в JSON” → “напиши код, который создаёт объект EventObject с полями из схемы”. Это превращает размытую задачу в точную инженерную спецификацию.
Рамочная структура метода
[RETRIEVAL] → Генерация примеров событий для контекста
↓
[PLANNING] → k гипотез (триггер + тип события + уверенность + обоснование)
↓
[CODING] → Генерация Python кода для гипотезы с max уверенностью
↓
[VERIFICATION] → 3 проверки (семантика, типы, структура)
↓
├─ Passed → Финальный результат
└─ Failed → Патч кода (до t попыток) ИЛИ откат к следующей гипотезе
Готовый промпт для старта
Промпта “скопируй-вставь” нет — AEC это архитектура из 4 агентов. Ниже — упрощённая версия для понимания логики Planning Agent:
# System
Ты — ассистент для извлечения событий.
Получив текст и определения типов событий (Python dataclass),
верни JSON массив объектов с ключами 'trigger' и 'event_type'.
# User
Определения событий:
@dataclass
class Databreach:
mention: str
tool: List[str]
number_of_data: List[str]
victim: List[str]
time: List[str]
place: List[str]
@dataclass
class Ransom:
mention: str
tool: List[str]
damage_amount: List[str]
victim: List[str]
time: List[str]
price: List[str]
place: List[str]
Текст:
{твой_текст}
Верни JSON массив вида:
[
{"trigger": "<слово-триггер>", "event_type": "<тип события>",
"confidence": <0-1>, "rationale": "<почему это событие>"}
]
Пример для "Хакеры потребовали миллион долларов после взлома серверов банка в пятницу":
[
{"trigger": "потребовали", "event_type": "Ransom",
"confidence": 0.9, "rationale": "Требование выкупа — ключевой признак Ransom"},
{"trigger": "взлома", "event_type": "Databreach",
"confidence": 0.85, "rationale": "Проникновение в систему указывает на утечку данных"}
]
Это только Planning Agent. Полная система требует интеграции всех 4 агентов + логики проверки.
1. Суть исследования
Извлечение событий (Event Extraction) — задача структурированного предсказания: найти в тексте триггер события (например, “забастовка") и его аргументы (кто, где, когда). Традиционные методы требуют размеченных примеров для каждого типа события, что дорого и не масштабируется. Zero-shot подход позволяет работать с новыми типами событий, используя только их текстовое описание.
Проблема: прямой промптинг LLM даёт неполные или структурно некорректные результаты. Модель может неправильно классифицировать триггер (перепутать “strike” как удар вместо забастовки), пропустить аргументы или нарушить схему данных (добавить несуществующее поле, использовать неверный тип).
Исследователи из University of Electronic Science and Technology of China предложили AEC — мультиагентный фреймворк, который разбивает задачу на 4 специализированных этапа: поиск примеров, планирование гипотез, генерация кода и верификация. Ключевая идея: представить схему события как Python класс, что позволяет детерминированно проверять корректность через компиляцию кода.
Эксперименты на 5 датасетах (новости, биомедицина, кибербезопасность) и 6 LLM (Llama3, Qwen2.5, GPT-3.5/4) показали стабильное превосходство над базовыми методами: +3-10% на идентификации триггеров, +4-6% на классификации событий, +2-4% на извлечении аргументов.
2. Что работает
Группировка по смыслу
Что работает:
- Мультиагентная декомпозиция — разделение на 4 специализированных агента даёт +5-8% точности vs прямого промптинга
- Schema-as-code — представление схемы как Python класса обеспечивает детерминированную валидацию
- Dual-loop refinement — двойной цикл (патч кода → откат к следующей гипотезе) повышает структурную точность на 7-10%
- Retrieval Agent — генерация примеров событий улучшает контекстное понимание на 5-6%
Что НЕ работает (из сравнения с базовыми методами):
- DirectEE (прямой промптинг) — даёт на 10-15% хуже результаты из-за отсутствия структурированного рассуждения
- ChatIE (диалоговый подход) — итеративные вопросы без явной декомпозиции уступают AEC на 3-7%
Главный вывод:
Разделение задачи на специализированные этапы + представление схемы как исполняемого кода = систематическое устранение неоднозначности и гарантия структурной корректности. LLM лучше работают с кодом, чем с абстрактными инструкциями о формате JSON.
Детальный разбор ключевых элементов
1. Retrieval Agent — генерация контекстных примеров
Что: Агент генерирует k примеров предложений (по умолчанию k=3), которые иллюстрируют, как данный тип события может быть выражен в тексте.
Почему работает: Вдохновлено analogical prompting — примеры служат “мостом” между абстрактной схемой и конкретным текстом. Они помогают модели понять, какие лингвистические паттерны соответствуют событию, снижая ошибки ранней классификации.
Пример: Для события Databreach с ролями (tool, number-of-data, victim, time, place) агент генерирует:
"Hackers used malware to breach the company's database last Tuesday,
stealing 10,000 customer records from its New York office."
Цифры: Удаление Retrieval Agent снижает TI на 5.6% и TC на 6.3% (FewEvent, Llama3-70B). На GPT-4o падение ещё больше: -5.6% TI, -6.3% TC.
2. Planning Agent — ранжированные гипотезы с обоснованием
Что: Агент анализирует текст и генерирует k гипотез вида (триггер, тип_события, уверенность, обоснование), отсортированных по уверенности.
Почему работает: Вместо единственного предсказания модель рассматривает несколько вариантов. Обоснование (rationale) заставляет модель явно объяснить свой выбор, что улучшает качество рассуждений. Если первая гипотеза не проходит проверку, система откатывается к следующей.
Пример: Для текста “Union leaders announced a city-wide strike to demand better wages":
[
{"trigger": "strike", "event_type": "Protest",
"confidence": 0.9, "rationale": "Забастовка — форма протеста"},
{"trigger": "announced", "event_type": "Announcement",
"confidence": 0.7, "rationale": "Объявление о действии"}
]
Цифры: Удаление rationale снижает TC на 4.5% (FewEvent, Llama3-70B). Увеличение k с 1 до 3 даёт +3.6% TI и +3.4% TC (ACE, GPT-4o).
3. Coding Agent — генерация исполняемого кода
Что: Агент преобразует выбранную гипотезу в Python код, который создаёт объект EventObject с заполненными полями из схемы.
Почему работает: LLM обучены на огромных объёмах кода и понимают структурированные данные лучше через код, чем через JSON. Код можно детерминированно проверить компилятором, что невозможно с текстовым JSON.
Пример: Для гипотезы ("strike", "Protest", 0.9) генерируется:
event = EventObject(
event_type="Protest",
trigger="strike",
arguments={
"initiator": ["Union leaders"],
"location": ["city-wide"],
"purpose": ["demand better wages"],
"reason": ["unfair labor practices"]
}
)
Цифры: Coding Agent с верификацией даёт +7.1% AC vs базового GuidelineEE (ACE, Llama3-70B).
4. Verification Agent — трёхступенчатая проверка
Что: Агент выполняет 3 детерминированных теста:
- Semantic Check — триггер присутствует в тексте и семантически совместим с типом события
- Type Check — все аргументы соответствуют типам из схемы (List[str], str и т.д.)
- Structural Check — код компилируется, содержит только разрешённые поля, сериализуется
Почему работает: Детерминированная проверка даёт точную диагностику ошибок (как компилятор), что позволяет итеративно исправлять код. Если проверка не пройдена, агент получает конкретное сообщение об ошибке (например, “ValidationError: field ‘trigger’ missing") и патчит код.
Пример ошибки:
# Сгенерированный код
event = EventObject(
event_type="Protest",
arguments={"initiator": ["Union leaders"]} # Нет поля trigger!
)
# Verification Agent возвращает:
(False, "ValidationError: field 'trigger' missing")
# Патч:
event = EventObject(
event_type="Protest",
trigger="strike", # Добавлено
arguments={"initiator": ["Union leaders"]}
)
Цифры: Удаление Verification Loop снижает AC на 6.2% (FewEvent, Llama3-70B) и на 7.3% (ACE, GPT-4o). Удаление только Structural Check даёт -1.1% AC (менее критично, но важно).
5. Dual-Loop Refinement — двойной цикл коррекции
Что: Внешний цикл перебирает k гипотез (от самой уверенной к менее уверенной). Внутренний цикл делает до t попыток (по умолчанию t=3) исправить код для текущей гипотезы. Если все попытки исчерпаны — откат к следующей гипотезе.
Почему работает: Даже если первая гипотеза верна, код может содержать ошибки (неверный тип, лишнее поле). Внутренний цикл исправляет технические ошибки. Если гипотеза в корне неверна — внешний цикл переключается на альтернативу.
Пример:
- Гипотеза 1 (confidence=0.9): “strike” → Protest. Код содержит ошибку типа → патч → успех.
- Если патч не помог за 3 попытки → откат к гипотезе 2 (confidence=0.7): “announced” → Announcement.
Цифры: Увеличение t с 1 до 3 даёт +2.2% TI и +4.4% TC (ACE, GPT-4o). Дальнейшее увеличение до t=5 даёт минимальный прирост (+0.1-0.5%), что указывает на насыщение.
3. Чего избегать
| Антипаттерн | Почему вредит | Что делать вместо |
|---|---|---|
| Прямой промптинг без декомпозиции | LLM пытается решить всё за один шаг → пропускает аргументы, нарушает схему. -10-15% точности vs AEC | Разбить на этапы: планирование → кодирование → проверка |
| JSON вместо кода | Нет детерминированной валидации → модель добавляет несуществующие поля, неверные типы | Использовать Python dataclass + Pydantic для схемы |
| Одна гипотеза без альтернатив | Если первое предсказание неверно — нет возможности исправить | Генерировать k гипотез с уверенностью, откатываться при ошибке |
| Отсутствие примеров (Retrieval) | Модель не понимает контекст → путает полисемичные триггеры ("strike” как удар vs забастовка). -5.6% TI | Генерировать примеры событий для контекста |
4. Промпты
Рамочный промпт (структура Planning Agent)
# БЛОК 1: Роль
System: Ты — ассистент для извлечения событий.
← Зачем: задаёт режим работы модели (event extraction mode)
# БЛОК 2: Задача
Получив текст и определения типов событий (Python dataclass),
верни JSON массив объектов с ключами 'trigger', 'event_type',
'confidence', 'rationale'.
← Зачем: точная спецификация выхода
# БЛОК 3: Схема событий
User: Определения событий:
@dataclass
class {EventType1}:
mention: str
{role1}: List[str]
{role2}: List[str]
...
@dataclass
class {EventType2}:
mention: str
{role1}: List[str]
...
← Зачем: даёт модели структурированное представление возможных событий
Пример заполнения для кибербезопасности:
@dataclass
class Databreach:
mention: str
tool: List[str] # инструмент атаки
number_of_data: List[str] # объём утечки
victim: List[str] # жертва
time: List[str]
place: List[str]
# БЛОК 4: Входной текст
Текст:
{твой_текст}
← Пример: "Хакеры использовали фишинг для взлома базы данных компании
в среду, похитив 5000 записей из больницы."
# БЛОК 5: Формат вывода
Верни JSON массив вида:
[
{
"trigger": "<слово-триггер из текста>",
"event_type": "<один из типов выше>",
"confidence": <число 0-1>,
"rationale": "<почему это событие данного типа>"
}
]
← Зачем: структурирует ответ для автоматического парсинга
Пример заполнения:
[
{
"trigger": "взлома",
"event_type": "Databreach",
"confidence": 0.9,
"rationale": "Проникновение в базу данных — ключевой признак утечки"
}
]
Готовый промпт (Planning Agent для кибербезопасности)
System: Ты — ассистент для извлечения событий из текстов о кибербезопасности.
Получив текст и определения типов событий (Python dataclass), верни JSON массив
объектов с ключами 'trigger', 'event_type', 'confidence', 'rationale'.
User: Определения событий:
@dataclass
class Databreach:
"""Утечка данных — несанкционированный доступ к информации"""
mention: str
tool: List[str] # инструмент атаки (malware, phishing, SQL injection)
number_of_data: List[str] # объём утечки (количество записей, пользователей)
victim: List[str] # организация-жертва
time: List[str] # когда произошло
place: List[str] # где (страна, город, система)
@dataclass
class Ransom:
"""Вымогательство — требование выкупа за данные/доступ"""
mention: str
tool: List[str] # инструмент (ransomware, DDoS)
damage_amount: List[str] # ущерб в деньгах
victim: List[str] # жертва
time: List[str]
price: List[str] # сумма выкупа
place: List[str]
@dataclass
class PatchVulnerability:
"""Исправление уязвимости — выпуск патча безопасности"""
mention: str
patch: List[str] # название патча/обновления
cve: List[str] # CVE идентификатор
time: List[str]
vulnerable_system: List[str] # уязвимая система
Текст:
Хакеры потребовали миллион долларов после взлома серверов банка в пятницу,
используя программу-вымогатель. Компания выпустила экстренный патч для
устранения уязвимости CVE-2024-1234 в веб-сервере.
Верни JSON массив вида:
[
{
"trigger": "<слово-триггер из текста>",
"event_type": "<Databreach | Ransom | PatchVulnerability>",
"confidence": <0.0-1.0>,
"rationale": "<почему это событие данного типа — 1 предложение>"
}
]
Требования:
- Триггер должен быть точным словом/фразой из текста
- Confidence отражает уверенность на основе контекста
- Rationale объясняет связь триггера с типом события
- Если несколько событий — верни все в массиве, отсортированные по confidence (убывание)
Ожидаемый ответ:
[
{
"trigger": "потребовали",
"event_type": "Ransom",
"confidence": 0.95,
"rationale": "Требование выкупа — прямой признак вымогательства"
},
{
"trigger": "взлома",
"event_type": "Databreach",
"confidence": 0.9,
"rationale": "Проникновение в серверы указывает на утечку данных"
},
{
"trigger": "выпустила",
"event_type": "PatchVulnerability",
"confidence": 0.85,
"rationale": "Выпуск патча для CVE — исправление уязвимости"
}
]
Ключевые элементы:
- "Python dataclass" — использование знакомого формата кода повышает точность структурированного вывода (исследование показывает, что LLM лучше работают с кодом)
- "confidence": <0.0-1.0> — числовая уверенность позволяет ранжировать гипотезы для dual-loop refinement
- "rationale" — явное обоснование улучшает качество рассуждений на 4.5% TC (ablation study)
- Конкретные примеры в комментариях — “(malware, phishing, SQL injection)” вместо абстрактного “инструмент атаки” снижает неоднозначность
5. Оригинальные материалы из исследования
Оригинальный промпт: Verification Agent (Semantic + Type + Structural Check)
System: Ты — верификатор для извлечения событий. Проверь следующий
объект события в контексте исходного текста и определения.
Определи любые семантические, типовые или структурные ошибки.
User: Текст: "Во вторник компания выпустила патч для уязвимости в веб-сервере."
Определение события:
@dataclass
class PatchVulnerability:
mention: str
patch: List[str]
cve: List[str]
time: List[str]
vulnerable_system: List[str]
Кандидат объекта события:
{
"event_type": "PatchVulnerability",
"trigger": "выпустила",
"arguments": {
"patch": ["security update"],
"cve": ["CVE-2021-1234"],
"time": ["Во вторник"],
"vulnerable_system": [1234] # ОШИБКА: должно быть List[str], а не List[int]
}
}
Верни список сообщений об ошибках или пустой список, если событие валидно.
Результат:
[
"Type error: vulnerable_system must be List[str], got List[int]. Value 1234 should be '1234' or 'веб-сервер'."
]
Описание конкретных механик промпта:
Трёхуровневая проверка (из статьи):
- T1 (Semantic):
assert "выпустила" in Text == True+ семантическая совместимость триггера с типом события - T2 (Type):
assert all(isinstance(x, str) for x in arguments["vulnerable_system"]) == True— проверка типов через Python - T3 (Structural):
assert EventObject.model_validate(event) == True— Pydantic валидация структуры
- T1 (Semantic):
Детерминированная диагностика: Вместо размытого “что-то не так” модель получает точное сообщение как от компилятора: “ValueError: initiator must be List[str]” или “ValidationError: field ‘trigger’ missing”
Итеративное исправление: Если проверка не пройдена, Coding Agent получает ошибку и делает до t=3 попыток исправить код. Если не помогло — откат к следующей гипотезе из Planning Agent.
6. Пример (было/стало)
Пример: Извлечение события кибератаки из новостного текста
Контекст: Задача — извлечь событие Databreach из текста о кибератаке. Схема требует идентифицировать триггер, инструмент атаки, жертву, время и объём утечки.
Было (DirectEE — прямой промптинг):
Промпт:
Извлеки событие типа Databreach из текста. Верни JSON с полями:
trigger, tool, victim, time, number_of_data.
Текст: "Хакеры использовали фишинг для взлома базы данных больницы
в среду, похитив 5000 записей пациентов."
Результат:
{
"trigger": "взлома",
"tool": "фишинг",
"victim": "больница",
"time": "в среду",
"number_of_data": "5000",
"attack_type": "phishing" // ОШИБКА: несуществующее поле
}
Проблемы:
- Добавлено поле
attack_type, которого нет в схеме - Нет валидации типов (все значения строки, а не List[str])
- Нет проверки на полноту (пропущено “записей пациентов")
Стало (AEC — мультиагентный подход):
Шаг 1 — Retrieval Agent генерирует пример:
"Attackers used malware to breach the company's database last Tuesday,
stealing 10,000 customer records from its New York office."
Шаг 2 — Planning Agent создаёт гипотезы:
[
{
"trigger": "взлома",
"event_type": "Databreach",
"confidence": 0.95,
"rationale": "Проникновение в базу данных — прямой признак утечки"
},
{
"trigger": "похитив",
"event_type": "Databreach",
"confidence": 0.8,
"rationale": "Кража данных также указывает на утечку"
}
]
Шаг 3 — Coding Agent генерирует код для первой гипотезы:
event = EventObject(
event_type="Databreach",
trigger="взлома",
arguments={
"tool": ["фишинг"],
"victim": ["больницы"],
"time": ["в среду"],
"number_of_data": ["5000 записей пациентов"]
}
)
Шаг 4 — Verification Agent проверяет:
- ✅ T1 (Semantic): “взлома” есть в тексте, семантически совместимо с Databreach
- ✅ T2 (Type): все аргументы List[str]
- ✅ T3 (Structural): код компилируется, только разрешённые поля
Финальный результат:
EventObject(
event_type="Databreach",
trigger="взлома",
arguments={
"tool": ["фишинг"],
"victim": ["больницы"],
"time": ["в среду"],
"number_of_data": ["5000 записей пациентов"]
}
)
Что изменилось и почему сработало:
- Retrieval Agent — пример события дал модели контекст, как должна выглядеть утечка данных
- Planning Agent — несколько гипотез с обоснованием позволили выбрать наиболее уверенный триггер
- Coding Agent — генерация Python кода вместо JSON обеспечила строгую типизацию (List[str])
- Verification Agent — детерминированная проверка отсекла несуществующее поле
attack_typeи гарантировала соответствие схеме
Результат: +100% структурная корректность (0 нарушений схемы vs 1 в DirectEE), полнота аргументов (все роли заполнены).
7. Ограничения
На чём тестировалось:
- 5 датасетов: FewEvent (общие новости), ACE 2005 (новости), GENIA (биомедицина), SPEED (эпидемиология), CASIE (кибербезопасность)
- 6 LLM: Llama3-8B/70B, Qwen2.5-14B/72B, GPT-3.5-turbo, GPT-4o
- Только zero-shot (без обучающих примеров)
Когда метод может не работать:
- Сложные многословные триггеры: CASIE содержит 54.6% многословных триггеров ("data breach incident") — для них может потребоваться дополнительная настройка
- Очень длинные документы: GENIA и CASIE имеют 250-280 токенов на документ — для более длинных текстов может потребоваться chunking
- Домены без чётких схем: метод требует явного определения ролей события в виде dataclass — для слабоструктурированных доменов это может быть сложно
Важные оговорки авторов:
- Производительность зависит от качества базовой LLM — на Llama3-8B результаты на 10-15% хуже, чем на GPT-4o
- Увеличение k (гипотез) и t (попыток патча) даёт прирост, но насыщается при k=3, t=3 — дальнейшее увеличение добавляет вычислительные затраты без значимого улучшения
- Метод не тестировался на few-shot сценариях — возможно, добавление примеров даст дополнительный прирост
8. Оценка
| Критерий | Макс. | Баллы | Обоснование |
|---|---|---|---|
| Новизна | 35 | 30 | Первое применение мультиагентной архитектуры + schema-as-code для zero-shot event extraction |
| Практичность | 35 | 22 | Архитектурный паттерн с чёткой логикой, но требует интеграции 4 агентов + валидации |
| Воспроизводимость | 25 | 20 | Детальное описание промптов, код опубликован, но нужна адаптация под свою задачу |
| Доказательства | 20 | 18 | 5 датасетов, 6 LLM, ablation studies — солидная эмпирическая база |
| Штраф за барьер | -25 | Высокий: Нужна интеграция 4 агентов, реализация логики проверки, определение схем как Python dataclass, настройка промптов | |
| ИТОГО | 65/100 |
Детализация штрафа за барьер входа
Уровень: Высокий
Что нужно для применения:
- Определить схему событий в виде Python dataclass с типами полей
- Реализовать 4 агента (Retrieval, Planning, Coding, Verification) с промптами
- Написать логику трёхступенчатой проверки (Semantic, Type, Structural)
- Реализовать dual-loop refinement (внешний цикл по гипотезам, внутренний — по патчам)
- Интегрировать с LLM API (GPT-4, Claude, Llama)
- Настроить параметры k (гипотез) и t (попыток патча) под свою задачу
Почему штраф -25, а не -35:
- Код опубликован на GitHub — можно использовать как основу
- Промпты детально описаны в статье — не нужно изобретать с нуля
- Не требуется обучение модели или GPU — работает через API
Почему НЕ “Средний” барьер (-10-15):
- Это не готовый промпт “скопируй-вставь”
- Требуется программирование (Python) для интеграции агентов
- Нужно адаптировать схемы под свой домен
Интерпретация
Категория: Полезное
Главная ценность: Архитектурный паттерн для структурированного извлечения информации через мультиагентную декомпозицию + детерминированную валидацию. Показывает, что представление схемы как кода даёт +5-10% точности vs прямого промптинга.
Кому полезно:
- ML-инженерам, строящим системы извлечения событий/сущностей
- Разработчикам NLP-пайплайнов, работающим со структурированными данными
- Исследователям, изучающим мультиагентные системы для LLM
Кому НЕ полезно:
- Практикам, ищущим готовый промпт для быстрого применения (требуется интеграция)
- Тем, кто работает с неструктурированными задачами без чётких схем
- Проектам с ограниченным бюджетом API (dual-loop требует множественных вызовов LLM)
Ресурсы:
- Код и данные: https://github.com/UESTC-GQJ/Agent-Event-Coder
- Базовый фреймворк: TextEE (Huang et al. 2024)
- Вдохновение: Analogical Prompting (Yasunaga et al. 2024)
