Запросы
Request говорит, на что модель должна ответить: какая модель, системный
промпт, диалог на данный момент, инструменты, которые она может вызвать, и
несколько параметров. Он собирается через Request::new(model) и цепочку
методов.
use svir::prelude::*;
fn request(question: &str) -> Request {
Request::new("qwen3-27b")
.system("Answer in one paragraph.")
.reasoning(Effort::Low)
.max_tokens(2048)
.temperature(0.2)
.user(question)
}
Билдер
Каждый метод принимает self и возвращает его, поэтому запрос собирается
одной цепочкой или переприсваивается по мере роста:
request = request.user(line);. Вызов метода с отброшенным результатом ничего
не меняет.
| Метод | Что делает |
|---|---|
system(text) | Системный промпт. Один на запрос; повторный вызов заменяет его |
user(text) | Добавляет сообщение пользователя с этим текстом |
message(message) | Добавляет сообщение, собранное через Message |
assistant(completion) | Добавляет ответ модели — с текстом, вызовами инструментов и рассуждениями |
tool_result(call_id, content) | Добавляет результат одного вызова инструмента |
tool_results(results) | Добавляет результаты, по сообщению на каждый |
tool(tool) / tools(&toolbox) | Описывает инструменты, которые модель может вызвать; см. Инструменты |
tool_choice(choice) | Может ли или должна ли модель вызвать инструмент; см. Инструменты |
response_format(format) | Ответ в JSON или в JSON по схеме; см. Структурированный вывод |
reasoning(effort) | Насколько глубоко модели рассуждать |
max_tokens(n) | Максимум генерируемых токенов. На большинстве серверов рассуждения тоже в него входят |
temperature(t) | Температура сэмплирования |
include_usage(bool) | Переопределяет умолчание клиента (включено) — запрашивать ли расход токенов |
send_reasoning(bool) | Отправляет рассуждения из прошлых ответов обратно (по умолчанию выключено) |
Неустановленное не отправляется: запрос без max_tokens или temperature
оставляет их на усмотрение сервера, а выбор инструмента или формат ответа,
оставленные по умолчанию, тоже не отправляются.
Поля открыты для чтения (request.model, request.messages) — этим
пользуется слой. Request реализует Clone и
сериализуется; см. Диалоги.
Role — это User, Assistant или Tool. Системной роли нет: каждый
протокол кладёт системный промпт в своё место, поэтому он живёт в
Request::system, а размещает его адаптер.
Сообщения и части
Message — это роль и её части по порядку. request.user(text) — сокращение
для частого случая; соберите Message, когда в реплике больше, чем текст.
use svir::prelude::*;
fn compare() -> Request {
let question = Message::user("What changed between these two?")
.with(Image::path("before.png"))
.with(Image::path("after.png"))
.with(TextFile::path("diff.patch"));
Request::new("qwen3-27b").message(question)
}
| Конструктор | Назначение |
|---|---|
Message::user(text) | Реплика пользователя. Пустой текст не добавляет части, поэтому Message::user("") с изображениями несёт только изображения |
Message::assistant(text) | Реплика модели, восстановленная из сохранённого текста. Для живого ответа используйте request.assistant(done) |
Message::tool_result(result) | Один результат инструмента |
.with(part) | Добавляет часть: &str, String, Image, TextFile, ToolCall, ToolResult, Reasoning |
Какие части допустимы для роли, проверяется при отправке запроса. Нарушение —
ErrorKind::Unsupported: ничего не выбрасывается молча.
| Роль | Части |
|---|---|
User | Текст, изображения, текстовые файлы |
Assistant | Текст, рассуждения, вызовы инструментов |
Tool | Только результаты инструментов |
Изображения и текстовые файлы описаны во Вложениях.
Глубина рассуждений
Effort — это Off, Low, Medium, High или XHigh. Если не задано,
ничего не отправляется и выбор остаётся за сервером. Off просит не
рассуждать; послушается ли модель — её дело.
use svir::prelude::*;
fn think_hard(question: &str) -> Request {
Request::new("qwen3-27b")
.reasoning(Effort::High)
// Reasoning spends the output budget too; leave room for the answer.
.max_tokens(8192)
.user(question)
}
Некоторые серверы сразу отвергают поле reasoning_effort. Клиент справляется
с этим сам: запрос отправляется ещё раз без поля, один раз, и сервер
запоминается. См.
обработку совместимости.
Сами рассуждения приходят как Event::Reasoning, пока ответ стримится, и в
Completion::reasoning в конце; см. Чтение ответа.
Чего запрос сказать не может
В svir 0.1 нет стоп-последовательностей, и запрашивается один вариант
ответа. Ответ всегда приходит потоком; complete собирает его. Если серверу
нужно поле, которого нет в таблице выше, — svir 0.1 его не передаёт.