Ваш агент опять проигнорировал вызов инструмента? Это лечится
Вы составляете идеальный промпт, описываете инструменты, но в критический момент LLM-агент вместо вызова search_web начинает философствовать. Знакомо? Проблема не в вашем промпте. Она глубже — в том, как модель принимает решение о tool-call. И оказывается, это решение можно предсказать и даже принудительно активировать за 2-3 токена до того, как модель сама об этом "подумает".
Что такое агентский сигнал Â и почему о нём молчат
В конце 2025 года исследователи из Anthropic и независимых лаб обнаружили любопытный артефакт. Анализируя скрытые состояния (hidden states) больших моделей вроде Claude 4, Gemini 3.0 и GPT-4.5 Turbo во время работы агентов, они нашли устойчивый паттерн. Внутри активаций определённых слоёв за несколько шагов до генерации JSON-схемы или ключевых слов вроде "function_call" появляется чёткий сигнал. Его условно обозначили Â (Agentic signal).
 — это не магический токен. Это линейно-сепарабельное подпространство в скрытом пространстве модели, которое коррелирует с намерением выполнить инструментальное действие. Проще говоря, модель уже "решила" вызвать инструмент, но ещё не начала об этом говорить.
Звучит как академическая абстракция? На практике это значит, что если детектировать Â, можно:
- На 26-58% сократить случаи пропущенных tool-call (цифра зависит от задачи и модели).
- Ускорить реакцию агента, предсказывая необходимость вызова заранее.
- Уменьшить количество токенов в диалоге, прерывая бессмысленные рассуждения.
Линейный пробник — ваш ключ к скрытому состоянию
Вам не нужно дообучать 400-миллиардную модель. Достаточно обучить крошечный линейный классификатор (пробник) на нескольких сотнях примеров. Он будет смотреть на скрытые состояния модели и предсказывать: "через N токенов будет tool-call".
1 Соберите датасет активаций
Запустите своего агента (например, на базе архитектуры из LEGO) на 300-500 различных запросах. Для каждого токена в ответе сохраняйте скрытые состояния с определённых слоёв (обычно из последней трети трансформера). Разметьте данные: 1 — если в течение следующих 5 токенов начнётся tool-call, 0 — если нет.
# Псевдокод сбора активаций для GPT-4.5 Turbo через Hugging Face (актуально на 08.03.2026)
import torch
from transformers import AutoModelForCausalLM, AutoTokenizer
model_name = "openai/gpt-4.5-turbo"
tokenizer = AutoTokenizer.from_pretrained(model_name)
model = AutoModelForCausalLM.from_pretrained(model_name, output_hidden_states=True)
hidden_states_cache = []
labels = []
for dialog in training_dialogs:
inputs = tokenizer(dialog, return_tensors="pt")
with torch.no_grad():
outputs = model(**inputs, output_hidden_states=True)
# Берём скрытые состояния с 40-го слоя (пример для 80-слойной модели)
# Точный слой нужно подбирать экспериментально
layer_hidden = outputs.hidden_states[40] # [batch, seq_len, hidden_size]
# Для каждого позиции токена смотрим вперёд на 5 токенов
for pos in range(layer_hidden.shape[1] - 5):
hidden_vector = layer_hidden[0, pos, :].numpy()
# Проверяем, есть ли в следующих токенах начало tool-call
future_tokens = tokenizer.decode(inputs['input_ids'][0, pos+1:pos+6])
label = 1 if "function_call" in future_tokens or "{\"name\"" in future_tokens else 0
hidden_states_cache.append(hidden_vector)
labels.append(label)
2 Обучите линейный классификатор
Используйте обычную логистическую регрессию или маленький MLP. Данных мало — модель должна быть простой, чтобы избежать переобучения.
from sklearn.linear_model import LogisticRegression
import numpy as np
X = np.array(hidden_states_cache)
y = np.array(labels)
# Разделяем на обучение и тест
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2)
probe = LogisticRegression(max_iter=1000, class_weight='balanced')
probe.fit(X_train, y_train)
print(f"Точность на тесте: {probe.score(X_test, y_test):.2%}")
# Хороший пробник даёт accuracy 75-90%
3 Интегрируйте детектор в пайплайн агента
Теперь в реальном времени, пока модель генерирует ответ, подключаем пробник. Как только он детектирует Â с высокой уверенностью (например, вероятность >0.8), прерываем генерацию и принудительно активируем tool-call.
def generate_with_agent_signal(prompt, tools_description, probe, layer_index=40):
"""Генерация с мониторингом агентского сигнала"""
inputs = tokenizer(prompt + tools_description, return_tensors="pt")
generated_ids = inputs['input_ids'].clone()
for step in range(max_new_tokens):
with torch.no_grad():
outputs = model(generated_ids, output_hidden_states=True)
# Получаем скрытое состояние для последнего токена
last_hidden = outputs.hidden_states[layer_index][0, -1, :].numpy().reshape(1, -1)
# Предсказываем вероятность tool-call
prob_tool_call = probe.predict_proba(last_hidden)[0, 1]
if prob_tool_call > 0.8:
# Прерываем генерацию, запускаем инструмент
print(f"Детектирован Â (вероятность {prob_tool_call:.2f}). Принудительный tool-call.")
return execute_tool_based_on_context(generated_ids)
# Иначе генерируем следующий токен как обычно
next_token_logits = outputs.logits[0, -1, :]
next_token = torch.argmax(next_token_logits).unsqueeze(0)
generated_ids = torch.cat([generated_ids, next_token.unsqueeze(0)], dim=-1)
# Если модель сама начала tool-call — тоже выходим
if tokenizer.decode(next_token).startswith("{"):
break
return tokenizer.decode(generated_ids[0])
Где выстрелит, а где даст осечку
Метод не панацея. Он даёт максимальный прирост на задачах, где модель часто "стесняется" или откладывает вызов инструмента. Например, в ассистентах для проверки мошенников, где нужно быстро запросить данные из внешней базы.
Не ждите чуда на хаотичных или творческих задачах. Если модель объективно не знает, какой инструмент использовать, Â будет слабым. Пробник — это усилитель существующего намерения, а не генератор нового.
Основные ошибки при внедрении:
- Неправильный слой: Сигнал Â живёт в разных слоях у разных моделей. Для Llama 3.3 405B его искали в слоях 55-65, для Claude 4 — ближе к концу. Начните с последней трети и проведите поиск по слоям, измеряя accuracy пробника.
- Слишком агрессивный порог: Порог 0.8 — примерный. Если поставить 0.5, будет много ложных срабатываний, и агент начнёт вызывать инструменты на ровном месте. Если 0.95 — пропустит половину случаев. Калибруйте на валидационной выборке.
- Игнорирование контекста: Пробник смотрит только на одно скрытое состояние. Иногда полезно добавить в признаки конкатенацию активаций с предыдущих 2-3 токенов или использовать простую RNN.
Что делать, если сигнал слабый
Бывает. Особенно у небольших моделей вроде Mistral 2.0 или Qwen 2.5 7B. Три приёма для усиления:
- Фокусировка на конкретном инструменте: Обучайте отдельный пробник для каждого инструмента (например, для
searchи дляcalculator). Сигналы будут чище. - Аугментация данных: Добавьте в датасет примеры, где tool-call был нужен, но модель его пропустила. Это поможет пробнику научиться выявлять "упущенные возможности".
- Ансамблирование: Возьмите скрытые состояния с нескольких слоёв, обучите на них отдельные пробники и усредните их предсказания. Стабильность вырастет.
FAQ: коротко о главном
| Вопрос | Ответ |
|---|---|
| Работает ли это с OpenAI API? | Нет. OpenAI не даёт доступ к скрытым состояниям. Метод работает только с open-source моделями, которые можно запустить локально или через совместимые API (Anyscale, Together.ai с опцией output_hidden_states). |
| На сколько увеличивается задержка? | Минимально. Линейный классификатор работает за микросекунды. Основные накладные расходы — на извлечение скрытых состояний, но это уже часть forward pass. |
| Нужно ли переобучать пробник для каждой новой версии модели? | Да. Сигнал Â дрейфует между версиями. Для GPT-4.5 и GPT-4.5 Turbo пробники будут разными. Но переобучение — это 30 минут на сбор данных и 5 минут на тренировку. |
| Есть ли готовые реализации? | На 08.03.2026 появляются первые библиотеки. Смотрите llm-probe и инструменты от интерпретируемостных групп (Anthropic, EleutherAI). |
Использование Â — это не просто хак. Это шаг к более контролируемым и предсказуемым агентам. Когда вы понимаете, что происходит внутри чёрного ящика, вы перестаёте быть заложником промптов. Вы начинаете инженерно управлять поведением.
Следующий уровень — использовать этот сигнал не только для принуждения, но и для отладки многоагентных систем. Если подсистема планирования не генерирует Â в ожидаемых ситуациях, значит, проблема в знаниях или архитектуре, а не в исполнении.
Попробуйте. Соберите датасет из 200 диалогов, обучите пробник за вечер. Даже если прирост будет не 58%, а скромные 15%, это уже окупит время. Потому что 15% — это каждый седьмой падающий tool-call, который теперь работает.