# Переменные и макросы НадTavern Краткая, человеко‑понятная шпаргалка по тому, какие переменные и макросы доступны в шаблонах (в том числе в Prompt Blocks), как они устроены и как их правильно использовать. Док ниже соответствует текущему коду. Реализация формирует единый «контекст» переменных для всех нод пайплайна, дополняет его выходами уже выполненных нод, а узел ProviderCall добавляет свои служебные структуры для удобной сборки промпта. Ссылки на код: - Формирование контекста запроса: [build_macro_context()](agentui/api/server.py:142) - Исполнитель пайплайна и снапшот OUT: [PipelineExecutor.run()](agentui/pipeline/executor.py:136) - Узел провайдера (Prompt Blocks → provider payload): [ProviderCallNode.run()](agentui/pipeline/executor.py:650) - Шаблоны/макросы ([[...]] и {{ ... }}): [render_template_simple()](agentui/pipeline/templating.py:187) - Короткая форма [[OUTx]] (извлечение текста): [_best_text_from_outputs()](agentui/pipeline/templating.py:124) - Прямой форвард запросов: [RawForwardNode.run()](agentui/pipeline/executor.py:833) --- ## 1) Общие переменные контекста (для всех нод) Эти переменные доступны в шаблонах любой ноды. Они добавляются на стороне сервера при обработке входящего HTTP‑запроса. - model — строка с именем модели. Пример: "gpt-4o-mini" - vendor_format — вендор/протокол запроса: "openai" | "gemini" | "claude" | "unknown" - system — «системный» текст, если он был во входящем запросе; иначе пустая строка. - params — стандартные параметры генерации (можно использовать как дефолты) - params.temperature — число с плавающей точкой (по умолчанию 0.7) - params.max_tokens — целое или null - params.top_p — число (по умолчанию 1.0) - params.stop — массив строк или null - chat — сведения о чате во входящем запросе - chat.last_user — последнее сообщение пользователя (строка) - chat.messages — массив сообщений в унифицированной форме: - role — "system" | "user" | "assistant" | "tool" - content — содержимое (обычно строка) - name — опционально, строка - tool_call_id — опционально - incoming — детали ВХОДЯЩЕГО HTTP‑запроса - incoming.method — метод ("POST" и т.п.) - incoming.url — полный URL (в query ключи маскируются для логов) - incoming.path — путь (например, /v1/chat/completions) - incoming.query — строка query без вопросительного знака - incoming.query_params — объект со всеми query‑параметрами - incoming.headers — объект всех заголовков запроса - incoming.json — сырой JSON тела запроса, как прислал клиент - incoming.api_keys — удобные «срезы» ключей - incoming.api_keys.authorization — значение из заголовка Authorization (если есть) - incoming.api_keys.key — значение из query (?key=...) — удобно для Gemini Пример использования в шаблоне: - [[VAR:incoming.api_keys.key]] — возьмёт ключ из строки запроса (?key=...). - [[VAR:incoming.headers.x-api-key]] — возьмёт ключ из заголовка x-api-key (типично для Anthropic). - {{ params.temperature|default(0.7) }} — безопасно подставит число, если не задано во входящих данных. --- ## 2) Выходы нод (OUT) и ссылки на них Во время исполнения пайплайна результаты предыдущих нод собираются в снапшот OUT и доступны при рендере шаблонов следующих нод: - OUT — словарь выходов нод, ключи — id нод в пайплайне (например, "n1", "n2"). - OUT.n1, OUT.n2, ... — объект результата соответствующей ноды. Формы доступа: - Полная форма: [[OUT:n1.result.choices.0.message.content]] (или фигурными скобками: {{ OUT.n1.result.choices.0.message.content }}) - Короткая форма «просто текст»: [[OUT1]], [[OUT2]], ... Это эвристика: берётся самое вероятное «текстовое» поле из результата (см. [_best_text_from_outputs()](agentui/pipeline/templating.py:121)). Что возвращают встроенные ноды: - ProviderCall: - OUT.nX.result — сырой JSON ответа провайдера - OUT.nX.response_text — уже извлечённый «лучший текст» (строка) - RawForward: - OUT.nX.result — JSON, как пришёл от апстрима (или {"error": "...", "text": "..."} при не‑JSON ответе) Подсказка по короткой форме [[OUTx]]: - OpenAI: вернёт choices[0].message.content - Gemini: вернёт candidates[0].content.parts[0].text - Claude: склеит content[].text - Если явных полей нет — выполнит «глубокий поиск» по ключам "text"/"content" --- ## 3) Макросы подстановки и синтаксис В шаблонах доступны обе формы подстановки: 1) Квадратные скобки [[ ... ]] — простая подстановка - [[VAR:путь]] — взять значение из контекста по точечному пути Пример: [[VAR:incoming.json.max_tokens]] - [[OUT:путь]] — взять значение из OUT (см. раздел выше) Пример: [[OUT:n1.result.choices.0.message.content]] - [[OUT1]] / [[OUT2]] — короткая форма «просто текст» - [[PROMPT]] — специальный JSON‑фрагмент из Prompt Blocks (см. ниже) 2) Фигурные скобки {{ ... }} — «джинджа‑лайт» - {{ путь }} — взять значение по пути из контекста (или из OUT.* если начать с OUT.) Пример: {{ OUT.n1.result }} - Фильтр по умолчанию: {{ что-то|default(значение) }} Примеры: - {{ params.temperature|default(0.7) }} - {{ incoming.json.stop|default([]) }} - {{ anthropic_version|default('2023-06-01') }} — см. «Опциональные поля» ниже - Фигурные скобки удобны там, где нужно вставить внутрь JSON не строку, а ЧИСЛО/ОБЪЕКТ/МАССИВ без кавычек и/или задать дефолт. --- ## 4) ProviderCall: Prompt Blocks, pm.* и [[PROMPT]] Узел ProviderCall собирает ваши Prompt Blocks (блоки вида: роль/текст/вкл‑выкл/порядок) в стандартные «сообщения» и превращает их в структуру для конкретного провайдера. Внутри шаблонов этого узла доступны: - pm — «сырьевые» структуры из Prompt Blocks - Для OpenAI: - pm.messages — массив { role, content, name? } - pm.system_text — один большой текст из всех system‑блоков - Для Gemini: - pm.contents — массив { role: "user"|"model", parts: [{text}] } - pm.systemInstruction — объект вида { parts: [{text}] } или пустой {} - pm.system_text — строка - Для Claude: - pm.system_text — строка - pm.system — то же самое (удобно подставлять в поле "system") - pm.messages — массив { role: "user"|"assistant", content: [{type:"text", text:"..."}] } - [[PROMPT]] — готовый JSON‑фрагмент на основе pm, безопасный для вставки внутрь шаблона: - OpenAI → подставит: "messages": [...] - Gemini → подставит: "contents": [...], "systemInstruction": {...} - Claude → подставит: "system": "...", "messages": [...] Зачем это нужно? - Чтобы 1) удобно собирать промпт из визуальных блоков, 2) не «сломать» JSON руками. Вы можете вручную использовать {{ pm.* }}, но [[PROMPT]] — рекомендуемый и самый безопасный вариант. --- ## 5) Частые сценарии и примеры Примеры ниже можно вклеивать в поле «template» ноды ProviderCall. Они уже используют [[PROMPT]] и аккуратные дефолты. OpenAI (POST /v1/chat/completions): ``` { "model": "{{ model }}", [[PROMPT]], "temperature": {{ incoming.json.temperature|default(params.temperature|default(0.7)) }}, "top_p": {{ incoming.json.top_p|default(params.top_p|default(1)) }}, "max_tokens": {{ incoming.json.max_tokens|default(params.max_tokens|default(256)) }}, "stop": {{ incoming.json.stop|default(params.stop|default([])) }} } ``` Gemini (POST /v1beta/models/{model}:generateContent): ``` { "model": "{{ model }}", [[PROMPT]], "safetySettings": {{ incoming.json.safetySettings|default([]) }}, "generationConfig": { "temperature": {{ incoming.json.generationConfig.temperature|default(params.temperature|default(0.7)) }}, "topP": {{ incoming.json.generationConfig.topP|default(params.top_p|default(1)) }}, "maxOutputTokens": {{ incoming.json.generationConfig.maxOutputTokens|default(params.max_tokens|default(256)) }}, "stopSequences": {{ incoming.json.generationConfig.stopSequences|default(params.stop|default([])) }} } } ``` Подсказка: ключ Gemini удобно брать из строки запроса: в endpoint используйте …?key=[[VAR:incoming.api_keys.key]] Claude (POST /v1/messages): ``` { "model": "{{ model }}", [[PROMPT]], "temperature": {{ incoming.json.temperature|default(params.temperature|default(0.7)) }}, "top_p": {{ incoming.json.top_p|default(params.top_p|default(1)) }}, "max_tokens": {{ incoming.json.max_tokens|default(params.max_tokens|default(256)) }}, "system": {{ pm.system|default("") }} } ``` Подсказка: ключ Anthropic обычно передают в заголовке x-api-key. В UI‑пресете это поле уже есть в headers. RawForward (прямой форвард входящего запроса): - Поля конфигурации base_url, override_path, extra_headers проходят через те же макросы, поэтому можно подставлять динамику: - base_url: https://generativelanguage.googleapis.com - override_path: [[VAR:incoming.path]] (или задать свой) - extra_headers (JSON): `{"X-Trace":"req-{{ incoming.query_params.session|default('no-session') }}"}` --- ## 6) Опциональные/редкие поля, о которых стоит знать - anthropic_version — используется как HTTP‑заголовок для Claude ("anthropic-version"). В тело запроса не вставляется. Если нужен дефолт, задавайте его в headers (например, в конфиге ноды/шаблоне заголовков). В шаблонах тела используйте [[PROMPT]]/pm.* без anthropic_version. - stream — в MVP стриминг отключён, сервер принудительно не стримит ответ. В шаблонах можно встретить поля stream, но по умолчанию они не включены. --- ## 7) Когда использовать [[...]] и когда {{ ... }} - Внутрь JSON как ОБЪЕКТ/МАССИВ/ЧИСЛО: используйте {{ ... }} (фигурные скобки вставляют «как есть», без кавычек, и умеют |default(...)) - Для строк/URL/заголовков/простых значений: можно использовать [[...]] (квадратные скобки удобны и короче писать) Примеры: - {{ pm.contents }} — вставит массив как настоящий массив (без кавычек) - {{ params.temperature|default(0.7) }} — безопасный дефолт для числа - [[VAR:incoming.api_keys.authorization]] — быстро подставить строку Authorization --- ## 8) Отладка и рекомендации - ProviderCall печатает в консоль DEBUG сведения: выбранный провайдер, конечный URL, первые символы тела запроса — удобно для проверки корректности шаблона. - Если «ничего не подставилось»: 1) Проверьте, что вы НЕ передаёте сырое входное тело напрямую в ProviderCall (узел строит тело из шаблона и Prompt Blocks). 2) Убедитесь, что итоговый JSON валиден (закрывающие скобки, запятые). 3) Проверьте точность путей в макросах (OUT vs OUTx, правильные id нод n1/n2/...). - Для ссылок на выходы предыдущих нод используйте [[OUT1]] как «просто текст», либо полные пути [[OUT:n1...]] для точного фрагмента. --- ## 9) Быстрая памятка по ключам доступа - Gemini: [[VAR:incoming.api_keys.key]] — рекомендовано; ключ приходит в query (?key=...). - OpenAI: [[VAR:incoming.headers.authorization]] (или [[VAR:incoming.api_keys.authorization]]) — стандартный Bearer‑токен. - Anthropic: [[VAR:incoming.headers.x-api-key]] — ключ в заголовке. --- ## 10) Ссылки на реализацию (для интересующихся деталями) - Контекст (переменные): [build_macro_context()](agentui/api/server.py:142) - Исполнение пайплайна, зависимости, снапшоты OUT: [PipelineExecutor.run()](agentui/pipeline/executor.py:136) - Узел провайдера (Prompt Blocks → провайдер): [ProviderCallNode.run()](agentui/pipeline/executor.py:650) - PM‑структуры для шаблонов: [ProviderCallNode._blocks_struct_for_template()](agentui/pipeline/executor.py:592) - Подстановка [[PROMPT]], макросы, дефолты: [render_template_simple()](agentui/pipeline/templating.py:187) - Короткая форма [[OUTx]] и поиск «лучшего текста»: [_best_text_from_outputs()](agentui/pipeline/templating.py:124) - Прямой форвард входящего запроса: [RawForwardNode.run()](agentui/pipeline/executor.py:833) - Детекция вендора по входному payload: [detect_vendor()](agentui/common/vendors.py:8) Удачного редактирования! --- ## Пользовательские переменные (SetVars) — «для людей» Задача: в начале пайплайна положить свои значения и потом использовать их в шаблонах одной строкой — например [[MY_KEY]] или {{ MAX_TOKENS }}. Где это в UI - В левой панели добавьте ноду SetVars и откройте её в инспекторе. - Жмите «Добавить переменную», у каждой переменной есть три поля: - name — имя переменной (латинские буквы/цифры/подчёркивание, не с цифры): MY_KEY, REGION, MAX_TOKENS - mode — режим обработки значения: - string — строка, в которой работают макросы ([[...]] и {{ ... }}) - expr — «мини‑формула» без макросов (подробнее ниже) - value — собственно значение Как потом вставлять переменные - Для строк (URL/заголовки/текст) — квадратные скобки: [[MY_KEY]] - Для чисел/массивов/объектов — фигурные скобки: {{ MAX_TOKENS }}, {{ GEN_CFG }} Примеры «как надо» - Переменная-строка (mode=string): - name: AUTH - value: "Bearer [[VAR:incoming.headers.authorization]]" - Использование в заголовке: "Authorization": "[[AUTH]]" - Переменная-число (mode=expr): - name: MAX_TOKENS - value: 128 + 64 - Использование в JSON: "max_tokens": {{ MAX_TOKENS }} - Переменная-объект (mode=expr): - name: GEN_CFG - value: {"temperature": 0.3, "topP": 0.9, "safe": true} - Использование: "generationConfig": {{ GEN_CFG }} Важно про два режима - string — это «шаблон». Внутри работают все макросы ([[VAR:...]], [[OUT:...]], [[PROMPT]], {{ ... }}). Значение прогоняется через рендер [render_template_simple()](agentui/pipeline/templating.py:184). - expr — это «мини‑формула». Внутри НЕТ макросов и НЕТ доступа к контексту; только литералы и операции (см. ниже). Вычисляет значение безопасно — без eval, на белом списке AST (реализация: [SetVarsNode._safe_eval_expr()](agentui/pipeline/executor.py:291)). Что умеет expr (мини‑формулы) - Числа и арифметика: 128 + 64, (5 * 60) + 30, 42 % 2, -5, 23 // 10 - Строки: "eu" + "-central" → "eu-central" (строки склеиваем знаком +) - Булева логика: (2 < 3) and (10 % 2 == 0), 1 < 2 < 5 - Коллекции: ["fast", "safe"], {"temperature": 0.3, "topP": 0.9, "safe": true} - JSON‑литералы: true/false/null, объекты и массивы — если выражение является чистым JSON, оно разбирается напрямую (без макросов), т.е. true→True, null→None и т.п. - Запрещено: функции (кроме специально разрешённых ниже), доступ к переменным/контексту, атрибуты/индексация/условные выражения. Рандом в expr - В expr доступны три простые функции случайности: - rand() → число с плавающей точкой в диапазоне [0, 1) - randint(a, b) → целое число от a до b включительно - choice(list) → случайный элемент из списка/кортежа - Примеры: - name: RAND_F, mode: expr, value: rand() - "temperature": {{ RAND_F }} - name: DICE, mode: expr, value: randint(1, 6) - "dice_roll": {{ DICE }} - name: PICK_MODEL, mode: expr, value: choice(["gpt-4o-mini", "gpt-4o", "o3-mini"]) - "model": "[[PICK_MODEL]]" - Зерна/seed нет — каждый запуск выдаёт новое значение. «Почему в expr нельзя подставлять переменные/макросы?» - Для безопасности и предсказуемости: expr — это закрытый мини‑язык без окружения. - Если нужно использовать другие переменные/макросы — делайте это в режиме string (там всё рендерится шаблонизатором). - Технические детали: защита реализована в [SetVarsNode._safe_eval_expr()](agentui/pipeline/executor.py:291), а вставка string‑значений — через [render_template_simple()](agentui/pipeline/templating.py:184). Как это работает внутри (если интересно) - SetVars исполняется как обычная нода пайплайна и отдаёт {"vars": {...}}. - Исполнитель добавляет эти значения в контекст для последующих нод как context.vars (см. [PipelineExecutor.run()](agentui/pipeline/executor.py:131)). - При рендере шаблонов: - [[NAME]] и {{ NAME }} подставляются с приоритетом из пользовательских переменных (см. обработку в [render_template_simple()](agentui/pipeline/templating.py:184)). - Сам SetVars считает переменные в порядке списка и возвращает их одним пакетом (внутри одной ноды значения не зависят друг от друга). Частые вопросы - «Хочу собрать строку с частями из внешнего запроса»: делайте mode=string и пишите: "Bearer [[VAR:incoming.headers.authorization]]". - «Хочу массив случайных чисел»: mode=expr → [rand(), rand(), rand()], а в JSON: "numbers": {{ MY_LIST }} - «Почему мои значения не сохраняются?» — нажмите «Сохранить параметры» в инспекторе ноды, затем «Сохранить пайплайн» в шапке. UI синхронизирует данные в node.data и сохраняет в pipeline.json (см. [static/editor.html](static/editor.html)). Ссылки на реализацию (для любопытных) - Нода переменных: [SetVarsNode](agentui/pipeline/executor.py:264), [SetVarsNode._safe_eval_expr()](agentui/pipeline/executor.py:291), [SetVarsNode.run()](agentui/pipeline/executor.py:354) - Исполнитель/контекст vars: [PipelineExecutor.run()](agentui/pipeline/executor.py:131) - Шаблоны и макросы (включая «голые» [[NAME]]/{{ NAME }}): [render_template_simple()](agentui/pipeline/templating.py:184)