Перейти к основному содержимому

Сэмплирование

Устарело с момента появления

MCP 2026-07-28 убрал сэмплирование как push-запрос, управляемый возможностями. Теперь клиент обрабатывает input-запросы sampling/createMessage внутри собственного цикла раундов MRTR, поэтому вызывающий client.tools().call по-прежнему видит один вызов.

Весь этот вид устарел с момента появления: Client::map_sampling помечен #[deprecated] и требует #[allow(deprecated)]. Кроме того, атрибутный макрос #[sampling] недоступен в сборке по умолчанию — он относится к легаси-модели с серверным push. Регистрируйте обработчик явным вызовом map_sampling.

В MCP именно клиент отвечает за выполнение запросов на сэмплирование LLM, инициируемых серверами. В отличие от традиционных архитектур, клиент:

  • Владеет доступом к модели и ключами API
  • Применяет локальные политики (стоимость, конфиденциальность, ограничения скорости)
  • Является посредником во всём взаимодействии с языковыми моделями

Серверы никогда не общаются с LLM напрямую — они только запрашивают сэмплирование.

Важная концептуальная модель

  • Сервер запрашивает сэмплирование
  • Клиент выполняет сэмплирование
  • Клиент принимает решение:
    • какую модель использовать
    • поддерживаются ли инструменты
    • как обрабатываются запросы
  • Клиент возвращает структурированные результаты серверу

Конфигурация клиента​

Поддержка сэмплирования должна быть явно включена на клиенте:

let mut client = Client::new()
.with_options(|opt| opt
.with_sampling(|s| s.with_tools()));
  • with_sampling() включает поддержку сэмплирования
  • with_tools() разрешает вызовы инструментов в процессе сэмплирования

Именно регистрация обработчика заставляет клиента объявлять clientCapabilities.sampling в каждом запросе; сервер может запросить только тот вид, который клиент объявил, а запрос к необъявившему клиенту приводит к ошибке, а не к подвисанию раунда.

Обработчик сэмплирования​

Зарегистрируйте обработчик через Client::map_sampling. Он получает CreateMessageRequestParams и возвращает CreateMessageResult или Result с ним, если может завершиться ошибкой.

use neva::prelude::*;
use neva::types::sampling::{CreateMessageRequestParams, CreateMessageResult};

async fn complete(params: CreateMessageRequestParams) -> CreateMessageResult {
// Здесь логика сэмплирования на стороне клиента
CreateMessageResult::assistant()
.with_model("o3-mini")
.with_content("...")
.end_turn()
}

#[tokio::main]
async fn main() -> Result<(), Error> {
let mut client = Client::new()
.with_options(|opt| opt.with_default_http());

// Устарело с момента появления, как и весь вид sampling.
#[allow(deprecated)]
client.map_sampling(complete);

client.connect().await?;

// Раунды MRTR происходят внутри этого единственного вызова.
let result = client.tools().call("summarize_report", [("topic", "EMEA")]).await?;

client.disconnect().await
}

Обработчик вызывается один раз на каждый раунд, в котором сервер вызывает Context::sample().

Обработчик, который может завершиться ошибкой​

Обращение к модели может не удаться — сервер модели недоступен, ключ неверен, контекст слишком длинный. Обработчик, который может завершиться ошибкой, возвращает Result<CreateMessageResult, Error>:

use neva::prelude::*;
use neva::types::sampling::{CreateMessageRequestParams, CreateMessageResult};

async fn complete(params: CreateMessageRequestParams) -> Result<CreateMessageResult, Error> {
if params.messages.is_empty() {
return Err(Error::new(ErrorCode::InvalidParams, "nothing to sample"));
}

Ok(CreateMessageResult::assistant()
.with_model("o3-mini")
.with_content("...")
.end_turn())
}

#[tokio::main]
async fn main() -> Result<(), Error> {
let mut client = Client::new()
.with_options(|opt| opt.with_default_http());

#[allow(deprecated)]
client.map_sampling(complete);

client.connect().await?;
client.disconnect().await
}

Куда уходит ошибка, зависит от поколения протокола. В MCP 2026-07-28 у сервера нет запроса, на который можно ответить, — сэмпл служит входными данными собственного вызова клиента, — поэтому ошибкой завершается вызов, который запросил сэмпл. Под legacy-spec ошибка становится ответом на серверный sampling/createMessage или завершает задачу сэмплирования.

Чтобы отвечать настоящей моделью, мост svir превращает запрос в обращение к модели, а её ответ — в сэмпл; сбой модели преобразуется в этот Error, так что обработчику достаточно ?.

Под флагом legacy-spec

Сэмплирование работает как серверный push-запрос, а атрибутный макрос #[sampling] регистрирует обработчик за вас. См. Легаси-спецификация.

Анализ запросов на сэмплирование​

Входящий CreateMessageRequestParams содержит:

  • Сообщения запроса
  • Системный запрос
  • Предпочтения модели
  • Метаданные инструментов
  • Предыдущие результаты инструментов (для многошагового сэмплирования)

Доступ к текстовым запросам​

let prompts: Vec<&TextContent> = params.text().collect();

Включает все накопленные текстовые сообщения пользователя и ассистента.

Определение запросов на использование инструментов​

Клиент может проверить, разрешает ли или ожидает ли сервер использование инструментов через tool_choice:

if params.tool_choice.is_some_and(|c| !c.is_none()) {
// Модели разрешено или требуется вызвать инструменты
}

Это позволяет клиенту решить, производить ли вызовы инструментов или конечный текст.

Использование инструментов​

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

CreateMessageResult::assistant()
.with_model("gpt-5")
.use_tools([
("get_weather", ("city", "London"))
])
примечание
  • Выполнение инструментов всегда осуществляется сервером
  • Клиент только возвращает намерение вызвать инструменты
  • Аргументы инструментов должны соответствовать схеме инструмента

Обработка результатов инструментов​

После выполнения инструментов сервером он отправит повторный запрос на сэмплирование, содержащий результаты инструментов.

Эти результаты доступны через:

let results: Vec<&ToolResult> = params.results().collect();

На этом этапе клиент обычно должен:

  • Интерпретировать выходные данные инструментов
  • Сформировать финальный ответ ассистента
  • Завершить шаг сэмплирования

Формирование финальных ответов​

Для возврата обычного сообщения ассистента и завершения цикла сэмплирования:

CreateMessageResult::assistant()
.with_model("gpt-5")
.with_content("Final response text")
.end_turn()

Вызов end_turn() сигнализирует серверу о завершении сэмплирования.

Когда настраивать сэмплирование клиента​

Рассмотрите пользовательскую логику сэмплирования, когда:

  • Нужно интегрировать проприетарные или локальные модели
  • Требуется точный контроль стоимости или задержки
  • Нужна фильтрация или аудит запросов
  • Требуются детерминированные или политически-управляемые ответы

Обучение на примерах​

Полный рабочий пример доступен здесь.